Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2015 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 : 0 : prepare_packets(size_t n, bool change, unsigned tid, ovs_be16 *dl_type)
34 : : {
35 : 0 : struct dp_packet_batch *pkt_batch = xzalloc(sizeof *pkt_batch);
36 : : struct flow flow;
37 : : size_t i;
38 : :
39 [ # # ]: 0 : ovs_assert(n <= ARRAY_SIZE(pkt_batch->packets));
40 : :
41 : 0 : dp_packet_batch_init(pkt_batch);
42 : 0 : pkt_batch->count = n;
43 : :
44 [ # # ]: 0 : for (i = 0; i < n; i++) {
45 : : struct udp_header *udp;
46 : 0 : struct dp_packet *pkt = dp_packet_new(sizeof payload/2);
47 : :
48 : 0 : dp_packet_put_hex(pkt, payload, NULL);
49 : 0 : flow_extract(pkt, &flow);
50 : :
51 : 0 : udp = dp_packet_l4(pkt);
52 : 0 : udp->udp_src = htons(ntohs(udp->udp_src) + tid);
53 : :
54 [ # # ]: 0 : if (change) {
55 : 0 : udp->udp_dst = htons(ntohs(udp->udp_dst) + i);
56 : : }
57 : :
58 : 0 : pkt_batch->packets[i] = pkt;
59 : 0 : *dl_type = flow.dl_type;
60 : : }
61 : :
62 : :
63 : 0 : return pkt_batch;
64 : : }
65 : :
66 : : static void
67 : 0 : destroy_packets(struct dp_packet_batch *pkt_batch)
68 : : {
69 : 0 : dp_packet_delete_batch(pkt_batch, true);
70 : 0 : free(pkt_batch);
71 : 0 : }
72 : :
73 : : struct thread_aux {
74 : : pthread_t thread;
75 : : unsigned tid;
76 : : };
77 : :
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;
82 : :
83 : : static void *
84 : 0 : ct_thread_main(void *aux_)
85 : : {
86 : 0 : struct thread_aux *aux = aux_;
87 : : struct dp_packet_batch *pkt_batch;
88 : : ovs_be16 dl_type;
89 : : size_t i;
90 : :
91 : 0 : pkt_batch = prepare_packets(batch_size, change_conn, aux->tid, &dl_type);
92 : 0 : ovs_barrier_block(&barrier);
93 [ # # ]: 0 : for (i = 0; i < n_pkts; i += batch_size) {
94 : 0 : conntrack_execute(&ct, pkt_batch, dl_type, true, 0, NULL, NULL, NULL);
95 : : }
96 : 0 : ovs_barrier_block(&barrier);
97 : 0 : destroy_packets(pkt_batch);
98 : :
99 : 0 : return NULL;
100 : : }
101 : :
102 : : static void
103 : 0 : test_benchmark(struct ovs_cmdl_context *ctx)
104 : : {
105 : : struct thread_aux *threads;
106 : : long long start;
107 : : unsigned i;
108 : :
109 : 0 : fatal_signal_init();
110 : :
111 : : /* Parse arguments */
112 : 0 : n_threads = strtoul(ctx->argv[1], NULL, 0);
113 [ # # ]: 0 : if (!n_threads) {
114 : 0 : ovs_fatal(0, "n_threads must be at least one");
115 : : }
116 : 0 : n_pkts = strtoul(ctx->argv[2], NULL, 0);
117 : 0 : batch_size = strtoul(ctx->argv[3], NULL, 0);
118 [ # # ][ # # ]: 0 : if (batch_size == 0 || batch_size > NETDEV_MAX_BURST) {
119 : 0 : ovs_fatal(0, "batch_size must be between 1 and NETDEV_MAX_BURST(%u)",
120 : : NETDEV_MAX_BURST);
121 : : }
122 [ # # ]: 0 : if (ctx->argc > 4) {
123 : 0 : change_conn = strtoul(ctx->argv[4], NULL, 0);
124 : : }
125 : :
126 : 0 : threads = xcalloc(n_threads, sizeof *threads);
127 : 0 : ovs_barrier_init(&barrier, n_threads + 1);
128 : 0 : conntrack_init(&ct);
129 : :
130 : : /* Create threads */
131 [ # # ]: 0 : for (i = 0; i < n_threads; i++) {
132 : 0 : threads[i].tid = i;
133 : 0 : threads[i].thread = ovs_thread_create("ct_thread", ct_thread_main,
134 : 0 : &threads[i]);
135 : : }
136 : : /* Starts the work inside the threads */
137 : 0 : ovs_barrier_block(&barrier);
138 : 0 : start = time_msec();
139 : :
140 : : /* Wait for the threads to finish the work */
141 : 0 : ovs_barrier_block(&barrier);
142 : 0 : printf("conntrack: %5lld ms\n", time_msec() - start);
143 : :
144 [ # # ]: 0 : for (i = 0; i < n_threads; i++) {
145 : 0 : xpthread_join(threads[i].thread, NULL);
146 : : }
147 : :
148 : 0 : conntrack_destroy(&ct);
149 : 0 : ovs_barrier_destroy(&barrier);
150 : 0 : free(threads);
151 : 0 : }
152 : :
153 : : static void
154 : 0 : pcap_batch_execute_conntrack(struct conntrack *ct,
155 : : struct dp_packet_batch *pkt_batch)
156 : : {
157 : : size_t i;
158 : : struct dp_packet_batch new_batch;
159 : 0 : ovs_be16 dl_type = htons(0);
160 : :
161 : 0 : dp_packet_batch_init(&new_batch);
162 : :
163 : : /* pkt_batch contains packets with different 'dl_type'. We have to
164 : : * call conntrack_execute() on packets with the same 'dl_type'. */
165 : :
166 [ # # ]: 0 : for (i = 0; i < pkt_batch->count; i++) {
167 : 0 : struct dp_packet *pkt = pkt_batch->packets[i];
168 : : struct flow flow;
169 : :
170 : : /* This also initializes the l3 and l4 pointers. */
171 : 0 : flow_extract(pkt, &flow);
172 : :
173 [ # # ]: 0 : if (!new_batch.count) {
174 : 0 : dl_type = flow.dl_type;
175 : : }
176 : :
177 [ # # ]: 0 : if (flow.dl_type != dl_type) {
178 : 0 : conntrack_execute(ct, &new_batch, dl_type, true, 0, NULL, NULL,
179 : : NULL);
180 : 0 : dp_packet_batch_init(&new_batch);
181 : : }
182 : 0 : new_batch.packets[new_batch.count++] = pkt;
183 : : }
184 : :
185 [ # # ]: 0 : if (new_batch.count) {
186 : 0 : conntrack_execute(ct, &new_batch, dl_type, true, 0, NULL, NULL, NULL);
187 : : }
188 : :
189 : 0 : }
190 : :
191 : : static void
192 : 0 : test_pcap(struct ovs_cmdl_context *ctx)
193 : : {
194 : : size_t total_count, i, batch_size;
195 : : FILE *pcap;
196 : : int err;
197 : :
198 : 0 : pcap = ovs_pcap_open(ctx->argv[1], "rb");
199 [ # # ]: 0 : if (!pcap) {
200 : 0 : return;
201 : : }
202 : :
203 : 0 : batch_size = 1;
204 [ # # ]: 0 : if (ctx->argc > 2) {
205 : 0 : batch_size = strtoul(ctx->argv[2], NULL, 0);
206 [ # # ][ # # ]: 0 : if (batch_size == 0 || batch_size > NETDEV_MAX_BURST) {
207 : 0 : ovs_fatal(0,
208 : : "batch_size must be between 1 and NETDEV_MAX_BURST(%u)",
209 : : NETDEV_MAX_BURST);
210 : : }
211 : : }
212 : :
213 : 0 : fatal_signal_init();
214 : :
215 : 0 : conntrack_init(&ct);
216 : 0 : total_count = 0;
217 : : for (;;) {
218 : : struct dp_packet_batch pkt_batch;
219 : :
220 : 0 : dp_packet_batch_init(&pkt_batch);
221 : :
222 [ # # ]: 0 : for (i = 0; i < batch_size; i++) {
223 : 0 : err = ovs_pcap_read(pcap, &pkt_batch.packets[i], NULL);
224 [ # # ]: 0 : if (err) {
225 : 0 : break;
226 : : }
227 : : }
228 : :
229 : 0 : pkt_batch.count = i;
230 [ # # ]: 0 : if (pkt_batch.count == 0) {
231 : 0 : break;
232 : : }
233 : :
234 : 0 : pcap_batch_execute_conntrack(&ct, &pkt_batch);
235 : :
236 [ # # ]: 0 : for (i = 0; i < pkt_batch.count; i++) {
237 : 0 : struct ds ds = DS_EMPTY_INITIALIZER;
238 : 0 : struct dp_packet *pkt = pkt_batch.packets[i];
239 : :
240 : 0 : total_count++;
241 : :
242 : 0 : format_flags(&ds, ct_state_to_string, pkt->md.ct_state, '|');
243 : 0 : printf("%"PRIuSIZE": %s\n", total_count, ds_cstr(&ds));
244 : :
245 : 0 : ds_destroy(&ds);
246 : : }
247 : :
248 : 0 : dp_packet_delete_batch(&pkt_batch, true);
249 [ # # ]: 0 : if (err) {
250 : 0 : break;
251 : : }
252 : 0 : }
253 : 0 : conntrack_destroy(&ct);
254 : : }
255 : :
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,
263 : : test_benchmark, OVS_RO},
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, OVS_RO},
268 : :
269 : : {NULL, NULL, 0, 0, NULL, OVS_RO},
270 : : };
271 : :
272 : : static void
273 : 0 : test_conntrack_main(int argc, char *argv[])
274 : : {
275 : 0 : struct ovs_cmdl_context ctx = {
276 : 0 : .argc = argc - 1,
277 : 0 : .argv = argv + 1,
278 : : };
279 : 0 : set_program_name(argv[0]);
280 : 0 : ovs_cmdl_run_command(&ctx, commands);
281 : 0 : }
282 : :
283 : 1174 : OVSTEST_REGISTER("test-conntrack", test_conntrack_main);
|