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