f5aef0fc2217e166f713960cbbfd585eaa0905a2
[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 <ctype.h>
38 #include <string.h>
39
40 #include <rte_common.h>
41 #include <rte_ethdev.h>
42 #include <cmdline_parse.h>
43 #include <rte_flow.h>
44
45 #include "testpmd.h"
46
47 /** Parser token indices. */
48 enum index {
49         /* Special tokens. */
50         ZERO = 0,
51         END,
52
53         /* Top-level command. */
54         FLOW,
55 };
56
57 /** Maximum number of subsequent tokens and arguments on the stack. */
58 #define CTX_STACK_SIZE 16
59
60 /** Parser context. */
61 struct context {
62         /** Stack of subsequent token lists to process. */
63         const enum index *next[CTX_STACK_SIZE];
64         enum index curr; /**< Current token index. */
65         enum index prev; /**< Index of the last token seen. */
66         int next_num; /**< Number of entries in next[]. */
67         uint32_t reparse:1; /**< Start over from the beginning. */
68         uint32_t eol:1; /**< EOL has been detected. */
69         uint32_t last:1; /**< No more arguments. */
70 };
71
72 /** Parser token definition. */
73 struct token {
74         /** Type displayed during completion (defaults to "TOKEN"). */
75         const char *type;
76         /** Help displayed during completion (defaults to token name). */
77         const char *help;
78         /**
79          * Lists of subsequent tokens to push on the stack. Each call to the
80          * parser consumes the last entry of that stack.
81          */
82         const enum index *const *next;
83         /**
84          * Token-processing callback, returns -1 in case of error, the
85          * length of the matched string otherwise. If NULL, attempts to
86          * match the token name.
87          *
88          * If buf is not NULL, the result should be stored in it according
89          * to context. An error is returned if not large enough.
90          */
91         int (*call)(struct context *ctx, const struct token *token,
92                     const char *str, unsigned int len,
93                     void *buf, unsigned int size);
94         /**
95          * Callback that provides possible values for this token, used for
96          * completion. Returns -1 in case of error, the number of possible
97          * values otherwise. If NULL, the token name is used.
98          *
99          * If buf is not NULL, entry index ent is written to buf and the
100          * full length of the entry is returned (same behavior as
101          * snprintf()).
102          */
103         int (*comp)(struct context *ctx, const struct token *token,
104                     unsigned int ent, char *buf, unsigned int size);
105         /** Mandatory token name, no default value. */
106         const char *name;
107 };
108
109 /** Static initializer for the next field. */
110 #define NEXT(...) (const enum index *const []){ __VA_ARGS__, NULL, }
111
112 /** Static initializer for a NEXT() entry. */
113 #define NEXT_ENTRY(...) (const enum index []){ __VA_ARGS__, ZERO, }
114
115 /** Parser output buffer layout expected by cmd_flow_parsed(). */
116 struct buffer {
117         enum index command; /**< Flow command. */
118         uint16_t port; /**< Affected port ID. */
119 };
120
121 static int parse_init(struct context *, const struct token *,
122                       const char *, unsigned int,
123                       void *, unsigned int);
124
125 /** Token definitions. */
126 static const struct token token_list[] = {
127         /* Special tokens. */
128         [ZERO] = {
129                 .name = "ZERO",
130                 .help = "null entry, abused as the entry point",
131                 .next = NEXT(NEXT_ENTRY(FLOW)),
132         },
133         [END] = {
134                 .name = "",
135                 .type = "RETURN",
136                 .help = "command may end here",
137         },
138         /* Top-level command. */
139         [FLOW] = {
140                 .name = "flow",
141                 .type = "{command} {port_id} [{arg} [...]]",
142                 .help = "manage ingress/egress flow rules",
143                 .call = parse_init,
144         },
145 };
146
147 /** Default parsing function for token name matching. */
148 static int
149 parse_default(struct context *ctx, const struct token *token,
150               const char *str, unsigned int len,
151               void *buf, unsigned int size)
152 {
153         (void)ctx;
154         (void)buf;
155         (void)size;
156         if (strncmp(str, token->name, len))
157                 return -1;
158         return len;
159 }
160
161 /** Parse flow command, initialize output buffer for subsequent tokens. */
162 static int
163 parse_init(struct context *ctx, const struct token *token,
164            const char *str, unsigned int len,
165            void *buf, unsigned int size)
166 {
167         struct buffer *out = buf;
168
169         /* Token name must match. */
170         if (parse_default(ctx, token, str, len, NULL, 0) < 0)
171                 return -1;
172         /* Nothing else to do if there is no buffer. */
173         if (!out)
174                 return len;
175         /* Make sure buffer is large enough. */
176         if (size < sizeof(*out))
177                 return -1;
178         /* Initialize buffer. */
179         memset(out, 0x00, sizeof(*out));
180         memset((uint8_t *)out + sizeof(*out), 0x22, size - sizeof(*out));
181         return len;
182 }
183
184 /** Internal context. */
185 static struct context cmd_flow_context;
186
187 /** Global parser instance (cmdline API). */
188 cmdline_parse_inst_t cmd_flow;
189
190 /** Initialize context. */
191 static void
192 cmd_flow_context_init(struct context *ctx)
193 {
194         /* A full memset() is not necessary. */
195         ctx->curr = ZERO;
196         ctx->prev = ZERO;
197         ctx->next_num = 0;
198         ctx->reparse = 0;
199         ctx->eol = 0;
200         ctx->last = 0;
201 }
202
203 /** Parse a token (cmdline API). */
204 static int
205 cmd_flow_parse(cmdline_parse_token_hdr_t *hdr, const char *src, void *result,
206                unsigned int size)
207 {
208         struct context *ctx = &cmd_flow_context;
209         const struct token *token;
210         const enum index *list;
211         int len;
212         int i;
213
214         (void)hdr;
215         /* Restart as requested. */
216         if (ctx->reparse)
217                 cmd_flow_context_init(ctx);
218         token = &token_list[ctx->curr];
219         /* Check argument length. */
220         ctx->eol = 0;
221         ctx->last = 1;
222         for (len = 0; src[len]; ++len)
223                 if (src[len] == '#' || isspace(src[len]))
224                         break;
225         if (!len)
226                 return -1;
227         /* Last argument and EOL detection. */
228         for (i = len; src[i]; ++i)
229                 if (src[i] == '#' || src[i] == '\r' || src[i] == '\n')
230                         break;
231                 else if (!isspace(src[i])) {
232                         ctx->last = 0;
233                         break;
234                 }
235         for (; src[i]; ++i)
236                 if (src[i] == '\r' || src[i] == '\n') {
237                         ctx->eol = 1;
238                         break;
239                 }
240         /* Initialize context if necessary. */
241         if (!ctx->next_num) {
242                 if (!token->next)
243                         return 0;
244                 ctx->next[ctx->next_num++] = token->next[0];
245         }
246         /* Process argument through candidates. */
247         ctx->prev = ctx->curr;
248         list = ctx->next[ctx->next_num - 1];
249         for (i = 0; list[i]; ++i) {
250                 const struct token *next = &token_list[list[i]];
251                 int tmp;
252
253                 ctx->curr = list[i];
254                 if (next->call)
255                         tmp = next->call(ctx, next, src, len, result, size);
256                 else
257                         tmp = parse_default(ctx, next, src, len, result, size);
258                 if (tmp == -1 || tmp != len)
259                         continue;
260                 token = next;
261                 break;
262         }
263         if (!list[i])
264                 return -1;
265         --ctx->next_num;
266         /* Push subsequent tokens if any. */
267         if (token->next)
268                 for (i = 0; token->next[i]; ++i) {
269                         if (ctx->next_num == RTE_DIM(ctx->next))
270                                 return -1;
271                         ctx->next[ctx->next_num++] = token->next[i];
272                 }
273         return len;
274 }
275
276 /** Return number of completion entries (cmdline API). */
277 static int
278 cmd_flow_complete_get_nb(cmdline_parse_token_hdr_t *hdr)
279 {
280         struct context *ctx = &cmd_flow_context;
281         const struct token *token = &token_list[ctx->curr];
282         const enum index *list;
283         int i;
284
285         (void)hdr;
286         /* Tell cmd_flow_parse() that context must be reinitialized. */
287         ctx->reparse = 1;
288         /* Count number of tokens in current list. */
289         if (ctx->next_num)
290                 list = ctx->next[ctx->next_num - 1];
291         else
292                 list = token->next[0];
293         for (i = 0; list[i]; ++i)
294                 ;
295         if (!i)
296                 return 0;
297         /*
298          * If there is a single token, use its completion callback, otherwise
299          * return the number of entries.
300          */
301         token = &token_list[list[0]];
302         if (i == 1 && token->comp) {
303                 /* Save index for cmd_flow_get_help(). */
304                 ctx->prev = list[0];
305                 return token->comp(ctx, token, 0, NULL, 0);
306         }
307         return i;
308 }
309
310 /** Return a completion entry (cmdline API). */
311 static int
312 cmd_flow_complete_get_elt(cmdline_parse_token_hdr_t *hdr, int index,
313                           char *dst, unsigned int size)
314 {
315         struct context *ctx = &cmd_flow_context;
316         const struct token *token = &token_list[ctx->curr];
317         const enum index *list;
318         int i;
319
320         (void)hdr;
321         /* Tell cmd_flow_parse() that context must be reinitialized. */
322         ctx->reparse = 1;
323         /* Count number of tokens in current list. */
324         if (ctx->next_num)
325                 list = ctx->next[ctx->next_num - 1];
326         else
327                 list = token->next[0];
328         for (i = 0; list[i]; ++i)
329                 ;
330         if (!i)
331                 return -1;
332         /* If there is a single token, use its completion callback. */
333         token = &token_list[list[0]];
334         if (i == 1 && token->comp) {
335                 /* Save index for cmd_flow_get_help(). */
336                 ctx->prev = list[0];
337                 return token->comp(ctx, token, index, dst, size) < 0 ? -1 : 0;
338         }
339         /* Otherwise make sure the index is valid and use defaults. */
340         if (index >= i)
341                 return -1;
342         token = &token_list[list[index]];
343         snprintf(dst, size, "%s", token->name);
344         /* Save index for cmd_flow_get_help(). */
345         ctx->prev = list[index];
346         return 0;
347 }
348
349 /** Populate help strings for current token (cmdline API). */
350 static int
351 cmd_flow_get_help(cmdline_parse_token_hdr_t *hdr, char *dst, unsigned int size)
352 {
353         struct context *ctx = &cmd_flow_context;
354         const struct token *token = &token_list[ctx->prev];
355
356         (void)hdr;
357         /* Tell cmd_flow_parse() that context must be reinitialized. */
358         ctx->reparse = 1;
359         if (!size)
360                 return -1;
361         /* Set token type and update global help with details. */
362         snprintf(dst, size, "%s", (token->type ? token->type : "TOKEN"));
363         if (token->help)
364                 cmd_flow.help_str = token->help;
365         else
366                 cmd_flow.help_str = token->name;
367         return 0;
368 }
369
370 /** Token definition template (cmdline API). */
371 static struct cmdline_token_hdr cmd_flow_token_hdr = {
372         .ops = &(struct cmdline_token_ops){
373                 .parse = cmd_flow_parse,
374                 .complete_get_nb = cmd_flow_complete_get_nb,
375                 .complete_get_elt = cmd_flow_complete_get_elt,
376                 .get_help = cmd_flow_get_help,
377         },
378         .offset = 0,
379 };
380
381 /** Populate the next dynamic token. */
382 static void
383 cmd_flow_tok(cmdline_parse_token_hdr_t **hdr,
384              cmdline_parse_token_hdr_t *(*hdrs)[])
385 {
386         struct context *ctx = &cmd_flow_context;
387
388         /* Always reinitialize context before requesting the first token. */
389         if (!(hdr - *hdrs))
390                 cmd_flow_context_init(ctx);
391         /* Return NULL when no more tokens are expected. */
392         if (!ctx->next_num && ctx->curr) {
393                 *hdr = NULL;
394                 return;
395         }
396         /* Determine if command should end here. */
397         if (ctx->eol && ctx->last && ctx->next_num) {
398                 const enum index *list = ctx->next[ctx->next_num - 1];
399                 int i;
400
401                 for (i = 0; list[i]; ++i) {
402                         if (list[i] != END)
403                                 continue;
404                         *hdr = NULL;
405                         return;
406                 }
407         }
408         *hdr = &cmd_flow_token_hdr;
409 }
410
411 /** Dispatch parsed buffer to function calls. */
412 static void
413 cmd_flow_parsed(const struct buffer *in)
414 {
415         switch (in->command) {
416         default:
417                 break;
418         }
419 }
420
421 /** Token generator and output processing callback (cmdline API). */
422 static void
423 cmd_flow_cb(void *arg0, struct cmdline *cl, void *arg2)
424 {
425         if (cl == NULL)
426                 cmd_flow_tok(arg0, arg2);
427         else
428                 cmd_flow_parsed(arg0);
429 }
430
431 /** Global parser instance (cmdline API). */
432 cmdline_parse_inst_t cmd_flow = {
433         .f = cmd_flow_cb,
434         .data = NULL, /**< Unused. */
435         .help_str = NULL, /**< Updated by cmd_flow_get_help(). */
436         .tokens = {
437                 NULL,
438         }, /**< Tokens are returned by cmd_flow_tok(). */
439 };