4 * Copyright 2016 6WIND S.A.
5 * Copyright 2016 Mellanox.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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.
42 #include <rte_common.h>
43 #include <rte_ethdev.h>
44 #include <rte_byteorder.h>
45 #include <cmdline_parse.h>
50 /** Parser token indices. */
64 /* Top-level command. */
67 /* Sub-level commands. */
74 /* Destroy arguments. */
80 /* Validate/create arguments. */
86 /* Validate/create pattern. */
93 /* Validate/create actions. */
101 /** Maximum number of subsequent tokens and arguments on the stack. */
102 #define CTX_STACK_SIZE 16
104 /** Parser context. */
106 /** Stack of subsequent token lists to process. */
107 const enum index *next[CTX_STACK_SIZE];
108 /** Arguments for stacked tokens. */
109 const void *args[CTX_STACK_SIZE];
110 enum index curr; /**< Current token index. */
111 enum index prev; /**< Index of the last token seen. */
112 int next_num; /**< Number of entries in next[]. */
113 int args_num; /**< Number of entries in args[]. */
114 uint32_t reparse:1; /**< Start over from the beginning. */
115 uint32_t eol:1; /**< EOL has been detected. */
116 uint32_t last:1; /**< No more arguments. */
117 uint16_t port; /**< Current port ID (for completions). */
118 uint32_t objdata; /**< Object-specific data. */
119 void *object; /**< Address of current object for relative offsets. */
122 /** Token argument. */
124 uint32_t hton:1; /**< Use network byte ordering. */
125 uint32_t sign:1; /**< Value is signed. */
126 uint32_t offset; /**< Relative offset from ctx->object. */
127 uint32_t size; /**< Field size. */
130 /** Parser token definition. */
132 /** Type displayed during completion (defaults to "TOKEN"). */
134 /** Help displayed during completion (defaults to token name). */
136 /** Private data used by parser functions. */
139 * Lists of subsequent tokens to push on the stack. Each call to the
140 * parser consumes the last entry of that stack.
142 const enum index *const *next;
143 /** Arguments stack for subsequent tokens that need them. */
144 const struct arg *const *args;
146 * Token-processing callback, returns -1 in case of error, the
147 * length of the matched string otherwise. If NULL, attempts to
148 * match the token name.
150 * If buf is not NULL, the result should be stored in it according
151 * to context. An error is returned if not large enough.
153 int (*call)(struct context *ctx, const struct token *token,
154 const char *str, unsigned int len,
155 void *buf, unsigned int size);
157 * Callback that provides possible values for this token, used for
158 * completion. Returns -1 in case of error, the number of possible
159 * values otherwise. If NULL, the token name is used.
161 * If buf is not NULL, entry index ent is written to buf and the
162 * full length of the entry is returned (same behavior as
165 int (*comp)(struct context *ctx, const struct token *token,
166 unsigned int ent, char *buf, unsigned int size);
167 /** Mandatory token name, no default value. */
171 /** Static initializer for the next field. */
172 #define NEXT(...) (const enum index *const []){ __VA_ARGS__, NULL, }
174 /** Static initializer for a NEXT() entry. */
175 #define NEXT_ENTRY(...) (const enum index []){ __VA_ARGS__, ZERO, }
177 /** Static initializer for the args field. */
178 #define ARGS(...) (const struct arg *const []){ __VA_ARGS__, NULL, }
180 /** Static initializer for ARGS() to target a field. */
181 #define ARGS_ENTRY(s, f) \
182 (&(const struct arg){ \
183 .offset = offsetof(s, f), \
184 .size = sizeof(((s *)0)->f), \
187 /** Static initializer for ARGS() to target a pointer. */
188 #define ARGS_ENTRY_PTR(s, f) \
189 (&(const struct arg){ \
190 .size = sizeof(*((s *)0)->f), \
193 /** Parser output buffer layout expected by cmd_flow_parsed(). */
195 enum index command; /**< Flow command. */
196 uint16_t port; /**< Affected port ID. */
199 struct rte_flow_attr attr;
200 struct rte_flow_item *pattern;
201 struct rte_flow_action *actions;
205 } vc; /**< Validate/create arguments. */
209 } destroy; /**< Destroy arguments. */
213 } list; /**< List arguments. */
214 } args; /**< Command arguments. */
217 /** Private data for pattern items. */
218 struct parse_item_priv {
219 enum rte_flow_item_type type; /**< Item type. */
220 uint32_t size; /**< Size of item specification structure. */
223 #define PRIV_ITEM(t, s) \
224 (&(const struct parse_item_priv){ \
225 .type = RTE_FLOW_ITEM_TYPE_ ## t, \
229 /** Private data for actions. */
230 struct parse_action_priv {
231 enum rte_flow_action_type type; /**< Action type. */
232 uint32_t size; /**< Size of action configuration structure. */
235 #define PRIV_ACTION(t, s) \
236 (&(const struct parse_action_priv){ \
237 .type = RTE_FLOW_ACTION_TYPE_ ## t, \
241 static const enum index next_vc_attr[] = {
250 static const enum index next_destroy_attr[] = {
256 static const enum index next_list_attr[] = {
262 static const enum index next_item[] = {
269 static const enum index next_action[] = {
276 static int parse_init(struct context *, const struct token *,
277 const char *, unsigned int,
278 void *, unsigned int);
279 static int parse_vc(struct context *, const struct token *,
280 const char *, unsigned int,
281 void *, unsigned int);
282 static int parse_destroy(struct context *, const struct token *,
283 const char *, unsigned int,
284 void *, unsigned int);
285 static int parse_flush(struct context *, const struct token *,
286 const char *, unsigned int,
287 void *, unsigned int);
288 static int parse_list(struct context *, const struct token *,
289 const char *, unsigned int,
290 void *, unsigned int);
291 static int parse_int(struct context *, const struct token *,
292 const char *, unsigned int,
293 void *, unsigned int);
294 static int parse_port(struct context *, const struct token *,
295 const char *, unsigned int,
296 void *, unsigned int);
297 static int comp_none(struct context *, const struct token *,
298 unsigned int, char *, unsigned int);
299 static int comp_port(struct context *, const struct token *,
300 unsigned int, char *, unsigned int);
301 static int comp_rule_id(struct context *, const struct token *,
302 unsigned int, char *, unsigned int);
304 /** Token definitions. */
305 static const struct token token_list[] = {
306 /* Special tokens. */
309 .help = "null entry, abused as the entry point",
310 .next = NEXT(NEXT_ENTRY(FLOW)),
315 .help = "command may end here",
321 .help = "integer value",
326 .name = "{unsigned}",
328 .help = "unsigned integer value",
335 .help = "rule identifier",
337 .comp = comp_rule_id,
342 .help = "port identifier",
347 .name = "{group_id}",
349 .help = "group identifier",
356 .help = "priority level",
360 /* Top-level command. */
363 .type = "{command} {port_id} [{arg} [...]]",
364 .help = "manage ingress/egress flow rules",
365 .next = NEXT(NEXT_ENTRY
373 /* Sub-level commands. */
376 .help = "check whether a flow rule can be created",
377 .next = NEXT(next_vc_attr, NEXT_ENTRY(PORT_ID)),
378 .args = ARGS(ARGS_ENTRY(struct buffer, port)),
383 .help = "create a flow rule",
384 .next = NEXT(next_vc_attr, NEXT_ENTRY(PORT_ID)),
385 .args = ARGS(ARGS_ENTRY(struct buffer, port)),
390 .help = "destroy specific flow rules",
391 .next = NEXT(NEXT_ENTRY(DESTROY_RULE), NEXT_ENTRY(PORT_ID)),
392 .args = ARGS(ARGS_ENTRY(struct buffer, port)),
393 .call = parse_destroy,
397 .help = "destroy all flow rules",
398 .next = NEXT(NEXT_ENTRY(PORT_ID)),
399 .args = ARGS(ARGS_ENTRY(struct buffer, port)),
404 .help = "list existing flow rules",
405 .next = NEXT(next_list_attr, NEXT_ENTRY(PORT_ID)),
406 .args = ARGS(ARGS_ENTRY(struct buffer, port)),
409 /* Destroy arguments. */
412 .help = "specify a rule identifier",
413 .next = NEXT(next_destroy_attr, NEXT_ENTRY(RULE_ID)),
414 .args = ARGS(ARGS_ENTRY_PTR(struct buffer, args.destroy.rule)),
415 .call = parse_destroy,
417 /* List arguments. */
420 .help = "specify a group",
421 .next = NEXT(next_list_attr, NEXT_ENTRY(GROUP_ID)),
422 .args = ARGS(ARGS_ENTRY_PTR(struct buffer, args.list.group)),
425 /* Validate/create attributes. */
428 .help = "specify a group",
429 .next = NEXT(next_vc_attr, NEXT_ENTRY(GROUP_ID)),
430 .args = ARGS(ARGS_ENTRY(struct rte_flow_attr, group)),
435 .help = "specify a priority level",
436 .next = NEXT(next_vc_attr, NEXT_ENTRY(PRIORITY_LEVEL)),
437 .args = ARGS(ARGS_ENTRY(struct rte_flow_attr, priority)),
442 .help = "affect rule to ingress",
443 .next = NEXT(next_vc_attr),
448 .help = "affect rule to egress",
449 .next = NEXT(next_vc_attr),
452 /* Validate/create pattern. */
455 .help = "submit a list of pattern items",
456 .next = NEXT(next_item),
461 .help = "specify next pattern item",
462 .next = NEXT(next_item),
466 .help = "end list of pattern items",
467 .priv = PRIV_ITEM(END, 0),
468 .next = NEXT(NEXT_ENTRY(ACTIONS)),
473 .help = "no-op pattern item",
474 .priv = PRIV_ITEM(VOID, 0),
475 .next = NEXT(NEXT_ENTRY(ITEM_NEXT)),
480 .help = "perform actions when pattern does not match",
481 .priv = PRIV_ITEM(INVERT, 0),
482 .next = NEXT(NEXT_ENTRY(ITEM_NEXT)),
485 /* Validate/create actions. */
488 .help = "submit a list of associated actions",
489 .next = NEXT(next_action),
494 .help = "specify next action",
495 .next = NEXT(next_action),
499 .help = "end list of actions",
500 .priv = PRIV_ACTION(END, 0),
505 .help = "no-op action",
506 .priv = PRIV_ACTION(VOID, 0),
507 .next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
510 [ACTION_PASSTHRU] = {
512 .help = "let subsequent rule process matched packets",
513 .priv = PRIV_ACTION(PASSTHRU, 0),
514 .next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
519 /** Remove and return last entry from argument stack. */
520 static const struct arg *
521 pop_args(struct context *ctx)
523 return ctx->args_num ? ctx->args[--ctx->args_num] : NULL;
526 /** Add entry on top of the argument stack. */
528 push_args(struct context *ctx, const struct arg *arg)
530 if (ctx->args_num == CTX_STACK_SIZE)
532 ctx->args[ctx->args_num++] = arg;
536 /** Default parsing function for token name matching. */
538 parse_default(struct context *ctx, const struct token *token,
539 const char *str, unsigned int len,
540 void *buf, unsigned int size)
545 if (strncmp(str, token->name, len))
550 /** Parse flow command, initialize output buffer for subsequent tokens. */
552 parse_init(struct context *ctx, const struct token *token,
553 const char *str, unsigned int len,
554 void *buf, unsigned int size)
556 struct buffer *out = buf;
558 /* Token name must match. */
559 if (parse_default(ctx, token, str, len, NULL, 0) < 0)
561 /* Nothing else to do if there is no buffer. */
564 /* Make sure buffer is large enough. */
565 if (size < sizeof(*out))
567 /* Initialize buffer. */
568 memset(out, 0x00, sizeof(*out));
569 memset((uint8_t *)out + sizeof(*out), 0x22, size - sizeof(*out));
575 /** Parse tokens for validate/create commands. */
577 parse_vc(struct context *ctx, const struct token *token,
578 const char *str, unsigned int len,
579 void *buf, unsigned int size)
581 struct buffer *out = buf;
585 /* Token name must match. */
586 if (parse_default(ctx, token, str, len, NULL, 0) < 0)
588 /* Nothing else to do if there is no buffer. */
592 if (ctx->curr != VALIDATE && ctx->curr != CREATE)
594 if (sizeof(*out) > size)
596 out->command = ctx->curr;
599 out->args.vc.data = (uint8_t *)out + size;
603 ctx->object = &out->args.vc.attr;
609 out->args.vc.attr.ingress = 1;
612 out->args.vc.attr.egress = 1;
615 out->args.vc.pattern =
616 (void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
618 ctx->object = out->args.vc.pattern;
621 out->args.vc.actions =
622 (void *)RTE_ALIGN_CEIL((uintptr_t)
623 (out->args.vc.pattern +
624 out->args.vc.pattern_n),
626 ctx->object = out->args.vc.actions;
633 if (!out->args.vc.actions) {
634 const struct parse_item_priv *priv = token->priv;
635 struct rte_flow_item *item =
636 out->args.vc.pattern + out->args.vc.pattern_n;
638 data_size = priv->size * 3; /* spec, last, mask */
639 data = (void *)RTE_ALIGN_FLOOR((uintptr_t)
640 (out->args.vc.data - data_size),
642 if ((uint8_t *)item + sizeof(*item) > data)
644 *item = (struct rte_flow_item){
647 ++out->args.vc.pattern_n;
650 const struct parse_action_priv *priv = token->priv;
651 struct rte_flow_action *action =
652 out->args.vc.actions + out->args.vc.actions_n;
654 data_size = priv->size; /* configuration */
655 data = (void *)RTE_ALIGN_FLOOR((uintptr_t)
656 (out->args.vc.data - data_size),
658 if ((uint8_t *)action + sizeof(*action) > data)
660 *action = (struct rte_flow_action){
663 ++out->args.vc.actions_n;
664 ctx->object = action;
666 memset(data, 0, data_size);
667 out->args.vc.data = data;
668 ctx->objdata = data_size;
672 /** Parse tokens for destroy command. */
674 parse_destroy(struct context *ctx, const struct token *token,
675 const char *str, unsigned int len,
676 void *buf, unsigned int size)
678 struct buffer *out = buf;
680 /* Token name must match. */
681 if (parse_default(ctx, token, str, len, NULL, 0) < 0)
683 /* Nothing else to do if there is no buffer. */
687 if (ctx->curr != DESTROY)
689 if (sizeof(*out) > size)
691 out->command = ctx->curr;
694 out->args.destroy.rule =
695 (void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
699 if (((uint8_t *)(out->args.destroy.rule + out->args.destroy.rule_n) +
700 sizeof(*out->args.destroy.rule)) > (uint8_t *)out + size)
703 ctx->object = out->args.destroy.rule + out->args.destroy.rule_n++;
707 /** Parse tokens for flush command. */
709 parse_flush(struct context *ctx, const struct token *token,
710 const char *str, unsigned int len,
711 void *buf, unsigned int size)
713 struct buffer *out = buf;
715 /* Token name must match. */
716 if (parse_default(ctx, token, str, len, NULL, 0) < 0)
718 /* Nothing else to do if there is no buffer. */
722 if (ctx->curr != FLUSH)
724 if (sizeof(*out) > size)
726 out->command = ctx->curr;
733 /** Parse tokens for list command. */
735 parse_list(struct context *ctx, const struct token *token,
736 const char *str, unsigned int len,
737 void *buf, unsigned int size)
739 struct buffer *out = buf;
741 /* Token name must match. */
742 if (parse_default(ctx, token, str, len, NULL, 0) < 0)
744 /* Nothing else to do if there is no buffer. */
748 if (ctx->curr != LIST)
750 if (sizeof(*out) > size)
752 out->command = ctx->curr;
755 out->args.list.group =
756 (void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
760 if (((uint8_t *)(out->args.list.group + out->args.list.group_n) +
761 sizeof(*out->args.list.group)) > (uint8_t *)out + size)
764 ctx->object = out->args.list.group + out->args.list.group_n++;
769 * Parse signed/unsigned integers 8 to 64-bit long.
771 * Last argument (ctx->args) is retrieved to determine integer type and
775 parse_int(struct context *ctx, const struct token *token,
776 const char *str, unsigned int len,
777 void *buf, unsigned int size)
779 const struct arg *arg = pop_args(ctx);
784 /* Argument is expected. */
789 (uintmax_t)strtoimax(str, &end, 0) :
790 strtoumax(str, &end, 0);
791 if (errno || (size_t)(end - str) != len)
795 buf = (uint8_t *)ctx->object + arg->offset;
798 case sizeof(uint8_t):
801 case sizeof(uint16_t):
802 *(uint16_t *)buf = arg->hton ? rte_cpu_to_be_16(u) : u;
804 case sizeof(uint32_t):
805 *(uint32_t *)buf = arg->hton ? rte_cpu_to_be_32(u) : u;
807 case sizeof(uint64_t):
808 *(uint64_t *)buf = arg->hton ? rte_cpu_to_be_64(u) : u;
819 /** Parse port and update context. */
821 parse_port(struct context *ctx, const struct token *token,
822 const char *str, unsigned int len,
823 void *buf, unsigned int size)
825 struct buffer *out = &(struct buffer){ .port = 0 };
835 ret = parse_int(ctx, token, str, len, out, size);
837 ctx->port = out->port;
843 /** No completion. */
845 comp_none(struct context *ctx, const struct token *token,
846 unsigned int ent, char *buf, unsigned int size)
856 /** Complete available ports. */
858 comp_port(struct context *ctx, const struct token *token,
859 unsigned int ent, char *buf, unsigned int size)
866 FOREACH_PORT(p, ports) {
868 return snprintf(buf, size, "%u", p);
876 /** Complete available rule IDs. */
878 comp_rule_id(struct context *ctx, const struct token *token,
879 unsigned int ent, char *buf, unsigned int size)
882 struct rte_port *port;
883 struct port_flow *pf;
886 if (port_id_is_invalid(ctx->port, DISABLED_WARN) ||
887 ctx->port == (uint16_t)RTE_PORT_ALL)
889 port = &ports[ctx->port];
890 for (pf = port->flow_list; pf != NULL; pf = pf->next) {
892 return snprintf(buf, size, "%u", pf->id);
900 /** Internal context. */
901 static struct context cmd_flow_context;
903 /** Global parser instance (cmdline API). */
904 cmdline_parse_inst_t cmd_flow;
906 /** Initialize context. */
908 cmd_flow_context_init(struct context *ctx)
910 /* A full memset() is not necessary. */
923 /** Parse a token (cmdline API). */
925 cmd_flow_parse(cmdline_parse_token_hdr_t *hdr, const char *src, void *result,
928 struct context *ctx = &cmd_flow_context;
929 const struct token *token;
930 const enum index *list;
935 /* Restart as requested. */
937 cmd_flow_context_init(ctx);
938 token = &token_list[ctx->curr];
939 /* Check argument length. */
942 for (len = 0; src[len]; ++len)
943 if (src[len] == '#' || isspace(src[len]))
947 /* Last argument and EOL detection. */
948 for (i = len; src[i]; ++i)
949 if (src[i] == '#' || src[i] == '\r' || src[i] == '\n')
951 else if (!isspace(src[i])) {
956 if (src[i] == '\r' || src[i] == '\n') {
960 /* Initialize context if necessary. */
961 if (!ctx->next_num) {
964 ctx->next[ctx->next_num++] = token->next[0];
966 /* Process argument through candidates. */
967 ctx->prev = ctx->curr;
968 list = ctx->next[ctx->next_num - 1];
969 for (i = 0; list[i]; ++i) {
970 const struct token *next = &token_list[list[i]];
975 tmp = next->call(ctx, next, src, len, result, size);
977 tmp = parse_default(ctx, next, src, len, result, size);
978 if (tmp == -1 || tmp != len)
986 /* Push subsequent tokens if any. */
988 for (i = 0; token->next[i]; ++i) {
989 if (ctx->next_num == RTE_DIM(ctx->next))
991 ctx->next[ctx->next_num++] = token->next[i];
993 /* Push arguments if any. */
995 for (i = 0; token->args[i]; ++i) {
996 if (ctx->args_num == RTE_DIM(ctx->args))
998 ctx->args[ctx->args_num++] = token->args[i];
1003 /** Return number of completion entries (cmdline API). */
1005 cmd_flow_complete_get_nb(cmdline_parse_token_hdr_t *hdr)
1007 struct context *ctx = &cmd_flow_context;
1008 const struct token *token = &token_list[ctx->curr];
1009 const enum index *list;
1013 /* Tell cmd_flow_parse() that context must be reinitialized. */
1015 /* Count number of tokens in current list. */
1017 list = ctx->next[ctx->next_num - 1];
1019 list = token->next[0];
1020 for (i = 0; list[i]; ++i)
1025 * If there is a single token, use its completion callback, otherwise
1026 * return the number of entries.
1028 token = &token_list[list[0]];
1029 if (i == 1 && token->comp) {
1030 /* Save index for cmd_flow_get_help(). */
1031 ctx->prev = list[0];
1032 return token->comp(ctx, token, 0, NULL, 0);
1037 /** Return a completion entry (cmdline API). */
1039 cmd_flow_complete_get_elt(cmdline_parse_token_hdr_t *hdr, int index,
1040 char *dst, unsigned int size)
1042 struct context *ctx = &cmd_flow_context;
1043 const struct token *token = &token_list[ctx->curr];
1044 const enum index *list;
1048 /* Tell cmd_flow_parse() that context must be reinitialized. */
1050 /* Count number of tokens in current list. */
1052 list = ctx->next[ctx->next_num - 1];
1054 list = token->next[0];
1055 for (i = 0; list[i]; ++i)
1059 /* If there is a single token, use its completion callback. */
1060 token = &token_list[list[0]];
1061 if (i == 1 && token->comp) {
1062 /* Save index for cmd_flow_get_help(). */
1063 ctx->prev = list[0];
1064 return token->comp(ctx, token, index, dst, size) < 0 ? -1 : 0;
1066 /* Otherwise make sure the index is valid and use defaults. */
1069 token = &token_list[list[index]];
1070 snprintf(dst, size, "%s", token->name);
1071 /* Save index for cmd_flow_get_help(). */
1072 ctx->prev = list[index];
1076 /** Populate help strings for current token (cmdline API). */
1078 cmd_flow_get_help(cmdline_parse_token_hdr_t *hdr, char *dst, unsigned int size)
1080 struct context *ctx = &cmd_flow_context;
1081 const struct token *token = &token_list[ctx->prev];
1084 /* Tell cmd_flow_parse() that context must be reinitialized. */
1088 /* Set token type and update global help with details. */
1089 snprintf(dst, size, "%s", (token->type ? token->type : "TOKEN"));
1091 cmd_flow.help_str = token->help;
1093 cmd_flow.help_str = token->name;
1097 /** Token definition template (cmdline API). */
1098 static struct cmdline_token_hdr cmd_flow_token_hdr = {
1099 .ops = &(struct cmdline_token_ops){
1100 .parse = cmd_flow_parse,
1101 .complete_get_nb = cmd_flow_complete_get_nb,
1102 .complete_get_elt = cmd_flow_complete_get_elt,
1103 .get_help = cmd_flow_get_help,
1108 /** Populate the next dynamic token. */
1110 cmd_flow_tok(cmdline_parse_token_hdr_t **hdr,
1111 cmdline_parse_token_hdr_t *(*hdrs)[])
1113 struct context *ctx = &cmd_flow_context;
1115 /* Always reinitialize context before requesting the first token. */
1117 cmd_flow_context_init(ctx);
1118 /* Return NULL when no more tokens are expected. */
1119 if (!ctx->next_num && ctx->curr) {
1123 /* Determine if command should end here. */
1124 if (ctx->eol && ctx->last && ctx->next_num) {
1125 const enum index *list = ctx->next[ctx->next_num - 1];
1128 for (i = 0; list[i]; ++i) {
1135 *hdr = &cmd_flow_token_hdr;
1138 /** Dispatch parsed buffer to function calls. */
1140 cmd_flow_parsed(const struct buffer *in)
1142 switch (in->command) {
1144 port_flow_validate(in->port, &in->args.vc.attr,
1145 in->args.vc.pattern, in->args.vc.actions);
1148 port_flow_create(in->port, &in->args.vc.attr,
1149 in->args.vc.pattern, in->args.vc.actions);
1152 port_flow_destroy(in->port, in->args.destroy.rule_n,
1153 in->args.destroy.rule);
1156 port_flow_flush(in->port);
1159 port_flow_list(in->port, in->args.list.group_n,
1160 in->args.list.group);
1167 /** Token generator and output processing callback (cmdline API). */
1169 cmd_flow_cb(void *arg0, struct cmdline *cl, void *arg2)
1172 cmd_flow_tok(arg0, arg2);
1174 cmd_flow_parsed(arg0);
1177 /** Global parser instance (cmdline API). */
1178 cmdline_parse_inst_t cmd_flow = {
1180 .data = NULL, /**< Unused. */
1181 .help_str = NULL, /**< Updated by cmd_flow_get_help(). */
1184 }, /**< Tokens are returned by cmd_flow_tok(). */