]> git.proxmox.com Git - mirror_ovs.git/blob - tests/test-conntrack.c
conntrack: Rename "master" connection to "parent" connection.
[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 struct dp_packet *pkt;
86 ovs_be16 dl_type;
87 size_t i;
88 long long now = time_msec();
89
90 pkt_batch = prepare_packets(batch_size, change_conn, aux->tid, &dl_type);
91 ovs_barrier_block(&barrier);
92 for (i = 0; i < n_pkts; i += batch_size) {
93 conntrack_execute(ct, pkt_batch, dl_type, false, true, 0, NULL, NULL,
94 0, 0, NULL, NULL, now, 0);
95 DP_PACKET_BATCH_FOR_EACH (j, pkt, pkt_batch) {
96 pkt_metadata_init_conn(&pkt->md);
97 }
98 }
99 ovs_barrier_block(&barrier);
100 destroy_packets(pkt_batch);
101
102 return NULL;
103 }
104
105 static void
106 test_benchmark(struct ovs_cmdl_context *ctx)
107 {
108 struct thread_aux *threads;
109 long long start;
110 unsigned i;
111
112 fatal_signal_init();
113
114 /* Parse arguments */
115 n_threads = strtoul(ctx->argv[1], NULL, 0);
116 if (!n_threads) {
117 ovs_fatal(0, "n_threads must be at least one");
118 }
119 n_pkts = strtoul(ctx->argv[2], NULL, 0);
120 batch_size = strtoul(ctx->argv[3], NULL, 0);
121 if (batch_size == 0 || batch_size > NETDEV_MAX_BURST) {
122 ovs_fatal(0, "batch_size must be between 1 and NETDEV_MAX_BURST(%u)",
123 NETDEV_MAX_BURST);
124 }
125 if (ctx->argc > 4) {
126 change_conn = strtoul(ctx->argv[4], NULL, 0);
127 }
128
129 threads = xcalloc(n_threads, sizeof *threads);
130 ovs_barrier_init(&barrier, n_threads + 1);
131 ct = conntrack_init();
132
133 /* Create threads */
134 for (i = 0; i < n_threads; i++) {
135 threads[i].tid = i;
136 threads[i].thread = ovs_thread_create("ct_thread", ct_thread_main,
137 &threads[i]);
138 }
139 /* Starts the work inside the threads */
140 ovs_barrier_block(&barrier);
141 start = time_msec();
142
143 /* Wait for the threads to finish the work */
144 ovs_barrier_block(&barrier);
145 printf("conntrack: %5lld ms\n", time_msec() - start);
146
147 for (i = 0; i < n_threads; i++) {
148 xpthread_join(threads[i].thread, NULL);
149 }
150
151 conntrack_destroy(ct);
152 ovs_barrier_destroy(&barrier);
153 free(threads);
154 }
155
156 static void
157 pcap_batch_execute_conntrack(struct conntrack *ct_,
158 struct dp_packet_batch *pkt_batch)
159 {
160 struct dp_packet_batch new_batch;
161 ovs_be16 dl_type = htons(0);
162 long long now = time_msec();
163
164 dp_packet_batch_init(&new_batch);
165
166 /* pkt_batch contains packets with different 'dl_type'. We have to
167 * call conntrack_execute() on packets with the same 'dl_type'. */
168 struct dp_packet *packet;
169 DP_PACKET_BATCH_FOR_EACH (i, packet, pkt_batch) {
170 struct flow flow;
171
172 /* This also initializes the l3 and l4 pointers. */
173 flow_extract(packet, &flow);
174
175 if (dp_packet_batch_is_empty(&new_batch)) {
176 dl_type = flow.dl_type;
177 }
178
179 if (flow.dl_type != dl_type) {
180 conntrack_execute(ct_, &new_batch, dl_type, false, true, 0,
181 NULL, NULL, 0, 0, NULL, NULL, now, 0);
182 dp_packet_batch_init(&new_batch);
183 }
184 dp_packet_batch_add(&new_batch, packet);
185 }
186
187 if (!dp_packet_batch_is_empty(&new_batch)) {
188 conntrack_execute(ct_, &new_batch, dl_type, false, true, 0, NULL, NULL,
189 0, 0, NULL, NULL, now, 0);
190 }
191
192 }
193
194 static void
195 test_pcap(struct ovs_cmdl_context *ctx)
196 {
197 size_t total_count, batch_size_;
198 struct pcap_file *pcap;
199 int err = 0;
200
201 pcap = ovs_pcap_open(ctx->argv[1], "rb");
202 if (!pcap) {
203 return;
204 }
205
206 batch_size_ = 1;
207 if (ctx->argc > 2) {
208 batch_size_ = strtoul(ctx->argv[2], NULL, 0);
209 if (batch_size_ == 0 || batch_size_ > NETDEV_MAX_BURST) {
210 ovs_fatal(0,
211 "batch_size must be between 1 and NETDEV_MAX_BURST(%u)",
212 NETDEV_MAX_BURST);
213 }
214 }
215
216 fatal_signal_init();
217
218 ct = conntrack_init();
219 total_count = 0;
220 for (;;) {
221 struct dp_packet *packet;
222 struct dp_packet_batch pkt_batch_;
223 struct dp_packet_batch *batch = &pkt_batch_;
224
225 dp_packet_batch_init(batch);
226 for (int i = 0; i < batch_size_; i++) {
227 err = ovs_pcap_read(pcap, &packet, NULL);
228 if (err) {
229 break;
230 }
231 dp_packet_batch_add(batch, packet);
232 }
233 if (dp_packet_batch_is_empty(batch)) {
234 break;
235 }
236 pcap_batch_execute_conntrack(ct, batch);
237
238 DP_PACKET_BATCH_FOR_EACH (i, packet, batch) {
239 struct ds ds = DS_EMPTY_INITIALIZER;
240
241 total_count++;
242
243 format_flags(&ds, ct_state_to_string, packet->md.ct_state, '|');
244 printf("%"PRIuSIZE": %s\n", total_count, ds_cstr(&ds));
245
246 ds_destroy(&ds);
247 }
248
249 dp_packet_delete_batch(batch, true);
250 }
251 conntrack_destroy(ct);
252 ovs_pcap_close(pcap);
253 }
254 \f
255 static const struct ovs_cmdl_command commands[] = {
256 /* Connection tracker tests. */
257 /* Starts 'n_threads' threads. Each thread will send 'n_pkts' packets to
258 * the connection tracker, 'batch_size' per call. If 'change_connection'
259 * is '1', each packet in a batch will have a different source and
260 * destination port */
261 {"benchmark", "n_threads n_pkts batch_size [change_connection]", 3, 4,
262 test_benchmark, OVS_RO},
263 /* Reads packets from 'file' and sends them to the connection tracker,
264 * 'batch_size' (1 by default) per call, with the commit flag set.
265 * Prints the ct_state of each packet. */
266 {"pcap", "file [batch_size]", 1, 2, test_pcap, OVS_RO},
267
268 {NULL, NULL, 0, 0, NULL, OVS_RO},
269 };
270
271 static void
272 test_conntrack_main(int argc, char *argv[])
273 {
274 struct ovs_cmdl_context ctx = {
275 .argc = argc - 1,
276 .argv = argv + 1,
277 };
278 set_program_name(argv[0]);
279 ovs_cmdl_run_command(&ctx, commands);
280 }
281
282 OVSTEST_REGISTER("test-conntrack", test_conntrack_main);