xref: /dpdk/app/test-acl/main.c (revision f7c9651c830e4a54c81b0357fca768787d3d6402)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2010-2014 Intel Corporation
3  */
4 
5 #include <rte_string_fns.h>
6 #include <rte_acl.h>
7 #include <getopt.h>
8 #include <string.h>
9 
10 #include <rte_cycles.h>
11 #include <rte_per_lcore.h>
12 #include <rte_lcore.h>
13 #include <rte_ip.h>
14 
15 #define	PRINT_USAGE_START	"%s [EAL options] --\n"
16 
17 #define	RTE_LOGTYPE_TESTACL	RTE_LOGTYPE_USER1
18 
19 #define	APP_NAME	"TESTACL"
20 
21 #define GET_CB_FIELD(in, fd, base, lim, dlm)	do {            \
22 	unsigned long val;                                      \
23 	char *end_fld;                                          \
24 	errno = 0;                                              \
25 	val = strtoul((in), &end_fld, (base));                  \
26 	if (errno != 0 || end_fld[0] != (dlm) || val > (lim))   \
27 		return -EINVAL;                               \
28 	(fd) = (typeof(fd))val;                                 \
29 	(in) = end_fld + 1;                                     \
30 } while (0)
31 
32 #define	OPT_RULE_FILE		"rulesf"
33 #define	OPT_TRACE_FILE		"tracef"
34 #define	OPT_RULE_NUM		"rulenum"
35 #define	OPT_TRACE_NUM		"tracenum"
36 #define	OPT_TRACE_STEP		"tracestep"
37 #define	OPT_SEARCH_ALG		"alg"
38 #define	OPT_BLD_CATEGORIES	"bldcat"
39 #define	OPT_RUN_CATEGORIES	"runcat"
40 #define	OPT_MAX_SIZE		"maxsize"
41 #define	OPT_ITER_NUM		"iter"
42 #define	OPT_VERBOSE		"verbose"
43 #define	OPT_IPV6		"ipv6"
44 
45 #define	TRACE_DEFAULT_NUM	0x10000
46 #define	TRACE_STEP_MAX		0x1000
47 #define	TRACE_STEP_DEF		0x100
48 
49 #define	RULE_NUM		0x10000
50 
51 #define COMMENT_LEAD_CHAR	'#'
52 
53 enum {
54 	DUMP_NONE,
55 	DUMP_SEARCH,
56 	DUMP_PKT,
57 	DUMP_MAX
58 };
59 
60 enum {
61 	IPV6_FRMT_NONE,
62 	IPV6_FRMT_U32,
63 	IPV6_FRMT_U64,
64 };
65 
66 struct acl_alg {
67 	const char *name;
68 	enum rte_acl_classify_alg alg;
69 };
70 
71 static const struct acl_alg acl_alg[] = {
72 	{
73 		.name = "scalar",
74 		.alg = RTE_ACL_CLASSIFY_SCALAR,
75 	},
76 	{
77 		.name = "sse",
78 		.alg = RTE_ACL_CLASSIFY_SSE,
79 	},
80 	{
81 		.name = "avx2",
82 		.alg = RTE_ACL_CLASSIFY_AVX2,
83 	},
84 	{
85 		.name = "neon",
86 		.alg = RTE_ACL_CLASSIFY_NEON,
87 	},
88 	{
89 		.name = "altivec",
90 		.alg = RTE_ACL_CLASSIFY_ALTIVEC,
91 	},
92 	{
93 		.name = "avx512x16",
94 		.alg = RTE_ACL_CLASSIFY_AVX512X16,
95 	},
96 	{
97 		.name = "avx512x32",
98 		.alg = RTE_ACL_CLASSIFY_AVX512X32,
99 	},
100 };
101 
102 static struct {
103 	const char         *prgname;
104 	const char         *rule_file;
105 	const char         *trace_file;
106 	size_t              max_size;
107 	uint32_t            bld_categories;
108 	uint32_t            run_categories;
109 	uint32_t            nb_rules;
110 	uint32_t            nb_traces;
111 	uint32_t            trace_step;
112 	uint32_t            trace_sz;
113 	uint32_t            iter_num;
114 	uint32_t            verbose;
115 	uint32_t            ipv6;
116 	struct acl_alg      alg;
117 	uint32_t            used_traces;
118 	void               *traces;
119 	struct rte_acl_ctx *acx;
120 } config = {
121 	.bld_categories = 3,
122 	.run_categories = 1,
123 	.nb_rules = RULE_NUM,
124 	.nb_traces = TRACE_DEFAULT_NUM,
125 	.trace_step = TRACE_STEP_DEF,
126 	.iter_num = 1,
127 	.verbose = DUMP_MAX,
128 	.alg = {
129 		.name = "default",
130 		.alg = RTE_ACL_CLASSIFY_DEFAULT,
131 	},
132 	.ipv6 = IPV6_FRMT_NONE,
133 };
134 
135 static struct rte_acl_param prm = {
136 	.name = APP_NAME,
137 	.socket_id = SOCKET_ID_ANY,
138 };
139 
140 /*
141  * Rule and trace formats definitions.
142  */
143 
144 struct ipv4_5tuple {
145 	uint8_t  proto;
146 	uint32_t ip_src;
147 	uint32_t ip_dst;
148 	uint16_t port_src;
149 	uint16_t port_dst;
150 };
151 
152 enum {
153 	PROTO_FIELD_IPV4,
154 	SRC_FIELD_IPV4,
155 	DST_FIELD_IPV4,
156 	SRCP_FIELD_IPV4,
157 	DSTP_FIELD_IPV4,
158 	NUM_FIELDS_IPV4
159 };
160 
161 /*
162  * That effectively defines order of IPV4VLAN classifications:
163  *  - PROTO
164  *  - VLAN (TAG and DOMAIN)
165  *  - SRC IP ADDRESS
166  *  - DST IP ADDRESS
167  *  - PORTS (SRC and DST)
168  */
169 enum {
170 	RTE_ACL_IPV4VLAN_PROTO,
171 	RTE_ACL_IPV4VLAN_VLAN,
172 	RTE_ACL_IPV4VLAN_SRC,
173 	RTE_ACL_IPV4VLAN_DST,
174 	RTE_ACL_IPV4VLAN_PORTS,
175 	RTE_ACL_IPV4VLAN_NUM
176 };
177 
178 struct rte_acl_field_def ipv4_defs[NUM_FIELDS_IPV4] = {
179 	{
180 		.type = RTE_ACL_FIELD_TYPE_BITMASK,
181 		.size = sizeof(uint8_t),
182 		.field_index = PROTO_FIELD_IPV4,
183 		.input_index = RTE_ACL_IPV4VLAN_PROTO,
184 		.offset = offsetof(struct ipv4_5tuple, proto),
185 	},
186 	{
187 		.type = RTE_ACL_FIELD_TYPE_MASK,
188 		.size = sizeof(uint32_t),
189 		.field_index = SRC_FIELD_IPV4,
190 		.input_index = RTE_ACL_IPV4VLAN_SRC,
191 		.offset = offsetof(struct ipv4_5tuple, ip_src),
192 	},
193 	{
194 		.type = RTE_ACL_FIELD_TYPE_MASK,
195 		.size = sizeof(uint32_t),
196 		.field_index = DST_FIELD_IPV4,
197 		.input_index = RTE_ACL_IPV4VLAN_DST,
198 		.offset = offsetof(struct ipv4_5tuple, ip_dst),
199 	},
200 	{
201 		.type = RTE_ACL_FIELD_TYPE_RANGE,
202 		.size = sizeof(uint16_t),
203 		.field_index = SRCP_FIELD_IPV4,
204 		.input_index = RTE_ACL_IPV4VLAN_PORTS,
205 		.offset = offsetof(struct ipv4_5tuple, port_src),
206 	},
207 	{
208 		.type = RTE_ACL_FIELD_TYPE_RANGE,
209 		.size = sizeof(uint16_t),
210 		.field_index = DSTP_FIELD_IPV4,
211 		.input_index = RTE_ACL_IPV4VLAN_PORTS,
212 		.offset = offsetof(struct ipv4_5tuple, port_dst),
213 	},
214 };
215 
216 #define	IPV6_ADDR_LEN	16
217 #define	IPV6_ADDR_U16	(IPV6_ADDR_LEN / sizeof(uint16_t))
218 #define	IPV6_ADDR_U32	(IPV6_ADDR_LEN / sizeof(uint32_t))
219 #define	IPV6_ADDR_U64	(IPV6_ADDR_LEN / sizeof(uint64_t))
220 
221 struct ipv6_5tuple {
222 	uint8_t  proto;
223 	uint32_t ip_src[IPV6_ADDR_U32];
224 	uint32_t ip_dst[IPV6_ADDR_U32];
225 	uint16_t port_src;
226 	uint16_t port_dst;
227 };
228 
229 /* treat IPV6 address as uint32_t[4] (default mode) */
230 enum {
231 	PROTO_FIELD_IPV6,
232 	SRC1_FIELD_IPV6,
233 	SRC2_FIELD_IPV6,
234 	SRC3_FIELD_IPV6,
235 	SRC4_FIELD_IPV6,
236 	DST1_FIELD_IPV6,
237 	DST2_FIELD_IPV6,
238 	DST3_FIELD_IPV6,
239 	DST4_FIELD_IPV6,
240 	SRCP_FIELD_IPV6,
241 	DSTP_FIELD_IPV6,
242 	NUM_FIELDS_IPV6
243 };
244 
245 /* treat IPV6 address as uint64_t[2] (default mode) */
246 enum {
247 	PROTO_FIELD_IPV6_U64,
248 	SRC1_FIELD_IPV6_U64,
249 	SRC2_FIELD_IPV6_U64,
250 	DST1_FIELD_IPV6_U64,
251 	DST2_FIELD_IPV6_U64,
252 	SRCP_FIELD_IPV6_U64,
253 	DSTP_FIELD_IPV6_U64,
254 	NUM_FIELDS_IPV6_U64
255 };
256 
257 enum {
258 	PROTO_INDEX_IPV6_U64 = PROTO_FIELD_IPV6_U64,
259 	SRC1_INDEX_IPV6_U64 = SRC1_FIELD_IPV6_U64,
260 	SRC2_INDEX_IPV6_U64 = SRC2_FIELD_IPV6_U64 + 1,
261 	DST1_INDEX_IPV6_U64 = DST1_FIELD_IPV6_U64 + 2,
262 	DST2_INDEX_IPV6_U64 = DST2_FIELD_IPV6_U64 + 3,
263 	PRT_INDEX_IPV6_U64 = SRCP_FIELD_IPV6 + 4,
264 };
265 
266 struct rte_acl_field_def ipv6_defs[NUM_FIELDS_IPV6] = {
267 	{
268 		.type = RTE_ACL_FIELD_TYPE_BITMASK,
269 		.size = sizeof(uint8_t),
270 		.field_index = PROTO_FIELD_IPV6,
271 		.input_index = PROTO_FIELD_IPV6,
272 		.offset = offsetof(struct ipv6_5tuple, proto),
273 	},
274 	{
275 		.type = RTE_ACL_FIELD_TYPE_MASK,
276 		.size = sizeof(uint32_t),
277 		.field_index = SRC1_FIELD_IPV6,
278 		.input_index = SRC1_FIELD_IPV6,
279 		.offset = offsetof(struct ipv6_5tuple, ip_src[0]),
280 	},
281 	{
282 		.type = RTE_ACL_FIELD_TYPE_MASK,
283 		.size = sizeof(uint32_t),
284 		.field_index = SRC2_FIELD_IPV6,
285 		.input_index = SRC2_FIELD_IPV6,
286 		.offset = offsetof(struct ipv6_5tuple, ip_src[1]),
287 	},
288 	{
289 		.type = RTE_ACL_FIELD_TYPE_MASK,
290 		.size = sizeof(uint32_t),
291 		.field_index = SRC3_FIELD_IPV6,
292 		.input_index = SRC3_FIELD_IPV6,
293 		.offset = offsetof(struct ipv6_5tuple, ip_src[2]),
294 	},
295 	{
296 		.type = RTE_ACL_FIELD_TYPE_MASK,
297 		.size = sizeof(uint32_t),
298 		.field_index = SRC4_FIELD_IPV6,
299 		.input_index = SRC4_FIELD_IPV6,
300 		.offset = offsetof(struct ipv6_5tuple, ip_src[3]),
301 	},
302 	{
303 		.type = RTE_ACL_FIELD_TYPE_MASK,
304 		.size = sizeof(uint32_t),
305 		.field_index = DST1_FIELD_IPV6,
306 		.input_index = DST1_FIELD_IPV6,
307 		.offset = offsetof(struct ipv6_5tuple, ip_dst[0]),
308 	},
309 	{
310 		.type = RTE_ACL_FIELD_TYPE_MASK,
311 		.size = sizeof(uint32_t),
312 		.field_index = DST2_FIELD_IPV6,
313 		.input_index = DST2_FIELD_IPV6,
314 		.offset = offsetof(struct ipv6_5tuple, ip_dst[1]),
315 	},
316 	{
317 		.type = RTE_ACL_FIELD_TYPE_MASK,
318 		.size = sizeof(uint32_t),
319 		.field_index = DST3_FIELD_IPV6,
320 		.input_index = DST3_FIELD_IPV6,
321 		.offset = offsetof(struct ipv6_5tuple, ip_dst[2]),
322 	},
323 	{
324 		.type = RTE_ACL_FIELD_TYPE_MASK,
325 		.size = sizeof(uint32_t),
326 		.field_index = DST4_FIELD_IPV6,
327 		.input_index = DST4_FIELD_IPV6,
328 		.offset = offsetof(struct ipv6_5tuple, ip_dst[3]),
329 	},
330 	{
331 		.type = RTE_ACL_FIELD_TYPE_RANGE,
332 		.size = sizeof(uint16_t),
333 		.field_index = SRCP_FIELD_IPV6,
334 		.input_index = SRCP_FIELD_IPV6,
335 		.offset = offsetof(struct ipv6_5tuple, port_src),
336 	},
337 	{
338 		.type = RTE_ACL_FIELD_TYPE_RANGE,
339 		.size = sizeof(uint16_t),
340 		.field_index = DSTP_FIELD_IPV6,
341 		.input_index = SRCP_FIELD_IPV6,
342 		.offset = offsetof(struct ipv6_5tuple, port_dst),
343 	},
344 };
345 
346 struct rte_acl_field_def ipv6_u64_defs[NUM_FIELDS_IPV6_U64] = {
347 	{
348 		.type = RTE_ACL_FIELD_TYPE_BITMASK,
349 		.size = sizeof(uint8_t),
350 		.field_index = PROTO_FIELD_IPV6_U64,
351 		.input_index = PROTO_FIELD_IPV6_U64,
352 		.offset = offsetof(struct ipv6_5tuple, proto),
353 	},
354 	{
355 		.type = RTE_ACL_FIELD_TYPE_MASK,
356 		.size = sizeof(uint64_t),
357 		.field_index = SRC1_FIELD_IPV6_U64,
358 		.input_index = SRC1_INDEX_IPV6_U64,
359 		.offset = offsetof(struct ipv6_5tuple, ip_src[0]),
360 	},
361 	{
362 		.type = RTE_ACL_FIELD_TYPE_MASK,
363 		.size = sizeof(uint64_t),
364 		.field_index = SRC2_FIELD_IPV6_U64,
365 		.input_index = SRC2_INDEX_IPV6_U64,
366 		.offset = offsetof(struct ipv6_5tuple, ip_src[2]),
367 	},
368 	{
369 		.type = RTE_ACL_FIELD_TYPE_MASK,
370 		.size = sizeof(uint64_t),
371 		.field_index = DST1_FIELD_IPV6_U64,
372 		.input_index = DST1_INDEX_IPV6_U64,
373 		.offset = offsetof(struct ipv6_5tuple, ip_dst[0]),
374 	},
375 	{
376 		.type = RTE_ACL_FIELD_TYPE_MASK,
377 		.size = sizeof(uint64_t),
378 		.field_index = DST2_FIELD_IPV6_U64,
379 		.input_index = DST2_INDEX_IPV6_U64,
380 		.offset = offsetof(struct ipv6_5tuple, ip_dst[2]),
381 	},
382 	{
383 		.type = RTE_ACL_FIELD_TYPE_RANGE,
384 		.size = sizeof(uint16_t),
385 		.field_index = SRCP_FIELD_IPV6_U64,
386 		.input_index = PRT_INDEX_IPV6_U64,
387 		.offset = offsetof(struct ipv6_5tuple, port_src),
388 	},
389 	{
390 		.type = RTE_ACL_FIELD_TYPE_RANGE,
391 		.size = sizeof(uint16_t),
392 		.field_index = DSTP_FIELD_IPV6_U64,
393 		.input_index = PRT_INDEX_IPV6_U64,
394 		.offset = offsetof(struct ipv6_5tuple, port_dst),
395 	},
396 };
397 
398 enum {
399 	CB_FLD_SRC_ADDR,
400 	CB_FLD_DST_ADDR,
401 	CB_FLD_SRC_PORT_LOW,
402 	CB_FLD_SRC_PORT_DLM,
403 	CB_FLD_SRC_PORT_HIGH,
404 	CB_FLD_DST_PORT_LOW,
405 	CB_FLD_DST_PORT_DLM,
406 	CB_FLD_DST_PORT_HIGH,
407 	CB_FLD_PROTO,
408 	CB_FLD_NUM,
409 };
410 
411 enum {
412 	CB_TRC_SRC_ADDR,
413 	CB_TRC_DST_ADDR,
414 	CB_TRC_SRC_PORT,
415 	CB_TRC_DST_PORT,
416 	CB_TRC_PROTO,
417 	CB_TRC_NUM,
418 };
419 
420 RTE_ACL_RULE_DEF(acl_rule, RTE_ACL_MAX_FIELDS);
421 
422 static const char cb_port_delim[] = ":";
423 
424 static char line[LINE_MAX];
425 
426 #define	dump_verbose(lvl, fh, fmt, ...)	do { \
427 	if ((lvl) <= (int32_t)config.verbose)        \
428 		fprintf(fh, fmt, ##__VA_ARGS__);         \
429 } while (0)
430 
431 
432 /*
433  * Parse ClassBench input trace (test vectors and expected results) file.
434  * Expected format:
435  * <src_ipv4_addr> <space> <dst_ipv4_addr> <space> \
436  * <src_port> <space> <dst_port> <space> <proto>
437  */
438 static int
439 parse_cb_ipv4_trace(char *str, struct ipv4_5tuple *v)
440 {
441 	int i;
442 	char *s, *sp, *in[CB_TRC_NUM];
443 	static const char *dlm = " \t\n";
444 
445 	s = str;
446 	for (i = 0; i != RTE_DIM(in); i++) {
447 		in[i] = strtok_r(s, dlm, &sp);
448 		if (in[i] == NULL)
449 			return -EINVAL;
450 		s = NULL;
451 	}
452 
453 	GET_CB_FIELD(in[CB_TRC_SRC_ADDR], v->ip_src, 0, UINT32_MAX, 0);
454 	GET_CB_FIELD(in[CB_TRC_DST_ADDR], v->ip_dst, 0, UINT32_MAX, 0);
455 	GET_CB_FIELD(in[CB_TRC_SRC_PORT], v->port_src, 0, UINT16_MAX, 0);
456 	GET_CB_FIELD(in[CB_TRC_DST_PORT], v->port_dst, 0, UINT16_MAX, 0);
457 	GET_CB_FIELD(in[CB_TRC_PROTO], v->proto, 0, UINT8_MAX, 0);
458 
459 	/* convert to network byte order. */
460 	v->ip_src = rte_cpu_to_be_32(v->ip_src);
461 	v->ip_dst = rte_cpu_to_be_32(v->ip_dst);
462 	v->port_src = rte_cpu_to_be_16(v->port_src);
463 	v->port_dst = rte_cpu_to_be_16(v->port_dst);
464 
465 	return 0;
466 }
467 
468 static int
469 parse_cb_ipv6_addr_trace(const char *in, uint32_t v[IPV6_ADDR_U32])
470 {
471 	if (inet_pton(AF_INET6, in, v) != 1)
472 		return -EINVAL;
473 
474 	return 0;
475 }
476 
477 /*
478  * Parse ClassBench input trace (test vectors and expected results) file.
479  * Expected format:
480  * <src_ipv6_addr> <space> <dst_ipv6_addr> <space> \
481  * <src_port> <space> <dst_port> <space> <proto>
482  */
483 static int
484 parse_cb_ipv6_trace(char *str, struct ipv6_5tuple *v)
485 {
486 	int32_t i, rc;
487 	char *s, *sp, *in[CB_TRC_NUM];
488 	static const char *dlm = " \t\n";
489 
490 	s = str;
491 	for (i = 0; i != RTE_DIM(in); i++) {
492 		in[i] = strtok_r(s, dlm, &sp);
493 		if (in[i] == NULL)
494 			return -EINVAL;
495 		s = NULL;
496 	}
497 
498 	/* get ip6 src address. */
499 	rc = parse_cb_ipv6_addr_trace(in[CB_TRC_SRC_ADDR], v->ip_src);
500 	if (rc != 0)
501 		return rc;
502 
503 	/* get ip6 dst address. */
504 	rc = parse_cb_ipv6_addr_trace(in[CB_TRC_DST_ADDR], v->ip_dst);
505 	if (rc != 0)
506 		return rc;
507 
508 	GET_CB_FIELD(in[CB_TRC_SRC_PORT], v->port_src, 0, UINT16_MAX, 0);
509 	GET_CB_FIELD(in[CB_TRC_DST_PORT], v->port_dst, 0, UINT16_MAX, 0);
510 	GET_CB_FIELD(in[CB_TRC_PROTO], v->proto, 0, UINT8_MAX, 0);
511 
512 	/* convert to network byte order. */
513 	v->port_src = rte_cpu_to_be_16(v->port_src);
514 	v->port_dst = rte_cpu_to_be_16(v->port_dst);
515 
516 	return 0;
517 }
518 
519 /* Bypass comment and empty lines */
520 static int
521 skip_line(const char *buf)
522 {
523 	uint32_t i;
524 
525 	for (i = 0; isspace(buf[i]) != 0; i++)
526 		;
527 
528 	if (buf[i] == 0 || buf[i] == COMMENT_LEAD_CHAR)
529 		return 1;
530 
531 	return 0;
532 }
533 
534 static void
535 tracef_init(void)
536 {
537 	static const char name[] = APP_NAME;
538 	FILE *f;
539 	size_t sz;
540 	uint32_t i, k, n;
541 	struct ipv4_5tuple *v;
542 	struct ipv6_5tuple *w;
543 
544 	sz = config.nb_traces * (config.ipv6 ? sizeof(*w) : sizeof(*v));
545 	config.traces = rte_zmalloc_socket(name, sz, RTE_CACHE_LINE_SIZE,
546 			SOCKET_ID_ANY);
547 	if (config.traces == NULL)
548 		rte_exit(EXIT_FAILURE, "Cannot allocate %zu bytes for "
549 			"requested %u number of trace records\n",
550 			sz, config.nb_traces);
551 
552 	f = fopen(config.trace_file, "r");
553 	if (f == NULL)
554 		rte_exit(-EINVAL, "failed to open file: %s\n",
555 			config.trace_file);
556 
557 	v = config.traces;
558 	w = config.traces;
559 	k = 0;
560 	n = 0;
561 	for (i = 0; n != config.nb_traces; i++) {
562 
563 		if (fgets(line, sizeof(line), f) == NULL)
564 			break;
565 
566 		if (skip_line(line) != 0) {
567 			k++;
568 			continue;
569 		}
570 
571 		n = i - k;
572 
573 		if (config.ipv6) {
574 			if (parse_cb_ipv6_trace(line, w + n) != 0)
575 				rte_exit(EXIT_FAILURE,
576 					"%s: failed to parse ipv6 trace "
577 					"record at line %u\n",
578 					config.trace_file, i + 1);
579 		} else {
580 			if (parse_cb_ipv4_trace(line, v + n) != 0)
581 				rte_exit(EXIT_FAILURE,
582 					"%s: failed to parse ipv4 trace "
583 					"record at line %u\n",
584 					config.trace_file, i + 1);
585 		}
586 	}
587 
588 	config.used_traces = i - k;
589 	fclose(f);
590 }
591 
592 static int
593 parse_ipv6_u32_net(char *in, struct rte_acl_field field[IPV6_ADDR_U32])
594 {
595 	char *sa, *sm, *sv;
596 	uint32_t i, m, v[IPV6_ADDR_U32];
597 
598 	const char *dlm = "/";
599 	const uint32_t nbu32 = sizeof(uint32_t) * CHAR_BIT;
600 
601 	/* get address. */
602 	sv = NULL;
603 	sa = strtok_r(in, dlm, &sv);
604 	if (sa == NULL)
605 		return -EINVAL;
606 	sm = strtok_r(NULL, dlm, &sv);
607 	if (sm == NULL)
608 		return -EINVAL;
609 
610 	if (inet_pton(AF_INET6, sa, v) != 1)
611 		return -EINVAL;
612 
613 	v[0] = rte_be_to_cpu_32(v[0]);
614 	v[1] = rte_be_to_cpu_32(v[1]);
615 	v[2] = rte_be_to_cpu_32(v[2]);
616 	v[3] = rte_be_to_cpu_32(v[3]);
617 
618 	/* get mask. */
619 	GET_CB_FIELD(sm, m, 0, CHAR_BIT * sizeof(v), 0);
620 
621 	/* put all together. */
622 	for (i = 0; i != RTE_DIM(v); i++) {
623 		if (m >= (i + 1) * nbu32)
624 			field[i].mask_range.u32 = nbu32;
625 		else
626 			field[i].mask_range.u32 = m > (i * nbu32) ?
627 				m - (i * nbu32) : 0;
628 
629 		field[i].value.u32 = v[i];
630 	}
631 
632 	return 0;
633 }
634 
635 static int
636 parse_ipv6_u64_net(char *in, struct rte_acl_field field[IPV6_ADDR_U64])
637 {
638 	char *sa, *sm, *sv;
639 	uint32_t i, m;
640 	uint64_t v[IPV6_ADDR_U64];
641 
642 	const char *dlm = "/";
643 	const uint32_t nbu64 = sizeof(uint64_t) * CHAR_BIT;
644 
645 	/* get address. */
646 	sv = NULL;
647 	sa = strtok_r(in, dlm, &sv);
648 	if (sa == NULL)
649 		return -EINVAL;
650 	sm = strtok_r(NULL, dlm, &sv);
651 	if (sm == NULL)
652 		return -EINVAL;
653 
654 	if (inet_pton(AF_INET6, sa, v) != 1)
655 		return -EINVAL;
656 
657 	v[0] = rte_be_to_cpu_64(v[0]);
658 	v[1] = rte_be_to_cpu_64(v[1]);
659 
660 	/* get mask. */
661 	GET_CB_FIELD(sm, m, 0, CHAR_BIT * sizeof(v), 0);
662 
663 	/* put all together. */
664 	for (i = 0; i != RTE_DIM(v); i++) {
665 		if (m >= (i + 1) * nbu64)
666 			field[i].mask_range.u32 = nbu64;
667 		else
668 			field[i].mask_range.u32 = m > (i * nbu64) ?
669 				m - (i * nbu64) : 0;
670 
671 		field[i].value.u64 = v[i];
672 	}
673 
674 	return 0;
675 }
676 
677 static int
678 parse_cb_ipv6_rule(char *str, struct acl_rule *v, int frmt)
679 {
680 	int i, rc;
681 	uint32_t fidx;
682 	const uint32_t *field_map;
683 	char *s, *sp, *in[CB_FLD_NUM];
684 	int (*parse_ipv6_net)(char *s, struct rte_acl_field f[]);
685 
686 	static const char *dlm = " \t\n";
687 
688 	static const uint32_t field_map_u32[CB_FLD_NUM] = {
689 		[CB_FLD_SRC_ADDR] = SRC1_FIELD_IPV6,
690 		[CB_FLD_DST_ADDR] = DST1_FIELD_IPV6,
691 		[CB_FLD_SRC_PORT_LOW] = SRCP_FIELD_IPV6,
692 		[CB_FLD_SRC_PORT_HIGH] = SRCP_FIELD_IPV6,
693 		[CB_FLD_DST_PORT_LOW] = DSTP_FIELD_IPV6,
694 		[CB_FLD_DST_PORT_HIGH] = DSTP_FIELD_IPV6,
695 		[CB_FLD_PROTO] = PROTO_FIELD_IPV6,
696 	};
697 
698 	static const uint32_t field_map_u64[CB_FLD_NUM] = {
699 		[CB_FLD_SRC_ADDR] = SRC1_FIELD_IPV6_U64,
700 		[CB_FLD_DST_ADDR] = DST1_FIELD_IPV6_U64,
701 		[CB_FLD_SRC_PORT_LOW] = SRCP_FIELD_IPV6_U64,
702 		[CB_FLD_SRC_PORT_HIGH] = SRCP_FIELD_IPV6_U64,
703 		[CB_FLD_DST_PORT_LOW] = DSTP_FIELD_IPV6_U64,
704 		[CB_FLD_DST_PORT_HIGH] = DSTP_FIELD_IPV6_U64,
705 		[CB_FLD_PROTO] = PROTO_FIELD_IPV6_U64,
706 	};
707 
708 	if (frmt == IPV6_FRMT_U32) {
709 		field_map = field_map_u32;
710 		parse_ipv6_net = parse_ipv6_u32_net;
711 	} else if (frmt == IPV6_FRMT_U64) {
712 		field_map = field_map_u64;
713 		parse_ipv6_net = parse_ipv6_u64_net;
714 	} else
715 		return -ENOTSUP;
716 
717 	/*
718 	 * Skip leading '@'
719 	 */
720 	if (strchr(str, '@') != str)
721 		return -EINVAL;
722 
723 	s = str + 1;
724 
725 	for (i = 0; i != RTE_DIM(in); i++) {
726 		in[i] = strtok_r(s, dlm, &sp);
727 		if (in[i] == NULL)
728 			return -EINVAL;
729 		s = NULL;
730 	}
731 
732 	fidx = CB_FLD_SRC_ADDR;
733 	rc = parse_ipv6_net(in[fidx], v->field + field_map[fidx]);
734 	if (rc != 0) {
735 		RTE_LOG(ERR, TESTACL,
736 			"failed to read source address/mask: %s\n", in[fidx]);
737 		return rc;
738 	}
739 
740 	fidx = CB_FLD_DST_ADDR;
741 	rc = parse_ipv6_net(in[fidx], v->field + field_map[fidx]);
742 	if (rc != 0) {
743 		RTE_LOG(ERR, TESTACL,
744 			"failed to read destination address/mask: %s\n",
745 			in[fidx]);
746 		return rc;
747 	}
748 
749 	/* source port. */
750 	fidx = CB_FLD_SRC_PORT_LOW;
751 	GET_CB_FIELD(in[fidx], v->field[field_map[fidx]].value.u16,
752 		0, UINT16_MAX, 0);
753 
754 	fidx = CB_FLD_SRC_PORT_HIGH;
755 	GET_CB_FIELD(in[fidx], v->field[field_map[fidx]].mask_range.u16,
756 		0, UINT16_MAX, 0);
757 
758 	if (strncmp(in[CB_FLD_SRC_PORT_DLM], cb_port_delim,
759 			sizeof(cb_port_delim)) != 0)
760 		return -EINVAL;
761 
762 	/* destination port. */
763 	fidx = CB_FLD_DST_PORT_LOW;
764 	GET_CB_FIELD(in[fidx], v->field[field_map[fidx]].value.u16,
765 		0, UINT16_MAX, 0);
766 
767 	fidx = CB_FLD_DST_PORT_HIGH;
768 	GET_CB_FIELD(in[fidx], v->field[field_map[fidx]].mask_range.u16,
769 		0, UINT16_MAX, 0);
770 
771 	if (strncmp(in[CB_FLD_DST_PORT_DLM], cb_port_delim,
772 			sizeof(cb_port_delim)) != 0)
773 		return -EINVAL;
774 
775 	fidx = CB_FLD_PROTO;
776 	GET_CB_FIELD(in[fidx], v->field[field_map[fidx]].value.u8,
777 		0, UINT8_MAX, '/');
778 	GET_CB_FIELD(in[fidx], v->field[field_map[fidx]].mask_range.u8,
779 		0, UINT8_MAX, 0);
780 
781 	return 0;
782 }
783 
784 static int
785 parse_cb_ipv6_u32_rule(char *str, struct acl_rule *v)
786 {
787 	return parse_cb_ipv6_rule(str, v, IPV6_FRMT_U32);
788 }
789 
790 static int
791 parse_cb_ipv6_u64_rule(char *str, struct acl_rule *v)
792 {
793 	return parse_cb_ipv6_rule(str, v, IPV6_FRMT_U64);
794 }
795 
796 static int
797 parse_ipv4_net(char *in, uint32_t *addr, uint32_t *mask_len)
798 {
799 	char *sa, *sm, *sv;
800 	uint32_t m, v;
801 
802 	const char *dlm = "/";
803 
804 	sv = NULL;
805 	sa = strtok_r(in, dlm, &sv);
806 	if (sa == NULL)
807 		return -EINVAL;
808 	sm = strtok_r(NULL, dlm, &sv);
809 	if (sm == NULL)
810 		return -EINVAL;
811 
812 	if (inet_pton(AF_INET, sa, &v) != 1)
813 		return -EINVAL;
814 
815 	addr[0] = rte_be_to_cpu_32(v);
816 
817 	GET_CB_FIELD(sm, m, 0, sizeof(uint32_t) * CHAR_BIT, 0);
818 	mask_len[0] = m;
819 
820 	return 0;
821 }
822 /*
823  * Parse ClassBench rules file.
824  * Expected format:
825  * '@'<src_ipv4_addr>'/'<masklen> <space> \
826  * <dst_ipv4_addr>'/'<masklen> <space> \
827  * <src_port_low> <space> ":" <src_port_high> <space> \
828  * <dst_port_low> <space> ":" <dst_port_high> <space> \
829  * <proto>'/'<mask>
830  */
831 static int
832 parse_cb_ipv4_rule(char *str, struct acl_rule *v)
833 {
834 	int i, rc;
835 	char *s, *sp, *in[CB_FLD_NUM];
836 	static const char *dlm = " \t\n";
837 
838 	/*
839 	 * Skip leading '@'
840 	 */
841 	if (strchr(str, '@') != str)
842 		return -EINVAL;
843 
844 	s = str + 1;
845 
846 	for (i = 0; i != RTE_DIM(in); i++) {
847 		in[i] = strtok_r(s, dlm, &sp);
848 		if (in[i] == NULL)
849 			return -EINVAL;
850 		s = NULL;
851 	}
852 
853 	rc = parse_ipv4_net(in[CB_FLD_SRC_ADDR],
854 			&v->field[SRC_FIELD_IPV4].value.u32,
855 			&v->field[SRC_FIELD_IPV4].mask_range.u32);
856 	if (rc != 0) {
857 		RTE_LOG(ERR, TESTACL,
858 			"failed to read source address/mask: %s\n",
859 			in[CB_FLD_SRC_ADDR]);
860 		return rc;
861 	}
862 
863 	rc = parse_ipv4_net(in[CB_FLD_DST_ADDR],
864 			&v->field[DST_FIELD_IPV4].value.u32,
865 			&v->field[DST_FIELD_IPV4].mask_range.u32);
866 	if (rc != 0) {
867 		RTE_LOG(ERR, TESTACL,
868 			"failed to read destination address/mask: %s\n",
869 			in[CB_FLD_DST_ADDR]);
870 		return rc;
871 	}
872 
873 	/* source port. */
874 	GET_CB_FIELD(in[CB_FLD_SRC_PORT_LOW],
875 		v->field[SRCP_FIELD_IPV4].value.u16,
876 		0, UINT16_MAX, 0);
877 	GET_CB_FIELD(in[CB_FLD_SRC_PORT_HIGH],
878 		v->field[SRCP_FIELD_IPV4].mask_range.u16,
879 		0, UINT16_MAX, 0);
880 
881 	if (strncmp(in[CB_FLD_SRC_PORT_DLM], cb_port_delim,
882 			sizeof(cb_port_delim)) != 0)
883 		return -EINVAL;
884 
885 	/* destination port. */
886 	GET_CB_FIELD(in[CB_FLD_DST_PORT_LOW],
887 		v->field[DSTP_FIELD_IPV4].value.u16,
888 		0, UINT16_MAX, 0);
889 	GET_CB_FIELD(in[CB_FLD_DST_PORT_HIGH],
890 		v->field[DSTP_FIELD_IPV4].mask_range.u16,
891 		0, UINT16_MAX, 0);
892 
893 	if (strncmp(in[CB_FLD_DST_PORT_DLM], cb_port_delim,
894 			sizeof(cb_port_delim)) != 0)
895 		return -EINVAL;
896 
897 	GET_CB_FIELD(in[CB_FLD_PROTO], v->field[PROTO_FIELD_IPV4].value.u8,
898 		0, UINT8_MAX, '/');
899 	GET_CB_FIELD(in[CB_FLD_PROTO], v->field[PROTO_FIELD_IPV4].mask_range.u8,
900 		0, UINT8_MAX, 0);
901 
902 	return 0;
903 }
904 
905 typedef int (*parse_5tuple)(char *text, struct acl_rule *rule);
906 
907 static int
908 add_cb_rules(FILE *f, struct rte_acl_ctx *ctx)
909 {
910 	int rc;
911 	uint32_t i, k, n;
912 	struct acl_rule v;
913 	parse_5tuple parser;
914 
915 	static const parse_5tuple parser_func[] = {
916 		[IPV6_FRMT_NONE] = parse_cb_ipv4_rule,
917 		[IPV6_FRMT_U32] = parse_cb_ipv6_u32_rule,
918 		[IPV6_FRMT_U64] = parse_cb_ipv6_u64_rule,
919 	};
920 
921 	memset(&v, 0, sizeof(v));
922 	parser = parser_func[config.ipv6];
923 
924 	k = 0;
925 	for (i = 1; fgets(line, sizeof(line), f) != NULL; i++) {
926 
927 		if (skip_line(line) != 0) {
928 			k++;
929 			continue;
930 		}
931 
932 		n = i - k;
933 		rc = parser(line, &v);
934 		if (rc != 0) {
935 			RTE_LOG(ERR, TESTACL, "line %u: parse_cb_ipv4vlan_rule"
936 				" failed, error code: %d (%s)\n",
937 				i, rc, strerror(-rc));
938 			return rc;
939 		}
940 
941 		v.data.category_mask = RTE_LEN2MASK(RTE_ACL_MAX_CATEGORIES,
942 			typeof(v.data.category_mask));
943 		v.data.priority = RTE_ACL_MAX_PRIORITY - n;
944 		v.data.userdata = n;
945 
946 		rc = rte_acl_add_rules(ctx, (struct rte_acl_rule *)&v, 1);
947 		if (rc != 0) {
948 			RTE_LOG(ERR, TESTACL, "line %u: failed to add rules "
949 				"into ACL context, error code: %d (%s)\n",
950 				i, rc, strerror(-rc));
951 			return rc;
952 		}
953 	}
954 
955 	return 0;
956 }
957 
958 static void
959 acx_init(void)
960 {
961 	int ret;
962 	FILE *f;
963 	struct rte_acl_config cfg;
964 
965 	memset(&cfg, 0, sizeof(cfg));
966 
967 	/* setup ACL build config. */
968 	if (config.ipv6 == IPV6_FRMT_U32) {
969 		cfg.num_fields = RTE_DIM(ipv6_defs);
970 		memcpy(&cfg.defs, ipv6_defs, sizeof(ipv6_defs));
971 	} else if (config.ipv6 == IPV6_FRMT_U64) {
972 		cfg.num_fields = RTE_DIM(ipv6_u64_defs);
973 		memcpy(&cfg.defs, ipv6_u64_defs, sizeof(ipv6_u64_defs));
974 	} else {
975 		cfg.num_fields = RTE_DIM(ipv4_defs);
976 		memcpy(&cfg.defs, ipv4_defs, sizeof(ipv4_defs));
977 	}
978 	cfg.num_categories = config.bld_categories;
979 	cfg.max_size = config.max_size;
980 
981 	/* setup ACL creation parameters. */
982 	prm.rule_size = RTE_ACL_RULE_SZ(cfg.num_fields);
983 	prm.max_rule_num = config.nb_rules;
984 
985 	config.acx = rte_acl_create(&prm);
986 	if (config.acx == NULL)
987 		rte_exit(rte_errno, "failed to create ACL context\n");
988 
989 	/* set default classify method for this context. */
990 	if (config.alg.alg != RTE_ACL_CLASSIFY_DEFAULT) {
991 		ret = rte_acl_set_ctx_classify(config.acx, config.alg.alg);
992 		if (ret != 0)
993 			rte_exit(ret, "failed to setup %s method "
994 				"for ACL context\n", config.alg.name);
995 	}
996 
997 	/* add ACL rules. */
998 	f = fopen(config.rule_file, "r");
999 	if (f == NULL)
1000 		rte_exit(-EINVAL, "failed to open file %s\n",
1001 			config.rule_file);
1002 
1003 	ret = add_cb_rules(f, config.acx);
1004 	if (ret != 0)
1005 		rte_exit(ret, "failed to add rules into ACL context\n");
1006 
1007 	fclose(f);
1008 
1009 	/* perform build. */
1010 	ret = rte_acl_build(config.acx, &cfg);
1011 
1012 	dump_verbose(DUMP_NONE, stdout,
1013 		"rte_acl_build(%u) finished with %d\n",
1014 		config.bld_categories, ret);
1015 
1016 	rte_acl_dump(config.acx);
1017 
1018 	if (ret != 0)
1019 		rte_exit(ret, "failed to build search context\n");
1020 }
1021 
1022 static uint32_t
1023 search_ip5tuples_once(uint32_t categories, uint32_t step, const char *alg)
1024 {
1025 	int ret;
1026 	uint32_t i, j, k, n, r;
1027 	const uint8_t *data[step], *v;
1028 	uint32_t results[step * categories];
1029 
1030 	v = config.traces;
1031 	for (i = 0; i != config.used_traces; i += n) {
1032 
1033 		n = RTE_MIN(step, config.used_traces - i);
1034 
1035 		for (j = 0; j != n; j++) {
1036 			data[j] = v;
1037 			v += config.trace_sz;
1038 		}
1039 
1040 		ret = rte_acl_classify(config.acx, data, results,
1041 			n, categories);
1042 
1043 		if (ret != 0)
1044 			rte_exit(ret, "classify for ipv%c_5tuples returns %d\n",
1045 				config.ipv6 ? '6' : '4', ret);
1046 
1047 		for (r = 0, j = 0; j != n; j++) {
1048 			for (k = 0; k != categories; k++, r++) {
1049 				dump_verbose(DUMP_PKT, stdout,
1050 					"ipv%c_5tuple: %u, category: %u, "
1051 					"result: %u\n",
1052 					config.ipv6 ? '6' : '4',
1053 					i + j + 1, k, results[r] - 1);
1054 			}
1055 
1056 		}
1057 	}
1058 
1059 	dump_verbose(DUMP_SEARCH, stdout,
1060 		"%s(%u, %u, %s) returns %u\n", __func__,
1061 		categories, step, alg, i);
1062 	return i;
1063 }
1064 
1065 static int
1066 search_ip5tuples(__rte_unused void *arg)
1067 {
1068 	uint64_t pkt, start, tm;
1069 	uint32_t i, lcore;
1070 	long double st;
1071 
1072 	lcore = rte_lcore_id();
1073 	start = rte_rdtsc_precise();
1074 	pkt = 0;
1075 
1076 	for (i = 0; i != config.iter_num; i++) {
1077 		pkt += search_ip5tuples_once(config.run_categories,
1078 			config.trace_step, config.alg.name);
1079 	}
1080 
1081 	tm = rte_rdtsc_precise() - start;
1082 
1083 	st = (long double)tm / rte_get_timer_hz();
1084 	dump_verbose(DUMP_NONE, stdout,
1085 		"%s  @lcore %u: %" PRIu32 " iterations, %" PRIu64 " pkts, %"
1086 		PRIu32 " categories, %" PRIu64 " cycles (%.2Lf sec), "
1087 		"%.2Lf cycles/pkt, %.2Lf pkt/sec\n",
1088 		__func__, lcore, i, pkt,
1089 		config.run_categories, tm, st,
1090 		(pkt == 0) ? 0 : (long double)tm / pkt, pkt / st);
1091 
1092 	return 0;
1093 }
1094 
1095 static unsigned long
1096 get_ulong_opt(const char *opt, const char *name, size_t min, size_t max)
1097 {
1098 	unsigned long val;
1099 	char *end;
1100 
1101 	errno = 0;
1102 	val = strtoul(opt, &end, 0);
1103 	if (errno != 0 || end[0] != 0 || val > max || val < min)
1104 		rte_exit(-EINVAL, "invalid value: \"%s\" for option: %s\n",
1105 			opt, name);
1106 	return val;
1107 }
1108 
1109 static void
1110 get_alg_opt(const char *opt, const char *name)
1111 {
1112 	uint32_t i;
1113 
1114 	for (i = 0; i != RTE_DIM(acl_alg); i++) {
1115 		if (strcmp(opt, acl_alg[i].name) == 0) {
1116 			config.alg = acl_alg[i];
1117 			return;
1118 		}
1119 	}
1120 
1121 	rte_exit(-EINVAL, "invalid value: \"%s\" for option: %s\n",
1122 		opt, name);
1123 }
1124 
1125 static void
1126 get_ipv6_opt(const char *opt, const char *name)
1127 {
1128 	uint32_t i;
1129 
1130 	static const struct {
1131 		const char *name;
1132 		uint32_t val;
1133 	} ipv6_opt[] = {
1134 		{
1135 			.name = "4B",
1136 			.val = IPV6_FRMT_U32,
1137 		},
1138 		{
1139 			.name = "8B",
1140 			.val = IPV6_FRMT_U64,
1141 		},
1142 	};
1143 
1144 	for (i = 0; i != RTE_DIM(ipv6_opt); i++) {
1145 		if (strcmp(opt, ipv6_opt[i].name) == 0) {
1146 			config.ipv6 = ipv6_opt[i].val;
1147 			return;
1148 		}
1149 	}
1150 
1151 	rte_exit(-EINVAL, "invalid value: \"%s\" for option: %s\n",
1152 		opt, name);
1153 }
1154 
1155 
1156 static void
1157 print_usage(const char *prgname)
1158 {
1159 	uint32_t i, n, rc;
1160 	char buf[PATH_MAX];
1161 
1162 	n = 0;
1163 	buf[0] = 0;
1164 
1165 	for (i = 0; i < RTE_DIM(acl_alg) - 1; i++) {
1166 		rc = snprintf(buf + n, sizeof(buf) - n, "%s|",
1167 			acl_alg[i].name);
1168 		if (rc > sizeof(buf) - n)
1169 			break;
1170 		n += rc;
1171 	}
1172 
1173 	strlcpy(buf + n, acl_alg[i].name, sizeof(buf) - n);
1174 
1175 	fprintf(stdout,
1176 		PRINT_USAGE_START
1177 		"--" OPT_RULE_FILE "=<rules set file>\n"
1178 		"[--" OPT_TRACE_FILE "=<input traces file>]\n"
1179 		"[--" OPT_RULE_NUM
1180 			"=<maximum number of rules for ACL context>]\n"
1181 		"[--" OPT_TRACE_NUM
1182 			"=<number of traces to read binary file in>]\n"
1183 		"[--" OPT_TRACE_STEP
1184 			"=<number of traces to classify per one call>]\n"
1185 		"[--" OPT_BLD_CATEGORIES
1186 			"=<number of categories to build with>]\n"
1187 		"[--" OPT_RUN_CATEGORIES
1188 			"=<number of categories to run with> "
1189 			"should be either 1 or multiple of %zu, "
1190 			"but not greater then %u]\n"
1191 		"[--" OPT_MAX_SIZE
1192 			"=<size limit (in bytes) for runtime ACL structures> "
1193 			"leave 0 for default behaviour]\n"
1194 		"[--" OPT_ITER_NUM "=<number of iterations to perform>]\n"
1195 		"[--" OPT_VERBOSE "=<verbose level>]\n"
1196 		"[--" OPT_SEARCH_ALG "=%s]\n"
1197 		"[--" OPT_IPV6 "(=4B | 8B) <IPv6 rules and trace files>]\n",
1198 		prgname, RTE_ACL_RESULTS_MULTIPLIER,
1199 		(uint32_t)RTE_ACL_MAX_CATEGORIES,
1200 		buf);
1201 }
1202 
1203 static void
1204 dump_config(FILE *f)
1205 {
1206 	fprintf(f, "%s:\n", __func__);
1207 	fprintf(f, "%s:%s\n", OPT_RULE_FILE, config.rule_file);
1208 	fprintf(f, "%s:%s\n", OPT_TRACE_FILE, config.trace_file);
1209 	fprintf(f, "%s:%u\n", OPT_RULE_NUM, config.nb_rules);
1210 	fprintf(f, "%s:%u\n", OPT_TRACE_NUM, config.nb_traces);
1211 	fprintf(f, "%s:%u\n", OPT_TRACE_STEP, config.trace_step);
1212 	fprintf(f, "%s:%u\n", OPT_BLD_CATEGORIES, config.bld_categories);
1213 	fprintf(f, "%s:%u\n", OPT_RUN_CATEGORIES, config.run_categories);
1214 	fprintf(f, "%s:%zu\n", OPT_MAX_SIZE, config.max_size);
1215 	fprintf(f, "%s:%u\n", OPT_ITER_NUM, config.iter_num);
1216 	fprintf(f, "%s:%u\n", OPT_VERBOSE, config.verbose);
1217 	fprintf(f, "%s:%u(%s)\n", OPT_SEARCH_ALG, config.alg.alg,
1218 		config.alg.name);
1219 	fprintf(f, "%s:%u\n", OPT_IPV6, config.ipv6);
1220 }
1221 
1222 static void
1223 check_config(void)
1224 {
1225 	if (config.rule_file == NULL) {
1226 		print_usage(config.prgname);
1227 		rte_exit(-EINVAL, "mandatory option %s is not specified\n",
1228 			OPT_RULE_FILE);
1229 	}
1230 }
1231 
1232 
1233 static void
1234 get_input_opts(int argc, char **argv)
1235 {
1236 	static struct option lgopts[] = {
1237 		{OPT_RULE_FILE, 1, 0, 0},
1238 		{OPT_TRACE_FILE, 1, 0, 0},
1239 		{OPT_TRACE_NUM, 1, 0, 0},
1240 		{OPT_RULE_NUM, 1, 0, 0},
1241 		{OPT_MAX_SIZE, 1, 0, 0},
1242 		{OPT_TRACE_STEP, 1, 0, 0},
1243 		{OPT_BLD_CATEGORIES, 1, 0, 0},
1244 		{OPT_RUN_CATEGORIES, 1, 0, 0},
1245 		{OPT_ITER_NUM, 1, 0, 0},
1246 		{OPT_VERBOSE, 1, 0, 0},
1247 		{OPT_SEARCH_ALG, 1, 0, 0},
1248 		{OPT_IPV6, 2, 0, 0},
1249 		{NULL, 0, 0, 0}
1250 	};
1251 
1252 	int opt, opt_idx;
1253 
1254 	while ((opt = getopt_long(argc, argv, "", lgopts,  &opt_idx)) != EOF) {
1255 
1256 		if (opt != 0) {
1257 			print_usage(config.prgname);
1258 			rte_exit(-EINVAL, "unknown option: %c", opt);
1259 		}
1260 
1261 		if (strcmp(lgopts[opt_idx].name, OPT_RULE_FILE) == 0) {
1262 			config.rule_file = optarg;
1263 		} else if (strcmp(lgopts[opt_idx].name, OPT_TRACE_FILE) == 0) {
1264 			config.trace_file = optarg;
1265 		} else if (strcmp(lgopts[opt_idx].name, OPT_RULE_NUM) == 0) {
1266 			config.nb_rules = get_ulong_opt(optarg,
1267 				lgopts[opt_idx].name, 1, RTE_ACL_MAX_INDEX + 1);
1268 		} else if (strcmp(lgopts[opt_idx].name, OPT_MAX_SIZE) == 0) {
1269 			config.max_size = get_ulong_opt(optarg,
1270 				lgopts[opt_idx].name, 0, SIZE_MAX);
1271 		} else if (strcmp(lgopts[opt_idx].name, OPT_TRACE_NUM) == 0) {
1272 			config.nb_traces = get_ulong_opt(optarg,
1273 				lgopts[opt_idx].name, 1, UINT32_MAX);
1274 		} else if (strcmp(lgopts[opt_idx].name, OPT_TRACE_STEP) == 0) {
1275 			config.trace_step = get_ulong_opt(optarg,
1276 				lgopts[opt_idx].name, 1, TRACE_STEP_MAX);
1277 		} else if (strcmp(lgopts[opt_idx].name,
1278 				OPT_BLD_CATEGORIES) == 0) {
1279 			config.bld_categories = get_ulong_opt(optarg,
1280 				lgopts[opt_idx].name, 1,
1281 				RTE_ACL_MAX_CATEGORIES);
1282 		} else if (strcmp(lgopts[opt_idx].name,
1283 				OPT_RUN_CATEGORIES) == 0) {
1284 			config.run_categories = get_ulong_opt(optarg,
1285 				lgopts[opt_idx].name, 1,
1286 				RTE_ACL_MAX_CATEGORIES);
1287 		} else if (strcmp(lgopts[opt_idx].name, OPT_ITER_NUM) == 0) {
1288 			config.iter_num = get_ulong_opt(optarg,
1289 				lgopts[opt_idx].name, 1, INT32_MAX);
1290 		} else if (strcmp(lgopts[opt_idx].name, OPT_VERBOSE) == 0) {
1291 			config.verbose = get_ulong_opt(optarg,
1292 				lgopts[opt_idx].name, DUMP_NONE, DUMP_MAX);
1293 		} else if (strcmp(lgopts[opt_idx].name,
1294 				OPT_SEARCH_ALG) == 0) {
1295 			get_alg_opt(optarg, lgopts[opt_idx].name);
1296 		} else if (strcmp(lgopts[opt_idx].name, OPT_IPV6) == 0) {
1297 			config.ipv6 = IPV6_FRMT_U32;
1298 			if (optarg != NULL)
1299 				get_ipv6_opt(optarg, lgopts[opt_idx].name);
1300 		}
1301 	}
1302 	config.trace_sz = config.ipv6 ? sizeof(struct ipv6_5tuple) :
1303 						sizeof(struct ipv4_5tuple);
1304 
1305 }
1306 
1307 int
1308 main(int argc, char **argv)
1309 {
1310 	int ret;
1311 	uint32_t lcore;
1312 
1313 	ret = rte_eal_init(argc, argv);
1314 	if (ret < 0)
1315 		rte_panic("Cannot init EAL\n");
1316 
1317 	argc -= ret;
1318 	argv += ret;
1319 
1320 	config.prgname = argv[0];
1321 
1322 	get_input_opts(argc, argv);
1323 	dump_config(stdout);
1324 	check_config();
1325 
1326 	acx_init();
1327 
1328 	if (config.trace_file != NULL)
1329 		tracef_init();
1330 
1331 	RTE_LCORE_FOREACH_WORKER(lcore)
1332 		 rte_eal_remote_launch(search_ip5tuples, NULL, lcore);
1333 
1334 	search_ip5tuples(NULL);
1335 
1336 	rte_eal_mp_wait_lcore();
1337 
1338 	rte_acl_free(config.acx);
1339 	return 0;
1340 }
1341