]> git.proxmox.com Git - mirror_ovs.git/blob - tests/test-conntrack.c
oss-fuzz: Fix miniflow_target.c.
[mirror_ovs.git] / tests / test-conntrack.c
1 /*
2 * Copyright (c) 2015, 2017 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 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
56 dp_packet_batch_add(pkt_batch, pkt);
57 *dl_type = flow.dl_type;
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
75 static struct conntrack *ct;
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;
85 ovs_be16 dl_type;
86 size_t i;
87 long long now = time_msec();
88
89 pkt_batch = prepare_packets(batch_size, change_conn, aux->tid, &dl_type);
90 ovs_barrier_block(&barrier);
91 for (i = 0; i < n_pkts; i += batch_size) {
92 conntrack_execute(ct, pkt_batch, dl_type, false, true, 0, NULL, NULL,
93 0, 0, NULL, NULL, now, 0);
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);
127 ct = conntrack_init();
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
147 conntrack_destroy(ct);
148 ovs_barrier_destroy(&barrier);
149 free(threads);
150 }
151
152 static void
153 pcap_batch_execute_conntrack(struct conntrack *ct_,
154 struct dp_packet_batch *pkt_batch)
155 {
156 struct dp_packet_batch new_batch;
157 ovs_be16 dl_type = htons(0);
158 long long now = time_msec();
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'. */
164 struct dp_packet *packet;
165 DP_PACKET_BATCH_FOR_EACH (i, packet, pkt_batch) {
166 struct flow flow;
167
168 /* This also initializes the l3 and l4 pointers. */
169 flow_extract(packet, &flow);
170
171 if (dp_packet_batch_is_empty(&new_batch)) {
172 dl_type = flow.dl_type;
173 }
174
175 if (flow.dl_type != dl_type) {
176 conntrack_execute(ct_, &new_batch, dl_type, false, true, 0,
177 NULL, NULL, 0, 0, NULL, NULL, now, 0);
178 dp_packet_batch_init(&new_batch);
179 }
180 dp_packet_batch_add(&new_batch, packet);
181 }
182
183 if (!dp_packet_batch_is_empty(&new_batch)) {
184 conntrack_execute(ct_, &new_batch, dl_type, false, true, 0, NULL, NULL,
185 0, 0, NULL, NULL, now, 0);
186 }
187
188 }
189
190 static void
191 test_pcap(struct ovs_cmdl_context *ctx)
192 {
193 size_t total_count, batch_size_;
194 struct pcap_file *pcap;
195 int err = 0;
196
197 pcap = ovs_pcap_open(ctx->argv[1], "rb");
198 if (!pcap) {
199 return;
200 }
201
202 batch_size_ = 1;
203 if (ctx->argc > 2) {
204 batch_size_ = strtoul(ctx->argv[2], NULL, 0);
205 if (batch_size_ == 0 || batch_size_ > NETDEV_MAX_BURST) {
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
214 ct = conntrack_init();
215 total_count = 0;
216 for (;;) {
217 struct dp_packet *packet;
218 struct dp_packet_batch pkt_batch_;
219 struct dp_packet_batch *batch = &pkt_batch_;
220
221 dp_packet_batch_init(batch);
222 for (int i = 0; i < batch_size_; i++) {
223 err = ovs_pcap_read(pcap, &packet, NULL);
224 if (err) {
225 break;
226 }
227 dp_packet_batch_add(batch, packet);
228 }
229 if (dp_packet_batch_is_empty(batch)) {
230 break;
231 }
232 pcap_batch_execute_conntrack(ct, batch);
233
234 DP_PACKET_BATCH_FOR_EACH (i, packet, batch) {
235 struct ds ds = DS_EMPTY_INITIALIZER;
236
237 total_count++;
238
239 format_flags(&ds, ct_state_to_string, packet->md.ct_state, '|');
240 printf("%"PRIuSIZE": %s\n", total_count, ds_cstr(&ds));
241
242 ds_destroy(&ds);
243 }
244
245 dp_packet_delete_batch(batch, true);
246 }
247 conntrack_destroy(ct);
248 ovs_pcap_close(pcap);
249 }
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,
258 test_benchmark, OVS_RO},
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. */
262 {"pcap", "file [batch_size]", 1, 2, test_pcap, OVS_RO},
263
264 {NULL, NULL, 0, 0, NULL, OVS_RO},
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);