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. */
62 /* Top-level command. */
65 /* Sub-level commands. */
73 /** Maximum number of subsequent tokens and arguments on the stack. */
74 #define CTX_STACK_SIZE 16
76 /** Parser context. */
78 /** Stack of subsequent token lists to process. */
79 const enum index *next[CTX_STACK_SIZE];
80 /** Arguments for stacked tokens. */
81 const void *args[CTX_STACK_SIZE];
82 enum index curr; /**< Current token index. */
83 enum index prev; /**< Index of the last token seen. */
84 int next_num; /**< Number of entries in next[]. */
85 int args_num; /**< Number of entries in args[]. */
86 uint32_t reparse:1; /**< Start over from the beginning. */
87 uint32_t eol:1; /**< EOL has been detected. */
88 uint32_t last:1; /**< No more arguments. */
89 uint16_t port; /**< Current port ID (for completions). */
90 void *object; /**< Address of current object for relative offsets. */
93 /** Token argument. */
95 uint32_t hton:1; /**< Use network byte ordering. */
96 uint32_t sign:1; /**< Value is signed. */
97 uint32_t offset; /**< Relative offset from ctx->object. */
98 uint32_t size; /**< Field size. */
101 /** Parser token definition. */
103 /** Type displayed during completion (defaults to "TOKEN"). */
105 /** Help displayed during completion (defaults to token name). */
108 * Lists of subsequent tokens to push on the stack. Each call to the
109 * parser consumes the last entry of that stack.
111 const enum index *const *next;
112 /** Arguments stack for subsequent tokens that need them. */
113 const struct arg *const *args;
115 * Token-processing callback, returns -1 in case of error, the
116 * length of the matched string otherwise. If NULL, attempts to
117 * match the token name.
119 * If buf is not NULL, the result should be stored in it according
120 * to context. An error is returned if not large enough.
122 int (*call)(struct context *ctx, const struct token *token,
123 const char *str, unsigned int len,
124 void *buf, unsigned int size);
126 * Callback that provides possible values for this token, used for
127 * completion. Returns -1 in case of error, the number of possible
128 * values otherwise. If NULL, the token name is used.
130 * If buf is not NULL, entry index ent is written to buf and the
131 * full length of the entry is returned (same behavior as
134 int (*comp)(struct context *ctx, const struct token *token,
135 unsigned int ent, char *buf, unsigned int size);
136 /** Mandatory token name, no default value. */
140 /** Static initializer for the next field. */
141 #define NEXT(...) (const enum index *const []){ __VA_ARGS__, NULL, }
143 /** Static initializer for a NEXT() entry. */
144 #define NEXT_ENTRY(...) (const enum index []){ __VA_ARGS__, ZERO, }
146 /** Static initializer for the args field. */
147 #define ARGS(...) (const struct arg *const []){ __VA_ARGS__, NULL, }
149 /** Static initializer for ARGS() to target a field. */
150 #define ARGS_ENTRY(s, f) \
151 (&(const struct arg){ \
152 .offset = offsetof(s, f), \
153 .size = sizeof(((s *)0)->f), \
156 /** Static initializer for ARGS() to target a pointer. */
157 #define ARGS_ENTRY_PTR(s, f) \
158 (&(const struct arg){ \
159 .size = sizeof(*((s *)0)->f), \
162 /** Parser output buffer layout expected by cmd_flow_parsed(). */
164 enum index command; /**< Flow command. */
165 uint16_t port; /**< Affected port ID. */
170 } list; /**< List arguments. */
171 } args; /**< Command arguments. */
174 static const enum index next_list_attr[] = {
180 static int parse_init(struct context *, const struct token *,
181 const char *, unsigned int,
182 void *, unsigned int);
183 static int parse_flush(struct context *, const struct token *,
184 const char *, unsigned int,
185 void *, unsigned int);
186 static int parse_list(struct context *, const struct token *,
187 const char *, unsigned int,
188 void *, unsigned int);
189 static int parse_int(struct context *, const struct token *,
190 const char *, unsigned int,
191 void *, unsigned int);
192 static int parse_port(struct context *, const struct token *,
193 const char *, unsigned int,
194 void *, unsigned int);
195 static int comp_none(struct context *, const struct token *,
196 unsigned int, char *, unsigned int);
197 static int comp_port(struct context *, const struct token *,
198 unsigned int, char *, unsigned int);
200 /** Token definitions. */
201 static const struct token token_list[] = {
202 /* Special tokens. */
205 .help = "null entry, abused as the entry point",
206 .next = NEXT(NEXT_ENTRY(FLOW)),
211 .help = "command may end here",
217 .help = "integer value",
222 .name = "{unsigned}",
224 .help = "unsigned integer value",
231 .help = "port identifier",
236 .name = "{group_id}",
238 .help = "group identifier",
242 /* Top-level command. */
245 .type = "{command} {port_id} [{arg} [...]]",
246 .help = "manage ingress/egress flow rules",
247 .next = NEXT(NEXT_ENTRY
252 /* Sub-level commands. */
255 .help = "destroy all flow rules",
256 .next = NEXT(NEXT_ENTRY(PORT_ID)),
257 .args = ARGS(ARGS_ENTRY(struct buffer, port)),
262 .help = "list existing flow rules",
263 .next = NEXT(next_list_attr, NEXT_ENTRY(PORT_ID)),
264 .args = ARGS(ARGS_ENTRY(struct buffer, port)),
267 /* List arguments. */
270 .help = "specify a group",
271 .next = NEXT(next_list_attr, NEXT_ENTRY(GROUP_ID)),
272 .args = ARGS(ARGS_ENTRY_PTR(struct buffer, args.list.group)),
277 /** Remove and return last entry from argument stack. */
278 static const struct arg *
279 pop_args(struct context *ctx)
281 return ctx->args_num ? ctx->args[--ctx->args_num] : NULL;
284 /** Add entry on top of the argument stack. */
286 push_args(struct context *ctx, const struct arg *arg)
288 if (ctx->args_num == CTX_STACK_SIZE)
290 ctx->args[ctx->args_num++] = arg;
294 /** Default parsing function for token name matching. */
296 parse_default(struct context *ctx, const struct token *token,
297 const char *str, unsigned int len,
298 void *buf, unsigned int size)
303 if (strncmp(str, token->name, len))
308 /** Parse flow command, initialize output buffer for subsequent tokens. */
310 parse_init(struct context *ctx, const struct token *token,
311 const char *str, unsigned int len,
312 void *buf, unsigned int size)
314 struct buffer *out = buf;
316 /* Token name must match. */
317 if (parse_default(ctx, token, str, len, NULL, 0) < 0)
319 /* Nothing else to do if there is no buffer. */
322 /* Make sure buffer is large enough. */
323 if (size < sizeof(*out))
325 /* Initialize buffer. */
326 memset(out, 0x00, sizeof(*out));
327 memset((uint8_t *)out + sizeof(*out), 0x22, size - sizeof(*out));
332 /** Parse tokens for flush command. */
334 parse_flush(struct context *ctx, const struct token *token,
335 const char *str, unsigned int len,
336 void *buf, unsigned int size)
338 struct buffer *out = buf;
340 /* Token name must match. */
341 if (parse_default(ctx, token, str, len, NULL, 0) < 0)
343 /* Nothing else to do if there is no buffer. */
347 if (ctx->curr != FLUSH)
349 if (sizeof(*out) > size)
351 out->command = ctx->curr;
357 /** Parse tokens for list command. */
359 parse_list(struct context *ctx, const struct token *token,
360 const char *str, unsigned int len,
361 void *buf, unsigned int size)
363 struct buffer *out = buf;
365 /* Token name must match. */
366 if (parse_default(ctx, token, str, len, NULL, 0) < 0)
368 /* Nothing else to do if there is no buffer. */
372 if (ctx->curr != LIST)
374 if (sizeof(*out) > size)
376 out->command = ctx->curr;
378 out->args.list.group =
379 (void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
383 if (((uint8_t *)(out->args.list.group + out->args.list.group_n) +
384 sizeof(*out->args.list.group)) > (uint8_t *)out + size)
386 ctx->object = out->args.list.group + out->args.list.group_n++;
391 * Parse signed/unsigned integers 8 to 64-bit long.
393 * Last argument (ctx->args) is retrieved to determine integer type and
397 parse_int(struct context *ctx, const struct token *token,
398 const char *str, unsigned int len,
399 void *buf, unsigned int size)
401 const struct arg *arg = pop_args(ctx);
406 /* Argument is expected. */
411 (uintmax_t)strtoimax(str, &end, 0) :
412 strtoumax(str, &end, 0);
413 if (errno || (size_t)(end - str) != len)
417 buf = (uint8_t *)ctx->object + arg->offset;
420 case sizeof(uint8_t):
423 case sizeof(uint16_t):
424 *(uint16_t *)buf = arg->hton ? rte_cpu_to_be_16(u) : u;
426 case sizeof(uint32_t):
427 *(uint32_t *)buf = arg->hton ? rte_cpu_to_be_32(u) : u;
429 case sizeof(uint64_t):
430 *(uint64_t *)buf = arg->hton ? rte_cpu_to_be_64(u) : u;
441 /** Parse port and update context. */
443 parse_port(struct context *ctx, const struct token *token,
444 const char *str, unsigned int len,
445 void *buf, unsigned int size)
447 struct buffer *out = &(struct buffer){ .port = 0 };
456 ret = parse_int(ctx, token, str, len, out, size);
458 ctx->port = out->port;
464 /** No completion. */
466 comp_none(struct context *ctx, const struct token *token,
467 unsigned int ent, char *buf, unsigned int size)
477 /** Complete available ports. */
479 comp_port(struct context *ctx, const struct token *token,
480 unsigned int ent, char *buf, unsigned int size)
487 FOREACH_PORT(p, ports) {
489 return snprintf(buf, size, "%u", p);
497 /** Internal context. */
498 static struct context cmd_flow_context;
500 /** Global parser instance (cmdline API). */
501 cmdline_parse_inst_t cmd_flow;
503 /** Initialize context. */
505 cmd_flow_context_init(struct context *ctx)
507 /* A full memset() is not necessary. */
519 /** Parse a token (cmdline API). */
521 cmd_flow_parse(cmdline_parse_token_hdr_t *hdr, const char *src, void *result,
524 struct context *ctx = &cmd_flow_context;
525 const struct token *token;
526 const enum index *list;
531 /* Restart as requested. */
533 cmd_flow_context_init(ctx);
534 token = &token_list[ctx->curr];
535 /* Check argument length. */
538 for (len = 0; src[len]; ++len)
539 if (src[len] == '#' || isspace(src[len]))
543 /* Last argument and EOL detection. */
544 for (i = len; src[i]; ++i)
545 if (src[i] == '#' || src[i] == '\r' || src[i] == '\n')
547 else if (!isspace(src[i])) {
552 if (src[i] == '\r' || src[i] == '\n') {
556 /* Initialize context if necessary. */
557 if (!ctx->next_num) {
560 ctx->next[ctx->next_num++] = token->next[0];
562 /* Process argument through candidates. */
563 ctx->prev = ctx->curr;
564 list = ctx->next[ctx->next_num - 1];
565 for (i = 0; list[i]; ++i) {
566 const struct token *next = &token_list[list[i]];
571 tmp = next->call(ctx, next, src, len, result, size);
573 tmp = parse_default(ctx, next, src, len, result, size);
574 if (tmp == -1 || tmp != len)
582 /* Push subsequent tokens if any. */
584 for (i = 0; token->next[i]; ++i) {
585 if (ctx->next_num == RTE_DIM(ctx->next))
587 ctx->next[ctx->next_num++] = token->next[i];
589 /* Push arguments if any. */
591 for (i = 0; token->args[i]; ++i) {
592 if (ctx->args_num == RTE_DIM(ctx->args))
594 ctx->args[ctx->args_num++] = token->args[i];
599 /** Return number of completion entries (cmdline API). */
601 cmd_flow_complete_get_nb(cmdline_parse_token_hdr_t *hdr)
603 struct context *ctx = &cmd_flow_context;
604 const struct token *token = &token_list[ctx->curr];
605 const enum index *list;
609 /* Tell cmd_flow_parse() that context must be reinitialized. */
611 /* Count number of tokens in current list. */
613 list = ctx->next[ctx->next_num - 1];
615 list = token->next[0];
616 for (i = 0; list[i]; ++i)
621 * If there is a single token, use its completion callback, otherwise
622 * return the number of entries.
624 token = &token_list[list[0]];
625 if (i == 1 && token->comp) {
626 /* Save index for cmd_flow_get_help(). */
628 return token->comp(ctx, token, 0, NULL, 0);
633 /** Return a completion entry (cmdline API). */
635 cmd_flow_complete_get_elt(cmdline_parse_token_hdr_t *hdr, int index,
636 char *dst, unsigned int size)
638 struct context *ctx = &cmd_flow_context;
639 const struct token *token = &token_list[ctx->curr];
640 const enum index *list;
644 /* Tell cmd_flow_parse() that context must be reinitialized. */
646 /* Count number of tokens in current list. */
648 list = ctx->next[ctx->next_num - 1];
650 list = token->next[0];
651 for (i = 0; list[i]; ++i)
655 /* If there is a single token, use its completion callback. */
656 token = &token_list[list[0]];
657 if (i == 1 && token->comp) {
658 /* Save index for cmd_flow_get_help(). */
660 return token->comp(ctx, token, index, dst, size) < 0 ? -1 : 0;
662 /* Otherwise make sure the index is valid and use defaults. */
665 token = &token_list[list[index]];
666 snprintf(dst, size, "%s", token->name);
667 /* Save index for cmd_flow_get_help(). */
668 ctx->prev = list[index];
672 /** Populate help strings for current token (cmdline API). */
674 cmd_flow_get_help(cmdline_parse_token_hdr_t *hdr, char *dst, unsigned int size)
676 struct context *ctx = &cmd_flow_context;
677 const struct token *token = &token_list[ctx->prev];
680 /* Tell cmd_flow_parse() that context must be reinitialized. */
684 /* Set token type and update global help with details. */
685 snprintf(dst, size, "%s", (token->type ? token->type : "TOKEN"));
687 cmd_flow.help_str = token->help;
689 cmd_flow.help_str = token->name;
693 /** Token definition template (cmdline API). */
694 static struct cmdline_token_hdr cmd_flow_token_hdr = {
695 .ops = &(struct cmdline_token_ops){
696 .parse = cmd_flow_parse,
697 .complete_get_nb = cmd_flow_complete_get_nb,
698 .complete_get_elt = cmd_flow_complete_get_elt,
699 .get_help = cmd_flow_get_help,
704 /** Populate the next dynamic token. */
706 cmd_flow_tok(cmdline_parse_token_hdr_t **hdr,
707 cmdline_parse_token_hdr_t *(*hdrs)[])
709 struct context *ctx = &cmd_flow_context;
711 /* Always reinitialize context before requesting the first token. */
713 cmd_flow_context_init(ctx);
714 /* Return NULL when no more tokens are expected. */
715 if (!ctx->next_num && ctx->curr) {
719 /* Determine if command should end here. */
720 if (ctx->eol && ctx->last && ctx->next_num) {
721 const enum index *list = ctx->next[ctx->next_num - 1];
724 for (i = 0; list[i]; ++i) {
731 *hdr = &cmd_flow_token_hdr;
734 /** Dispatch parsed buffer to function calls. */
736 cmd_flow_parsed(const struct buffer *in)
738 switch (in->command) {
740 port_flow_flush(in->port);
743 port_flow_list(in->port, in->args.list.group_n,
744 in->args.list.group);
751 /** Token generator and output processing callback (cmdline API). */
753 cmd_flow_cb(void *arg0, struct cmdline *cl, void *arg2)
756 cmd_flow_tok(arg0, arg2);
758 cmd_flow_parsed(arg0);
761 /** Global parser instance (cmdline API). */
762 cmdline_parse_inst_t cmd_flow = {
764 .data = NULL, /**< Unused. */
765 .help_str = NULL, /**< Updated by cmd_flow_get_help(). */
768 }, /**< Tokens are returned by cmd_flow_tok(). */