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
;
87 long long now
= time_msec();
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
);
95 ovs_barrier_block(&barrier
);
96 destroy_packets(pkt_batch
);
102 test_benchmark(struct ovs_cmdl_context
*ctx
)
104 struct thread_aux
*threads
;
110 /* Parse arguments */
111 n_threads
= strtoul(ctx
->argv
[1], NULL
, 0);
113 ovs_fatal(0, "n_threads must be at least one");
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)",
122 change_conn
= strtoul(ctx
->argv
[4], NULL
, 0);
125 threads
= xcalloc(n_threads
, sizeof *threads
);
126 ovs_barrier_init(&barrier
, n_threads
+ 1);
127 ct
= conntrack_init();
130 for (i
= 0; i
< n_threads
; i
++) {
132 threads
[i
].thread
= ovs_thread_create("ct_thread", ct_thread_main
,
135 /* Starts the work inside the threads */
136 ovs_barrier_block(&barrier
);
139 /* Wait for the threads to finish the work */
140 ovs_barrier_block(&barrier
);
141 printf("conntrack: %5lld ms\n", time_msec() - start
);
143 for (i
= 0; i
< n_threads
; i
++) {
144 xpthread_join(threads
[i
].thread
, NULL
);
147 conntrack_destroy(ct
);
148 ovs_barrier_destroy(&barrier
);
153 pcap_batch_execute_conntrack(struct conntrack
*ct_
,
154 struct dp_packet_batch
*pkt_batch
)
156 struct dp_packet_batch new_batch
;
157 ovs_be16 dl_type
= htons(0);
158 long long now
= time_msec();
160 dp_packet_batch_init(&new_batch
);
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
) {
168 /* This also initializes the l3 and l4 pointers. */
169 flow_extract(packet
, &flow
);
171 if (dp_packet_batch_is_empty(&new_batch
)) {
172 dl_type
= flow
.dl_type
;
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
);
178 dp_packet_batch_init(&new_batch
);
180 dp_packet_batch_add(&new_batch
, packet
);
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
);
191 test_pcap(struct ovs_cmdl_context
*ctx
)
193 size_t total_count
, batch_size_
;
194 struct pcap_file
*pcap
;
197 pcap
= ovs_pcap_open(ctx
->argv
[1], "rb");
204 batch_size_
= strtoul(ctx
->argv
[2], NULL
, 0);
205 if (batch_size_
== 0 || batch_size_
> NETDEV_MAX_BURST
) {
207 "batch_size must be between 1 and NETDEV_MAX_BURST(%u)",
214 ct
= conntrack_init();
217 struct dp_packet
*packet
;
218 struct dp_packet_batch pkt_batch_
;
219 struct dp_packet_batch
*batch
= &pkt_batch_
;
221 dp_packet_batch_init(batch
);
222 for (int i
= 0; i
< batch_size_
; i
++) {
223 err
= ovs_pcap_read(pcap
, &packet
, NULL
);
227 dp_packet_batch_add(batch
, packet
);
229 if (dp_packet_batch_is_empty(batch
)) {
232 pcap_batch_execute_conntrack(ct
, batch
);
234 DP_PACKET_BATCH_FOR_EACH (i
, packet
, batch
) {
235 struct ds ds
= DS_EMPTY_INITIALIZER
;
239 format_flags(&ds
, ct_state_to_string
, packet
->md
.ct_state
, '|');
240 printf("%"PRIuSIZE
": %s\n", total_count
, ds_cstr(&ds
));
245 dp_packet_delete_batch(batch
, true);
247 conntrack_destroy(ct
);
248 ovs_pcap_close(pcap
);
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
},
264 {NULL
, NULL
, 0, 0, NULL
, OVS_RO
},
268 test_conntrack_main(int argc
, char *argv
[])
270 struct ovs_cmdl_context ctx
= {
274 set_program_name(argv
[0]);
275 ovs_cmdl_run_command(&ctx
, commands
);
278 OVSTEST_REGISTER("test-conntrack", test_conntrack_main
);