app/test: packet framework unit tests
[dpdk.git] / app / test / test_table_acl.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 #ifdef RTE_LIBRTE_ACL
35
36 #include <rte_hexdump.h>
37 #include "test_table.h"
38 #include "test_table_acl.h"
39
40 #define IPv4(a, b, c, d) ((uint32_t)(((a) & 0xff) << 24) |              \
41         (((b) & 0xff) << 16) |                                          \
42         (((c) & 0xff) << 8) |                                           \
43         ((d) & 0xff))
44
45 static const char cb_port_delim[] = ":";
46
47 /*
48  * Rule and trace formats definitions.
49  **/
50
51 struct ipv4_5tuple {
52         uint8_t  proto;
53         uint32_t ip_src;
54         uint32_t ip_dst;
55         uint16_t port_src;
56         uint16_t port_dst;
57 };
58
59 enum {
60         PROTO_FIELD_IPV4,
61         SRC_FIELD_IPV4,
62         DST_FIELD_IPV4,
63         SRCP_FIELD_IPV4,
64         DSTP_FIELD_IPV4,
65         NUM_FIELDS_IPV4
66 };
67
68 struct rte_acl_field_def ipv4_defs[NUM_FIELDS_IPV4] = {
69         {
70                 .type = RTE_ACL_FIELD_TYPE_BITMASK,
71                 .size = sizeof(uint8_t),
72                 .field_index = PROTO_FIELD_IPV4,
73                 .input_index = PROTO_FIELD_IPV4,
74                 .offset = offsetof(struct ipv4_5tuple, proto),
75         },
76         {
77                 .type = RTE_ACL_FIELD_TYPE_MASK,
78                 .size = sizeof(uint32_t),
79                 .field_index = SRC_FIELD_IPV4,
80                 .input_index = SRC_FIELD_IPV4,
81                 .offset = offsetof(struct ipv4_5tuple, ip_src),
82         },
83         {
84                 .type = RTE_ACL_FIELD_TYPE_MASK,
85                 .size = sizeof(uint32_t),
86                 .field_index = DST_FIELD_IPV4,
87                 .input_index = DST_FIELD_IPV4,
88                 .offset = offsetof(struct ipv4_5tuple, ip_dst),
89         },
90         {
91                 .type = RTE_ACL_FIELD_TYPE_RANGE,
92                 .size = sizeof(uint16_t),
93                 .field_index = SRCP_FIELD_IPV4,
94                 .input_index = SRCP_FIELD_IPV4,
95                 .offset = offsetof(struct ipv4_5tuple, port_src),
96         },
97         {
98                 .type = RTE_ACL_FIELD_TYPE_RANGE,
99                 .size = sizeof(uint16_t),
100                 .field_index = DSTP_FIELD_IPV4,
101                 .input_index = SRCP_FIELD_IPV4,
102                 .offset = offsetof(struct ipv4_5tuple, port_dst),
103         },
104 };
105
106 struct rte_table_acl_rule_add_params table_acl_IPv4_rule;
107
108 typedef int (*parse_5tuple)(char *text,
109         struct rte_table_acl_rule_add_params *rule);
110
111 /*
112 * The order of the fields in the rule string after the initial '@'
113 */
114 enum {
115         CB_FLD_SRC_ADDR,
116         CB_FLD_DST_ADDR,
117         CB_FLD_SRC_PORT_RANGE,
118         CB_FLD_DST_PORT_RANGE,
119         CB_FLD_PROTO,
120         CB_FLD_NUM,
121 };
122
123
124 #define GET_CB_FIELD(in, fd, base, lim, dlm)                            \
125 do {                                                                    \
126         unsigned long val;                                              \
127         char *end;                                                      \
128                                                                         \
129         errno = 0;                                                      \
130         val = strtoul((in), &end, (base));                              \
131         if (errno != 0 || end[0] != (dlm) || val > (lim))               \
132                 return -EINVAL;                                         \
133         (fd) = (typeof(fd)) val;                                        \
134         (in) = end + 1;                                                 \
135 } while (0)
136
137
138
139
140 static int
141 parse_ipv4_net(const char *in, uint32_t *addr, uint32_t *mask_len)
142 {
143         uint8_t a, b, c, d, m;
144
145         GET_CB_FIELD(in, a, 0, UINT8_MAX, '.');
146         GET_CB_FIELD(in, b, 0, UINT8_MAX, '.');
147         GET_CB_FIELD(in, c, 0, UINT8_MAX, '.');
148         GET_CB_FIELD(in, d, 0, UINT8_MAX, '/');
149         GET_CB_FIELD(in, m, 0, sizeof(uint32_t) * CHAR_BIT, 0);
150
151         addr[0] = IPv4(a, b, c, d);
152         mask_len[0] = m;
153
154         return 0;
155 }
156
157 static int
158 parse_port_range(const char *in, uint16_t *port_low, uint16_t *port_high)
159 {
160         uint16_t a, b;
161
162         GET_CB_FIELD(in, a, 0, UINT16_MAX, ':');
163         GET_CB_FIELD(in, b, 0, UINT16_MAX, 0);
164
165         port_low[0] = a;
166         port_high[0] = b;
167
168         return 0;
169 }
170
171 static int
172 parse_cb_ipv4_rule(char *str, struct rte_table_acl_rule_add_params *v)
173 {
174         int i, rc;
175         char *s, *sp, *in[CB_FLD_NUM];
176         static const char *dlm = " \t\n";
177
178         /*
179         ** Skip leading '@'
180         */
181         if (strchr(str, '@') != str)
182                 return -EINVAL;
183
184         s = str + 1;
185
186         /*
187         * Populate the 'in' array with the location of each
188         * field in the string we're parsing
189         */
190         for (i = 0; i != DIM(in); i++) {
191                 in[i] = strtok_r(s, dlm, &sp);
192                 if (in[i] == NULL)
193                         return -EINVAL;
194                 s = NULL;
195         }
196
197         /* Parse x.x.x.x/x */
198         rc = parse_ipv4_net(in[CB_FLD_SRC_ADDR],
199                 &v->field_value[SRC_FIELD_IPV4].value.u32,
200                 &v->field_value[SRC_FIELD_IPV4].mask_range.u32);
201         if (rc != 0) {
202                 RTE_LOG(ERR, PIPELINE, "failed to read src address/mask: %s\n",
203                         in[CB_FLD_SRC_ADDR]);
204                 return rc;
205         }
206
207         printf("V=%u, mask=%u\n", v->field_value[SRC_FIELD_IPV4].value.u32,
208                 v->field_value[SRC_FIELD_IPV4].mask_range.u32);
209
210         /* Parse x.x.x.x/x */
211         rc = parse_ipv4_net(in[CB_FLD_DST_ADDR],
212                 &v->field_value[DST_FIELD_IPV4].value.u32,
213                 &v->field_value[DST_FIELD_IPV4].mask_range.u32);
214         if (rc != 0) {
215                 RTE_LOG(ERR, PIPELINE, "failed to read dest address/mask: %s\n",
216                         in[CB_FLD_DST_ADDR]);
217                 return rc;
218         }
219
220         printf("V=%u, mask=%u\n", v->field_value[DST_FIELD_IPV4].value.u32,
221         v->field_value[DST_FIELD_IPV4].mask_range.u32);
222         /* Parse n:n */
223         rc = parse_port_range(in[CB_FLD_SRC_PORT_RANGE],
224                 &v->field_value[SRCP_FIELD_IPV4].value.u16,
225                 &v->field_value[SRCP_FIELD_IPV4].mask_range.u16);
226         if (rc != 0) {
227                 RTE_LOG(ERR, PIPELINE, "failed to read source port range: %s\n",
228                         in[CB_FLD_SRC_PORT_RANGE]);
229                 return rc;
230         }
231
232         printf("V=%u, mask=%u\n", v->field_value[SRCP_FIELD_IPV4].value.u16,
233                 v->field_value[SRCP_FIELD_IPV4].mask_range.u16);
234         /* Parse n:n */
235         rc = parse_port_range(in[CB_FLD_DST_PORT_RANGE],
236                 &v->field_value[DSTP_FIELD_IPV4].value.u16,
237                 &v->field_value[DSTP_FIELD_IPV4].mask_range.u16);
238         if (rc != 0) {
239                 RTE_LOG(ERR, PIPELINE, "failed to read dest port range: %s\n",
240                         in[CB_FLD_DST_PORT_RANGE]);
241                 return rc;
242         }
243
244         printf("V=%u, mask=%u\n", v->field_value[DSTP_FIELD_IPV4].value.u16,
245                 v->field_value[DSTP_FIELD_IPV4].mask_range.u16);
246         /* parse 0/0xnn */
247         GET_CB_FIELD(in[CB_FLD_PROTO],
248                 v->field_value[PROTO_FIELD_IPV4].value.u8,
249                 0, UINT8_MAX, '/');
250         GET_CB_FIELD(in[CB_FLD_PROTO],
251                 v->field_value[PROTO_FIELD_IPV4].mask_range.u8,
252                 0, UINT8_MAX, 0);
253
254         printf("V=%u, mask=%u\n",
255                 (unsigned int)v->field_value[PROTO_FIELD_IPV4].value.u8,
256                 v->field_value[PROTO_FIELD_IPV4].mask_range.u8);
257         return 0;
258 }
259
260
261 /*
262  * The format for these rules DO NOT need the port ranges to be
263  * separated by ' : ', just ':'. It's a lot more readable and
264  * cleaner, IMO.
265  */
266 char lines[][128] = {
267         "@0.0.0.0/0 0.0.0.0/0 0:65535 0:65535 2/0xff", /* Protocol check */
268         "@192.168.3.1/32 0.0.0.0/0 0:65535 0:65535 0/0", /* Src IP checl */
269         "@0.0.0.0/0 10.4.4.1/32 0:65535 0:65535 0/0", /* dst IP check */
270         "@0.0.0.0/0 0.0.0.0/0 105:105 0:65535 0/0", /* src port check */
271         "@0.0.0.0/0 0.0.0.0/0 0:65535 206:206 0/0", /* dst port check */
272 };
273
274 char line[128];
275
276
277 static int
278 setup_acl_pipeline(void)
279 {
280         int ret;
281         int i;
282         struct rte_pipeline_params pipeline_params = {
283                 .name = "PIPELINE",
284                 .socket_id = 0,
285         };
286         uint32_t n;
287         struct rte_table_acl_rule_add_params rule_params;
288         struct rte_pipeline_table_acl_rule_delete_params *delete_params;
289         parse_5tuple parser;
290         char acl_name[64];
291
292         /* Pipeline configuration */
293         p = rte_pipeline_create(&pipeline_params);
294         if (p == NULL) {
295                 RTE_LOG(INFO, PIPELINE, "%s: Failed to configure pipeline\n",
296                         __func__);
297                 goto fail;
298         }
299
300         /* Input port configuration */
301         for (i = 0; i < N_PORTS; i++) {
302                 struct rte_port_ring_reader_params port_ring_params = {
303                         .ring = rings_rx[i],
304                 };
305
306                 struct rte_pipeline_port_in_params port_params = {
307                         .ops = &rte_port_ring_reader_ops,
308                         .arg_create = (void *) &port_ring_params,
309                         .f_action = NULL,
310                         .burst_size = BURST_SIZE,
311                 };
312
313                 /* Put in action for some ports */
314                 if (i)
315                         port_params.f_action = port_in_action;
316
317                 ret = rte_pipeline_port_in_create(p, &port_params,
318                         &port_in_id[i]);
319                 if (ret) {
320                         rte_panic("Unable to configure input port %d, ret:%d\n",
321                                 i, ret);
322                         goto fail;
323                 }
324         }
325
326         /* output Port configuration */
327         for (i = 0; i < N_PORTS; i++) {
328                 struct rte_port_ring_writer_params port_ring_params = {
329                         .ring = rings_tx[i],
330                         .tx_burst_sz = BURST_SIZE,
331                 };
332
333                 struct rte_pipeline_port_out_params port_params = {
334                         .ops = &rte_port_ring_writer_ops,
335                         .arg_create = (void *) &port_ring_params,
336                         .f_action = NULL,
337                         .arg_ah = NULL,
338                 };
339
340
341                 if (rte_pipeline_port_out_create(p, &port_params,
342                         &port_out_id[i])) {
343                         rte_panic("Unable to configure output port %d\n", i);
344                         goto fail;
345                 }
346         }
347
348         /* Table configuration  */
349         for (i = 0; i < N_PORTS; i++) {
350                 struct rte_pipeline_table_params table_params;
351
352                 /* Set up defaults for stub */
353                 table_params.ops = &rte_table_stub_ops;
354                 table_params.arg_create = NULL;
355                 table_params.f_action_hit = action_handler_hit;
356                 table_params.f_action_miss = NULL;
357                 table_params.action_data_size = 0;
358
359                 RTE_LOG(INFO, PIPELINE, "miss_action=%x\n",
360                         table_entry_miss_action);
361
362                 printf("RTE_ACL_RULE_SZ(%zu) = %zu\n", DIM(ipv4_defs),
363                         RTE_ACL_RULE_SZ(DIM(ipv4_defs)));
364
365                 struct rte_table_acl_params acl_params;
366
367                 acl_params.n_rules = 1 << 5;
368                 acl_params.n_rule_fields = DIM(ipv4_defs);
369                 rte_snprintf(acl_name, sizeof(acl_name), "ACL%d", i);
370                 acl_params.name = acl_name;
371                 memcpy(acl_params.field_format, ipv4_defs, sizeof(ipv4_defs));
372
373                 table_params.ops = &rte_table_acl_ops;
374                 table_params.arg_create = &acl_params;
375
376                 if (rte_pipeline_table_create(p, &table_params, &table_id[i])) {
377                         rte_panic("Unable to configure table %u\n", i);
378                         goto fail;
379                 }
380
381                 if (connect_miss_action_to_table) {
382                         if (rte_pipeline_table_create(p, &table_params,
383                                 &table_id[i+2])) {
384                                 rte_panic("Unable to configure table %u\n", i);
385                                 goto fail;
386                         }
387                 }
388         }
389
390         for (i = 0; i < N_PORTS; i++) {
391                 if (rte_pipeline_port_in_connect_to_table(p, port_in_id[i],
392                         table_id[i])) {
393                         rte_panic("Unable to connect input port %u to "
394                                 "table %u\n",
395                                 port_in_id[i],  table_id[i]);
396                         goto fail;
397                 }
398         }
399
400         /* Add entries to tables */
401         for (i = 0; i < N_PORTS; i++) {
402                 struct rte_pipeline_table_entry table_entry = {
403                         .action = RTE_PIPELINE_ACTION_PORT,
404                         {.port_id = port_out_id[i^1]},
405                 };
406                 int key_found;
407                 struct rte_pipeline_table_entry *entry_ptr;
408
409                 memset(&rule_params, 0, sizeof(rule_params));
410                 parser = parse_cb_ipv4_rule;
411
412                 for (n = 1; n <= 5; n++) {
413                         rte_snprintf(line, sizeof(line), "%s", lines[n-1]);
414                         printf("PARSING [%s]\n", line);
415
416                         ret = parser(line, &rule_params);
417                         if (ret != 0) {
418                                 RTE_LOG(ERR, PIPELINE,
419                                         "line %u: parse_cb_ipv4vlan_rule"
420                                         " failed, error code: %d (%s)\n",
421                                         n, ret, strerror(-ret));
422                                 return ret;
423                         }
424
425                         rule_params.priority = RTE_ACL_MAX_PRIORITY - n;
426
427                         ret = rte_pipeline_table_entry_add(p, table_id[i],
428                                 &rule_params,
429                                 &table_entry, &key_found, &entry_ptr);
430                         if (ret < 0) {
431                                 rte_panic("Add entry to table %u failed (%d)\n",
432                                         table_id[i], ret);
433                                 goto fail;
434                         }
435                 }
436
437                 /* delete a few rules */
438                 for (n = 2; n <= 3; n++) {
439                         rte_snprintf(line, sizeof(line), "%s", lines[n-1]);
440                         printf("PARSING [%s]\n", line);
441
442                         ret = parser(line, &rule_params);
443                         if (ret != 0) {
444                                 RTE_LOG(ERR, PIPELINE, "line %u: parse rule "
445                                         " failed, error code: %d (%s)\n",
446                                         n, ret, strerror(-ret));
447                                 return ret;
448                         }
449
450                         delete_params = (struct
451                                 rte_pipeline_table_acl_rule_delete_params *)
452                                 &(rule_params.field_value[0]);
453                         ret = rte_pipeline_table_entry_delete(p, table_id[i],
454                                 delete_params, &key_found, NULL);
455                         if (ret < 0) {
456                                 rte_panic("Add entry to table %u failed (%d)\n",
457                                         table_id[i], ret);
458                                 goto fail;
459                         } else
460                                 printf("Deleted Rule.\n");
461                 }
462
463
464                 /* Try to add duplicates */
465                 for (n = 1; n <= 5; n++) {
466                         rte_snprintf(line, sizeof(line), "%s", lines[n-1]);
467                         printf("PARSING [%s]\n", line);
468
469                         ret = parser(line, &rule_params);
470                         if (ret != 0) {
471                                 RTE_LOG(ERR, PIPELINE, "line %u: parse rule"
472                                         " failed, error code: %d (%s)\n",
473                                         n, ret, strerror(-ret));
474                                 return ret;
475                         }
476
477                         rule_params.priority = RTE_ACL_MAX_PRIORITY - n;
478
479                         ret = rte_pipeline_table_entry_add(p, table_id[i],
480                                 &rule_params,
481                                 &table_entry, &key_found, &entry_ptr);
482                         if (ret < 0) {
483                                 rte_panic("Add entry to table %u failed (%d)\n",
484                                         table_id[i], ret);
485                                 goto fail;
486                         }
487                 }
488         }
489
490         /* Enable input ports */
491         for (i = 0; i < N_PORTS ; i++)
492                 if (rte_pipeline_port_in_enable(p, port_in_id[i]))
493                         rte_panic("Unable to enable input port %u\n",
494                                 port_in_id[i]);
495
496         /* Check pipeline consistency */
497         if (rte_pipeline_check(p) < 0) {
498                 rte_panic("Pipeline consistency check failed\n");
499                 goto fail;
500         }
501
502         return  0;
503 fail:
504
505         return -1;
506 }
507
508 static int
509 test_pipeline_single_filter(int expected_count)
510 {
511         int i, j, ret, tx_count;
512         struct ipv4_5tuple five_tuple;
513
514         /* Allocate a few mbufs and manually insert into the rings. */
515         for (i = 0; i < N_PORTS; i++) {
516                 for (j = 0; j < 8; j++) {
517                         struct rte_mbuf *mbuf;
518
519                         mbuf = rte_pktmbuf_alloc(pool);
520                         memset(mbuf->pkt.data, 0x00,
521                                 sizeof(struct ipv4_5tuple));
522
523                         five_tuple.proto = j;
524                         five_tuple.ip_src = rte_bswap32(IPv4(192, 168, j, 1));
525                         five_tuple.ip_dst = rte_bswap32(IPv4(10, 4, j, 1));
526                         five_tuple.port_src = rte_bswap16(100 + j);
527                         five_tuple.port_dst = rte_bswap16(200 + j);
528
529                         memcpy(mbuf->pkt.data, &five_tuple,
530                                 sizeof(struct ipv4_5tuple));
531                         RTE_LOG(INFO, PIPELINE, "%s: Enqueue onto ring %d\n",
532                                 __func__, i);
533                         rte_ring_enqueue(rings_rx[i], mbuf);
534                 }
535         }
536
537         /* Run pipeline once */
538         rte_pipeline_run(p);
539
540         rte_pipeline_flush(p);
541
542         tx_count = 0;
543
544         for (i = 0; i < N_PORTS; i++) {
545                 void *objs[RING_TX_SIZE];
546                 struct rte_mbuf *mbuf;
547
548                 ret = rte_ring_sc_dequeue_burst(rings_tx[i], objs, 10);
549                 if (ret <= 0) {
550                         printf("Got no objects from ring %d - error code %d\n",
551                                 i, ret);
552                 } else {
553                         printf("Got %d object(s) from ring %d!\n", ret, i);
554                         for (j = 0; j < ret; j++) {
555                                 mbuf = (struct rte_mbuf *)objs[j];
556                                 rte_hexdump(stdout, "mbuf", mbuf->pkt.data, 64);
557                                 rte_pktmbuf_free(mbuf);
558                         }
559                         tx_count += ret;
560                 }
561         }
562
563         if (tx_count != expected_count) {
564                 RTE_LOG(INFO, PIPELINE,
565                         "%s: Unexpected packets for ACL test, "
566                         "expected %d, got %d\n",
567                         __func__, expected_count, tx_count);
568                 goto fail;
569         }
570
571         rte_pipeline_free(p);
572
573         return  0;
574 fail:
575         return -1;
576
577 }
578
579 int
580 test_table_ACL(void)
581 {
582
583
584         override_hit_mask = 0xFF; /* All packets are a hit */
585
586         setup_acl_pipeline();
587         if (test_pipeline_single_filter(10) < 0)
588                 return -1;
589
590         return 0;
591 }
592
593 #endif