net: add rte prefix to IP defines
[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 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 }