app/testpmd: support flow integer
[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
60         /* Top-level command. */
61         FLOW,
62 };
63
64 /** Maximum number of subsequent tokens and arguments on the stack. */
65 #define CTX_STACK_SIZE 16
66
67 /** Parser context. */
68 struct context {
69         /** Stack of subsequent token lists to process. */
70         const enum index *next[CTX_STACK_SIZE];
71         /** Arguments for stacked tokens. */
72         const void *args[CTX_STACK_SIZE];
73         enum index curr; /**< Current token index. */
74         enum index prev; /**< Index of the last token seen. */
75         int next_num; /**< Number of entries in next[]. */
76         int args_num; /**< Number of entries in args[]. */
77         uint32_t reparse:1; /**< Start over from the beginning. */
78         uint32_t eol:1; /**< EOL has been detected. */
79         uint32_t last:1; /**< No more arguments. */
80         void *object; /**< Address of current object for relative offsets. */
81 };
82
83 /** Token argument. */
84 struct arg {
85         uint32_t hton:1; /**< Use network byte ordering. */
86         uint32_t sign:1; /**< Value is signed. */
87         uint32_t offset; /**< Relative offset from ctx->object. */
88         uint32_t size; /**< Field size. */
89 };
90
91 /** Parser token definition. */
92 struct token {
93         /** Type displayed during completion (defaults to "TOKEN"). */
94         const char *type;
95         /** Help displayed during completion (defaults to token name). */
96         const char *help;
97         /**
98          * Lists of subsequent tokens to push on the stack. Each call to the
99          * parser consumes the last entry of that stack.
100          */
101         const enum index *const *next;
102         /** Arguments stack for subsequent tokens that need them. */
103         const struct arg *const *args;
104         /**
105          * Token-processing callback, returns -1 in case of error, the
106          * length of the matched string otherwise. If NULL, attempts to
107          * match the token name.
108          *
109          * If buf is not NULL, the result should be stored in it according
110          * to context. An error is returned if not large enough.
111          */
112         int (*call)(struct context *ctx, const struct token *token,
113                     const char *str, unsigned int len,
114                     void *buf, unsigned int size);
115         /**
116          * Callback that provides possible values for this token, used for
117          * completion. Returns -1 in case of error, the number of possible
118          * values otherwise. If NULL, the token name is used.
119          *
120          * If buf is not NULL, entry index ent is written to buf and the
121          * full length of the entry is returned (same behavior as
122          * snprintf()).
123          */
124         int (*comp)(struct context *ctx, const struct token *token,
125                     unsigned int ent, char *buf, unsigned int size);
126         /** Mandatory token name, no default value. */
127         const char *name;
128 };
129
130 /** Static initializer for the next field. */
131 #define NEXT(...) (const enum index *const []){ __VA_ARGS__, NULL, }
132
133 /** Static initializer for a NEXT() entry. */
134 #define NEXT_ENTRY(...) (const enum index []){ __VA_ARGS__, ZERO, }
135
136 /** Static initializer for the args field. */
137 #define ARGS(...) (const struct arg *const []){ __VA_ARGS__, NULL, }
138
139 /** Static initializer for ARGS() to target a field. */
140 #define ARGS_ENTRY(s, f) \
141         (&(const struct arg){ \
142                 .offset = offsetof(s, f), \
143                 .size = sizeof(((s *)0)->f), \
144         })
145
146 /** Static initializer for ARGS() to target a pointer. */
147 #define ARGS_ENTRY_PTR(s, f) \
148         (&(const struct arg){ \
149                 .size = sizeof(*((s *)0)->f), \
150         })
151
152 /** Parser output buffer layout expected by cmd_flow_parsed(). */
153 struct buffer {
154         enum index command; /**< Flow command. */
155         uint16_t port; /**< Affected port ID. */
156 };
157
158 static int parse_init(struct context *, const struct token *,
159                       const char *, unsigned int,
160                       void *, unsigned int);
161 static int parse_int(struct context *, const struct token *,
162                      const char *, unsigned int,
163                      void *, unsigned int);
164 static int comp_none(struct context *, const struct token *,
165                      unsigned int, char *, unsigned int);
166
167 /** Token definitions. */
168 static const struct token token_list[] = {
169         /* Special tokens. */
170         [ZERO] = {
171                 .name = "ZERO",
172                 .help = "null entry, abused as the entry point",
173                 .next = NEXT(NEXT_ENTRY(FLOW)),
174         },
175         [END] = {
176                 .name = "",
177                 .type = "RETURN",
178                 .help = "command may end here",
179         },
180         /* Common tokens. */
181         [INTEGER] = {
182                 .name = "{int}",
183                 .type = "INTEGER",
184                 .help = "integer value",
185                 .call = parse_int,
186                 .comp = comp_none,
187         },
188         [UNSIGNED] = {
189                 .name = "{unsigned}",
190                 .type = "UNSIGNED",
191                 .help = "unsigned integer value",
192                 .call = parse_int,
193                 .comp = comp_none,
194         },
195         /* Top-level command. */
196         [FLOW] = {
197                 .name = "flow",
198                 .type = "{command} {port_id} [{arg} [...]]",
199                 .help = "manage ingress/egress flow rules",
200                 .call = parse_init,
201         },
202 };
203
204 /** Remove and return last entry from argument stack. */
205 static const struct arg *
206 pop_args(struct context *ctx)
207 {
208         return ctx->args_num ? ctx->args[--ctx->args_num] : NULL;
209 }
210
211 /** Add entry on top of the argument stack. */
212 static int
213 push_args(struct context *ctx, const struct arg *arg)
214 {
215         if (ctx->args_num == CTX_STACK_SIZE)
216                 return -1;
217         ctx->args[ctx->args_num++] = arg;
218         return 0;
219 }
220
221 /** Default parsing function for token name matching. */
222 static int
223 parse_default(struct context *ctx, const struct token *token,
224               const char *str, unsigned int len,
225               void *buf, unsigned int size)
226 {
227         (void)ctx;
228         (void)buf;
229         (void)size;
230         if (strncmp(str, token->name, len))
231                 return -1;
232         return len;
233 }
234
235 /** Parse flow command, initialize output buffer for subsequent tokens. */
236 static int
237 parse_init(struct context *ctx, const struct token *token,
238            const char *str, unsigned int len,
239            void *buf, unsigned int size)
240 {
241         struct buffer *out = buf;
242
243         /* Token name must match. */
244         if (parse_default(ctx, token, str, len, NULL, 0) < 0)
245                 return -1;
246         /* Nothing else to do if there is no buffer. */
247         if (!out)
248                 return len;
249         /* Make sure buffer is large enough. */
250         if (size < sizeof(*out))
251                 return -1;
252         /* Initialize buffer. */
253         memset(out, 0x00, sizeof(*out));
254         memset((uint8_t *)out + sizeof(*out), 0x22, size - sizeof(*out));
255         ctx->object = out;
256         return len;
257 }
258
259 /**
260  * Parse signed/unsigned integers 8 to 64-bit long.
261  *
262  * Last argument (ctx->args) is retrieved to determine integer type and
263  * storage location.
264  */
265 static int
266 parse_int(struct context *ctx, const struct token *token,
267           const char *str, unsigned int len,
268           void *buf, unsigned int size)
269 {
270         const struct arg *arg = pop_args(ctx);
271         uintmax_t u;
272         char *end;
273
274         (void)token;
275         /* Argument is expected. */
276         if (!arg)
277                 return -1;
278         errno = 0;
279         u = arg->sign ?
280                 (uintmax_t)strtoimax(str, &end, 0) :
281                 strtoumax(str, &end, 0);
282         if (errno || (size_t)(end - str) != len)
283                 goto error;
284         if (!ctx->object)
285                 return len;
286         buf = (uint8_t *)ctx->object + arg->offset;
287         size = arg->size;
288         switch (size) {
289         case sizeof(uint8_t):
290                 *(uint8_t *)buf = u;
291                 break;
292         case sizeof(uint16_t):
293                 *(uint16_t *)buf = arg->hton ? rte_cpu_to_be_16(u) : u;
294                 break;
295         case sizeof(uint32_t):
296                 *(uint32_t *)buf = arg->hton ? rte_cpu_to_be_32(u) : u;
297                 break;
298         case sizeof(uint64_t):
299                 *(uint64_t *)buf = arg->hton ? rte_cpu_to_be_64(u) : u;
300                 break;
301         default:
302                 goto error;
303         }
304         return len;
305 error:
306         push_args(ctx, arg);
307         return -1;
308 }
309
310 /** No completion. */
311 static int
312 comp_none(struct context *ctx, const struct token *token,
313           unsigned int ent, char *buf, unsigned int size)
314 {
315         (void)ctx;
316         (void)token;
317         (void)ent;
318         (void)buf;
319         (void)size;
320         return 0;
321 }
322
323 /** Internal context. */
324 static struct context cmd_flow_context;
325
326 /** Global parser instance (cmdline API). */
327 cmdline_parse_inst_t cmd_flow;
328
329 /** Initialize context. */
330 static void
331 cmd_flow_context_init(struct context *ctx)
332 {
333         /* A full memset() is not necessary. */
334         ctx->curr = ZERO;
335         ctx->prev = ZERO;
336         ctx->next_num = 0;
337         ctx->args_num = 0;
338         ctx->reparse = 0;
339         ctx->eol = 0;
340         ctx->last = 0;
341         ctx->object = NULL;
342 }
343
344 /** Parse a token (cmdline API). */
345 static int
346 cmd_flow_parse(cmdline_parse_token_hdr_t *hdr, const char *src, void *result,
347                unsigned int size)
348 {
349         struct context *ctx = &cmd_flow_context;
350         const struct token *token;
351         const enum index *list;
352         int len;
353         int i;
354
355         (void)hdr;
356         /* Restart as requested. */
357         if (ctx->reparse)
358                 cmd_flow_context_init(ctx);
359         token = &token_list[ctx->curr];
360         /* Check argument length. */
361         ctx->eol = 0;
362         ctx->last = 1;
363         for (len = 0; src[len]; ++len)
364                 if (src[len] == '#' || isspace(src[len]))
365                         break;
366         if (!len)
367                 return -1;
368         /* Last argument and EOL detection. */
369         for (i = len; src[i]; ++i)
370                 if (src[i] == '#' || src[i] == '\r' || src[i] == '\n')
371                         break;
372                 else if (!isspace(src[i])) {
373                         ctx->last = 0;
374                         break;
375                 }
376         for (; src[i]; ++i)
377                 if (src[i] == '\r' || src[i] == '\n') {
378                         ctx->eol = 1;
379                         break;
380                 }
381         /* Initialize context if necessary. */
382         if (!ctx->next_num) {
383                 if (!token->next)
384                         return 0;
385                 ctx->next[ctx->next_num++] = token->next[0];
386         }
387         /* Process argument through candidates. */
388         ctx->prev = ctx->curr;
389         list = ctx->next[ctx->next_num - 1];
390         for (i = 0; list[i]; ++i) {
391                 const struct token *next = &token_list[list[i]];
392                 int tmp;
393
394                 ctx->curr = list[i];
395                 if (next->call)
396                         tmp = next->call(ctx, next, src, len, result, size);
397                 else
398                         tmp = parse_default(ctx, next, src, len, result, size);
399                 if (tmp == -1 || tmp != len)
400                         continue;
401                 token = next;
402                 break;
403         }
404         if (!list[i])
405                 return -1;
406         --ctx->next_num;
407         /* Push subsequent tokens if any. */
408         if (token->next)
409                 for (i = 0; token->next[i]; ++i) {
410                         if (ctx->next_num == RTE_DIM(ctx->next))
411                                 return -1;
412                         ctx->next[ctx->next_num++] = token->next[i];
413                 }
414         /* Push arguments if any. */
415         if (token->args)
416                 for (i = 0; token->args[i]; ++i) {
417                         if (ctx->args_num == RTE_DIM(ctx->args))
418                                 return -1;
419                         ctx->args[ctx->args_num++] = token->args[i];
420                 }
421         return len;
422 }
423
424 /** Return number of completion entries (cmdline API). */
425 static int
426 cmd_flow_complete_get_nb(cmdline_parse_token_hdr_t *hdr)
427 {
428         struct context *ctx = &cmd_flow_context;
429         const struct token *token = &token_list[ctx->curr];
430         const enum index *list;
431         int i;
432
433         (void)hdr;
434         /* Tell cmd_flow_parse() that context must be reinitialized. */
435         ctx->reparse = 1;
436         /* Count number of tokens in current list. */
437         if (ctx->next_num)
438                 list = ctx->next[ctx->next_num - 1];
439         else
440                 list = token->next[0];
441         for (i = 0; list[i]; ++i)
442                 ;
443         if (!i)
444                 return 0;
445         /*
446          * If there is a single token, use its completion callback, otherwise
447          * return the number of entries.
448          */
449         token = &token_list[list[0]];
450         if (i == 1 && token->comp) {
451                 /* Save index for cmd_flow_get_help(). */
452                 ctx->prev = list[0];
453                 return token->comp(ctx, token, 0, NULL, 0);
454         }
455         return i;
456 }
457
458 /** Return a completion entry (cmdline API). */
459 static int
460 cmd_flow_complete_get_elt(cmdline_parse_token_hdr_t *hdr, int index,
461                           char *dst, unsigned int size)
462 {
463         struct context *ctx = &cmd_flow_context;
464         const struct token *token = &token_list[ctx->curr];
465         const enum index *list;
466         int i;
467
468         (void)hdr;
469         /* Tell cmd_flow_parse() that context must be reinitialized. */
470         ctx->reparse = 1;
471         /* Count number of tokens in current list. */
472         if (ctx->next_num)
473                 list = ctx->next[ctx->next_num - 1];
474         else
475                 list = token->next[0];
476         for (i = 0; list[i]; ++i)
477                 ;
478         if (!i)
479                 return -1;
480         /* If there is a single token, use its completion callback. */
481         token = &token_list[list[0]];
482         if (i == 1 && token->comp) {
483                 /* Save index for cmd_flow_get_help(). */
484                 ctx->prev = list[0];
485                 return token->comp(ctx, token, index, dst, size) < 0 ? -1 : 0;
486         }
487         /* Otherwise make sure the index is valid and use defaults. */
488         if (index >= i)
489                 return -1;
490         token = &token_list[list[index]];
491         snprintf(dst, size, "%s", token->name);
492         /* Save index for cmd_flow_get_help(). */
493         ctx->prev = list[index];
494         return 0;
495 }
496
497 /** Populate help strings for current token (cmdline API). */
498 static int
499 cmd_flow_get_help(cmdline_parse_token_hdr_t *hdr, char *dst, unsigned int size)
500 {
501         struct context *ctx = &cmd_flow_context;
502         const struct token *token = &token_list[ctx->prev];
503
504         (void)hdr;
505         /* Tell cmd_flow_parse() that context must be reinitialized. */
506         ctx->reparse = 1;
507         if (!size)
508                 return -1;
509         /* Set token type and update global help with details. */
510         snprintf(dst, size, "%s", (token->type ? token->type : "TOKEN"));
511         if (token->help)
512                 cmd_flow.help_str = token->help;
513         else
514                 cmd_flow.help_str = token->name;
515         return 0;
516 }
517
518 /** Token definition template (cmdline API). */
519 static struct cmdline_token_hdr cmd_flow_token_hdr = {
520         .ops = &(struct cmdline_token_ops){
521                 .parse = cmd_flow_parse,
522                 .complete_get_nb = cmd_flow_complete_get_nb,
523                 .complete_get_elt = cmd_flow_complete_get_elt,
524                 .get_help = cmd_flow_get_help,
525         },
526         .offset = 0,
527 };
528
529 /** Populate the next dynamic token. */
530 static void
531 cmd_flow_tok(cmdline_parse_token_hdr_t **hdr,
532              cmdline_parse_token_hdr_t *(*hdrs)[])
533 {
534         struct context *ctx = &cmd_flow_context;
535
536         /* Always reinitialize context before requesting the first token. */
537         if (!(hdr - *hdrs))
538                 cmd_flow_context_init(ctx);
539         /* Return NULL when no more tokens are expected. */
540         if (!ctx->next_num && ctx->curr) {
541                 *hdr = NULL;
542                 return;
543         }
544         /* Determine if command should end here. */
545         if (ctx->eol && ctx->last && ctx->next_num) {
546                 const enum index *list = ctx->next[ctx->next_num - 1];
547                 int i;
548
549                 for (i = 0; list[i]; ++i) {
550                         if (list[i] != END)
551                                 continue;
552                         *hdr = NULL;
553                         return;
554                 }
555         }
556         *hdr = &cmd_flow_token_hdr;
557 }
558
559 /** Dispatch parsed buffer to function calls. */
560 static void
561 cmd_flow_parsed(const struct buffer *in)
562 {
563         switch (in->command) {
564         default:
565                 break;
566         }
567 }
568
569 /** Token generator and output processing callback (cmdline API). */
570 static void
571 cmd_flow_cb(void *arg0, struct cmdline *cl, void *arg2)
572 {
573         if (cl == NULL)
574                 cmd_flow_tok(arg0, arg2);
575         else
576                 cmd_flow_parsed(arg0);
577 }
578
579 /** Global parser instance (cmdline API). */
580 cmdline_parse_inst_t cmd_flow = {
581         .f = cmd_flow_cb,
582         .data = NULL, /**< Unused. */
583         .help_str = NULL, /**< Updated by cmd_flow_get_help(). */
584         .tokens = {
585                 NULL,
586         }, /**< Tokens are returned by cmd_flow_tok(). */
587 };