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