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