xref: /dpdk/app/test-acl/main.c (revision 200bc52e5aa0d72e70464c9cd22b55cf536ed13c)
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 enum {
52 	DUMP_NONE,
53 	DUMP_SEARCH,
54 	DUMP_PKT,
55 	DUMP_MAX
56 };
57 
58 struct acl_alg {
59 	const char *name;
60 	enum rte_acl_classify_alg alg;
61 };
62 
63 static const struct acl_alg acl_alg[] = {
64 	{
65 		.name = "scalar",
66 		.alg = RTE_ACL_CLASSIFY_SCALAR,
67 	},
68 	{
69 		.name = "sse",
70 		.alg = RTE_ACL_CLASSIFY_SSE,
71 	},
72 	{
73 		.name = "avx2",
74 		.alg = RTE_ACL_CLASSIFY_AVX2,
75 	},
76 	{
77 		.name = "neon",
78 		.alg = RTE_ACL_CLASSIFY_NEON,
79 	},
80 	{
81 		.name = "altivec",
82 		.alg = RTE_ACL_CLASSIFY_ALTIVEC,
83 	},
84 };
85 
86 static struct {
87 	const char         *prgname;
88 	const char         *rule_file;
89 	const char         *trace_file;
90 	size_t              max_size;
91 	uint32_t            bld_categories;
92 	uint32_t            run_categories;
93 	uint32_t            nb_rules;
94 	uint32_t            nb_traces;
95 	uint32_t            trace_step;
96 	uint32_t            trace_sz;
97 	uint32_t            iter_num;
98 	uint32_t            verbose;
99 	uint32_t            ipv6;
100 	struct acl_alg      alg;
101 	uint32_t            used_traces;
102 	void               *traces;
103 	struct rte_acl_ctx *acx;
104 } config = {
105 	.bld_categories = 3,
106 	.run_categories = 1,
107 	.nb_rules = RULE_NUM,
108 	.nb_traces = TRACE_DEFAULT_NUM,
109 	.trace_step = TRACE_STEP_DEF,
110 	.iter_num = 1,
111 	.verbose = DUMP_MAX,
112 	.alg = {
113 		.name = "default",
114 		.alg = RTE_ACL_CLASSIFY_DEFAULT,
115 	},
116 	.ipv6 = 0
117 };
118 
119 static struct rte_acl_param prm = {
120 	.name = APP_NAME,
121 	.socket_id = SOCKET_ID_ANY,
122 };
123 
124 /*
125  * Rule and trace formats definitions.
126  */
127 
128 struct ipv4_5tuple {
129 	uint8_t  proto;
130 	uint32_t ip_src;
131 	uint32_t ip_dst;
132 	uint16_t port_src;
133 	uint16_t port_dst;
134 };
135 
136 enum {
137 	PROTO_FIELD_IPV4,
138 	SRC_FIELD_IPV4,
139 	DST_FIELD_IPV4,
140 	SRCP_FIELD_IPV4,
141 	DSTP_FIELD_IPV4,
142 	NUM_FIELDS_IPV4
143 };
144 
145 /*
146  * That effectively defines order of IPV4VLAN classifications:
147  *  - PROTO
148  *  - VLAN (TAG and DOMAIN)
149  *  - SRC IP ADDRESS
150  *  - DST IP ADDRESS
151  *  - PORTS (SRC and DST)
152  */
153 enum {
154 	RTE_ACL_IPV4VLAN_PROTO,
155 	RTE_ACL_IPV4VLAN_VLAN,
156 	RTE_ACL_IPV4VLAN_SRC,
157 	RTE_ACL_IPV4VLAN_DST,
158 	RTE_ACL_IPV4VLAN_PORTS,
159 	RTE_ACL_IPV4VLAN_NUM
160 };
161 
162 struct rte_acl_field_def ipv4_defs[NUM_FIELDS_IPV4] = {
163 	{
164 		.type = RTE_ACL_FIELD_TYPE_BITMASK,
165 		.size = sizeof(uint8_t),
166 		.field_index = PROTO_FIELD_IPV4,
167 		.input_index = RTE_ACL_IPV4VLAN_PROTO,
168 		.offset = offsetof(struct ipv4_5tuple, proto),
169 	},
170 	{
171 		.type = RTE_ACL_FIELD_TYPE_MASK,
172 		.size = sizeof(uint32_t),
173 		.field_index = SRC_FIELD_IPV4,
174 		.input_index = RTE_ACL_IPV4VLAN_SRC,
175 		.offset = offsetof(struct ipv4_5tuple, ip_src),
176 	},
177 	{
178 		.type = RTE_ACL_FIELD_TYPE_MASK,
179 		.size = sizeof(uint32_t),
180 		.field_index = DST_FIELD_IPV4,
181 		.input_index = RTE_ACL_IPV4VLAN_DST,
182 		.offset = offsetof(struct ipv4_5tuple, ip_dst),
183 	},
184 	{
185 		.type = RTE_ACL_FIELD_TYPE_RANGE,
186 		.size = sizeof(uint16_t),
187 		.field_index = SRCP_FIELD_IPV4,
188 		.input_index = RTE_ACL_IPV4VLAN_PORTS,
189 		.offset = offsetof(struct ipv4_5tuple, port_src),
190 	},
191 	{
192 		.type = RTE_ACL_FIELD_TYPE_RANGE,
193 		.size = sizeof(uint16_t),
194 		.field_index = DSTP_FIELD_IPV4,
195 		.input_index = RTE_ACL_IPV4VLAN_PORTS,
196 		.offset = offsetof(struct ipv4_5tuple, port_dst),
197 	},
198 };
199 
200 #define	IPV6_ADDR_LEN	16
201 #define	IPV6_ADDR_U16	(IPV6_ADDR_LEN / sizeof(uint16_t))
202 #define	IPV6_ADDR_U32	(IPV6_ADDR_LEN / sizeof(uint32_t))
203 
204 struct ipv6_5tuple {
205 	uint8_t  proto;
206 	uint32_t ip_src[IPV6_ADDR_U32];
207 	uint32_t ip_dst[IPV6_ADDR_U32];
208 	uint16_t port_src;
209 	uint16_t port_dst;
210 };
211 
212 enum {
213 	PROTO_FIELD_IPV6,
214 	SRC1_FIELD_IPV6,
215 	SRC2_FIELD_IPV6,
216 	SRC3_FIELD_IPV6,
217 	SRC4_FIELD_IPV6,
218 	DST1_FIELD_IPV6,
219 	DST2_FIELD_IPV6,
220 	DST3_FIELD_IPV6,
221 	DST4_FIELD_IPV6,
222 	SRCP_FIELD_IPV6,
223 	DSTP_FIELD_IPV6,
224 	NUM_FIELDS_IPV6
225 };
226 
227 struct rte_acl_field_def ipv6_defs[NUM_FIELDS_IPV6] = {
228 	{
229 		.type = RTE_ACL_FIELD_TYPE_BITMASK,
230 		.size = sizeof(uint8_t),
231 		.field_index = PROTO_FIELD_IPV6,
232 		.input_index = PROTO_FIELD_IPV6,
233 		.offset = offsetof(struct ipv6_5tuple, proto),
234 	},
235 	{
236 		.type = RTE_ACL_FIELD_TYPE_MASK,
237 		.size = sizeof(uint32_t),
238 		.field_index = SRC1_FIELD_IPV6,
239 		.input_index = SRC1_FIELD_IPV6,
240 		.offset = offsetof(struct ipv6_5tuple, ip_src[0]),
241 	},
242 	{
243 		.type = RTE_ACL_FIELD_TYPE_MASK,
244 		.size = sizeof(uint32_t),
245 		.field_index = SRC2_FIELD_IPV6,
246 		.input_index = SRC2_FIELD_IPV6,
247 		.offset = offsetof(struct ipv6_5tuple, ip_src[1]),
248 	},
249 	{
250 		.type = RTE_ACL_FIELD_TYPE_MASK,
251 		.size = sizeof(uint32_t),
252 		.field_index = SRC3_FIELD_IPV6,
253 		.input_index = SRC3_FIELD_IPV6,
254 		.offset = offsetof(struct ipv6_5tuple, ip_src[2]),
255 	},
256 	{
257 		.type = RTE_ACL_FIELD_TYPE_MASK,
258 		.size = sizeof(uint32_t),
259 		.field_index = SRC4_FIELD_IPV6,
260 		.input_index = SRC4_FIELD_IPV6,
261 		.offset = offsetof(struct ipv6_5tuple, ip_src[3]),
262 	},
263 	{
264 		.type = RTE_ACL_FIELD_TYPE_MASK,
265 		.size = sizeof(uint32_t),
266 		.field_index = DST1_FIELD_IPV6,
267 		.input_index = DST1_FIELD_IPV6,
268 		.offset = offsetof(struct ipv6_5tuple, ip_dst[0]),
269 	},
270 	{
271 		.type = RTE_ACL_FIELD_TYPE_MASK,
272 		.size = sizeof(uint32_t),
273 		.field_index = DST2_FIELD_IPV6,
274 		.input_index = DST2_FIELD_IPV6,
275 		.offset = offsetof(struct ipv6_5tuple, ip_dst[1]),
276 	},
277 	{
278 		.type = RTE_ACL_FIELD_TYPE_MASK,
279 		.size = sizeof(uint32_t),
280 		.field_index = DST3_FIELD_IPV6,
281 		.input_index = DST3_FIELD_IPV6,
282 		.offset = offsetof(struct ipv6_5tuple, ip_dst[2]),
283 	},
284 	{
285 		.type = RTE_ACL_FIELD_TYPE_MASK,
286 		.size = sizeof(uint32_t),
287 		.field_index = DST4_FIELD_IPV6,
288 		.input_index = DST4_FIELD_IPV6,
289 		.offset = offsetof(struct ipv6_5tuple, ip_dst[3]),
290 	},
291 	{
292 		.type = RTE_ACL_FIELD_TYPE_RANGE,
293 		.size = sizeof(uint16_t),
294 		.field_index = SRCP_FIELD_IPV6,
295 		.input_index = SRCP_FIELD_IPV6,
296 		.offset = offsetof(struct ipv6_5tuple, port_src),
297 	},
298 	{
299 		.type = RTE_ACL_FIELD_TYPE_RANGE,
300 		.size = sizeof(uint16_t),
301 		.field_index = DSTP_FIELD_IPV6,
302 		.input_index = SRCP_FIELD_IPV6,
303 		.offset = offsetof(struct ipv6_5tuple, port_dst),
304 	},
305 };
306 
307 
308 enum {
309 	CB_FLD_SRC_ADDR,
310 	CB_FLD_DST_ADDR,
311 	CB_FLD_SRC_PORT_LOW,
312 	CB_FLD_SRC_PORT_DLM,
313 	CB_FLD_SRC_PORT_HIGH,
314 	CB_FLD_DST_PORT_LOW,
315 	CB_FLD_DST_PORT_DLM,
316 	CB_FLD_DST_PORT_HIGH,
317 	CB_FLD_PROTO,
318 	CB_FLD_NUM,
319 };
320 
321 enum {
322 	CB_TRC_SRC_ADDR,
323 	CB_TRC_DST_ADDR,
324 	CB_TRC_SRC_PORT,
325 	CB_TRC_DST_PORT,
326 	CB_TRC_PROTO,
327 	CB_TRC_NUM,
328 };
329 
330 RTE_ACL_RULE_DEF(acl_rule, RTE_ACL_MAX_FIELDS);
331 
332 static const char cb_port_delim[] = ":";
333 
334 static char line[LINE_MAX];
335 
336 #define	dump_verbose(lvl, fh, fmt, args...)	do { \
337 	if ((lvl) <= (int32_t)config.verbose)        \
338 		fprintf(fh, fmt, ##args);            \
339 } while (0)
340 
341 
342 /*
343  * Parse ClassBench input trace (test vectors and expected results) file.
344  * Expected format:
345  * <src_ipv4_addr> <space> <dst_ipv4_addr> <space> \
346  * <src_port> <space> <dst_port> <space> <proto>
347  */
348 static int
349 parse_cb_ipv4_trace(char *str, struct ipv4_5tuple *v)
350 {
351 	int i;
352 	char *s, *sp, *in[CB_TRC_NUM];
353 	static const char *dlm = " \t\n";
354 
355 	s = str;
356 	for (i = 0; i != RTE_DIM(in); i++) {
357 		in[i] = strtok_r(s, dlm, &sp);
358 		if (in[i] == NULL)
359 			return -EINVAL;
360 		s = NULL;
361 	}
362 
363 	GET_CB_FIELD(in[CB_TRC_SRC_ADDR], v->ip_src, 0, UINT32_MAX, 0);
364 	GET_CB_FIELD(in[CB_TRC_DST_ADDR], v->ip_dst, 0, UINT32_MAX, 0);
365 	GET_CB_FIELD(in[CB_TRC_SRC_PORT], v->port_src, 0, UINT16_MAX, 0);
366 	GET_CB_FIELD(in[CB_TRC_DST_PORT], v->port_dst, 0, UINT16_MAX, 0);
367 	GET_CB_FIELD(in[CB_TRC_PROTO], v->proto, 0, UINT8_MAX, 0);
368 
369 	/* convert to network byte order. */
370 	v->ip_src = rte_cpu_to_be_32(v->ip_src);
371 	v->ip_dst = rte_cpu_to_be_32(v->ip_dst);
372 	v->port_src = rte_cpu_to_be_16(v->port_src);
373 	v->port_dst = rte_cpu_to_be_16(v->port_dst);
374 
375 	return 0;
376 }
377 
378 /*
379  * Parses IPV6 address, exepcts the following format:
380  * XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX (where X - is a hexedecimal digit).
381  */
382 static int
383 parse_ipv6_addr(const char *in, const char **end, uint32_t v[IPV6_ADDR_U32],
384 	char dlm)
385 {
386 	uint32_t addr[IPV6_ADDR_U16];
387 
388 	GET_CB_FIELD(in, addr[0], 16, UINT16_MAX, ':');
389 	GET_CB_FIELD(in, addr[1], 16, UINT16_MAX, ':');
390 	GET_CB_FIELD(in, addr[2], 16, UINT16_MAX, ':');
391 	GET_CB_FIELD(in, addr[3], 16, UINT16_MAX, ':');
392 	GET_CB_FIELD(in, addr[4], 16, UINT16_MAX, ':');
393 	GET_CB_FIELD(in, addr[5], 16, UINT16_MAX, ':');
394 	GET_CB_FIELD(in, addr[6], 16, UINT16_MAX, ':');
395 	GET_CB_FIELD(in, addr[7], 16, UINT16_MAX, dlm);
396 
397 	*end = in;
398 
399 	v[0] = (addr[0] << 16) + addr[1];
400 	v[1] = (addr[2] << 16) + addr[3];
401 	v[2] = (addr[4] << 16) + addr[5];
402 	v[3] = (addr[6] << 16) + addr[7];
403 
404 	return 0;
405 }
406 
407 static int
408 parse_cb_ipv6_addr_trace(const char *in, uint32_t v[IPV6_ADDR_U32])
409 {
410 	int32_t rc;
411 	const char *end;
412 
413 	rc = parse_ipv6_addr(in, &end, v, 0);
414 	if (rc != 0)
415 		return rc;
416 
417 	v[0] = rte_cpu_to_be_32(v[0]);
418 	v[1] = rte_cpu_to_be_32(v[1]);
419 	v[2] = rte_cpu_to_be_32(v[2]);
420 	v[3] = rte_cpu_to_be_32(v[3]);
421 
422 	return 0;
423 }
424 
425 /*
426  * Parse ClassBench input trace (test vectors and expected results) file.
427  * Expected format:
428  * <src_ipv6_addr> <space> <dst_ipv6_addr> <space> \
429  * <src_port> <space> <dst_port> <space> <proto>
430  */
431 static int
432 parse_cb_ipv6_trace(char *str, struct ipv6_5tuple *v)
433 {
434 	int32_t i, rc;
435 	char *s, *sp, *in[CB_TRC_NUM];
436 	static const char *dlm = " \t\n";
437 
438 	s = str;
439 	for (i = 0; i != RTE_DIM(in); i++) {
440 		in[i] = strtok_r(s, dlm, &sp);
441 		if (in[i] == NULL)
442 			return -EINVAL;
443 		s = NULL;
444 	}
445 
446 	/* get ip6 src address. */
447 	rc = parse_cb_ipv6_addr_trace(in[CB_TRC_SRC_ADDR], v->ip_src);
448 	if (rc != 0)
449 		return rc;
450 
451 	/* get ip6 dst address. */
452 	rc = parse_cb_ipv6_addr_trace(in[CB_TRC_DST_ADDR], v->ip_dst);
453 	if (rc != 0)
454 		return rc;
455 
456 	GET_CB_FIELD(in[CB_TRC_SRC_PORT], v->port_src, 0, UINT16_MAX, 0);
457 	GET_CB_FIELD(in[CB_TRC_DST_PORT], v->port_dst, 0, UINT16_MAX, 0);
458 	GET_CB_FIELD(in[CB_TRC_PROTO], v->proto, 0, UINT8_MAX, 0);
459 
460 	/* convert to network byte order. */
461 	v->port_src = rte_cpu_to_be_16(v->port_src);
462 	v->port_dst = rte_cpu_to_be_16(v->port_dst);
463 
464 	return 0;
465 }
466 
467 static void
468 tracef_init(void)
469 {
470 	static const char name[] = APP_NAME;
471 	FILE *f;
472 	size_t sz;
473 	uint32_t n;
474 	struct ipv4_5tuple *v;
475 	struct ipv6_5tuple *w;
476 
477 	sz = config.nb_traces * (config.ipv6 ? sizeof(*w) : sizeof(*v));
478 	config.traces = rte_zmalloc_socket(name, sz, RTE_CACHE_LINE_SIZE,
479 			SOCKET_ID_ANY);
480 	if (config.traces == NULL)
481 		rte_exit(EXIT_FAILURE, "Cannot allocate %zu bytes for "
482 			"requested %u number of trace records\n",
483 			sz, config.nb_traces);
484 
485 	f = fopen(config.trace_file, "r");
486 	if (f == NULL)
487 		rte_exit(-EINVAL, "failed to open file: %s\n",
488 			config.trace_file);
489 
490 	v = config.traces;
491 	w = config.traces;
492 	for (n = 0; n != config.nb_traces; n++) {
493 
494 		if (fgets(line, sizeof(line), f) == NULL)
495 			break;
496 
497 		if (config.ipv6) {
498 			if (parse_cb_ipv6_trace(line, w + n) != 0)
499 				rte_exit(EXIT_FAILURE,
500 					"%s: failed to parse ipv6 trace "
501 					"record at line %u\n",
502 					config.trace_file, n + 1);
503 		} else {
504 			if (parse_cb_ipv4_trace(line, v + n) != 0)
505 				rte_exit(EXIT_FAILURE,
506 					"%s: failed to parse ipv4 trace "
507 					"record at line %u\n",
508 					config.trace_file, n + 1);
509 		}
510 	}
511 
512 	config.used_traces = n;
513 	fclose(f);
514 }
515 
516 static int
517 parse_ipv6_net(const char *in, struct rte_acl_field field[4])
518 {
519 	int32_t rc;
520 	const char *mp;
521 	uint32_t i, m, v[4];
522 	const uint32_t nbu32 = sizeof(uint32_t) * CHAR_BIT;
523 
524 	/* get address. */
525 	rc = parse_ipv6_addr(in, &mp, v, '/');
526 	if (rc != 0)
527 		return rc;
528 
529 	/* get mask. */
530 	GET_CB_FIELD(mp, m, 0, CHAR_BIT * sizeof(v), 0);
531 
532 	/* put all together. */
533 	for (i = 0; i != RTE_DIM(v); i++) {
534 		if (m >= (i + 1) * nbu32)
535 			field[i].mask_range.u32 = nbu32;
536 		else
537 			field[i].mask_range.u32 = m > (i * nbu32) ?
538 				m - (i * 32) : 0;
539 
540 		field[i].value.u32 = v[i];
541 	}
542 
543 	return 0;
544 }
545 
546 
547 static int
548 parse_cb_ipv6_rule(char *str, struct acl_rule *v)
549 {
550 	int i, rc;
551 	char *s, *sp, *in[CB_FLD_NUM];
552 	static const char *dlm = " \t\n";
553 
554 	/*
555 	 * Skip leading '@'
556 	 */
557 	if (strchr(str, '@') != str)
558 		return -EINVAL;
559 
560 	s = str + 1;
561 
562 	for (i = 0; i != RTE_DIM(in); i++) {
563 		in[i] = strtok_r(s, dlm, &sp);
564 		if (in[i] == NULL)
565 			return -EINVAL;
566 		s = NULL;
567 	}
568 
569 	rc = parse_ipv6_net(in[CB_FLD_SRC_ADDR], v->field + SRC1_FIELD_IPV6);
570 	if (rc != 0) {
571 		RTE_LOG(ERR, TESTACL,
572 			"failed to read source address/mask: %s\n",
573 			in[CB_FLD_SRC_ADDR]);
574 		return rc;
575 	}
576 
577 	rc = parse_ipv6_net(in[CB_FLD_DST_ADDR], v->field + DST1_FIELD_IPV6);
578 	if (rc != 0) {
579 		RTE_LOG(ERR, TESTACL,
580 			"failed to read destination address/mask: %s\n",
581 			in[CB_FLD_DST_ADDR]);
582 		return rc;
583 	}
584 
585 	/* source port. */
586 	GET_CB_FIELD(in[CB_FLD_SRC_PORT_LOW],
587 		v->field[SRCP_FIELD_IPV6].value.u16,
588 		0, UINT16_MAX, 0);
589 	GET_CB_FIELD(in[CB_FLD_SRC_PORT_HIGH],
590 		v->field[SRCP_FIELD_IPV6].mask_range.u16,
591 		0, UINT16_MAX, 0);
592 
593 	if (strncmp(in[CB_FLD_SRC_PORT_DLM], cb_port_delim,
594 			sizeof(cb_port_delim)) != 0)
595 		return -EINVAL;
596 
597 	/* destination port. */
598 	GET_CB_FIELD(in[CB_FLD_DST_PORT_LOW],
599 		v->field[DSTP_FIELD_IPV6].value.u16,
600 		0, UINT16_MAX, 0);
601 	GET_CB_FIELD(in[CB_FLD_DST_PORT_HIGH],
602 		v->field[DSTP_FIELD_IPV6].mask_range.u16,
603 		0, UINT16_MAX, 0);
604 
605 	if (strncmp(in[CB_FLD_DST_PORT_DLM], cb_port_delim,
606 			sizeof(cb_port_delim)) != 0)
607 		return -EINVAL;
608 
609 	GET_CB_FIELD(in[CB_FLD_PROTO], v->field[PROTO_FIELD_IPV6].value.u8,
610 		0, UINT8_MAX, '/');
611 	GET_CB_FIELD(in[CB_FLD_PROTO], v->field[PROTO_FIELD_IPV6].mask_range.u8,
612 		0, UINT8_MAX, 0);
613 
614 	return 0;
615 }
616 
617 static int
618 parse_ipv4_net(const char *in, uint32_t *addr, uint32_t *mask_len)
619 {
620 	uint8_t a, b, c, d, m;
621 
622 	GET_CB_FIELD(in, a, 0, UINT8_MAX, '.');
623 	GET_CB_FIELD(in, b, 0, UINT8_MAX, '.');
624 	GET_CB_FIELD(in, c, 0, UINT8_MAX, '.');
625 	GET_CB_FIELD(in, d, 0, UINT8_MAX, '/');
626 	GET_CB_FIELD(in, m, 0, sizeof(uint32_t) * CHAR_BIT, 0);
627 
628 	addr[0] = RTE_IPv4(a, b, c, d);
629 	mask_len[0] = m;
630 
631 	return 0;
632 }
633 /*
634  * Parse ClassBench rules file.
635  * Expected format:
636  * '@'<src_ipv4_addr>'/'<masklen> <space> \
637  * <dst_ipv4_addr>'/'<masklen> <space> \
638  * <src_port_low> <space> ":" <src_port_high> <space> \
639  * <dst_port_low> <space> ":" <dst_port_high> <space> \
640  * <proto>'/'<mask>
641  */
642 static int
643 parse_cb_ipv4_rule(char *str, struct acl_rule *v)
644 {
645 	int i, rc;
646 	char *s, *sp, *in[CB_FLD_NUM];
647 	static const char *dlm = " \t\n";
648 
649 	/*
650 	 * Skip leading '@'
651 	 */
652 	if (strchr(str, '@') != str)
653 		return -EINVAL;
654 
655 	s = str + 1;
656 
657 	for (i = 0; i != RTE_DIM(in); i++) {
658 		in[i] = strtok_r(s, dlm, &sp);
659 		if (in[i] == NULL)
660 			return -EINVAL;
661 		s = NULL;
662 	}
663 
664 	rc = parse_ipv4_net(in[CB_FLD_SRC_ADDR],
665 			&v->field[SRC_FIELD_IPV4].value.u32,
666 			&v->field[SRC_FIELD_IPV4].mask_range.u32);
667 	if (rc != 0) {
668 		RTE_LOG(ERR, TESTACL,
669 			"failed to read source address/mask: %s\n",
670 			in[CB_FLD_SRC_ADDR]);
671 		return rc;
672 	}
673 
674 	rc = parse_ipv4_net(in[CB_FLD_DST_ADDR],
675 			&v->field[DST_FIELD_IPV4].value.u32,
676 			&v->field[DST_FIELD_IPV4].mask_range.u32);
677 	if (rc != 0) {
678 		RTE_LOG(ERR, TESTACL,
679 			"failed to read destination address/mask: %s\n",
680 			in[CB_FLD_DST_ADDR]);
681 		return rc;
682 	}
683 
684 	/* source port. */
685 	GET_CB_FIELD(in[CB_FLD_SRC_PORT_LOW],
686 		v->field[SRCP_FIELD_IPV4].value.u16,
687 		0, UINT16_MAX, 0);
688 	GET_CB_FIELD(in[CB_FLD_SRC_PORT_HIGH],
689 		v->field[SRCP_FIELD_IPV4].mask_range.u16,
690 		0, UINT16_MAX, 0);
691 
692 	if (strncmp(in[CB_FLD_SRC_PORT_DLM], cb_port_delim,
693 			sizeof(cb_port_delim)) != 0)
694 		return -EINVAL;
695 
696 	/* destination port. */
697 	GET_CB_FIELD(in[CB_FLD_DST_PORT_LOW],
698 		v->field[DSTP_FIELD_IPV4].value.u16,
699 		0, UINT16_MAX, 0);
700 	GET_CB_FIELD(in[CB_FLD_DST_PORT_HIGH],
701 		v->field[DSTP_FIELD_IPV4].mask_range.u16,
702 		0, UINT16_MAX, 0);
703 
704 	if (strncmp(in[CB_FLD_DST_PORT_DLM], cb_port_delim,
705 			sizeof(cb_port_delim)) != 0)
706 		return -EINVAL;
707 
708 	GET_CB_FIELD(in[CB_FLD_PROTO], v->field[PROTO_FIELD_IPV4].value.u8,
709 		0, UINT8_MAX, '/');
710 	GET_CB_FIELD(in[CB_FLD_PROTO], v->field[PROTO_FIELD_IPV4].mask_range.u8,
711 		0, UINT8_MAX, 0);
712 
713 	return 0;
714 }
715 
716 typedef int (*parse_5tuple)(char *text, struct acl_rule *rule);
717 
718 static int
719 add_cb_rules(FILE *f, struct rte_acl_ctx *ctx)
720 {
721 	int rc;
722 	uint32_t n;
723 	struct acl_rule v;
724 	parse_5tuple parser;
725 
726 	memset(&v, 0, sizeof(v));
727 	parser = (config.ipv6 != 0) ? parse_cb_ipv6_rule : parse_cb_ipv4_rule;
728 
729 	for (n = 1; fgets(line, sizeof(line), f) != NULL; n++) {
730 
731 		rc = parser(line, &v);
732 		if (rc != 0) {
733 			RTE_LOG(ERR, TESTACL, "line %u: parse_cb_ipv4vlan_rule"
734 				" failed, error code: %d (%s)\n",
735 				n, rc, strerror(-rc));
736 			return rc;
737 		}
738 
739 		v.data.category_mask = RTE_LEN2MASK(RTE_ACL_MAX_CATEGORIES,
740 			typeof(v.data.category_mask));
741 		v.data.priority = RTE_ACL_MAX_PRIORITY - n;
742 		v.data.userdata = n;
743 
744 		rc = rte_acl_add_rules(ctx, (struct rte_acl_rule *)&v, 1);
745 		if (rc != 0) {
746 			RTE_LOG(ERR, TESTACL, "line %u: failed to add rules "
747 				"into ACL context, error code: %d (%s)\n",
748 				n, rc, strerror(-rc));
749 			return rc;
750 		}
751 	}
752 
753 	return 0;
754 }
755 
756 static void
757 acx_init(void)
758 {
759 	int ret;
760 	FILE *f;
761 	struct rte_acl_config cfg;
762 
763 	memset(&cfg, 0, sizeof(cfg));
764 
765 	/* setup ACL build config. */
766 	if (config.ipv6) {
767 		cfg.num_fields = RTE_DIM(ipv6_defs);
768 		memcpy(&cfg.defs, ipv6_defs, sizeof(ipv6_defs));
769 	} else {
770 		cfg.num_fields = RTE_DIM(ipv4_defs);
771 		memcpy(&cfg.defs, ipv4_defs, sizeof(ipv4_defs));
772 	}
773 	cfg.num_categories = config.bld_categories;
774 	cfg.max_size = config.max_size;
775 
776 	/* setup ACL creation parameters. */
777 	prm.rule_size = RTE_ACL_RULE_SZ(cfg.num_fields);
778 	prm.max_rule_num = config.nb_rules;
779 
780 	config.acx = rte_acl_create(&prm);
781 	if (config.acx == NULL)
782 		rte_exit(rte_errno, "failed to create ACL context\n");
783 
784 	/* set default classify method for this context. */
785 	if (config.alg.alg != RTE_ACL_CLASSIFY_DEFAULT) {
786 		ret = rte_acl_set_ctx_classify(config.acx, config.alg.alg);
787 		if (ret != 0)
788 			rte_exit(ret, "failed to setup %s method "
789 				"for ACL context\n", config.alg.name);
790 	}
791 
792 	/* add ACL rules. */
793 	f = fopen(config.rule_file, "r");
794 	if (f == NULL)
795 		rte_exit(-EINVAL, "failed to open file %s\n",
796 			config.rule_file);
797 
798 	ret = add_cb_rules(f, config.acx);
799 	if (ret != 0)
800 		rte_exit(ret, "failed to add rules into ACL context\n");
801 
802 	fclose(f);
803 
804 	/* perform build. */
805 	ret = rte_acl_build(config.acx, &cfg);
806 
807 	dump_verbose(DUMP_NONE, stdout,
808 		"rte_acl_build(%u) finished with %d\n",
809 		config.bld_categories, ret);
810 
811 	rte_acl_dump(config.acx);
812 
813 	if (ret != 0)
814 		rte_exit(ret, "failed to build search context\n");
815 }
816 
817 static uint32_t
818 search_ip5tuples_once(uint32_t categories, uint32_t step, const char *alg)
819 {
820 	int ret;
821 	uint32_t i, j, k, n, r;
822 	const uint8_t *data[step], *v;
823 	uint32_t results[step * categories];
824 
825 	v = config.traces;
826 	for (i = 0; i != config.used_traces; i += n) {
827 
828 		n = RTE_MIN(step, config.used_traces - i);
829 
830 		for (j = 0; j != n; j++) {
831 			data[j] = v;
832 			v += config.trace_sz;
833 		}
834 
835 		ret = rte_acl_classify(config.acx, data, results,
836 			n, categories);
837 
838 		if (ret != 0)
839 			rte_exit(ret, "classify for ipv%c_5tuples returns %d\n",
840 				config.ipv6 ? '6' : '4', ret);
841 
842 		for (r = 0, j = 0; j != n; j++) {
843 			for (k = 0; k != categories; k++, r++) {
844 				dump_verbose(DUMP_PKT, stdout,
845 					"ipv%c_5tuple: %u, category: %u, "
846 					"result: %u\n",
847 					config.ipv6 ? '6' : '4',
848 					i + j + 1, k, results[r] - 1);
849 			}
850 
851 		}
852 	}
853 
854 	dump_verbose(DUMP_SEARCH, stdout,
855 		"%s(%u, %u, %s) returns %u\n", __func__,
856 		categories, step, alg, i);
857 	return i;
858 }
859 
860 static int
861 search_ip5tuples(__attribute__((unused)) void *arg)
862 {
863 	uint64_t pkt, start, tm;
864 	uint32_t i, lcore;
865 
866 	lcore = rte_lcore_id();
867 	start = rte_rdtsc();
868 	pkt = 0;
869 
870 	for (i = 0; i != config.iter_num; i++) {
871 		pkt += search_ip5tuples_once(config.run_categories,
872 			config.trace_step, config.alg.name);
873 	}
874 
875 	tm = rte_rdtsc() - start;
876 	dump_verbose(DUMP_NONE, stdout,
877 		"%s  @lcore %u: %" PRIu32 " iterations, %" PRIu64 " pkts, %"
878 		PRIu32 " categories, %" PRIu64 " cycles, %#Lf cycles/pkt\n",
879 		__func__, lcore, i, pkt, config.run_categories,
880 		tm, (pkt == 0) ? 0 : (long double)tm / pkt);
881 
882 	return 0;
883 }
884 
885 static unsigned long
886 get_ulong_opt(const char *opt, const char *name, size_t min, size_t max)
887 {
888 	unsigned long val;
889 	char *end;
890 
891 	errno = 0;
892 	val = strtoul(opt, &end, 0);
893 	if (errno != 0 || end[0] != 0 || val > max || val < min)
894 		rte_exit(-EINVAL, "invalid value: \"%s\" for option: %s\n",
895 			opt, name);
896 	return val;
897 }
898 
899 static void
900 get_alg_opt(const char *opt, const char *name)
901 {
902 	uint32_t i;
903 
904 	for (i = 0; i != RTE_DIM(acl_alg); i++) {
905 		if (strcmp(opt, acl_alg[i].name) == 0) {
906 			config.alg = acl_alg[i];
907 			return;
908 		}
909 	}
910 
911 	rte_exit(-EINVAL, "invalid value: \"%s\" for option: %s\n",
912 		opt, name);
913 }
914 
915 static void
916 print_usage(const char *prgname)
917 {
918 	uint32_t i, n, rc;
919 	char buf[PATH_MAX];
920 
921 	n = 0;
922 	buf[0] = 0;
923 
924 	for (i = 0; i < RTE_DIM(acl_alg) - 1; i++) {
925 		rc = snprintf(buf + n, sizeof(buf) - n, "%s|",
926 			acl_alg[i].name);
927 		if (rc > sizeof(buf) - n)
928 			break;
929 		n += rc;
930 	}
931 
932 	strlcpy(buf + n, acl_alg[i].name, sizeof(buf) - n);
933 
934 	fprintf(stdout,
935 		PRINT_USAGE_START
936 		"--" OPT_RULE_FILE "=<rules set file>\n"
937 		"[--" OPT_TRACE_FILE "=<input traces file>]\n"
938 		"[--" OPT_RULE_NUM
939 			"=<maximum number of rules for ACL context>]\n"
940 		"[--" OPT_TRACE_NUM
941 			"=<number of traces to read binary file in>]\n"
942 		"[--" OPT_TRACE_STEP
943 			"=<number of traces to classify per one call>]\n"
944 		"[--" OPT_BLD_CATEGORIES
945 			"=<number of categories to build with>]\n"
946 		"[--" OPT_RUN_CATEGORIES
947 			"=<number of categories to run with> "
948 			"should be either 1 or multiple of %zu, "
949 			"but not greater then %u]\n"
950 		"[--" OPT_MAX_SIZE
951 			"=<size limit (in bytes) for runtime ACL strucutures> "
952 			"leave 0 for default behaviour]\n"
953 		"[--" OPT_ITER_NUM "=<number of iterations to perform>]\n"
954 		"[--" OPT_VERBOSE "=<verbose level>]\n"
955 		"[--" OPT_SEARCH_ALG "=%s]\n"
956 		"[--" OPT_IPV6 "=<IPv6 rules and trace files>]\n",
957 		prgname, RTE_ACL_RESULTS_MULTIPLIER,
958 		(uint32_t)RTE_ACL_MAX_CATEGORIES,
959 		buf);
960 }
961 
962 static void
963 dump_config(FILE *f)
964 {
965 	fprintf(f, "%s:\n", __func__);
966 	fprintf(f, "%s:%s\n", OPT_RULE_FILE, config.rule_file);
967 	fprintf(f, "%s:%s\n", OPT_TRACE_FILE, config.trace_file);
968 	fprintf(f, "%s:%u\n", OPT_RULE_NUM, config.nb_rules);
969 	fprintf(f, "%s:%u\n", OPT_TRACE_NUM, config.nb_traces);
970 	fprintf(f, "%s:%u\n", OPT_TRACE_STEP, config.trace_step);
971 	fprintf(f, "%s:%u\n", OPT_BLD_CATEGORIES, config.bld_categories);
972 	fprintf(f, "%s:%u\n", OPT_RUN_CATEGORIES, config.run_categories);
973 	fprintf(f, "%s:%zu\n", OPT_MAX_SIZE, config.max_size);
974 	fprintf(f, "%s:%u\n", OPT_ITER_NUM, config.iter_num);
975 	fprintf(f, "%s:%u\n", OPT_VERBOSE, config.verbose);
976 	fprintf(f, "%s:%u(%s)\n", OPT_SEARCH_ALG, config.alg.alg,
977 		config.alg.name);
978 	fprintf(f, "%s:%u\n", OPT_IPV6, config.ipv6);
979 }
980 
981 static void
982 check_config(void)
983 {
984 	if (config.rule_file == NULL) {
985 		print_usage(config.prgname);
986 		rte_exit(-EINVAL, "mandatory option %s is not specified\n",
987 			OPT_RULE_FILE);
988 	}
989 }
990 
991 
992 static void
993 get_input_opts(int argc, char **argv)
994 {
995 	static struct option lgopts[] = {
996 		{OPT_RULE_FILE, 1, 0, 0},
997 		{OPT_TRACE_FILE, 1, 0, 0},
998 		{OPT_TRACE_NUM, 1, 0, 0},
999 		{OPT_RULE_NUM, 1, 0, 0},
1000 		{OPT_MAX_SIZE, 1, 0, 0},
1001 		{OPT_TRACE_STEP, 1, 0, 0},
1002 		{OPT_BLD_CATEGORIES, 1, 0, 0},
1003 		{OPT_RUN_CATEGORIES, 1, 0, 0},
1004 		{OPT_ITER_NUM, 1, 0, 0},
1005 		{OPT_VERBOSE, 1, 0, 0},
1006 		{OPT_SEARCH_ALG, 1, 0, 0},
1007 		{OPT_IPV6, 0, 0, 0},
1008 		{NULL, 0, 0, 0}
1009 	};
1010 
1011 	int opt, opt_idx;
1012 
1013 	while ((opt = getopt_long(argc, argv, "", lgopts,  &opt_idx)) != EOF) {
1014 
1015 		if (opt != 0) {
1016 			print_usage(config.prgname);
1017 			rte_exit(-EINVAL, "unknown option: %c", opt);
1018 		}
1019 
1020 		if (strcmp(lgopts[opt_idx].name, OPT_RULE_FILE) == 0) {
1021 			config.rule_file = optarg;
1022 		} else if (strcmp(lgopts[opt_idx].name, OPT_TRACE_FILE) == 0) {
1023 			config.trace_file = optarg;
1024 		} else if (strcmp(lgopts[opt_idx].name, OPT_RULE_NUM) == 0) {
1025 			config.nb_rules = get_ulong_opt(optarg,
1026 				lgopts[opt_idx].name, 1, RTE_ACL_MAX_INDEX + 1);
1027 		} else if (strcmp(lgopts[opt_idx].name, OPT_MAX_SIZE) == 0) {
1028 			config.max_size = get_ulong_opt(optarg,
1029 				lgopts[opt_idx].name, 0, SIZE_MAX);
1030 		} else if (strcmp(lgopts[opt_idx].name, OPT_TRACE_NUM) == 0) {
1031 			config.nb_traces = get_ulong_opt(optarg,
1032 				lgopts[opt_idx].name, 1, UINT32_MAX);
1033 		} else if (strcmp(lgopts[opt_idx].name, OPT_TRACE_STEP) == 0) {
1034 			config.trace_step = get_ulong_opt(optarg,
1035 				lgopts[opt_idx].name, 1, TRACE_STEP_MAX);
1036 		} else if (strcmp(lgopts[opt_idx].name,
1037 				OPT_BLD_CATEGORIES) == 0) {
1038 			config.bld_categories = get_ulong_opt(optarg,
1039 				lgopts[opt_idx].name, 1,
1040 				RTE_ACL_MAX_CATEGORIES);
1041 		} else if (strcmp(lgopts[opt_idx].name,
1042 				OPT_RUN_CATEGORIES) == 0) {
1043 			config.run_categories = get_ulong_opt(optarg,
1044 				lgopts[opt_idx].name, 1,
1045 				RTE_ACL_MAX_CATEGORIES);
1046 		} else if (strcmp(lgopts[opt_idx].name, OPT_ITER_NUM) == 0) {
1047 			config.iter_num = get_ulong_opt(optarg,
1048 				lgopts[opt_idx].name, 1, INT32_MAX);
1049 		} else if (strcmp(lgopts[opt_idx].name, OPT_VERBOSE) == 0) {
1050 			config.verbose = get_ulong_opt(optarg,
1051 				lgopts[opt_idx].name, DUMP_NONE, DUMP_MAX);
1052 		} else if (strcmp(lgopts[opt_idx].name,
1053 				OPT_SEARCH_ALG) == 0) {
1054 			get_alg_opt(optarg, lgopts[opt_idx].name);
1055 		} else if (strcmp(lgopts[opt_idx].name, OPT_IPV6) == 0) {
1056 			config.ipv6 = 1;
1057 		}
1058 	}
1059 	config.trace_sz = config.ipv6 ? sizeof(struct ipv6_5tuple) :
1060 						sizeof(struct ipv4_5tuple);
1061 
1062 }
1063 
1064 int
1065 main(int argc, char **argv)
1066 {
1067 	int ret;
1068 	uint32_t lcore;
1069 
1070 	ret = rte_eal_init(argc, argv);
1071 	if (ret < 0)
1072 		rte_panic("Cannot init EAL\n");
1073 
1074 	argc -= ret;
1075 	argv += ret;
1076 
1077 	config.prgname = argv[0];
1078 
1079 	get_input_opts(argc, argv);
1080 	dump_config(stdout);
1081 	check_config();
1082 
1083 	acx_init();
1084 
1085 	if (config.trace_file != NULL)
1086 		tracef_init();
1087 
1088 	RTE_LCORE_FOREACH_SLAVE(lcore)
1089 		 rte_eal_remote_launch(search_ip5tuples, NULL, lcore);
1090 
1091 	search_ip5tuples(NULL);
1092 
1093 	rte_eal_mp_wait_lcore();
1094 
1095 	rte_acl_free(config.acx);
1096 	return 0;
1097 }
1098