2 * Copyright (c) 2015, 2017 Nicira, Inc.
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:
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include "conntrack.h"
20 #include "dp-packet.h"
21 #include "fatal-signal.h"
24 #include "ovs-thread.h"
26 #include "pcap-file.h"
29 static const char payload
[] = "50540000000a50540000000908004500001c0000000000"
30 "11a4cd0a0101010a0101020001000200080000";
32 static struct dp_packet_batch
*
33 prepare_packets(size_t n
, bool change
, unsigned tid
, ovs_be16
*dl_type
)
35 struct dp_packet_batch
*pkt_batch
= xzalloc(sizeof *pkt_batch
);
39 ovs_assert(n
<= ARRAY_SIZE(pkt_batch
->packets
));
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);
46 dp_packet_put_hex(pkt
, payload
, NULL
);
47 flow_extract(pkt
, &flow
);
49 udp
= dp_packet_l4(pkt
);
50 udp
->udp_src
= htons(ntohs(udp
->udp_src
) + tid
);
53 udp
->udp_dst
= htons(ntohs(udp
->udp_dst
) + i
);
56 dp_packet_batch_add(pkt_batch
, pkt
);
57 *dl_type
= flow
.dl_type
;
64 destroy_packets(struct dp_packet_batch
*pkt_batch
)
66 dp_packet_delete_batch(pkt_batch
, true);
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
;
81 ct_thread_main(void *aux_
)
83 struct thread_aux
*aux
= aux_
;
84 struct dp_packet_batch
*pkt_batch
;
85 struct dp_packet
*pkt
;
88 long long now
= time_msec();
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
);
99 ovs_barrier_block(&barrier
);
100 destroy_packets(pkt_batch
);
106 test_benchmark(struct ovs_cmdl_context
*ctx
)
108 struct thread_aux
*threads
;
114 /* Parse arguments */
115 n_threads
= strtoul(ctx
->argv
[1], NULL
, 0);
117 ovs_fatal(0, "n_threads must be at least one");
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)",
126 change_conn
= strtoul(ctx
->argv
[4], NULL
, 0);
129 threads
= xcalloc(n_threads
, sizeof *threads
);
130 ovs_barrier_init(&barrier
, n_threads
+ 1);
131 ct
= conntrack_init();
134 for (i
= 0; i
< n_threads
; i
++) {
136 threads
[i
].thread
= ovs_thread_create("ct_thread", ct_thread_main
,
139 /* Starts the work inside the threads */
140 ovs_barrier_block(&barrier
);
143 /* Wait for the threads to finish the work */
144 ovs_barrier_block(&barrier
);
145 printf("conntrack: %5lld ms\n", time_msec() - start
);
147 for (i
= 0; i
< n_threads
; i
++) {
148 xpthread_join(threads
[i
].thread
, NULL
);
151 conntrack_destroy(ct
);
152 ovs_barrier_destroy(&barrier
);
157 pcap_batch_execute_conntrack(struct conntrack
*ct_
,
158 struct dp_packet_batch
*pkt_batch
)
160 struct dp_packet_batch new_batch
;
161 ovs_be16 dl_type
= htons(0);
162 long long now
= time_msec();
164 dp_packet_batch_init(&new_batch
);
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
) {
172 /* This also initializes the l3 and l4 pointers. */
173 flow_extract(packet
, &flow
);
175 if (dp_packet_batch_is_empty(&new_batch
)) {
176 dl_type
= flow
.dl_type
;
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
);
184 dp_packet_batch_add(&new_batch
, packet
);
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);
195 test_pcap(struct ovs_cmdl_context
*ctx
)
197 size_t total_count
, batch_size_
;
198 struct pcap_file
*pcap
;
201 pcap
= ovs_pcap_open(ctx
->argv
[1], "rb");
208 batch_size_
= strtoul(ctx
->argv
[2], NULL
, 0);
209 if (batch_size_
== 0 || batch_size_
> NETDEV_MAX_BURST
) {
211 "batch_size must be between 1 and NETDEV_MAX_BURST(%u)",
218 ct
= conntrack_init();
221 struct dp_packet
*packet
;
222 struct dp_packet_batch pkt_batch_
;
223 struct dp_packet_batch
*batch
= &pkt_batch_
;
225 dp_packet_batch_init(batch
);
226 for (int i
= 0; i
< batch_size_
; i
++) {
227 err
= ovs_pcap_read(pcap
, &packet
, NULL
);
231 dp_packet_batch_add(batch
, packet
);
233 if (dp_packet_batch_is_empty(batch
)) {
236 pcap_batch_execute_conntrack(ct
, batch
);
238 DP_PACKET_BATCH_FOR_EACH (i
, packet
, batch
) {
239 struct ds ds
= DS_EMPTY_INITIALIZER
;
243 format_flags(&ds
, ct_state_to_string
, packet
->md
.ct_state
, '|');
244 printf("%"PRIuSIZE
": %s\n", total_count
, ds_cstr(&ds
));
249 dp_packet_delete_batch(batch
, true);
251 conntrack_destroy(ct
);
252 ovs_pcap_close(pcap
);
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
},
268 {NULL
, NULL
, 0, 0, NULL
, OVS_RO
},
272 test_conntrack_main(int argc
, char *argv
[])
274 struct ovs_cmdl_context ctx
= {
278 set_program_name(argv
[0]);
279 ovs_cmdl_run_command(&ctx
, commands
);
282 OVSTEST_REGISTER("test-conntrack", test_conntrack_main
);