]> git.proxmox.com Git - mirror_ovs.git/blob - tests/test-conntrack.c
ovn: specify addresses of type "router" lsps as "router"
[mirror_ovs.git] / tests / test-conntrack.c
1 /*
2 * Copyright (c) 2015 Nicira, Inc.
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"
26 #include "pcap-file.h"
27 #include "timeval.h"
28
29 static const char payload[] = "50540000000a50540000000908004500001c0000000000"
30 "11a4cd0a0101010a0101020001000200080000";
31
32 static struct dp_packet_batch *
33 prepare_packets(size_t n, bool change, unsigned tid, ovs_be16 *dl_type)
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);
42 pkt_batch->count = n;
43
44 for (i = 0; i < n; i++) {
45 struct udp_header *udp;
46 struct dp_packet *pkt = dp_packet_new(sizeof payload/2);
47
48 dp_packet_put_hex(pkt, payload, NULL);
49 flow_extract(pkt, &flow);
50
51 udp = dp_packet_l4(pkt);
52 udp->udp_src = htons(ntohs(udp->udp_src) + tid);
53
54 if (change) {
55 udp->udp_dst = htons(ntohs(udp->udp_dst) + i);
56 }
57
58 pkt_batch->packets[i] = pkt;
59 *dl_type = flow.dl_type;
60 }
61
62
63 return pkt_batch;
64 }
65
66 static void
67 destroy_packets(struct dp_packet_batch *pkt_batch)
68 {
69 dp_packet_delete_batch(pkt_batch, true);
70 free(pkt_batch);
71 }
72
73 struct thread_aux {
74 pthread_t thread;
75 unsigned tid;
76 };
77
78 static struct conntrack ct;
79 static unsigned long n_threads, n_pkts, batch_size;
80 static bool change_conn = false;
81 static struct ovs_barrier barrier;
82
83 static void *
84 ct_thread_main(void *aux_)
85 {
86 struct thread_aux *aux = aux_;
87 struct dp_packet_batch *pkt_batch;
88 ovs_be16 dl_type;
89 size_t i;
90
91 pkt_batch = prepare_packets(batch_size, change_conn, aux->tid, &dl_type);
92 ovs_barrier_block(&barrier);
93 for (i = 0; i < n_pkts; i += batch_size) {
94 conntrack_execute(&ct, pkt_batch, dl_type, true, 0, NULL, NULL, NULL);
95 }
96 ovs_barrier_block(&barrier);
97 destroy_packets(pkt_batch);
98
99 return NULL;
100 }
101
102 static void
103 test_benchmark(struct ovs_cmdl_context *ctx)
104 {
105 struct thread_aux *threads;
106 long long start;
107 unsigned i;
108
109 fatal_signal_init();
110
111 /* Parse arguments */
112 n_threads = strtoul(ctx->argv[1], NULL, 0);
113 if (!n_threads) {
114 ovs_fatal(0, "n_threads must be at least one");
115 }
116 n_pkts = strtoul(ctx->argv[2], NULL, 0);
117 batch_size = strtoul(ctx->argv[3], NULL, 0);
118 if (batch_size == 0 || batch_size > NETDEV_MAX_BURST) {
119 ovs_fatal(0, "batch_size must be between 1 and NETDEV_MAX_BURST(%u)",
120 NETDEV_MAX_BURST);
121 }
122 if (ctx->argc > 4) {
123 change_conn = strtoul(ctx->argv[4], NULL, 0);
124 }
125
126 threads = xcalloc(n_threads, sizeof *threads);
127 ovs_barrier_init(&barrier, n_threads + 1);
128 conntrack_init(&ct);
129
130 /* Create threads */
131 for (i = 0; i < n_threads; i++) {
132 threads[i].tid = i;
133 threads[i].thread = ovs_thread_create("ct_thread", ct_thread_main,
134 &threads[i]);
135 }
136 /* Starts the work inside the threads */
137 ovs_barrier_block(&barrier);
138 start = time_msec();
139
140 /* Wait for the threads to finish the work */
141 ovs_barrier_block(&barrier);
142 printf("conntrack: %5lld ms\n", time_msec() - start);
143
144 for (i = 0; i < n_threads; i++) {
145 xpthread_join(threads[i].thread, NULL);
146 }
147
148 conntrack_destroy(&ct);
149 ovs_barrier_destroy(&barrier);
150 free(threads);
151 }
152
153 static void
154 pcap_batch_execute_conntrack(struct conntrack *ct,
155 struct dp_packet_batch *pkt_batch)
156 {
157 size_t i;
158 struct dp_packet_batch new_batch;
159 ovs_be16 dl_type = htons(0);
160
161 dp_packet_batch_init(&new_batch);
162
163 /* pkt_batch contains packets with different 'dl_type'. We have to
164 * call conntrack_execute() on packets with the same 'dl_type'. */
165
166 for (i = 0; i < pkt_batch->count; i++) {
167 struct dp_packet *pkt = pkt_batch->packets[i];
168 struct flow flow;
169
170 /* This also initializes the l3 and l4 pointers. */
171 flow_extract(pkt, &flow);
172
173 if (!new_batch.count) {
174 dl_type = flow.dl_type;
175 }
176
177 if (flow.dl_type != dl_type) {
178 conntrack_execute(ct, &new_batch, dl_type, true, 0, NULL, NULL,
179 NULL);
180 dp_packet_batch_init(&new_batch);
181 }
182 new_batch.packets[new_batch.count++] = pkt;
183 }
184
185 if (new_batch.count) {
186 conntrack_execute(ct, &new_batch, dl_type, true, 0, NULL, NULL, NULL);
187 }
188
189 }
190
191 static void
192 test_pcap(struct ovs_cmdl_context *ctx)
193 {
194 size_t total_count, i, batch_size;
195 FILE *pcap;
196 int err;
197
198 pcap = ovs_pcap_open(ctx->argv[1], "rb");
199 if (!pcap) {
200 return;
201 }
202
203 batch_size = 1;
204 if (ctx->argc > 2) {
205 batch_size = strtoul(ctx->argv[2], NULL, 0);
206 if (batch_size == 0 || batch_size > NETDEV_MAX_BURST) {
207 ovs_fatal(0,
208 "batch_size must be between 1 and NETDEV_MAX_BURST(%u)",
209 NETDEV_MAX_BURST);
210 }
211 }
212
213 fatal_signal_init();
214
215 conntrack_init(&ct);
216 total_count = 0;
217 for (;;) {
218 struct dp_packet_batch pkt_batch;
219
220 dp_packet_batch_init(&pkt_batch);
221
222 for (i = 0; i < batch_size; i++) {
223 err = ovs_pcap_read(pcap, &pkt_batch.packets[i], NULL);
224 if (err) {
225 break;
226 }
227 }
228
229 pkt_batch.count = i;
230 if (pkt_batch.count == 0) {
231 break;
232 }
233
234 pcap_batch_execute_conntrack(&ct, &pkt_batch);
235
236 for (i = 0; i < pkt_batch.count; i++) {
237 struct ds ds = DS_EMPTY_INITIALIZER;
238 struct dp_packet *pkt = pkt_batch.packets[i];
239
240 total_count++;
241
242 format_flags(&ds, ct_state_to_string, pkt->md.ct_state, '|');
243 printf("%"PRIuSIZE": %s\n", total_count, ds_cstr(&ds));
244
245 ds_destroy(&ds);
246 }
247
248 dp_packet_delete_batch(&pkt_batch, true);
249 if (err) {
250 break;
251 }
252 }
253 conntrack_destroy(&ct);
254 }
255 \f
256 static const struct ovs_cmdl_command commands[] = {
257 /* Connection tracker tests. */
258 /* Starts 'n_threads' threads. Each thread will send 'n_pkts' packets to
259 * the connection tracker, 'batch_size' per call. If 'change_connection'
260 * is '1', each packet in a batch will have a different source and
261 * destination port */
262 {"benchmark", "n_threads n_pkts batch_size [change_connection]", 3, 4,
263 test_benchmark, OVS_RO},
264 /* Reads packets from 'file' and sends them to the connection tracker,
265 * 'batch_size' (1 by default) per call, with the commit flag set.
266 * Prints the ct_state of each packet. */
267 {"pcap", "file [batch_size]", 1, 2, test_pcap, OVS_RO},
268
269 {NULL, NULL, 0, 0, NULL, OVS_RO},
270 };
271
272 static void
273 test_conntrack_main(int argc, char *argv[])
274 {
275 struct ovs_cmdl_context ctx = {
276 .argc = argc - 1,
277 .argv = argv + 1,
278 };
279 set_program_name(argv[0]);
280 ovs_cmdl_run_command(&ctx, commands);
281 }
282
283 OVSTEST_REGISTER("test-conntrack", test_conntrack_main);