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