app/testpmd: add flow list command
[dpdk.git] / app / test-pmd / cmdline_flow.c
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright 2016 6WIND S.A.
5  *   Copyright 2016 Mellanox.
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 6WIND S.A. 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 <stddef.h>
35 #include <stdint.h>
36 #include <stdio.h>
37 #include <inttypes.h>
38 #include <errno.h>
39 #include <ctype.h>
40 #include <string.h>
41
42 #include <rte_common.h>
43 #include <rte_ethdev.h>
44 #include <rte_byteorder.h>
45 #include <cmdline_parse.h>
46 #include <rte_flow.h>
47
48 #include "testpmd.h"
49
50 /** Parser token indices. */
51 enum index {
52         /* Special tokens. */
53         ZERO = 0,
54         END,
55
56         /* Common tokens. */
57         INTEGER,
58         UNSIGNED,
59         PORT_ID,
60         GROUP_ID,
61
62         /* Top-level command. */
63         FLOW,
64
65         /* Sub-level commands. */
66         LIST,
67
68         /* List arguments. */
69         LIST_GROUP,
70 };
71
72 /** Maximum number of subsequent tokens and arguments on the stack. */
73 #define CTX_STACK_SIZE 16
74
75 /** Parser context. */
76 struct context {
77         /** Stack of subsequent token lists to process. */
78         const enum index *next[CTX_STACK_SIZE];
79         /** Arguments for stacked tokens. */
80         const void *args[CTX_STACK_SIZE];
81         enum index curr; /**< Current token index. */
82         enum index prev; /**< Index of the last token seen. */
83         int next_num; /**< Number of entries in next[]. */
84         int args_num; /**< Number of entries in args[]. */
85         uint32_t reparse:1; /**< Start over from the beginning. */
86         uint32_t eol:1; /**< EOL has been detected. */
87         uint32_t last:1; /**< No more arguments. */
88         uint16_t port; /**< Current port ID (for completions). */
89         void *object; /**< Address of current object for relative offsets. */
90 };
91
92 /** Token argument. */
93 struct arg {
94         uint32_t hton:1; /**< Use network byte ordering. */
95         uint32_t sign:1; /**< Value is signed. */
96         uint32_t offset; /**< Relative offset from ctx->object. */
97         uint32_t size; /**< Field size. */
98 };
99
100 /** Parser token definition. */
101 struct token {
102         /** Type displayed during completion (defaults to "TOKEN"). */
103         const char *type;
104         /** Help displayed during completion (defaults to token name). */
105         const char *help;
106         /**
107          * Lists of subsequent tokens to push on the stack. Each call to the
108          * parser consumes the last entry of that stack.
109          */
110         const enum index *const *next;
111         /** Arguments stack for subsequent tokens that need them. */
112         const struct arg *const *args;
113         /**
114          * Token-processing callback, returns -1 in case of error, the
115          * length of the matched string otherwise. If NULL, attempts to
116          * match the token name.
117          *
118          * If buf is not NULL, the result should be stored in it according
119          * to context. An error is returned if not large enough.
120          */
121         int (*call)(struct context *ctx, const struct token *token,
122                     const char *str, unsigned int len,
123                     void *buf, unsigned int size);
124         /**
125          * Callback that provides possible values for this token, used for
126          * completion. Returns -1 in case of error, the number of possible
127          * values otherwise. If NULL, the token name is used.
128          *
129          * If buf is not NULL, entry index ent is written to buf and the
130          * full length of the entry is returned (same behavior as
131          * snprintf()).
132          */
133         int (*comp)(struct context *ctx, const struct token *token,
134                     unsigned int ent, char *buf, unsigned int size);
135         /** Mandatory token name, no default value. */
136         const char *name;
137 };
138
139 /** Static initializer for the next field. */
140 #define NEXT(...) (const enum index *const []){ __VA_ARGS__, NULL, }
141
142 /** Static initializer for a NEXT() entry. */
143 #define NEXT_ENTRY(...) (const enum index []){ __VA_ARGS__, ZERO, }
144
145 /** Static initializer for the args field. */
146 #define ARGS(...) (const struct arg *const []){ __VA_ARGS__, NULL, }
147
148 /** Static initializer for ARGS() to target a field. */
149 #define ARGS_ENTRY(s, f) \
150         (&(const struct arg){ \
151                 .offset = offsetof(s, f), \
152                 .size = sizeof(((s *)0)->f), \
153         })
154
155 /** Static initializer for ARGS() to target a pointer. */
156 #define ARGS_ENTRY_PTR(s, f) \
157         (&(const struct arg){ \
158                 .size = sizeof(*((s *)0)->f), \
159         })
160
161 /** Parser output buffer layout expected by cmd_flow_parsed(). */
162 struct buffer {
163         enum index command; /**< Flow command. */
164         uint16_t port; /**< Affected port ID. */
165         union {
166                 struct {
167                         uint32_t *group;
168                         uint32_t group_n;
169                 } list; /**< List arguments. */
170         } args; /**< Command arguments. */
171 };
172
173 static const enum index next_list_attr[] = {
174         LIST_GROUP,
175         END,
176         ZERO,
177 };
178
179 static int parse_init(struct context *, const struct token *,
180                       const char *, unsigned int,
181                       void *, unsigned int);
182 static int parse_list(struct context *, const struct token *,
183                       const char *, unsigned int,
184                       void *, unsigned int);
185 static int parse_int(struct context *, const struct token *,
186                      const char *, unsigned int,
187                      void *, unsigned int);
188 static int parse_port(struct context *, const struct token *,
189                       const char *, unsigned int,
190                       void *, unsigned int);
191 static int comp_none(struct context *, const struct token *,
192                      unsigned int, char *, unsigned int);
193 static int comp_port(struct context *, const struct token *,
194                      unsigned int, char *, unsigned int);
195
196 /** Token definitions. */
197 static const struct token token_list[] = {
198         /* Special tokens. */
199         [ZERO] = {
200                 .name = "ZERO",
201                 .help = "null entry, abused as the entry point",
202                 .next = NEXT(NEXT_ENTRY(FLOW)),
203         },
204         [END] = {
205                 .name = "",
206                 .type = "RETURN",
207                 .help = "command may end here",
208         },
209         /* Common tokens. */
210         [INTEGER] = {
211                 .name = "{int}",
212                 .type = "INTEGER",
213                 .help = "integer value",
214                 .call = parse_int,
215                 .comp = comp_none,
216         },
217         [UNSIGNED] = {
218                 .name = "{unsigned}",
219                 .type = "UNSIGNED",
220                 .help = "unsigned integer value",
221                 .call = parse_int,
222                 .comp = comp_none,
223         },
224         [PORT_ID] = {
225                 .name = "{port_id}",
226                 .type = "PORT ID",
227                 .help = "port identifier",
228                 .call = parse_port,
229                 .comp = comp_port,
230         },
231         [GROUP_ID] = {
232                 .name = "{group_id}",
233                 .type = "GROUP ID",
234                 .help = "group identifier",
235                 .call = parse_int,
236                 .comp = comp_none,
237         },
238         /* Top-level command. */
239         [FLOW] = {
240                 .name = "flow",
241                 .type = "{command} {port_id} [{arg} [...]]",
242                 .help = "manage ingress/egress flow rules",
243                 .next = NEXT(NEXT_ENTRY(LIST)),
244                 .call = parse_init,
245         },
246         /* Sub-level commands. */
247         [LIST] = {
248                 .name = "list",
249                 .help = "list existing flow rules",
250                 .next = NEXT(next_list_attr, NEXT_ENTRY(PORT_ID)),
251                 .args = ARGS(ARGS_ENTRY(struct buffer, port)),
252                 .call = parse_list,
253         },
254         /* List arguments. */
255         [LIST_GROUP] = {
256                 .name = "group",
257                 .help = "specify a group",
258                 .next = NEXT(next_list_attr, NEXT_ENTRY(GROUP_ID)),
259                 .args = ARGS(ARGS_ENTRY_PTR(struct buffer, args.list.group)),
260                 .call = parse_list,
261         },
262 };
263
264 /** Remove and return last entry from argument stack. */
265 static const struct arg *
266 pop_args(struct context *ctx)
267 {
268         return ctx->args_num ? ctx->args[--ctx->args_num] : NULL;
269 }
270
271 /** Add entry on top of the argument stack. */
272 static int
273 push_args(struct context *ctx, const struct arg *arg)
274 {
275         if (ctx->args_num == CTX_STACK_SIZE)
276                 return -1;
277         ctx->args[ctx->args_num++] = arg;
278         return 0;
279 }
280
281 /** Default parsing function for token name matching. */
282 static int
283 parse_default(struct context *ctx, const struct token *token,
284               const char *str, unsigned int len,
285               void *buf, unsigned int size)
286 {
287         (void)ctx;
288         (void)buf;
289         (void)size;
290         if (strncmp(str, token->name, len))
291                 return -1;
292         return len;
293 }
294
295 /** Parse flow command, initialize output buffer for subsequent tokens. */
296 static int
297 parse_init(struct context *ctx, const struct token *token,
298            const char *str, unsigned int len,
299            void *buf, unsigned int size)
300 {
301         struct buffer *out = buf;
302
303         /* Token name must match. */
304         if (parse_default(ctx, token, str, len, NULL, 0) < 0)
305                 return -1;
306         /* Nothing else to do if there is no buffer. */
307         if (!out)
308                 return len;
309         /* Make sure buffer is large enough. */
310         if (size < sizeof(*out))
311                 return -1;
312         /* Initialize buffer. */
313         memset(out, 0x00, sizeof(*out));
314         memset((uint8_t *)out + sizeof(*out), 0x22, size - sizeof(*out));
315         ctx->object = out;
316         return len;
317 }
318
319 /** Parse tokens for list command. */
320 static int
321 parse_list(struct context *ctx, const struct token *token,
322            const char *str, unsigned int len,
323            void *buf, unsigned int size)
324 {
325         struct buffer *out = buf;
326
327         /* Token name must match. */
328         if (parse_default(ctx, token, str, len, NULL, 0) < 0)
329                 return -1;
330         /* Nothing else to do if there is no buffer. */
331         if (!out)
332                 return len;
333         if (!out->command) {
334                 if (ctx->curr != LIST)
335                         return -1;
336                 if (sizeof(*out) > size)
337                         return -1;
338                 out->command = ctx->curr;
339                 ctx->object = out;
340                 out->args.list.group =
341                         (void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
342                                                sizeof(double));
343                 return len;
344         }
345         if (((uint8_t *)(out->args.list.group + out->args.list.group_n) +
346              sizeof(*out->args.list.group)) > (uint8_t *)out + size)
347                 return -1;
348         ctx->object = out->args.list.group + out->args.list.group_n++;
349         return len;
350 }
351
352 /**
353  * Parse signed/unsigned integers 8 to 64-bit long.
354  *
355  * Last argument (ctx->args) is retrieved to determine integer type and
356  * storage location.
357  */
358 static int
359 parse_int(struct context *ctx, const struct token *token,
360           const char *str, unsigned int len,
361           void *buf, unsigned int size)
362 {
363         const struct arg *arg = pop_args(ctx);
364         uintmax_t u;
365         char *end;
366
367         (void)token;
368         /* Argument is expected. */
369         if (!arg)
370                 return -1;
371         errno = 0;
372         u = arg->sign ?
373                 (uintmax_t)strtoimax(str, &end, 0) :
374                 strtoumax(str, &end, 0);
375         if (errno || (size_t)(end - str) != len)
376                 goto error;
377         if (!ctx->object)
378                 return len;
379         buf = (uint8_t *)ctx->object + arg->offset;
380         size = arg->size;
381         switch (size) {
382         case sizeof(uint8_t):
383                 *(uint8_t *)buf = u;
384                 break;
385         case sizeof(uint16_t):
386                 *(uint16_t *)buf = arg->hton ? rte_cpu_to_be_16(u) : u;
387                 break;
388         case sizeof(uint32_t):
389                 *(uint32_t *)buf = arg->hton ? rte_cpu_to_be_32(u) : u;
390                 break;
391         case sizeof(uint64_t):
392                 *(uint64_t *)buf = arg->hton ? rte_cpu_to_be_64(u) : u;
393                 break;
394         default:
395                 goto error;
396         }
397         return len;
398 error:
399         push_args(ctx, arg);
400         return -1;
401 }
402
403 /** Parse port and update context. */
404 static int
405 parse_port(struct context *ctx, const struct token *token,
406            const char *str, unsigned int len,
407            void *buf, unsigned int size)
408 {
409         struct buffer *out = &(struct buffer){ .port = 0 };
410         int ret;
411
412         if (buf)
413                 out = buf;
414         else {
415                 ctx->object = out;
416                 size = sizeof(*out);
417         }
418         ret = parse_int(ctx, token, str, len, out, size);
419         if (ret >= 0)
420                 ctx->port = out->port;
421         if (!buf)
422                 ctx->object = NULL;
423         return ret;
424 }
425
426 /** No completion. */
427 static int
428 comp_none(struct context *ctx, const struct token *token,
429           unsigned int ent, char *buf, unsigned int size)
430 {
431         (void)ctx;
432         (void)token;
433         (void)ent;
434         (void)buf;
435         (void)size;
436         return 0;
437 }
438
439 /** Complete available ports. */
440 static int
441 comp_port(struct context *ctx, const struct token *token,
442           unsigned int ent, char *buf, unsigned int size)
443 {
444         unsigned int i = 0;
445         portid_t p;
446
447         (void)ctx;
448         (void)token;
449         FOREACH_PORT(p, ports) {
450                 if (buf && i == ent)
451                         return snprintf(buf, size, "%u", p);
452                 ++i;
453         }
454         if (buf)
455                 return -1;
456         return i;
457 }
458
459 /** Internal context. */
460 static struct context cmd_flow_context;
461
462 /** Global parser instance (cmdline API). */
463 cmdline_parse_inst_t cmd_flow;
464
465 /** Initialize context. */
466 static void
467 cmd_flow_context_init(struct context *ctx)
468 {
469         /* A full memset() is not necessary. */
470         ctx->curr = ZERO;
471         ctx->prev = ZERO;
472         ctx->next_num = 0;
473         ctx->args_num = 0;
474         ctx->reparse = 0;
475         ctx->eol = 0;
476         ctx->last = 0;
477         ctx->port = 0;
478         ctx->object = NULL;
479 }
480
481 /** Parse a token (cmdline API). */
482 static int
483 cmd_flow_parse(cmdline_parse_token_hdr_t *hdr, const char *src, void *result,
484                unsigned int size)
485 {
486         struct context *ctx = &cmd_flow_context;
487         const struct token *token;
488         const enum index *list;
489         int len;
490         int i;
491
492         (void)hdr;
493         /* Restart as requested. */
494         if (ctx->reparse)
495                 cmd_flow_context_init(ctx);
496         token = &token_list[ctx->curr];
497         /* Check argument length. */
498         ctx->eol = 0;
499         ctx->last = 1;
500         for (len = 0; src[len]; ++len)
501                 if (src[len] == '#' || isspace(src[len]))
502                         break;
503         if (!len)
504                 return -1;
505         /* Last argument and EOL detection. */
506         for (i = len; src[i]; ++i)
507                 if (src[i] == '#' || src[i] == '\r' || src[i] == '\n')
508                         break;
509                 else if (!isspace(src[i])) {
510                         ctx->last = 0;
511                         break;
512                 }
513         for (; src[i]; ++i)
514                 if (src[i] == '\r' || src[i] == '\n') {
515                         ctx->eol = 1;
516                         break;
517                 }
518         /* Initialize context if necessary. */
519         if (!ctx->next_num) {
520                 if (!token->next)
521                         return 0;
522                 ctx->next[ctx->next_num++] = token->next[0];
523         }
524         /* Process argument through candidates. */
525         ctx->prev = ctx->curr;
526         list = ctx->next[ctx->next_num - 1];
527         for (i = 0; list[i]; ++i) {
528                 const struct token *next = &token_list[list[i]];
529                 int tmp;
530
531                 ctx->curr = list[i];
532                 if (next->call)
533                         tmp = next->call(ctx, next, src, len, result, size);
534                 else
535                         tmp = parse_default(ctx, next, src, len, result, size);
536                 if (tmp == -1 || tmp != len)
537                         continue;
538                 token = next;
539                 break;
540         }
541         if (!list[i])
542                 return -1;
543         --ctx->next_num;
544         /* Push subsequent tokens if any. */
545         if (token->next)
546                 for (i = 0; token->next[i]; ++i) {
547                         if (ctx->next_num == RTE_DIM(ctx->next))
548                                 return -1;
549                         ctx->next[ctx->next_num++] = token->next[i];
550                 }
551         /* Push arguments if any. */
552         if (token->args)
553                 for (i = 0; token->args[i]; ++i) {
554                         if (ctx->args_num == RTE_DIM(ctx->args))
555                                 return -1;
556                         ctx->args[ctx->args_num++] = token->args[i];
557                 }
558         return len;
559 }
560
561 /** Return number of completion entries (cmdline API). */
562 static int
563 cmd_flow_complete_get_nb(cmdline_parse_token_hdr_t *hdr)
564 {
565         struct context *ctx = &cmd_flow_context;
566         const struct token *token = &token_list[ctx->curr];
567         const enum index *list;
568         int i;
569
570         (void)hdr;
571         /* Tell cmd_flow_parse() that context must be reinitialized. */
572         ctx->reparse = 1;
573         /* Count number of tokens in current list. */
574         if (ctx->next_num)
575                 list = ctx->next[ctx->next_num - 1];
576         else
577                 list = token->next[0];
578         for (i = 0; list[i]; ++i)
579                 ;
580         if (!i)
581                 return 0;
582         /*
583          * If there is a single token, use its completion callback, otherwise
584          * return the number of entries.
585          */
586         token = &token_list[list[0]];
587         if (i == 1 && token->comp) {
588                 /* Save index for cmd_flow_get_help(). */
589                 ctx->prev = list[0];
590                 return token->comp(ctx, token, 0, NULL, 0);
591         }
592         return i;
593 }
594
595 /** Return a completion entry (cmdline API). */
596 static int
597 cmd_flow_complete_get_elt(cmdline_parse_token_hdr_t *hdr, int index,
598                           char *dst, unsigned int size)
599 {
600         struct context *ctx = &cmd_flow_context;
601         const struct token *token = &token_list[ctx->curr];
602         const enum index *list;
603         int i;
604
605         (void)hdr;
606         /* Tell cmd_flow_parse() that context must be reinitialized. */
607         ctx->reparse = 1;
608         /* Count number of tokens in current list. */
609         if (ctx->next_num)
610                 list = ctx->next[ctx->next_num - 1];
611         else
612                 list = token->next[0];
613         for (i = 0; list[i]; ++i)
614                 ;
615         if (!i)
616                 return -1;
617         /* If there is a single token, use its completion callback. */
618         token = &token_list[list[0]];
619         if (i == 1 && token->comp) {
620                 /* Save index for cmd_flow_get_help(). */
621                 ctx->prev = list[0];
622                 return token->comp(ctx, token, index, dst, size) < 0 ? -1 : 0;
623         }
624         /* Otherwise make sure the index is valid and use defaults. */
625         if (index >= i)
626                 return -1;
627         token = &token_list[list[index]];
628         snprintf(dst, size, "%s", token->name);
629         /* Save index for cmd_flow_get_help(). */
630         ctx->prev = list[index];
631         return 0;
632 }
633
634 /** Populate help strings for current token (cmdline API). */
635 static int
636 cmd_flow_get_help(cmdline_parse_token_hdr_t *hdr, char *dst, unsigned int size)
637 {
638         struct context *ctx = &cmd_flow_context;
639         const struct token *token = &token_list[ctx->prev];
640
641         (void)hdr;
642         /* Tell cmd_flow_parse() that context must be reinitialized. */
643         ctx->reparse = 1;
644         if (!size)
645                 return -1;
646         /* Set token type and update global help with details. */
647         snprintf(dst, size, "%s", (token->type ? token->type : "TOKEN"));
648         if (token->help)
649                 cmd_flow.help_str = token->help;
650         else
651                 cmd_flow.help_str = token->name;
652         return 0;
653 }
654
655 /** Token definition template (cmdline API). */
656 static struct cmdline_token_hdr cmd_flow_token_hdr = {
657         .ops = &(struct cmdline_token_ops){
658                 .parse = cmd_flow_parse,
659                 .complete_get_nb = cmd_flow_complete_get_nb,
660                 .complete_get_elt = cmd_flow_complete_get_elt,
661                 .get_help = cmd_flow_get_help,
662         },
663         .offset = 0,
664 };
665
666 /** Populate the next dynamic token. */
667 static void
668 cmd_flow_tok(cmdline_parse_token_hdr_t **hdr,
669              cmdline_parse_token_hdr_t *(*hdrs)[])
670 {
671         struct context *ctx = &cmd_flow_context;
672
673         /* Always reinitialize context before requesting the first token. */
674         if (!(hdr - *hdrs))
675                 cmd_flow_context_init(ctx);
676         /* Return NULL when no more tokens are expected. */
677         if (!ctx->next_num && ctx->curr) {
678                 *hdr = NULL;
679                 return;
680         }
681         /* Determine if command should end here. */
682         if (ctx->eol && ctx->last && ctx->next_num) {
683                 const enum index *list = ctx->next[ctx->next_num - 1];
684                 int i;
685
686                 for (i = 0; list[i]; ++i) {
687                         if (list[i] != END)
688                                 continue;
689                         *hdr = NULL;
690                         return;
691                 }
692         }
693         *hdr = &cmd_flow_token_hdr;
694 }
695
696 /** Dispatch parsed buffer to function calls. */
697 static void
698 cmd_flow_parsed(const struct buffer *in)
699 {
700         switch (in->command) {
701         case LIST:
702                 port_flow_list(in->port, in->args.list.group_n,
703                                in->args.list.group);
704                 break;
705         default:
706                 break;
707         }
708 }
709
710 /** Token generator and output processing callback (cmdline API). */
711 static void
712 cmd_flow_cb(void *arg0, struct cmdline *cl, void *arg2)
713 {
714         if (cl == NULL)
715                 cmd_flow_tok(arg0, arg2);
716         else
717                 cmd_flow_parsed(arg0);
718 }
719
720 /** Global parser instance (cmdline API). */
721 cmdline_parse_inst_t cmd_flow = {
722         .f = cmd_flow_cb,
723         .data = NULL, /**< Unused. */
724         .help_str = NULL, /**< Updated by cmd_flow_get_help(). */
725         .tokens = {
726                 NULL,
727         }, /**< Tokens are returned by cmd_flow_tok(). */
728 };