bpf: allow self-xor operation
[dpdk.git] / app / test-acl / main.c
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 }