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