]>
Commit | Line | Data |
---|---|---|
8cb14626 | 1 | /* |
7a36af87 | 2 | * Copyright (c) 2015, 2017 Nicira, Inc. |
8cb14626 DDP |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at: | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | ||
17 | #include <config.h> | |
18 | #include "conntrack.h" | |
19 | ||
20 | #include "dp-packet.h" | |
21 | #include "fatal-signal.h" | |
22 | #include "flow.h" | |
23 | #include "netdev.h" | |
24 | #include "ovs-thread.h" | |
25 | #include "ovstest.h" | |
b4124903 | 26 | #include "pcap-file.h" |
8cb14626 DDP |
27 | #include "timeval.h" |
28 | ||
29 | static const char payload[] = "50540000000a50540000000908004500001c0000000000" | |
30 | "11a4cd0a0101010a0101020001000200080000"; | |
31 | ||
32 | static struct dp_packet_batch * | |
66e4ad8a | 33 | prepare_packets(size_t n, bool change, unsigned tid, ovs_be16 *dl_type) |
8cb14626 DDP |
34 | { |
35 | struct dp_packet_batch *pkt_batch = xzalloc(sizeof *pkt_batch); | |
36 | struct flow flow; | |
37 | size_t i; | |
38 | ||
39 | ovs_assert(n <= ARRAY_SIZE(pkt_batch->packets)); | |
40 | ||
41 | dp_packet_batch_init(pkt_batch); | |
8cb14626 DDP |
42 | for (i = 0; i < n; i++) { |
43 | struct udp_header *udp; | |
44 | struct dp_packet *pkt = dp_packet_new(sizeof payload/2); | |
45 | ||
46 | dp_packet_put_hex(pkt, payload, NULL); | |
47 | flow_extract(pkt, &flow); | |
48 | ||
49 | udp = dp_packet_l4(pkt); | |
50 | udp->udp_src = htons(ntohs(udp->udp_src) + tid); | |
51 | ||
52 | if (change) { | |
53 | udp->udp_dst = htons(ntohs(udp->udp_dst) + i); | |
54 | } | |
55 | ||
72c84bc2 | 56 | dp_packet_batch_add(pkt_batch, pkt); |
66e4ad8a | 57 | *dl_type = flow.dl_type; |
8cb14626 DDP |
58 | } |
59 | ||
60 | return pkt_batch; | |
61 | } | |
62 | ||
63 | static void | |
64 | destroy_packets(struct dp_packet_batch *pkt_batch) | |
65 | { | |
66 | dp_packet_delete_batch(pkt_batch, true); | |
67 | free(pkt_batch); | |
68 | } | |
69 | ||
70 | struct thread_aux { | |
71 | pthread_t thread; | |
72 | unsigned tid; | |
73 | }; | |
74 | ||
57593fd2 | 75 | static struct conntrack *ct; |
8cb14626 DDP |
76 | static unsigned long n_threads, n_pkts, batch_size; |
77 | static bool change_conn = false; | |
78 | static struct ovs_barrier barrier; | |
79 | ||
80 | static void * | |
81 | ct_thread_main(void *aux_) | |
82 | { | |
83 | struct thread_aux *aux = aux_; | |
84 | struct dp_packet_batch *pkt_batch; | |
66e4ad8a | 85 | ovs_be16 dl_type; |
8cb14626 | 86 | size_t i; |
94053e66 | 87 | long long now = time_msec(); |
8cb14626 | 88 | |
66e4ad8a | 89 | pkt_batch = prepare_packets(batch_size, change_conn, aux->tid, &dl_type); |
8cb14626 DDP |
90 | ovs_barrier_block(&barrier); |
91 | for (i = 0; i < n_pkts; i += batch_size) { | |
57593fd2 | 92 | conntrack_execute(ct, pkt_batch, dl_type, false, true, 0, NULL, NULL, |
bd7d93f8 | 93 | 0, 0, NULL, NULL, now); |
8cb14626 DDP |
94 | } |
95 | ovs_barrier_block(&barrier); | |
96 | destroy_packets(pkt_batch); | |
97 | ||
98 | return NULL; | |
99 | } | |
100 | ||
101 | static void | |
102 | test_benchmark(struct ovs_cmdl_context *ctx) | |
103 | { | |
104 | struct thread_aux *threads; | |
105 | long long start; | |
106 | unsigned i; | |
107 | ||
108 | fatal_signal_init(); | |
109 | ||
110 | /* Parse arguments */ | |
111 | n_threads = strtoul(ctx->argv[1], NULL, 0); | |
112 | if (!n_threads) { | |
113 | ovs_fatal(0, "n_threads must be at least one"); | |
114 | } | |
115 | n_pkts = strtoul(ctx->argv[2], NULL, 0); | |
116 | batch_size = strtoul(ctx->argv[3], NULL, 0); | |
117 | if (batch_size == 0 || batch_size > NETDEV_MAX_BURST) { | |
118 | ovs_fatal(0, "batch_size must be between 1 and NETDEV_MAX_BURST(%u)", | |
119 | NETDEV_MAX_BURST); | |
120 | } | |
121 | if (ctx->argc > 4) { | |
122 | change_conn = strtoul(ctx->argv[4], NULL, 0); | |
123 | } | |
124 | ||
125 | threads = xcalloc(n_threads, sizeof *threads); | |
126 | ovs_barrier_init(&barrier, n_threads + 1); | |
57593fd2 | 127 | ct = conntrack_init(); |
8cb14626 DDP |
128 | |
129 | /* Create threads */ | |
130 | for (i = 0; i < n_threads; i++) { | |
131 | threads[i].tid = i; | |
132 | threads[i].thread = ovs_thread_create("ct_thread", ct_thread_main, | |
133 | &threads[i]); | |
134 | } | |
135 | /* Starts the work inside the threads */ | |
136 | ovs_barrier_block(&barrier); | |
137 | start = time_msec(); | |
138 | ||
139 | /* Wait for the threads to finish the work */ | |
140 | ovs_barrier_block(&barrier); | |
141 | printf("conntrack: %5lld ms\n", time_msec() - start); | |
142 | ||
143 | for (i = 0; i < n_threads; i++) { | |
144 | xpthread_join(threads[i].thread, NULL); | |
145 | } | |
146 | ||
57593fd2 | 147 | conntrack_destroy(ct); |
8cb14626 DDP |
148 | ovs_barrier_destroy(&barrier); |
149 | free(threads); | |
150 | } | |
b4124903 | 151 | |
66e4ad8a | 152 | static void |
396d492c | 153 | pcap_batch_execute_conntrack(struct conntrack *ct_, |
66e4ad8a DDP |
154 | struct dp_packet_batch *pkt_batch) |
155 | { | |
66e4ad8a DDP |
156 | struct dp_packet_batch new_batch; |
157 | ovs_be16 dl_type = htons(0); | |
94053e66 | 158 | long long now = time_msec(); |
66e4ad8a DDP |
159 | |
160 | dp_packet_batch_init(&new_batch); | |
161 | ||
162 | /* pkt_batch contains packets with different 'dl_type'. We have to | |
163 | * call conntrack_execute() on packets with the same 'dl_type'. */ | |
72c84bc2 | 164 | struct dp_packet *packet; |
e883448e | 165 | DP_PACKET_BATCH_FOR_EACH (i, packet, pkt_batch) { |
66e4ad8a DDP |
166 | struct flow flow; |
167 | ||
168 | /* This also initializes the l3 and l4 pointers. */ | |
72c84bc2 | 169 | flow_extract(packet, &flow); |
66e4ad8a | 170 | |
72c84bc2 | 171 | if (dp_packet_batch_is_empty(&new_batch)) { |
66e4ad8a DDP |
172 | dl_type = flow.dl_type; |
173 | } | |
174 | ||
175 | if (flow.dl_type != dl_type) { | |
396d492c | 176 | conntrack_execute(ct_, &new_batch, dl_type, false, true, 0, |
bd7d93f8 | 177 | NULL, NULL, 0, 0, NULL, NULL, now); |
66e4ad8a DDP |
178 | dp_packet_batch_init(&new_batch); |
179 | } | |
1270b6e5 | 180 | dp_packet_batch_add(&new_batch, packet); |
66e4ad8a DDP |
181 | } |
182 | ||
72c84bc2 | 183 | if (!dp_packet_batch_is_empty(&new_batch)) { |
396d492c | 184 | conntrack_execute(ct_, &new_batch, dl_type, false, true, 0, NULL, NULL, |
bd7d93f8 | 185 | 0, 0, NULL, NULL, now); |
66e4ad8a DDP |
186 | } |
187 | ||
188 | } | |
189 | ||
b4124903 DDP |
190 | static void |
191 | test_pcap(struct ovs_cmdl_context *ctx) | |
192 | { | |
396d492c | 193 | size_t total_count, batch_size_; |
b6e840ae | 194 | struct pcap_file *pcap; |
72c84bc2 | 195 | int err = 0; |
b4124903 DDP |
196 | |
197 | pcap = ovs_pcap_open(ctx->argv[1], "rb"); | |
198 | if (!pcap) { | |
199 | return; | |
200 | } | |
201 | ||
396d492c | 202 | batch_size_ = 1; |
b4124903 | 203 | if (ctx->argc > 2) { |
396d492c JP |
204 | batch_size_ = strtoul(ctx->argv[2], NULL, 0); |
205 | if (batch_size_ == 0 || batch_size_ > NETDEV_MAX_BURST) { | |
b4124903 DDP |
206 | ovs_fatal(0, |
207 | "batch_size must be between 1 and NETDEV_MAX_BURST(%u)", | |
208 | NETDEV_MAX_BURST); | |
209 | } | |
210 | } | |
211 | ||
212 | fatal_signal_init(); | |
213 | ||
57593fd2 | 214 | ct = conntrack_init(); |
b4124903 | 215 | total_count = 0; |
5b4db7b5 | 216 | for (;;) { |
72c84bc2 AZ |
217 | struct dp_packet *packet; |
218 | struct dp_packet_batch pkt_batch_; | |
219 | struct dp_packet_batch *batch = &pkt_batch_; | |
b4124903 | 220 | |
72c84bc2 | 221 | dp_packet_batch_init(batch); |
396d492c | 222 | for (int i = 0; i < batch_size_; i++) { |
5b4db7b5 BP |
223 | err = ovs_pcap_read(pcap, &packet, NULL); |
224 | if (err) { | |
225 | break; | |
226 | } | |
227 | dp_packet_batch_add(batch, packet); | |
228 | } | |
1270b6e5 | 229 | if (dp_packet_batch_is_empty(batch)) { |
b4124903 DDP |
230 | break; |
231 | } | |
57593fd2 | 232 | pcap_batch_execute_conntrack(ct, batch); |
b4124903 | 233 | |
e883448e | 234 | DP_PACKET_BATCH_FOR_EACH (i, packet, batch) { |
b4124903 | 235 | struct ds ds = DS_EMPTY_INITIALIZER; |
b4124903 DDP |
236 | |
237 | total_count++; | |
238 | ||
72c84bc2 | 239 | format_flags(&ds, ct_state_to_string, packet->md.ct_state, '|'); |
b4124903 DDP |
240 | printf("%"PRIuSIZE": %s\n", total_count, ds_cstr(&ds)); |
241 | ||
242 | ds_destroy(&ds); | |
243 | } | |
244 | ||
72c84bc2 | 245 | dp_packet_delete_batch(batch, true); |
b4124903 | 246 | } |
57593fd2 | 247 | conntrack_destroy(ct); |
b6e840ae | 248 | ovs_pcap_close(pcap); |
b4124903 | 249 | } |
8cb14626 DDP |
250 | \f |
251 | static const struct ovs_cmdl_command commands[] = { | |
252 | /* Connection tracker tests. */ | |
253 | /* Starts 'n_threads' threads. Each thread will send 'n_pkts' packets to | |
254 | * the connection tracker, 'batch_size' per call. If 'change_connection' | |
255 | * is '1', each packet in a batch will have a different source and | |
256 | * destination port */ | |
257 | {"benchmark", "n_threads n_pkts batch_size [change_connection]", 3, 4, | |
1f4a7252 | 258 | test_benchmark, OVS_RO}, |
b4124903 DDP |
259 | /* Reads packets from 'file' and sends them to the connection tracker, |
260 | * 'batch_size' (1 by default) per call, with the commit flag set. | |
261 | * Prints the ct_state of each packet. */ | |
1f4a7252 | 262 | {"pcap", "file [batch_size]", 1, 2, test_pcap, OVS_RO}, |
8cb14626 | 263 | |
1f4a7252 | 264 | {NULL, NULL, 0, 0, NULL, OVS_RO}, |
8cb14626 DDP |
265 | }; |
266 | ||
267 | static void | |
268 | test_conntrack_main(int argc, char *argv[]) | |
269 | { | |
270 | struct ovs_cmdl_context ctx = { | |
271 | .argc = argc - 1, | |
272 | .argv = argv + 1, | |
273 | }; | |
274 | set_program_name(argv[0]); | |
275 | ovs_cmdl_run_command(&ctx, commands); | |
276 | } | |
277 | ||
278 | OVSTEST_REGISTER("test-conntrack", test_conntrack_main); |