2 * Copyright (c) 2015 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
);
44 for (i
= 0; i
< n
; i
++) {
45 struct udp_header
*udp
;
46 struct dp_packet
*pkt
= dp_packet_new(sizeof payload
/2);
48 dp_packet_put_hex(pkt
, payload
, NULL
);
49 flow_extract(pkt
, &flow
);
51 udp
= dp_packet_l4(pkt
);
52 udp
->udp_src
= htons(ntohs(udp
->udp_src
) + tid
);
55 udp
->udp_dst
= htons(ntohs(udp
->udp_dst
) + i
);
58 pkt_batch
->packets
[i
] = pkt
;
59 *dl_type
= flow
.dl_type
;
67 destroy_packets(struct dp_packet_batch
*pkt_batch
)
69 dp_packet_delete_batch(pkt_batch
, true);
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
;
84 ct_thread_main(void *aux_
)
86 struct thread_aux
*aux
= aux_
;
87 struct dp_packet_batch
*pkt_batch
;
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
);
96 ovs_barrier_block(&barrier
);
97 destroy_packets(pkt_batch
);
103 test_benchmark(struct ovs_cmdl_context
*ctx
)
105 struct thread_aux
*threads
;
111 /* Parse arguments */
112 n_threads
= strtoul(ctx
->argv
[1], NULL
, 0);
114 ovs_fatal(0, "n_threads must be at least one");
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)",
123 change_conn
= strtoul(ctx
->argv
[4], NULL
, 0);
126 threads
= xcalloc(n_threads
, sizeof *threads
);
127 ovs_barrier_init(&barrier
, n_threads
+ 1);
131 for (i
= 0; i
< n_threads
; i
++) {
133 threads
[i
].thread
= ovs_thread_create("ct_thread", ct_thread_main
,
136 /* Starts the work inside the threads */
137 ovs_barrier_block(&barrier
);
140 /* Wait for the threads to finish the work */
141 ovs_barrier_block(&barrier
);
142 printf("conntrack: %5lld ms\n", time_msec() - start
);
144 for (i
= 0; i
< n_threads
; i
++) {
145 xpthread_join(threads
[i
].thread
, NULL
);
148 conntrack_destroy(&ct
);
149 ovs_barrier_destroy(&barrier
);
154 pcap_batch_execute_conntrack(struct conntrack
*ct
,
155 struct dp_packet_batch
*pkt_batch
)
158 struct dp_packet_batch new_batch
;
159 ovs_be16 dl_type
= htons(0);
161 dp_packet_batch_init(&new_batch
);
163 /* pkt_batch contains packets with different 'dl_type'. We have to
164 * call conntrack_execute() on packets with the same 'dl_type'. */
166 for (i
= 0; i
< pkt_batch
->count
; i
++) {
167 struct dp_packet
*pkt
= pkt_batch
->packets
[i
];
170 /* This also initializes the l3 and l4 pointers. */
171 flow_extract(pkt
, &flow
);
173 if (!new_batch
.count
) {
174 dl_type
= flow
.dl_type
;
177 if (flow
.dl_type
!= dl_type
) {
178 conntrack_execute(ct
, &new_batch
, dl_type
, true, 0, NULL
, NULL
,
180 dp_packet_batch_init(&new_batch
);
182 new_batch
.packets
[new_batch
.count
++] = pkt
;
185 if (new_batch
.count
) {
186 conntrack_execute(ct
, &new_batch
, dl_type
, true, 0, NULL
, NULL
, NULL
);
192 test_pcap(struct ovs_cmdl_context
*ctx
)
194 size_t total_count
, i
, batch_size
;
198 pcap
= ovs_pcap_open(ctx
->argv
[1], "rb");
205 batch_size
= strtoul(ctx
->argv
[2], NULL
, 0);
206 if (batch_size
== 0 || batch_size
> NETDEV_MAX_BURST
) {
208 "batch_size must be between 1 and NETDEV_MAX_BURST(%u)",
218 struct dp_packet_batch pkt_batch
;
220 dp_packet_batch_init(&pkt_batch
);
222 for (i
= 0; i
< batch_size
; i
++) {
223 err
= ovs_pcap_read(pcap
, &pkt_batch
.packets
[i
], NULL
);
230 if (pkt_batch
.count
== 0) {
234 pcap_batch_execute_conntrack(&ct
, &pkt_batch
);
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
];
242 format_flags(&ds
, ct_state_to_string
, pkt
->md
.ct_state
, '|');
243 printf("%"PRIuSIZE
": %s\n", total_count
, ds_cstr(&ds
));
248 dp_packet_delete_batch(&pkt_batch
, true);
253 conntrack_destroy(&ct
);
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,
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
},
269 {NULL
, NULL
, 0, 0, NULL
},
273 test_conntrack_main(int argc
, char *argv
[])
275 struct ovs_cmdl_context ctx
= {
279 set_program_name(argv
[0]);
280 ovs_cmdl_run_command(&ctx
, commands
);
283 OVSTEST_REGISTER("test-conntrack", test_conntrack_main
);