examples/pipeline: support packet mirroring
[dpdk.git] / examples / pipeline / cli.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2020 Intel Corporation
3  */
4
5 #include <stdio.h>
6 #include <stdint.h>
7 #include <stdlib.h>
8 #include <string.h>
9
10 #include <rte_common.h>
11 #include <rte_ethdev.h>
12 #include <rte_swx_port_ethdev.h>
13 #include <rte_swx_port_ring.h>
14 #include <rte_swx_port_source_sink.h>
15 #include <rte_swx_port_fd.h>
16 #include <rte_swx_pipeline.h>
17 #include <rte_swx_ctl.h>
18
19 #include "cli.h"
20
21 #include "obj.h"
22 #include "thread.h"
23
24 #ifndef CMD_MAX_TOKENS
25 #define CMD_MAX_TOKENS     256
26 #endif
27
28 #define MSG_OUT_OF_MEMORY   "Not enough memory.\n"
29 #define MSG_CMD_UNKNOWN     "Unknown command \"%s\".\n"
30 #define MSG_CMD_UNIMPLEM    "Command \"%s\" not implemented.\n"
31 #define MSG_ARG_NOT_ENOUGH  "Not enough arguments for command \"%s\".\n"
32 #define MSG_ARG_TOO_MANY    "Too many arguments for command \"%s\".\n"
33 #define MSG_ARG_MISMATCH    "Wrong number of arguments for command \"%s\".\n"
34 #define MSG_ARG_NOT_FOUND   "Argument \"%s\" not found.\n"
35 #define MSG_ARG_INVALID     "Invalid value for argument \"%s\".\n"
36 #define MSG_FILE_ERR        "Error in file \"%s\" at line %u.\n"
37 #define MSG_FILE_NOT_ENOUGH "Not enough rules in file \"%s\".\n"
38 #define MSG_CMD_FAIL        "Command \"%s\" failed.\n"
39
40 #define skip_white_spaces(pos)                  \
41 ({                                              \
42         __typeof__(pos) _p = (pos);             \
43         for ( ; isspace(*_p); _p++)             \
44                 ;                               \
45         _p;                                     \
46 })
47
48 static int
49 parser_read_uint64(uint64_t *value, const char *p)
50 {
51         char *next;
52         uint64_t val;
53
54         p = skip_white_spaces(p);
55         if (!isdigit(*p))
56                 return -EINVAL;
57
58         val = strtoul(p, &next, 0);
59         if (p == next)
60                 return -EINVAL;
61
62         p = next;
63         switch (*p) {
64         case 'T':
65                 val *= 1024ULL;
66                 /* fall through */
67         case 'G':
68                 val *= 1024ULL;
69                 /* fall through */
70         case 'M':
71                 val *= 1024ULL;
72                 /* fall through */
73         case 'k':
74         case 'K':
75                 val *= 1024ULL;
76                 p++;
77                 break;
78         }
79
80         p = skip_white_spaces(p);
81         if (*p != '\0')
82                 return -EINVAL;
83
84         *value = val;
85         return 0;
86 }
87
88 static int
89 parser_read_uint32(uint32_t *value, const char *p)
90 {
91         uint64_t val = 0;
92         int ret = parser_read_uint64(&val, p);
93
94         if (ret < 0)
95                 return ret;
96
97         if (val > UINT32_MAX)
98                 return -ERANGE;
99
100         *value = val;
101         return 0;
102 }
103
104 static int
105 parser_read_uint16(uint16_t *value, const char *p)
106 {
107         uint64_t val = 0;
108         int ret = parser_read_uint64(&val, p);
109
110         if (ret < 0)
111                 return ret;
112
113         if (val > UINT16_MAX)
114                 return -ERANGE;
115
116         *value = val;
117         return 0;
118 }
119
120 #define PARSE_DELIMITER " \f\n\r\t\v"
121
122 static int
123 parse_tokenize_string(char *string, char *tokens[], uint32_t *n_tokens)
124 {
125         uint32_t i;
126
127         if ((string == NULL) ||
128                 (tokens == NULL) ||
129                 (*n_tokens < 1))
130                 return -EINVAL;
131
132         for (i = 0; i < *n_tokens; i++) {
133                 tokens[i] = strtok_r(string, PARSE_DELIMITER, &string);
134                 if (tokens[i] == NULL)
135                         break;
136         }
137
138         if ((i == *n_tokens) && strtok_r(string, PARSE_DELIMITER, &string))
139                 return -E2BIG;
140
141         *n_tokens = i;
142         return 0;
143 }
144
145 static int
146 is_comment(char *in)
147 {
148         if ((strlen(in) && index("!#%;", in[0])) ||
149                 (strncmp(in, "//", 2) == 0) ||
150                 (strncmp(in, "--", 2) == 0))
151                 return 1;
152
153         return 0;
154 }
155
156 static const char cmd_mempool_help[] =
157 "mempool <mempool_name>\n"
158 "   buffer <buffer_size>\n"
159 "   pool <pool_size>\n"
160 "   cache <cache_size>\n"
161 "   cpu <cpu_id>\n";
162
163 static void
164 cmd_mempool(char **tokens,
165         uint32_t n_tokens,
166         char *out,
167         size_t out_size,
168         void *obj)
169 {
170         struct mempool_params p;
171         char *name;
172         struct mempool *mempool;
173
174         if (n_tokens != 10) {
175                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
176                 return;
177         }
178
179         name = tokens[1];
180
181         if (strcmp(tokens[2], "buffer") != 0) {
182                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "buffer");
183                 return;
184         }
185
186         if (parser_read_uint32(&p.buffer_size, tokens[3]) != 0) {
187                 snprintf(out, out_size, MSG_ARG_INVALID, "buffer_size");
188                 return;
189         }
190
191         if (strcmp(tokens[4], "pool") != 0) {
192                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pool");
193                 return;
194         }
195
196         if (parser_read_uint32(&p.pool_size, tokens[5]) != 0) {
197                 snprintf(out, out_size, MSG_ARG_INVALID, "pool_size");
198                 return;
199         }
200
201         if (strcmp(tokens[6], "cache") != 0) {
202                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cache");
203                 return;
204         }
205
206         if (parser_read_uint32(&p.cache_size, tokens[7]) != 0) {
207                 snprintf(out, out_size, MSG_ARG_INVALID, "cache_size");
208                 return;
209         }
210
211         if (strcmp(tokens[8], "cpu") != 0) {
212                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cpu");
213                 return;
214         }
215
216         if (parser_read_uint32(&p.cpu_id, tokens[9]) != 0) {
217                 snprintf(out, out_size, MSG_ARG_INVALID, "cpu_id");
218                 return;
219         }
220
221         mempool = mempool_create(obj, name, &p);
222         if (mempool == NULL) {
223                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
224                 return;
225         }
226 }
227
228 static const char cmd_link_help[] =
229 "link <link_name>\n"
230 "   dev <device_name> | port <port_id>\n"
231 "   rxq <n_queues> <queue_size> <mempool_name>\n"
232 "   txq <n_queues> <queue_size>\n"
233 "   promiscuous on | off\n"
234 "   [rss <qid_0> ... <qid_n>]\n";
235
236 static void
237 cmd_link(char **tokens,
238         uint32_t n_tokens,
239         char *out,
240         size_t out_size,
241         void *obj)
242 {
243         struct link_params p;
244         struct link_params_rss rss;
245         struct link *link;
246         char *name;
247
248         memset(&p, 0, sizeof(p));
249
250         if ((n_tokens < 13) || (n_tokens > 14 + LINK_RXQ_RSS_MAX)) {
251                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
252                 return;
253         }
254         name = tokens[1];
255
256         if (strcmp(tokens[2], "dev") == 0)
257                 p.dev_name = tokens[3];
258         else if (strcmp(tokens[2], "port") == 0) {
259                 p.dev_name = NULL;
260
261                 if (parser_read_uint16(&p.port_id, tokens[3]) != 0) {
262                         snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
263                         return;
264                 }
265         } else {
266                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "dev or port");
267                 return;
268         }
269
270         if (strcmp(tokens[4], "rxq") != 0) {
271                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq");
272                 return;
273         }
274
275         if (parser_read_uint32(&p.rx.n_queues, tokens[5]) != 0) {
276                 snprintf(out, out_size, MSG_ARG_INVALID, "n_queues");
277                 return;
278         }
279         if (parser_read_uint32(&p.rx.queue_size, tokens[6]) != 0) {
280                 snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
281                 return;
282         }
283
284         p.rx.mempool_name = tokens[7];
285
286         if (strcmp(tokens[8], "txq") != 0) {
287                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "txq");
288                 return;
289         }
290
291         if (parser_read_uint32(&p.tx.n_queues, tokens[9]) != 0) {
292                 snprintf(out, out_size, MSG_ARG_INVALID, "n_queues");
293                 return;
294         }
295
296         if (parser_read_uint32(&p.tx.queue_size, tokens[10]) != 0) {
297                 snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
298                 return;
299         }
300
301         if (strcmp(tokens[11], "promiscuous") != 0) {
302                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "promiscuous");
303                 return;
304         }
305
306         if (strcmp(tokens[12], "on") == 0)
307                 p.promiscuous = 1;
308         else if (strcmp(tokens[12], "off") == 0)
309                 p.promiscuous = 0;
310         else {
311                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "on or off");
312                 return;
313         }
314
315         /* RSS */
316         p.rx.rss = NULL;
317         if (n_tokens > 13) {
318                 uint32_t queue_id, i;
319
320                 if (strcmp(tokens[13], "rss") != 0) {
321                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rss");
322                         return;
323                 }
324
325                 p.rx.rss = &rss;
326
327                 rss.n_queues = 0;
328                 for (i = 14; i < n_tokens; i++) {
329                         if (parser_read_uint32(&queue_id, tokens[i]) != 0) {
330                                 snprintf(out, out_size, MSG_ARG_INVALID,
331                                         "queue_id");
332                                 return;
333                         }
334
335                         rss.queue_id[rss.n_queues] = queue_id;
336                         rss.n_queues++;
337                 }
338         }
339
340         link = link_create(obj, name, &p);
341         if (link == NULL) {
342                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
343                 return;
344         }
345 }
346
347 /* Print the link stats and info */
348 static void
349 print_link_info(struct link *link, char *out, size_t out_size)
350 {
351         struct rte_eth_stats stats;
352         struct rte_ether_addr mac_addr;
353         struct rte_eth_link eth_link;
354         uint16_t mtu;
355         int ret;
356
357         memset(&stats, 0, sizeof(stats));
358         rte_eth_stats_get(link->port_id, &stats);
359
360         ret = rte_eth_macaddr_get(link->port_id, &mac_addr);
361         if (ret != 0) {
362                 snprintf(out, out_size, "\n%s: MAC address get failed: %s",
363                          link->name, rte_strerror(-ret));
364                 return;
365         }
366
367         ret = rte_eth_link_get(link->port_id, &eth_link);
368         if (ret < 0) {
369                 snprintf(out, out_size, "\n%s: link get failed: %s",
370                          link->name, rte_strerror(-ret));
371                 return;
372         }
373
374         rte_eth_dev_get_mtu(link->port_id, &mtu);
375
376         snprintf(out, out_size,
377                 "\n"
378                 "%s: flags=<%s> mtu %u\n"
379                 "\tether " RTE_ETHER_ADDR_PRT_FMT " rxqueues %u txqueues %u\n"
380                 "\tport# %u  speed %s\n"
381                 "\tRX packets %" PRIu64"  bytes %" PRIu64"\n"
382                 "\tRX errors %" PRIu64"  missed %" PRIu64"  no-mbuf %" PRIu64"\n"
383                 "\tTX packets %" PRIu64"  bytes %" PRIu64"\n"
384                 "\tTX errors %" PRIu64"\n",
385                 link->name,
386                 eth_link.link_status == 0 ? "DOWN" : "UP",
387                 mtu,
388                 RTE_ETHER_ADDR_BYTES(&mac_addr),
389                 link->n_rxq,
390                 link->n_txq,
391                 link->port_id,
392                 rte_eth_link_speed_to_str(eth_link.link_speed),
393                 stats.ipackets,
394                 stats.ibytes,
395                 stats.ierrors,
396                 stats.imissed,
397                 stats.rx_nombuf,
398                 stats.opackets,
399                 stats.obytes,
400                 stats.oerrors);
401 }
402
403 /*
404  * link show [<link_name>]
405  */
406 static void
407 cmd_link_show(char **tokens,
408               uint32_t n_tokens,
409               char *out,
410               size_t out_size,
411               void *obj)
412 {
413         struct link *link;
414         char *link_name;
415
416         if (n_tokens != 2 && n_tokens != 3) {
417                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
418                 return;
419         }
420
421         if (n_tokens == 2) {
422                 link = link_next(obj, NULL);
423
424                 while (link != NULL) {
425                         out_size = out_size - strlen(out);
426                         out = &out[strlen(out)];
427
428                         print_link_info(link, out, out_size);
429                         link = link_next(obj, link);
430                 }
431         } else {
432                 out_size = out_size - strlen(out);
433                 out = &out[strlen(out)];
434
435                 link_name = tokens[2];
436                 link = link_find(obj, link_name);
437
438                 if (link == NULL) {
439                         snprintf(out, out_size, MSG_ARG_INVALID,
440                                         "Link does not exist");
441                         return;
442                 }
443                 print_link_info(link, out, out_size);
444         }
445 }
446
447 static const char cmd_ring_help[] =
448 "ring <ring_name> size <size> numa <numa_node>\n";
449
450 static void
451 cmd_ring(char **tokens,
452         uint32_t n_tokens,
453         char *out,
454         size_t out_size,
455         void *obj)
456 {
457         struct ring_params p;
458         char *name;
459         struct ring *ring;
460
461         if (n_tokens != 6) {
462                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
463                 return;
464         }
465
466         name = tokens[1];
467
468         if (strcmp(tokens[2], "size") != 0) {
469                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
470                 return;
471         }
472
473         if (parser_read_uint32(&p.size, tokens[3]) != 0) {
474                 snprintf(out, out_size, MSG_ARG_INVALID, "size");
475                 return;
476         }
477
478         if (strcmp(tokens[4], "numa") != 0) {
479                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
480                 return;
481         }
482
483         if (parser_read_uint32(&p.numa_node, tokens[5]) != 0) {
484                 snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
485                 return;
486         }
487
488         ring = ring_create(obj, name, &p);
489         if (!ring) {
490                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
491                 return;
492         }
493 }
494
495 static const char cmd_tap_help[] =
496 "tap <tap_name>\n";
497
498 static void
499 cmd_tap(char **tokens,
500         uint32_t n_tokens,
501         char *out,
502         size_t out_size,
503         void *obj)
504 {
505         struct tap *tap;
506         char *name;
507
508         if (n_tokens < 2) {
509                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
510                 return;
511         }
512         name = tokens[1];
513
514         tap = tap_create(obj, name);
515         if (tap == NULL) {
516                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
517                 return;
518         }
519 }
520
521 static const char cmd_pipeline_create_help[] =
522 "pipeline <pipeline_name> create <numa_node>\n";
523
524 static void
525 cmd_pipeline_create(char **tokens,
526         uint32_t n_tokens,
527         char *out,
528         size_t out_size,
529         void *obj)
530 {
531         struct pipeline *p;
532         char *name;
533         uint32_t numa_node;
534
535         if (n_tokens != 4) {
536                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
537                 return;
538         }
539
540         name = tokens[1];
541
542         if (parser_read_uint32(&numa_node, tokens[3]) != 0) {
543                 snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
544                 return;
545         }
546
547         p = pipeline_create(obj, name, (int)numa_node);
548         if (!p) {
549                 snprintf(out, out_size, "pipeline create error.");
550                 return;
551         }
552 }
553
554 static const char cmd_pipeline_port_in_help[] =
555 "pipeline <pipeline_name> port in <port_id>\n"
556 "   link <link_name> rxq <queue_id> bsz <burst_size>\n"
557 "   ring <ring_name> bsz <burst_size>\n"
558 "   | source <mempool_name> <file_name> loop <n_loops>\n"
559 "   | tap <tap_name> mempool <mempool_name> mtu <mtu> bsz <burst_size>\n";
560
561 static void
562 cmd_pipeline_port_in(char **tokens,
563         uint32_t n_tokens,
564         char *out,
565         size_t out_size,
566         void *obj)
567 {
568         struct pipeline *p;
569         int status;
570         uint32_t port_id = 0, t0;
571
572         if (n_tokens < 6) {
573                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
574                 return;
575         }
576
577         p = pipeline_find(obj, tokens[1]);
578         if (!p || p->ctl) {
579                 snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
580                 return;
581         }
582
583         if (strcmp(tokens[2], "port") != 0) {
584                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
585                 return;
586         }
587
588         if (strcmp(tokens[3], "in") != 0) {
589                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
590                 return;
591         }
592
593         if (parser_read_uint32(&port_id, tokens[4]) != 0) {
594                 snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
595                 return;
596         }
597
598         t0 = 5;
599
600         if (strcmp(tokens[t0], "link") == 0) {
601                 struct rte_swx_port_ethdev_reader_params params;
602                 struct link *link;
603
604                 if (n_tokens < t0 + 6) {
605                         snprintf(out, out_size, MSG_ARG_MISMATCH,
606                                 "pipeline port in link");
607                         return;
608                 }
609
610                 link = link_find(obj, tokens[t0 + 1]);
611                 if (!link) {
612                         snprintf(out, out_size, MSG_ARG_INVALID,
613                                 "link_name");
614                         return;
615                 }
616                 params.dev_name = link->dev_name;
617
618                 if (strcmp(tokens[t0 + 2], "rxq") != 0) {
619                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq");
620                         return;
621                 }
622
623                 if (parser_read_uint16(&params.queue_id, tokens[t0 + 3]) != 0) {
624                         snprintf(out, out_size, MSG_ARG_INVALID,
625                                 "queue_id");
626                         return;
627                 }
628
629                 if (strcmp(tokens[t0 + 4], "bsz") != 0) {
630                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
631                         return;
632                 }
633
634                 if (parser_read_uint32(&params.burst_size, tokens[t0 + 5])) {
635                         snprintf(out, out_size, MSG_ARG_INVALID,
636                                 "burst_size");
637                         return;
638                 }
639
640                 t0 += 6;
641
642                 status = rte_swx_pipeline_port_in_config(p->p,
643                         port_id,
644                         "ethdev",
645                         &params);
646         } else if (strcmp(tokens[t0], "ring") == 0) {
647                 struct rte_swx_port_ring_reader_params params;
648                 struct ring *ring;
649
650                 if (n_tokens < t0 + 4) {
651                         snprintf(out, out_size, MSG_ARG_MISMATCH,
652                                 "pipeline port in ring");
653                         return;
654                 }
655
656                 ring = ring_find(obj, tokens[t0 + 1]);
657                 if (!ring) {
658                         snprintf(out, out_size, MSG_ARG_INVALID,
659                                 "ring_name");
660                         return;
661                 }
662                 params.name = ring->name;
663
664                 if (strcmp(tokens[t0 + 2], "bsz") != 0) {
665                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
666                         return;
667                 }
668
669                 if (parser_read_uint32(&params.burst_size, tokens[t0 + 3])) {
670                         snprintf(out, out_size, MSG_ARG_INVALID,
671                                 "burst_size");
672                         return;
673                 }
674
675                 t0 += 4;
676
677                 status = rte_swx_pipeline_port_in_config(p->p,
678                         port_id,
679                         "ring",
680                         &params);
681         } else if (strcmp(tokens[t0], "source") == 0) {
682                 struct rte_swx_port_source_params params;
683                 struct mempool *mp;
684
685                 if (n_tokens < t0 + 5) {
686                         snprintf(out, out_size, MSG_ARG_MISMATCH,
687                                 "pipeline port in source");
688                         return;
689                 }
690
691                 mp = mempool_find(obj, tokens[t0 + 1]);
692                 if (!mp) {
693                         snprintf(out, out_size, MSG_ARG_INVALID,
694                                 "mempool_name");
695                         return;
696                 }
697                 params.pool = mp->m;
698
699                 params.file_name = tokens[t0 + 2];
700
701                 if (strcmp(tokens[t0 + 3], "loop") != 0) {
702                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "loop");
703                         return;
704                 }
705
706                 if (parser_read_uint64(&params.n_loops, tokens[t0 + 4])) {
707                         snprintf(out, out_size, MSG_ARG_INVALID,
708                                 "n_loops");
709                         return;
710                 }
711
712                 t0 += 5;
713
714                 status = rte_swx_pipeline_port_in_config(p->p,
715                         port_id,
716                         "source",
717                         &params);
718         } else if (strcmp(tokens[t0], "tap") == 0) {
719                 struct rte_swx_port_fd_reader_params params;
720                 struct tap *tap;
721                 struct mempool *mp;
722
723                 if (n_tokens < t0 + 8) {
724                         snprintf(out, out_size, MSG_ARG_MISMATCH,
725                                 "pipeline port in tap");
726                         return;
727                 }
728
729                 tap = tap_find(obj, tokens[t0 + 1]);
730                 if (!tap) {
731                         snprintf(out, out_size, MSG_ARG_INVALID,
732                                 "tap_name");
733                         return;
734                 }
735                 params.fd = tap->fd;
736
737                 if (strcmp(tokens[t0 + 2], "mempool") != 0) {
738                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
739                                 "mempool");
740                         return;
741                 }
742
743                 mp = mempool_find(obj, tokens[t0 + 3]);
744                 if (!mp) {
745                         snprintf(out, out_size, MSG_ARG_INVALID,
746                                 "mempool_name");
747                         return;
748                 }
749                 params.mempool = mp->m;
750
751                 if (strcmp(tokens[t0 + 4], "mtu") != 0) {
752                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
753                                 "mtu");
754                         return;
755                 }
756
757                 if (parser_read_uint32(&params.mtu, tokens[t0 + 5]) != 0) {
758                         snprintf(out, out_size, MSG_ARG_INVALID, "mtu");
759                         return;
760                 }
761
762                 if (strcmp(tokens[t0 + 6], "bsz") != 0) {
763                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
764                         return;
765                 }
766
767                 if (parser_read_uint32(&params.burst_size, tokens[t0 + 7])) {
768                         snprintf(out, out_size, MSG_ARG_INVALID,
769                                 "burst_size");
770                         return;
771                 }
772
773                 t0 += 8;
774
775                 status = rte_swx_pipeline_port_in_config(p->p,
776                         port_id,
777                         "fd",
778                         &params);
779
780         } else {
781                 snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
782                 return;
783         }
784
785         if (status) {
786                 snprintf(out, out_size, "port in error.");
787                 return;
788         }
789
790         if (n_tokens != t0) {
791                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
792                 return;
793         }
794 }
795
796 static const char cmd_pipeline_port_out_help[] =
797 "pipeline <pipeline_name> port out <port_id>\n"
798 "   link <link_name> txq <txq_id> bsz <burst_size>\n"
799 "   ring <ring_name> bsz <burst_size>\n"
800 "   | sink <file_name> | none\n"
801 "   | tap <tap_name> bsz <burst_size>\n";
802
803 static void
804 cmd_pipeline_port_out(char **tokens,
805         uint32_t n_tokens,
806         char *out,
807         size_t out_size,
808         void *obj)
809 {
810         struct pipeline *p;
811         int status;
812         uint32_t port_id = 0, t0;
813
814         if (n_tokens < 6) {
815                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
816                 return;
817         }
818
819         p = pipeline_find(obj, tokens[1]);
820         if (!p || p->ctl) {
821                 snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
822                 return;
823         }
824
825         if (strcmp(tokens[2], "port") != 0) {
826                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
827                 return;
828         }
829
830         if (strcmp(tokens[3], "out") != 0) {
831                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "out");
832                 return;
833         }
834
835         if (parser_read_uint32(&port_id, tokens[4]) != 0) {
836                 snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
837                 return;
838         }
839
840         t0 = 5;
841
842         if (strcmp(tokens[t0], "link") == 0) {
843                 struct rte_swx_port_ethdev_writer_params params;
844                 struct link *link;
845
846                 if (n_tokens < t0 + 6) {
847                         snprintf(out, out_size, MSG_ARG_MISMATCH,
848                                 "pipeline port out link");
849                         return;
850                 }
851
852                 link = link_find(obj, tokens[t0 + 1]);
853                 if (!link) {
854                         snprintf(out, out_size, MSG_ARG_INVALID,
855                                 "link_name");
856                         return;
857                 }
858                 params.dev_name = link->dev_name;
859
860                 if (strcmp(tokens[t0 + 2], "txq") != 0) {
861                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "txq");
862                         return;
863                 }
864
865                 if (parser_read_uint16(&params.queue_id, tokens[t0 + 3]) != 0) {
866                         snprintf(out, out_size, MSG_ARG_INVALID,
867                                 "queue_id");
868                         return;
869                 }
870
871                 if (strcmp(tokens[t0 + 4], "bsz") != 0) {
872                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
873                         return;
874                 }
875
876                 if (parser_read_uint32(&params.burst_size, tokens[t0 + 5])) {
877                         snprintf(out, out_size, MSG_ARG_INVALID,
878                                 "burst_size");
879                         return;
880                 }
881
882                 t0 += 6;
883
884                 status = rte_swx_pipeline_port_out_config(p->p,
885                         port_id,
886                         "ethdev",
887                         &params);
888         } else if (strcmp(tokens[t0], "ring") == 0) {
889                 struct rte_swx_port_ring_writer_params params;
890                 struct ring *ring;
891
892                 if (n_tokens < t0 + 4) {
893                         snprintf(out, out_size, MSG_ARG_MISMATCH,
894                                 "pipeline port out link");
895                         return;
896                 }
897
898                 ring = ring_find(obj, tokens[t0 + 1]);
899                 if (!ring) {
900                         snprintf(out, out_size, MSG_ARG_INVALID,
901                                 "ring_name");
902                         return;
903                 }
904                 params.name = ring->name;
905
906                 if (strcmp(tokens[t0 + 2], "bsz") != 0) {
907                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
908                         return;
909                 }
910
911                 if (parser_read_uint32(&params.burst_size, tokens[t0 + 3])) {
912                         snprintf(out, out_size, MSG_ARG_INVALID,
913                                 "burst_size");
914                         return;
915                 }
916
917                 t0 += 4;
918
919                 status = rte_swx_pipeline_port_out_config(p->p,
920                         port_id,
921                         "ring",
922                         &params);
923         } else if (strcmp(tokens[t0], "sink") == 0) {
924                 struct rte_swx_port_sink_params params;
925
926                 params.file_name = strcmp(tokens[t0 + 1], "none") ?
927                         tokens[t0 + 1] : NULL;
928
929                 t0 += 2;
930
931                 status = rte_swx_pipeline_port_out_config(p->p,
932                         port_id,
933                         "sink",
934                         &params);
935         } else if (strcmp(tokens[t0], "tap") == 0) {
936                 struct rte_swx_port_fd_writer_params params;
937                 struct tap *tap;
938
939                 if (n_tokens < t0 + 4) {
940                         snprintf(out, out_size, MSG_ARG_MISMATCH,
941                                 "pipeline port out tap");
942                         return;
943                 }
944
945                 tap = tap_find(obj, tokens[t0 + 1]);
946                 if (!tap) {
947                         snprintf(out, out_size, MSG_ARG_INVALID,
948                                 "tap_name");
949                         return;
950                 }
951                 params.fd = tap->fd;
952
953                 if (strcmp(tokens[t0 + 2], "bsz") != 0) {
954                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
955                         return;
956                 }
957
958                 if (parser_read_uint32(&params.burst_size, tokens[t0 + 3])) {
959                         snprintf(out, out_size, MSG_ARG_INVALID,
960                                 "burst_size");
961                         return;
962                 }
963
964                 t0 += 4;
965
966                 status = rte_swx_pipeline_port_out_config(p->p,
967                         port_id,
968                         "fd",
969                         &params);
970         } else {
971                 snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
972                 return;
973         }
974
975         if (status) {
976                 snprintf(out, out_size, "port out error.");
977                 return;
978         }
979
980         if (n_tokens != t0) {
981                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
982                 return;
983         }
984 }
985
986 static const char cmd_pipeline_build_help[] =
987 "pipeline <pipeline_name> build <spec_file>\n";
988
989 static void
990 cmd_pipeline_build(char **tokens,
991         uint32_t n_tokens,
992         char *out,
993         size_t out_size,
994         void *obj)
995 {
996         struct pipeline *p = NULL;
997         FILE *spec = NULL;
998         uint32_t err_line;
999         const char *err_msg;
1000         int status;
1001
1002         if (n_tokens != 4) {
1003                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1004                 return;
1005         }
1006
1007         p = pipeline_find(obj, tokens[1]);
1008         if (!p || p->ctl) {
1009                 snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
1010                 return;
1011         }
1012
1013         spec = fopen(tokens[3], "r");
1014         if (!spec) {
1015                 snprintf(out, out_size, "Cannot open file %s.\n", tokens[3]);
1016                 return;
1017         }
1018
1019         status = rte_swx_pipeline_build_from_spec(p->p,
1020                 spec,
1021                 &err_line,
1022                 &err_msg);
1023         fclose(spec);
1024         if (status) {
1025                 snprintf(out, out_size, "Error %d at line %u: %s\n.",
1026                         status, err_line, err_msg);
1027                 return;
1028         }
1029
1030         p->ctl = rte_swx_ctl_pipeline_create(p->p);
1031         if (!p->ctl) {
1032                 snprintf(out, out_size, "Pipeline control create failed.");
1033                 rte_swx_pipeline_free(p->p);
1034                 return;
1035         }
1036 }
1037
1038 static void
1039 table_entry_free(struct rte_swx_table_entry *entry)
1040 {
1041         if (!entry)
1042                 return;
1043
1044         free(entry->key);
1045         free(entry->key_mask);
1046         free(entry->action_data);
1047         free(entry);
1048 }
1049
1050 #ifndef MAX_LINE_SIZE
1051 #define MAX_LINE_SIZE 2048
1052 #endif
1053
1054 static int
1055 pipeline_table_entries_add(struct rte_swx_ctl_pipeline *p,
1056                            const char *table_name,
1057                            FILE *file,
1058                            uint32_t *file_line_number)
1059 {
1060         char *line = NULL;
1061         uint32_t line_id = 0;
1062         int status = 0;
1063
1064         /* Buffer allocation. */
1065         line = malloc(MAX_LINE_SIZE);
1066         if (!line)
1067                 return -ENOMEM;
1068
1069         /* File read. */
1070         for (line_id = 1; ; line_id++) {
1071                 struct rte_swx_table_entry *entry;
1072                 int is_blank_or_comment;
1073
1074                 if (fgets(line, MAX_LINE_SIZE, file) == NULL)
1075                         break;
1076
1077                 entry = rte_swx_ctl_pipeline_table_entry_read(p,
1078                                                               table_name,
1079                                                               line,
1080                                                               &is_blank_or_comment);
1081                 if (!entry) {
1082                         if (is_blank_or_comment)
1083                                 continue;
1084
1085                         status = -EINVAL;
1086                         goto error;
1087                 }
1088
1089                 status = rte_swx_ctl_pipeline_table_entry_add(p,
1090                                                               table_name,
1091                                                               entry);
1092                 table_entry_free(entry);
1093                 if (status)
1094                         goto error;
1095         }
1096
1097 error:
1098         free(line);
1099         *file_line_number = line_id;
1100         return status;
1101 }
1102
1103 static const char cmd_pipeline_table_add_help[] =
1104 "pipeline <pipeline_name> table <table_name> add <file_name>\n";
1105
1106 static void
1107 cmd_pipeline_table_add(char **tokens,
1108                        uint32_t n_tokens,
1109                        char *out,
1110                        size_t out_size,
1111                        void *obj)
1112 {
1113         struct pipeline *p;
1114         char *pipeline_name, *table_name, *file_name;
1115         FILE *file = NULL;
1116         uint32_t file_line_number = 0;
1117         int status;
1118
1119         if (n_tokens != 6) {
1120                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1121                 return;
1122         }
1123
1124         pipeline_name = tokens[1];
1125         p = pipeline_find(obj, pipeline_name);
1126         if (!p || !p->ctl) {
1127                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1128                 return;
1129         }
1130
1131         table_name = tokens[3];
1132
1133         file_name = tokens[5];
1134         file = fopen(file_name, "r");
1135         if (!file) {
1136                 snprintf(out, out_size, "Cannot open file %s.\n", file_name);
1137                 return;
1138         }
1139
1140         status = pipeline_table_entries_add(p->ctl,
1141                                             table_name,
1142                                             file,
1143                                             &file_line_number);
1144         if (status)
1145                 snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
1146                          file_name,
1147                          file_line_number);
1148
1149         fclose(file);
1150 }
1151
1152 static int
1153 pipeline_table_entries_delete(struct rte_swx_ctl_pipeline *p,
1154                               const char *table_name,
1155                               FILE *file,
1156                               uint32_t *file_line_number)
1157 {
1158         char *line = NULL;
1159         uint32_t line_id = 0;
1160         int status = 0;
1161
1162         /* Buffer allocation. */
1163         line = malloc(MAX_LINE_SIZE);
1164         if (!line)
1165                 return -ENOMEM;
1166
1167         /* File read. */
1168         for (line_id = 1; ; line_id++) {
1169                 struct rte_swx_table_entry *entry;
1170                 int is_blank_or_comment;
1171
1172                 if (fgets(line, MAX_LINE_SIZE, file) == NULL)
1173                         break;
1174
1175                 entry = rte_swx_ctl_pipeline_table_entry_read(p,
1176                                                               table_name,
1177                                                               line,
1178                                                               &is_blank_or_comment);
1179                 if (!entry) {
1180                         if (is_blank_or_comment)
1181                                 continue;
1182
1183                         status = -EINVAL;
1184                         goto error;
1185                 }
1186
1187                 status = rte_swx_ctl_pipeline_table_entry_delete(p,
1188                                                                  table_name,
1189                                                                  entry);
1190                 table_entry_free(entry);
1191                 if (status)
1192                         goto error;
1193         }
1194
1195 error:
1196         *file_line_number = line_id;
1197         free(line);
1198         return status;
1199 }
1200
1201 static const char cmd_pipeline_table_delete_help[] =
1202 "pipeline <pipeline_name> table <table_name> delete <file_name>\n";
1203
1204 static void
1205 cmd_pipeline_table_delete(char **tokens,
1206                           uint32_t n_tokens,
1207                           char *out,
1208                           size_t out_size,
1209                           void *obj)
1210 {
1211         struct pipeline *p;
1212         char *pipeline_name, *table_name, *file_name;
1213         FILE *file = NULL;
1214         uint32_t file_line_number = 0;
1215         int status;
1216
1217         if (n_tokens != 6) {
1218                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1219                 return;
1220         }
1221
1222         pipeline_name = tokens[1];
1223         p = pipeline_find(obj, pipeline_name);
1224         if (!p || !p->ctl) {
1225                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1226                 return;
1227         }
1228
1229         table_name = tokens[3];
1230
1231         file_name = tokens[5];
1232         file = fopen(file_name, "r");
1233         if (!file) {
1234                 snprintf(out, out_size, "Cannot open file %s.\n", file_name);
1235                 return;
1236         }
1237
1238         status = pipeline_table_entries_delete(p->ctl,
1239                                                table_name,
1240                                                file,
1241                                                &file_line_number);
1242         if (status)
1243                 snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
1244                          file_name,
1245                          file_line_number);
1246
1247         fclose(file);
1248 }
1249
1250 static int
1251 pipeline_table_default_entry_add(struct rte_swx_ctl_pipeline *p,
1252                                  const char *table_name,
1253                                  FILE *file,
1254                                  uint32_t *file_line_number)
1255 {
1256         char *line = NULL;
1257         uint32_t line_id = 0;
1258         int status = 0;
1259
1260         /* Buffer allocation. */
1261         line = malloc(MAX_LINE_SIZE);
1262         if (!line)
1263                 return -ENOMEM;
1264
1265         /* File read. */
1266         for (line_id = 1; ; line_id++) {
1267                 struct rte_swx_table_entry *entry;
1268                 int is_blank_or_comment;
1269
1270                 if (fgets(line, MAX_LINE_SIZE, file) == NULL)
1271                         break;
1272
1273                 entry = rte_swx_ctl_pipeline_table_entry_read(p,
1274                                                               table_name,
1275                                                               line,
1276                                                               &is_blank_or_comment);
1277                 if (!entry) {
1278                         if (is_blank_or_comment)
1279                                 continue;
1280
1281                         status = -EINVAL;
1282                         goto error;
1283                 }
1284
1285                 status = rte_swx_ctl_pipeline_table_default_entry_add(p,
1286                                                                       table_name,
1287                                                                       entry);
1288                 table_entry_free(entry);
1289                 if (status)
1290                         goto error;
1291         }
1292
1293 error:
1294         *file_line_number = line_id;
1295         free(line);
1296         return status;
1297 }
1298
1299 static const char cmd_pipeline_table_default_help[] =
1300 "pipeline <pipeline_name> table <table_name> default <file_name>\n";
1301
1302 static void
1303 cmd_pipeline_table_default(char **tokens,
1304                            uint32_t n_tokens,
1305                            char *out,
1306                            size_t out_size,
1307                            void *obj)
1308 {
1309         struct pipeline *p;
1310         char *pipeline_name, *table_name, *file_name;
1311         FILE *file = NULL;
1312         uint32_t file_line_number = 0;
1313         int status;
1314
1315         if (n_tokens != 6) {
1316                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1317                 return;
1318         }
1319
1320         pipeline_name = tokens[1];
1321         p = pipeline_find(obj, pipeline_name);
1322         if (!p || !p->ctl) {
1323                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1324                 return;
1325         }
1326
1327         table_name = tokens[3];
1328
1329         file_name = tokens[5];
1330         file = fopen(file_name, "r");
1331         if (!file) {
1332                 snprintf(out, out_size, "Cannot open file %s.\n", file_name);
1333                 return;
1334         }
1335
1336         status = pipeline_table_default_entry_add(p->ctl,
1337                                                   table_name,
1338                                                   file,
1339                                                   &file_line_number);
1340         if (status)
1341                 snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
1342                          file_name,
1343                          file_line_number);
1344
1345         fclose(file);
1346 }
1347
1348 static const char cmd_pipeline_table_show_help[] =
1349 "pipeline <pipeline_name> table <table_name> show [filename]\n";
1350
1351 static void
1352 cmd_pipeline_table_show(char **tokens,
1353         uint32_t n_tokens,
1354         char *out,
1355         size_t out_size,
1356         void *obj)
1357 {
1358         struct pipeline *p;
1359         char *pipeline_name, *table_name;
1360         FILE *file = NULL;
1361         int status;
1362
1363         if (n_tokens != 5 && n_tokens != 6) {
1364                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1365                 return;
1366         }
1367
1368         pipeline_name = tokens[1];
1369         p = pipeline_find(obj, pipeline_name);
1370         if (!p || !p->ctl) {
1371                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1372                 return;
1373         }
1374
1375         table_name = tokens[3];
1376         file = (n_tokens == 6) ? fopen(tokens[5], "w") : stdout;
1377         if (!file) {
1378                 snprintf(out, out_size, "Cannot open file %s.\n", tokens[5]);
1379                 return;
1380         }
1381
1382         status = rte_swx_ctl_pipeline_table_fprintf(file, p->ctl, table_name);
1383         if (status)
1384                 snprintf(out, out_size, MSG_ARG_INVALID, "table_name");
1385
1386         if (file)
1387                 fclose(file);
1388 }
1389
1390 static const char cmd_pipeline_selector_group_add_help[] =
1391 "pipeline <pipeline_name> selector <selector_name> group add\n";
1392
1393 static void
1394 cmd_pipeline_selector_group_add(char **tokens,
1395         uint32_t n_tokens,
1396         char *out,
1397         size_t out_size,
1398         void *obj)
1399 {
1400         struct pipeline *p;
1401         char *pipeline_name, *selector_name;
1402         uint32_t group_id;
1403         int status;
1404
1405         if (n_tokens != 6) {
1406                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1407                 return;
1408         }
1409
1410         pipeline_name = tokens[1];
1411         p = pipeline_find(obj, pipeline_name);
1412         if (!p || !p->ctl) {
1413                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1414                 return;
1415         }
1416
1417         if (strcmp(tokens[2], "selector") != 0) {
1418                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector");
1419                 return;
1420         }
1421
1422         selector_name = tokens[3];
1423
1424         if (strcmp(tokens[4], "group") ||
1425                 strcmp(tokens[5], "add")) {
1426                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group add");
1427                 return;
1428         }
1429
1430         status = rte_swx_ctl_pipeline_selector_group_add(p->ctl,
1431                 selector_name,
1432                 &group_id);
1433         if (status)
1434                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
1435         else
1436                 snprintf(out, out_size, "Group ID: %u\n", group_id);
1437 }
1438
1439 static const char cmd_pipeline_selector_group_delete_help[] =
1440 "pipeline <pipeline_name> selector <selector_name> group delete <group_id>\n";
1441
1442 static void
1443 cmd_pipeline_selector_group_delete(char **tokens,
1444         uint32_t n_tokens,
1445         char *out,
1446         size_t out_size,
1447         void *obj)
1448 {
1449         struct pipeline *p;
1450         char *pipeline_name, *selector_name;
1451         uint32_t group_id;
1452         int status;
1453
1454         if (n_tokens != 7) {
1455                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1456                 return;
1457         }
1458
1459         pipeline_name = tokens[1];
1460         p = pipeline_find(obj, pipeline_name);
1461         if (!p || !p->ctl) {
1462                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1463                 return;
1464         }
1465
1466         if (strcmp(tokens[2], "selector") != 0) {
1467                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector");
1468                 return;
1469         }
1470
1471         selector_name = tokens[3];
1472
1473         if (strcmp(tokens[4], "group") ||
1474                 strcmp(tokens[5], "delete")) {
1475                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group delete");
1476                 return;
1477         }
1478
1479         if (parser_read_uint32(&group_id, tokens[6]) != 0) {
1480                 snprintf(out, out_size, MSG_ARG_INVALID, "group_id");
1481                 return;
1482         }
1483
1484         status = rte_swx_ctl_pipeline_selector_group_delete(p->ctl,
1485                 selector_name,
1486                 group_id);
1487         if (status)
1488                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
1489 }
1490
1491 #define GROUP_MEMBER_INFO_TOKENS_MAX 6
1492
1493 static int
1494 token_is_comment(const char *token)
1495 {
1496         if ((token[0] == '#') ||
1497             (token[0] == ';') ||
1498             ((token[0] == '/') && (token[1] == '/')))
1499                 return 1; /* TRUE. */
1500
1501         return 0; /* FALSE. */
1502 }
1503
1504 static int
1505 pipeline_selector_group_member_read(const char *string,
1506                                       uint32_t *group_id,
1507                                       uint32_t *member_id,
1508                                       uint32_t *weight,
1509                                       int *is_blank_or_comment)
1510 {
1511         char *token_array[GROUP_MEMBER_INFO_TOKENS_MAX], **tokens;
1512         char *s0 = NULL, *s;
1513         uint32_t n_tokens = 0, group_id_val = 0, member_id_val = 0, weight_val = 0;
1514         int blank_or_comment = 0;
1515
1516         /* Check input arguments. */
1517         if (!string || !string[0])
1518                 goto error;
1519
1520         /* Memory allocation. */
1521         s0 = strdup(string);
1522         if (!s0)
1523                 goto error;
1524
1525         /* Parse the string into tokens. */
1526         for (s = s0; ; ) {
1527                 char *token;
1528
1529                 token = strtok_r(s, " \f\n\r\t\v", &s);
1530                 if (!token || token_is_comment(token))
1531                         break;
1532
1533                 if (n_tokens >= GROUP_MEMBER_INFO_TOKENS_MAX)
1534                         goto error;
1535
1536                 token_array[n_tokens] = token;
1537                 n_tokens++;
1538         }
1539
1540         if (!n_tokens) {
1541                 blank_or_comment = 1;
1542                 goto error;
1543         }
1544
1545         tokens = token_array;
1546
1547         if (n_tokens < 4 ||
1548                 strcmp(tokens[0], "group") ||
1549                 strcmp(tokens[2], "member"))
1550                 goto error;
1551
1552         /*
1553          * Group ID.
1554          */
1555         if (parser_read_uint32(&group_id_val, tokens[1]) != 0)
1556                 goto error;
1557         *group_id = group_id_val;
1558
1559         /*
1560          * Member ID.
1561          */
1562         if (parser_read_uint32(&member_id_val, tokens[3]) != 0)
1563                 goto error;
1564         *member_id = member_id_val;
1565
1566         tokens += 4;
1567         n_tokens -= 4;
1568
1569         /*
1570          * Weight.
1571          */
1572         if (n_tokens && !strcmp(tokens[0], "weight")) {
1573                 if (n_tokens < 2)
1574                         goto error;
1575
1576                 if (parser_read_uint32(&weight_val, tokens[1]) != 0)
1577                         goto error;
1578                 *weight = weight_val;
1579
1580                 tokens += 2;
1581                 n_tokens -= 2;
1582         }
1583
1584         if (n_tokens)
1585                 goto error;
1586
1587         free(s0);
1588         return 0;
1589
1590 error:
1591         free(s0);
1592         if (is_blank_or_comment)
1593                 *is_blank_or_comment = blank_or_comment;
1594         return -EINVAL;
1595 }
1596
1597 static int
1598 pipeline_selector_group_members_add(struct rte_swx_ctl_pipeline *p,
1599                            const char *selector_name,
1600                            FILE *file,
1601                            uint32_t *file_line_number)
1602 {
1603         char *line = NULL;
1604         uint32_t line_id = 0;
1605         int status = 0;
1606
1607         /* Buffer allocation. */
1608         line = malloc(MAX_LINE_SIZE);
1609         if (!line)
1610                 return -ENOMEM;
1611
1612         /* File read. */
1613         for (line_id = 1; ; line_id++) {
1614                 uint32_t group_id, member_id, weight;
1615                 int is_blank_or_comment;
1616
1617                 if (fgets(line, MAX_LINE_SIZE, file) == NULL)
1618                         break;
1619
1620                 status = pipeline_selector_group_member_read(line,
1621                                                               &group_id,
1622                                                               &member_id,
1623                                                               &weight,
1624                                                               &is_blank_or_comment);
1625                 if (status) {
1626                         if (is_blank_or_comment)
1627                                 continue;
1628
1629                         goto error;
1630                 }
1631
1632                 status = rte_swx_ctl_pipeline_selector_group_member_add(p,
1633                         selector_name,
1634                         group_id,
1635                         member_id,
1636                         weight);
1637                 if (status)
1638                         goto error;
1639         }
1640
1641 error:
1642         free(line);
1643         *file_line_number = line_id;
1644         return status;
1645 }
1646
1647 static const char cmd_pipeline_selector_group_member_add_help[] =
1648 "pipeline <pipeline_name> selector <selector_name> group member add <file_name>";
1649
1650 static void
1651 cmd_pipeline_selector_group_member_add(char **tokens,
1652         uint32_t n_tokens,
1653         char *out,
1654         size_t out_size,
1655         void *obj)
1656 {
1657         struct pipeline *p;
1658         char *pipeline_name, *selector_name, *file_name;
1659         FILE *file = NULL;
1660         uint32_t file_line_number = 0;
1661         int status;
1662
1663         if (n_tokens != 8) {
1664                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1665                 return;
1666         }
1667
1668         pipeline_name = tokens[1];
1669         p = pipeline_find(obj, pipeline_name);
1670         if (!p || !p->ctl) {
1671                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1672                 return;
1673         }
1674
1675         if (strcmp(tokens[2], "selector") != 0) {
1676                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector");
1677                 return;
1678         }
1679
1680         selector_name = tokens[3];
1681
1682         if (strcmp(tokens[4], "group") ||
1683                 strcmp(tokens[5], "member") ||
1684                 strcmp(tokens[6], "add")) {
1685                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group member add");
1686                 return;
1687         }
1688
1689         file_name = tokens[7];
1690         file = fopen(file_name, "r");
1691         if (!file) {
1692                 snprintf(out, out_size, "Cannot open file %s.\n", file_name);
1693                 return;
1694         }
1695
1696         status = pipeline_selector_group_members_add(p->ctl,
1697                                             selector_name,
1698                                             file,
1699                                             &file_line_number);
1700         if (status)
1701                 snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
1702                          file_name,
1703                          file_line_number);
1704
1705         fclose(file);
1706 }
1707
1708 static int
1709 pipeline_selector_group_members_delete(struct rte_swx_ctl_pipeline *p,
1710                            const char *selector_name,
1711                            FILE *file,
1712                            uint32_t *file_line_number)
1713 {
1714         char *line = NULL;
1715         uint32_t line_id = 0;
1716         int status = 0;
1717
1718         /* Buffer allocation. */
1719         line = malloc(MAX_LINE_SIZE);
1720         if (!line)
1721                 return -ENOMEM;
1722
1723         /* File read. */
1724         for (line_id = 1; ; line_id++) {
1725                 uint32_t group_id, member_id, weight;
1726                 int is_blank_or_comment;
1727
1728                 if (fgets(line, MAX_LINE_SIZE, file) == NULL)
1729                         break;
1730
1731                 status = pipeline_selector_group_member_read(line,
1732                                                               &group_id,
1733                                                               &member_id,
1734                                                               &weight,
1735                                                               &is_blank_or_comment);
1736                 if (status) {
1737                         if (is_blank_or_comment)
1738                                 continue;
1739
1740                         goto error;
1741                 }
1742
1743                 status = rte_swx_ctl_pipeline_selector_group_member_delete(p,
1744                         selector_name,
1745                         group_id,
1746                         member_id);
1747                 if (status)
1748                         goto error;
1749         }
1750
1751 error:
1752         free(line);
1753         *file_line_number = line_id;
1754         return status;
1755 }
1756
1757 static const char cmd_pipeline_selector_group_member_delete_help[] =
1758 "pipeline <pipeline_name> selector <selector_name> group member delete <file_name>";
1759
1760 static void
1761 cmd_pipeline_selector_group_member_delete(char **tokens,
1762         uint32_t n_tokens,
1763         char *out,
1764         size_t out_size,
1765         void *obj)
1766 {
1767         struct pipeline *p;
1768         char *pipeline_name, *selector_name, *file_name;
1769         FILE *file = NULL;
1770         uint32_t file_line_number = 0;
1771         int status;
1772
1773         if (n_tokens != 8) {
1774                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1775                 return;
1776         }
1777
1778         pipeline_name = tokens[1];
1779         p = pipeline_find(obj, pipeline_name);
1780         if (!p || !p->ctl) {
1781                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1782                 return;
1783         }
1784
1785         if (strcmp(tokens[2], "selector") != 0) {
1786                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector");
1787                 return;
1788         }
1789
1790         selector_name = tokens[3];
1791
1792         if (strcmp(tokens[4], "group") ||
1793                 strcmp(tokens[5], "member") ||
1794                 strcmp(tokens[6], "delete")) {
1795                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group member delete");
1796                 return;
1797         }
1798
1799         file_name = tokens[7];
1800         file = fopen(file_name, "r");
1801         if (!file) {
1802                 snprintf(out, out_size, "Cannot open file %s.\n", file_name);
1803                 return;
1804         }
1805
1806         status = pipeline_selector_group_members_delete(p->ctl,
1807                                             selector_name,
1808                                             file,
1809                                             &file_line_number);
1810         if (status)
1811                 snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
1812                          file_name,
1813                          file_line_number);
1814
1815         fclose(file);
1816 }
1817
1818 static const char cmd_pipeline_selector_show_help[] =
1819 "pipeline <pipeline_name> selector <selector_name> show [filename]\n";
1820
1821 static void
1822 cmd_pipeline_selector_show(char **tokens,
1823         uint32_t n_tokens,
1824         char *out,
1825         size_t out_size,
1826         void *obj)
1827 {
1828         struct pipeline *p;
1829         char *pipeline_name, *selector_name;
1830         FILE *file = NULL;
1831         int status;
1832
1833         if (n_tokens != 5 && n_tokens != 6) {
1834                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1835                 return;
1836         }
1837
1838         pipeline_name = tokens[1];
1839         p = pipeline_find(obj, pipeline_name);
1840         if (!p || !p->ctl) {
1841                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1842                 return;
1843         }
1844
1845         selector_name = tokens[3];
1846
1847         file = (n_tokens == 6) ? fopen(tokens[5], "w") : stdout;
1848         if (!file) {
1849                 snprintf(out, out_size, "Cannot open file %s.\n", tokens[5]);
1850                 return;
1851         }
1852
1853         status = rte_swx_ctl_pipeline_selector_fprintf(file, p->ctl, selector_name);
1854         if (status)
1855                 snprintf(out, out_size, MSG_ARG_INVALID, "selector_name");
1856
1857         if (file)
1858                 fclose(file);
1859 }
1860
1861 static int
1862 pipeline_learner_default_entry_add(struct rte_swx_ctl_pipeline *p,
1863                                    const char *learner_name,
1864                                    FILE *file,
1865                                    uint32_t *file_line_number)
1866 {
1867         char *line = NULL;
1868         uint32_t line_id = 0;
1869         int status = 0;
1870
1871         /* Buffer allocation. */
1872         line = malloc(MAX_LINE_SIZE);
1873         if (!line)
1874                 return -ENOMEM;
1875
1876         /* File read. */
1877         for (line_id = 1; ; line_id++) {
1878                 struct rte_swx_table_entry *entry;
1879                 int is_blank_or_comment;
1880
1881                 if (fgets(line, MAX_LINE_SIZE, file) == NULL)
1882                         break;
1883
1884                 entry = rte_swx_ctl_pipeline_learner_default_entry_read(p,
1885                                                                         learner_name,
1886                                                                         line,
1887                                                                         &is_blank_or_comment);
1888                 if (!entry) {
1889                         if (is_blank_or_comment)
1890                                 continue;
1891
1892                         status = -EINVAL;
1893                         goto error;
1894                 }
1895
1896                 status = rte_swx_ctl_pipeline_learner_default_entry_add(p,
1897                                                                         learner_name,
1898                                                                         entry);
1899                 table_entry_free(entry);
1900                 if (status)
1901                         goto error;
1902         }
1903
1904 error:
1905         *file_line_number = line_id;
1906         free(line);
1907         return status;
1908 }
1909
1910 static const char cmd_pipeline_learner_default_help[] =
1911 "pipeline <pipeline_name> learner <learner_name> default <file_name>\n";
1912
1913 static void
1914 cmd_pipeline_learner_default(char **tokens,
1915                              uint32_t n_tokens,
1916                              char *out,
1917                              size_t out_size,
1918                              void *obj)
1919 {
1920         struct pipeline *p;
1921         char *pipeline_name, *learner_name, *file_name;
1922         FILE *file = NULL;
1923         uint32_t file_line_number = 0;
1924         int status;
1925
1926         if (n_tokens != 6) {
1927                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1928                 return;
1929         }
1930
1931         pipeline_name = tokens[1];
1932         p = pipeline_find(obj, pipeline_name);
1933         if (!p || !p->ctl) {
1934                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1935                 return;
1936         }
1937
1938         learner_name = tokens[3];
1939
1940         file_name = tokens[5];
1941         file = fopen(file_name, "r");
1942         if (!file) {
1943                 snprintf(out, out_size, "Cannot open file %s.\n", file_name);
1944                 return;
1945         }
1946
1947         status = pipeline_learner_default_entry_add(p->ctl,
1948                                                     learner_name,
1949                                                     file,
1950                                                     &file_line_number);
1951         if (status)
1952                 snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
1953                          file_name,
1954                          file_line_number);
1955
1956         fclose(file);
1957 }
1958
1959 static const char cmd_pipeline_commit_help[] =
1960 "pipeline <pipeline_name> commit\n";
1961
1962 static void
1963 cmd_pipeline_commit(char **tokens,
1964         uint32_t n_tokens,
1965         char *out,
1966         size_t out_size,
1967         void *obj)
1968 {
1969         struct pipeline *p;
1970         char *pipeline_name;
1971         int status;
1972
1973         if (n_tokens != 3) {
1974                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1975                 return;
1976         }
1977
1978         pipeline_name = tokens[1];
1979         p = pipeline_find(obj, pipeline_name);
1980         if (!p || !p->ctl) {
1981                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1982                 return;
1983         }
1984
1985         status = rte_swx_ctl_pipeline_commit(p->ctl, 1);
1986         if (status)
1987                 snprintf(out, out_size, "Commit failed. "
1988                         "Use \"commit\" to retry or \"abort\" to discard the pending work.\n");
1989 }
1990
1991 static const char cmd_pipeline_abort_help[] =
1992 "pipeline <pipeline_name> abort\n";
1993
1994 static void
1995 cmd_pipeline_abort(char **tokens,
1996         uint32_t n_tokens,
1997         char *out,
1998         size_t out_size,
1999         void *obj)
2000 {
2001         struct pipeline *p;
2002         char *pipeline_name;
2003
2004         if (n_tokens != 3) {
2005                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2006                 return;
2007         }
2008
2009         pipeline_name = tokens[1];
2010         p = pipeline_find(obj, pipeline_name);
2011         if (!p || !p->ctl) {
2012                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2013                 return;
2014         }
2015
2016         rte_swx_ctl_pipeline_abort(p->ctl);
2017 }
2018
2019 static const char cmd_pipeline_regrd_help[] =
2020 "pipeline <pipeline_name> regrd <register_array_name> <index>\n";
2021
2022 static void
2023 cmd_pipeline_regrd(char **tokens,
2024         uint32_t n_tokens,
2025         char *out,
2026         size_t out_size,
2027         void *obj)
2028 {
2029         struct pipeline *p;
2030         const char *name;
2031         uint64_t value;
2032         uint32_t idx;
2033         int status;
2034
2035         if (n_tokens != 5) {
2036                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2037                 return;
2038         }
2039
2040         p = pipeline_find(obj, tokens[1]);
2041         if (!p || !p->ctl) {
2042                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2043                 return;
2044         }
2045
2046         if (strcmp(tokens[2], "regrd")) {
2047                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "regrd");
2048                 return;
2049         }
2050
2051         name = tokens[3];
2052
2053         if (parser_read_uint32(&idx, tokens[4])) {
2054                 snprintf(out, out_size, MSG_ARG_INVALID, "index");
2055                 return;
2056         }
2057
2058         status = rte_swx_ctl_pipeline_regarray_read(p->p, name, idx, &value);
2059         if (status) {
2060                 snprintf(out, out_size, "Command failed.\n");
2061                 return;
2062         }
2063
2064         snprintf(out, out_size, "0x%" PRIx64 "\n", value);
2065 }
2066
2067 static const char cmd_pipeline_regwr_help[] =
2068 "pipeline <pipeline_name> regwr <register_array_name> <index> <value>\n";
2069
2070 static void
2071 cmd_pipeline_regwr(char **tokens,
2072         uint32_t n_tokens,
2073         char *out,
2074         size_t out_size,
2075         void *obj)
2076 {
2077         struct pipeline *p;
2078         const char *name;
2079         uint64_t value;
2080         uint32_t idx;
2081         int status;
2082
2083         if (n_tokens != 6) {
2084                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2085                 return;
2086         }
2087
2088         p = pipeline_find(obj, tokens[1]);
2089         if (!p || !p->ctl) {
2090                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2091                 return;
2092         }
2093
2094         if (strcmp(tokens[2], "regwr")) {
2095                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "regwr");
2096                 return;
2097         }
2098
2099         name = tokens[3];
2100
2101         if (parser_read_uint32(&idx, tokens[4])) {
2102                 snprintf(out, out_size, MSG_ARG_INVALID, "index");
2103                 return;
2104         }
2105
2106         if (parser_read_uint64(&value, tokens[5])) {
2107                 snprintf(out, out_size, MSG_ARG_INVALID, "value");
2108                 return;
2109         }
2110
2111         status = rte_swx_ctl_pipeline_regarray_write(p->p, name, idx, value);
2112         if (status) {
2113                 snprintf(out, out_size, "Command failed.\n");
2114                 return;
2115         }
2116 }
2117
2118 static const char cmd_pipeline_meter_profile_add_help[] =
2119 "pipeline <pipeline_name> meter profile <profile_name> add "
2120         "cir <cir> pir <pir> cbs <cbs> pbs <pbs>\n";
2121
2122 static void
2123 cmd_pipeline_meter_profile_add(char **tokens,
2124         uint32_t n_tokens,
2125         char *out,
2126         size_t out_size,
2127         void *obj)
2128 {
2129         struct rte_meter_trtcm_params params;
2130         struct pipeline *p;
2131         const char *profile_name;
2132         int status;
2133
2134         if (n_tokens != 14) {
2135                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2136                 return;
2137         }
2138
2139         p = pipeline_find(obj, tokens[1]);
2140         if (!p || !p->ctl) {
2141                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2142                 return;
2143         }
2144
2145         if (strcmp(tokens[2], "meter")) {
2146                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
2147                 return;
2148         }
2149
2150         if (strcmp(tokens[3], "profile")) {
2151                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
2152                 return;
2153         }
2154
2155         profile_name = tokens[4];
2156
2157         if (strcmp(tokens[5], "add")) {
2158                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
2159                 return;
2160         }
2161
2162         if (strcmp(tokens[6], "cir")) {
2163                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cir");
2164                 return;
2165         }
2166
2167         if (parser_read_uint64(&params.cir, tokens[7])) {
2168                 snprintf(out, out_size, MSG_ARG_INVALID, "cir");
2169                 return;
2170         }
2171
2172         if (strcmp(tokens[8], "pir")) {
2173                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pir");
2174                 return;
2175         }
2176
2177         if (parser_read_uint64(&params.pir, tokens[9])) {
2178                 snprintf(out, out_size, MSG_ARG_INVALID, "pir");
2179                 return;
2180         }
2181
2182         if (strcmp(tokens[10], "cbs")) {
2183                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cbs");
2184                 return;
2185         }
2186
2187         if (parser_read_uint64(&params.cbs, tokens[11])) {
2188                 snprintf(out, out_size, MSG_ARG_INVALID, "cbs");
2189                 return;
2190         }
2191
2192         if (strcmp(tokens[12], "pbs")) {
2193                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pbs");
2194                 return;
2195         }
2196
2197         if (parser_read_uint64(&params.pbs, tokens[13])) {
2198                 snprintf(out, out_size, MSG_ARG_INVALID, "pbs");
2199                 return;
2200         }
2201
2202         status = rte_swx_ctl_meter_profile_add(p->p, profile_name, &params);
2203         if (status) {
2204                 snprintf(out, out_size, "Command failed.\n");
2205                 return;
2206         }
2207 }
2208
2209 static const char cmd_pipeline_meter_profile_delete_help[] =
2210 "pipeline <pipeline_name> meter profile <profile_name> delete\n";
2211
2212 static void
2213 cmd_pipeline_meter_profile_delete(char **tokens,
2214         uint32_t n_tokens,
2215         char *out,
2216         size_t out_size,
2217         void *obj)
2218 {
2219         struct pipeline *p;
2220         const char *profile_name;
2221         int status;
2222
2223         if (n_tokens != 6) {
2224                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2225                 return;
2226         }
2227
2228         p = pipeline_find(obj, tokens[1]);
2229         if (!p || !p->ctl) {
2230                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2231                 return;
2232         }
2233
2234         if (strcmp(tokens[2], "meter")) {
2235                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
2236                 return;
2237         }
2238
2239         if (strcmp(tokens[3], "profile")) {
2240                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
2241                 return;
2242         }
2243
2244         profile_name = tokens[4];
2245
2246         if (strcmp(tokens[5], "delete")) {
2247                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
2248                 return;
2249         }
2250
2251         status = rte_swx_ctl_meter_profile_delete(p->p, profile_name);
2252         if (status) {
2253                 snprintf(out, out_size, "Command failed.\n");
2254                 return;
2255         }
2256 }
2257
2258 static const char cmd_pipeline_meter_reset_help[] =
2259 "pipeline <pipeline_name> meter <meter_array_name> from <index0> to <index1> "
2260         "reset\n";
2261
2262 static void
2263 cmd_pipeline_meter_reset(char **tokens,
2264         uint32_t n_tokens,
2265         char *out,
2266         size_t out_size,
2267         void *obj)
2268 {
2269         struct pipeline *p;
2270         const char *name;
2271         uint32_t idx0 = 0, idx1 = 0;
2272
2273         if (n_tokens != 9) {
2274                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2275                 return;
2276         }
2277
2278         p = pipeline_find(obj, tokens[1]);
2279         if (!p || !p->ctl) {
2280                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2281                 return;
2282         }
2283
2284         if (strcmp(tokens[2], "meter")) {
2285                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
2286                 return;
2287         }
2288
2289         name = tokens[3];
2290
2291         if (strcmp(tokens[4], "from")) {
2292                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "from");
2293                 return;
2294         }
2295
2296         if (parser_read_uint32(&idx0, tokens[5])) {
2297                 snprintf(out, out_size, MSG_ARG_INVALID, "index0");
2298                 return;
2299         }
2300
2301         if (strcmp(tokens[6], "to")) {
2302                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "to");
2303                 return;
2304         }
2305
2306         if (parser_read_uint32(&idx1, tokens[7]) || (idx1 < idx0)) {
2307                 snprintf(out, out_size, MSG_ARG_INVALID, "index1");
2308                 return;
2309         }
2310
2311         if (strcmp(tokens[8], "reset")) {
2312                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "reset");
2313                 return;
2314         }
2315
2316         for ( ; idx0 <= idx1; idx0++) {
2317                 int status;
2318
2319                 status = rte_swx_ctl_meter_reset(p->p, name, idx0);
2320                 if (status) {
2321                         snprintf(out, out_size, "Command failed for index %u.\n", idx0);
2322                         return;
2323                 }
2324         }
2325 }
2326
2327 static const char cmd_pipeline_meter_set_help[] =
2328 "pipeline <pipeline_name> meter <meter_array_name> from <index0> to <index1> "
2329         "set profile <profile_name>\n";
2330
2331 static void
2332 cmd_pipeline_meter_set(char **tokens,
2333         uint32_t n_tokens,
2334         char *out,
2335         size_t out_size,
2336         void *obj)
2337 {
2338         struct pipeline *p;
2339         const char *name, *profile_name;
2340         uint32_t idx0 = 0, idx1 = 0;
2341
2342         if (n_tokens != 11) {
2343                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2344                 return;
2345         }
2346
2347         p = pipeline_find(obj, tokens[1]);
2348         if (!p || !p->ctl) {
2349                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2350                 return;
2351         }
2352
2353         if (strcmp(tokens[2], "meter")) {
2354                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
2355                 return;
2356         }
2357
2358         name = tokens[3];
2359
2360         if (strcmp(tokens[4], "from")) {
2361                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "from");
2362                 return;
2363         }
2364
2365         if (parser_read_uint32(&idx0, tokens[5])) {
2366                 snprintf(out, out_size, MSG_ARG_INVALID, "index0");
2367                 return;
2368         }
2369
2370         if (strcmp(tokens[6], "to")) {
2371                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "to");
2372                 return;
2373         }
2374
2375         if (parser_read_uint32(&idx1, tokens[7]) || (idx1 < idx0)) {
2376                 snprintf(out, out_size, MSG_ARG_INVALID, "index1");
2377                 return;
2378         }
2379
2380         if (strcmp(tokens[8], "set")) {
2381                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "set");
2382                 return;
2383         }
2384
2385         if (strcmp(tokens[9], "profile")) {
2386                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
2387                 return;
2388         }
2389
2390         profile_name = tokens[10];
2391
2392         for ( ; idx0 <= idx1; idx0++) {
2393                 int status;
2394
2395                 status = rte_swx_ctl_meter_set(p->p, name, idx0, profile_name);
2396                 if (status) {
2397                         snprintf(out, out_size, "Command failed for index %u.\n", idx0);
2398                         return;
2399                 }
2400         }
2401 }
2402
2403 static const char cmd_pipeline_meter_stats_help[] =
2404 "pipeline <pipeline_name> meter <meter_array_name> from <index0> to <index1> "
2405         "stats\n";
2406
2407 static void
2408 cmd_pipeline_meter_stats(char **tokens,
2409         uint32_t n_tokens,
2410         char *out,
2411         size_t out_size,
2412         void *obj)
2413 {
2414         struct rte_swx_ctl_meter_stats stats;
2415         struct pipeline *p;
2416         const char *name;
2417         uint32_t idx0 = 0, idx1 = 0;
2418
2419         if (n_tokens != 9) {
2420                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2421                 return;
2422         }
2423
2424         p = pipeline_find(obj, tokens[1]);
2425         if (!p || !p->ctl) {
2426                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2427                 return;
2428         }
2429
2430         if (strcmp(tokens[2], "meter")) {
2431                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
2432                 return;
2433         }
2434
2435         name = tokens[3];
2436
2437         if (strcmp(tokens[4], "from")) {
2438                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "from");
2439                 return;
2440         }
2441
2442         if (parser_read_uint32(&idx0, tokens[5])) {
2443                 snprintf(out, out_size, MSG_ARG_INVALID, "index0");
2444                 return;
2445         }
2446
2447         if (strcmp(tokens[6], "to")) {
2448                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "to");
2449                 return;
2450         }
2451
2452         if (parser_read_uint32(&idx1, tokens[7]) || (idx1 < idx0)) {
2453                 snprintf(out, out_size, MSG_ARG_INVALID, "index1");
2454                 return;
2455         }
2456
2457         if (strcmp(tokens[8], "stats")) {
2458                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
2459                 return;
2460         }
2461
2462         /* Table header. */
2463         snprintf(out, out_size, "+-%7s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+\n",
2464                  "-------",
2465                  "----------------", "----------------", "----------------",
2466                  "----------------", "----------------", "----------------");
2467         out_size -= strlen(out);
2468         out += strlen(out);
2469
2470         snprintf(out, out_size, "| %4s | %16s | %16s | %16s | %16s | %16s | %16s |\n",
2471                  "METER #",
2472                  "GREEN (packets)", "YELLOW (packets)", "RED (packets)",
2473                  "GREEN (bytes)", "YELLOW (bytes)", "RED (bytes)");
2474         out_size -= strlen(out);
2475         out += strlen(out);
2476
2477         snprintf(out, out_size, "+-%7s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+\n",
2478                  "-------",
2479                  "----------------", "----------------", "----------------",
2480                  "----------------", "----------------", "----------------");
2481         out_size -= strlen(out);
2482         out += strlen(out);
2483
2484         /* Table rows. */
2485         for ( ; idx0 <= idx1; idx0++) {
2486                 int status;
2487
2488                 status = rte_swx_ctl_meter_stats_read(p->p, name, idx0, &stats);
2489                 if (status) {
2490                         snprintf(out, out_size, "Pipeline meter stats error at index %u.\n", idx0);
2491                         out_size -= strlen(out);
2492                         out += strlen(out);
2493                         return;
2494                 }
2495
2496                 snprintf(out, out_size, "| %7d | %16" PRIx64 " | %16" PRIx64 " | %16" PRIx64
2497                          " | %16" PRIx64 " | %16" PRIx64 " | %16" PRIx64 " |\n",
2498                          idx0,
2499                          stats.n_pkts[RTE_COLOR_GREEN],
2500                          stats.n_pkts[RTE_COLOR_YELLOW],
2501                          stats.n_pkts[RTE_COLOR_RED],
2502                          stats.n_bytes[RTE_COLOR_GREEN],
2503                          stats.n_bytes[RTE_COLOR_YELLOW],
2504                          stats.n_bytes[RTE_COLOR_RED]);
2505                 out_size -= strlen(out);
2506                 out += strlen(out);
2507         }
2508 }
2509
2510 static const char cmd_pipeline_stats_help[] =
2511 "pipeline <pipeline_name> stats\n";
2512
2513 static void
2514 cmd_pipeline_stats(char **tokens,
2515         uint32_t n_tokens,
2516         char *out,
2517         size_t out_size,
2518         void *obj)
2519 {
2520         struct rte_swx_ctl_pipeline_info info;
2521         struct pipeline *p;
2522         uint32_t i;
2523         int status;
2524
2525         if (n_tokens != 3) {
2526                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2527                 return;
2528         }
2529
2530         p = pipeline_find(obj, tokens[1]);
2531         if (!p || !p->ctl) {
2532                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2533                 return;
2534         }
2535
2536         if (strcmp(tokens[2], "stats")) {
2537                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
2538                 return;
2539         }
2540
2541         status = rte_swx_ctl_pipeline_info_get(p->p, &info);
2542         if (status) {
2543                 snprintf(out, out_size, "Pipeline info get error.");
2544                 return;
2545         }
2546
2547         snprintf(out, out_size, "Input ports:\n");
2548         out_size -= strlen(out);
2549         out += strlen(out);
2550
2551         for (i = 0; i < info.n_ports_in; i++) {
2552                 struct rte_swx_port_in_stats stats;
2553
2554                 rte_swx_ctl_pipeline_port_in_stats_read(p->p, i, &stats);
2555
2556                 snprintf(out, out_size, "\tPort %u:"
2557                         " packets %" PRIu64
2558                         " bytes %" PRIu64
2559                         " empty %" PRIu64 "\n",
2560                         i, stats.n_pkts, stats.n_bytes, stats.n_empty);
2561                 out_size -= strlen(out);
2562                 out += strlen(out);
2563         }
2564
2565         snprintf(out, out_size, "\nOutput ports:\n");
2566         out_size -= strlen(out);
2567         out += strlen(out);
2568
2569         for (i = 0; i < info.n_ports_out; i++) {
2570                 struct rte_swx_port_out_stats stats;
2571
2572                 rte_swx_ctl_pipeline_port_out_stats_read(p->p, i, &stats);
2573
2574                 if (i != info.n_ports_out - 1)
2575                         snprintf(out, out_size, "\tPort %u:", i);
2576                 else
2577                         snprintf(out, out_size, "\tDROP:");
2578
2579                 out_size -= strlen(out);
2580                 out += strlen(out);
2581
2582                 snprintf(out,
2583                         out_size,
2584                         " packets %" PRIu64
2585                         " bytes %" PRIu64
2586                         " clone %" PRIu64
2587                         " clonerr %" PRIu64 "\n",
2588                         stats.n_pkts,
2589                         stats.n_bytes,
2590                         stats.n_pkts_clone,
2591                         stats.n_pkts_clone_err);
2592
2593                 out_size -= strlen(out);
2594                 out += strlen(out);
2595         }
2596
2597         snprintf(out, out_size, "\nTables:\n");
2598         out_size -= strlen(out);
2599         out += strlen(out);
2600
2601         for (i = 0; i < info.n_tables; i++) {
2602                 struct rte_swx_ctl_table_info table_info;
2603                 uint64_t n_pkts_action[info.n_actions];
2604                 struct rte_swx_table_stats stats = {
2605                         .n_pkts_hit = 0,
2606                         .n_pkts_miss = 0,
2607                         .n_pkts_action = n_pkts_action,
2608                 };
2609                 uint32_t j;
2610
2611                 status = rte_swx_ctl_table_info_get(p->p, i, &table_info);
2612                 if (status) {
2613                         snprintf(out, out_size, "Table info get error.");
2614                         return;
2615                 }
2616
2617                 status = rte_swx_ctl_pipeline_table_stats_read(p->p, table_info.name, &stats);
2618                 if (status) {
2619                         snprintf(out, out_size, "Table stats read error.");
2620                         return;
2621                 }
2622
2623                 snprintf(out, out_size, "\tTable %s:\n"
2624                         "\t\tHit (packets): %" PRIu64 "\n"
2625                         "\t\tMiss (packets): %" PRIu64 "\n",
2626                         table_info.name,
2627                         stats.n_pkts_hit,
2628                         stats.n_pkts_miss);
2629                 out_size -= strlen(out);
2630                 out += strlen(out);
2631
2632                 for (j = 0; j < info.n_actions; j++) {
2633                         struct rte_swx_ctl_action_info action_info;
2634
2635                         status = rte_swx_ctl_action_info_get(p->p, j, &action_info);
2636                         if (status) {
2637                                 snprintf(out, out_size, "Action info get error.");
2638                                 return;
2639                         }
2640
2641                         snprintf(out, out_size, "\t\tAction %s (packets): %" PRIu64 "\n",
2642                                 action_info.name,
2643                                 stats.n_pkts_action[j]);
2644                         out_size -= strlen(out);
2645                         out += strlen(out);
2646                 }
2647         }
2648
2649         snprintf(out, out_size, "\nLearner tables:\n");
2650         out_size -= strlen(out);
2651         out += strlen(out);
2652
2653         for (i = 0; i < info.n_learners; i++) {
2654                 struct rte_swx_ctl_learner_info learner_info;
2655                 uint64_t n_pkts_action[info.n_actions];
2656                 struct rte_swx_learner_stats stats = {
2657                         .n_pkts_hit = 0,
2658                         .n_pkts_miss = 0,
2659                         .n_pkts_action = n_pkts_action,
2660                 };
2661                 uint32_t j;
2662
2663                 status = rte_swx_ctl_learner_info_get(p->p, i, &learner_info);
2664                 if (status) {
2665                         snprintf(out, out_size, "Learner table info get error.");
2666                         return;
2667                 }
2668
2669                 status = rte_swx_ctl_pipeline_learner_stats_read(p->p, learner_info.name, &stats);
2670                 if (status) {
2671                         snprintf(out, out_size, "Learner table stats read error.");
2672                         return;
2673                 }
2674
2675                 snprintf(out, out_size, "\tLearner table %s:\n"
2676                         "\t\tHit (packets): %" PRIu64 "\n"
2677                         "\t\tMiss (packets): %" PRIu64 "\n"
2678                         "\t\tLearn OK (packets): %" PRIu64 "\n"
2679                         "\t\tLearn error (packets): %" PRIu64 "\n"
2680                         "\t\tForget (packets): %" PRIu64 "\n",
2681                         learner_info.name,
2682                         stats.n_pkts_hit,
2683                         stats.n_pkts_miss,
2684                         stats.n_pkts_learn_ok,
2685                         stats.n_pkts_learn_err,
2686                         stats.n_pkts_forget);
2687                 out_size -= strlen(out);
2688                 out += strlen(out);
2689
2690                 for (j = 0; j < info.n_actions; j++) {
2691                         struct rte_swx_ctl_action_info action_info;
2692
2693                         status = rte_swx_ctl_action_info_get(p->p, j, &action_info);
2694                         if (status) {
2695                                 snprintf(out, out_size, "Action info get error.");
2696                                 return;
2697                         }
2698
2699                         snprintf(out, out_size, "\t\tAction %s (packets): %" PRIu64 "\n",
2700                                 action_info.name,
2701                                 stats.n_pkts_action[j]);
2702                         out_size -= strlen(out);
2703                         out += strlen(out);
2704                 }
2705         }
2706 }
2707
2708 static const char cmd_pipeline_mirror_help[] =
2709 "pipeline <pipeline_name> mirror slots <n_slots> sessions <n_sessions>\n";
2710
2711 static void
2712 cmd_pipeline_mirror(char **tokens,
2713         uint32_t n_tokens,
2714         char *out,
2715         size_t out_size,
2716         void *obj)
2717 {
2718         struct rte_swx_pipeline_mirroring_params params;
2719         struct pipeline *p;
2720         int status;
2721
2722         if (n_tokens != 7) {
2723                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2724                 return;
2725         }
2726
2727         if (strcmp(tokens[0], "pipeline")) {
2728                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
2729                 return;
2730         }
2731
2732         p = pipeline_find(obj, tokens[1]);
2733         if (!p) {
2734                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2735                 return;
2736         }
2737
2738         if (strcmp(tokens[2], "mirror")) {
2739                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mirror");
2740                 return;
2741         }
2742
2743         if (strcmp(tokens[3], "slots")) {
2744                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "slots");
2745                 return;
2746         }
2747
2748         if (parser_read_uint32(&params.n_slots, tokens[4])) {
2749                 snprintf(out, out_size, MSG_ARG_INVALID, "n_slots");
2750                 return;
2751         }
2752
2753         if (strcmp(tokens[5], "sessions")) {
2754                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "sessions");
2755                 return;
2756         }
2757
2758         if (parser_read_uint32(&params.n_sessions, tokens[6])) {
2759                 snprintf(out, out_size, MSG_ARG_INVALID, "n_sessions");
2760                 return;
2761         }
2762
2763         status = rte_swx_pipeline_mirroring_config(p->p, &params);
2764         if (status) {
2765                 snprintf(out, out_size, "Command failed!\n");
2766                 return;
2767         }
2768 }
2769
2770 static const char cmd_pipeline_mirror_session_help[] =
2771 "pipeline <pipeline_name> mirror session <session_id> port <port_id> clone fast | slow "
2772 "truncate <truncation_length>\n";
2773
2774 static void
2775 cmd_pipeline_mirror_session(char **tokens,
2776         uint32_t n_tokens,
2777         char *out,
2778         size_t out_size,
2779         void *obj)
2780 {
2781         struct rte_swx_pipeline_mirroring_session_params params;
2782         struct pipeline *p;
2783         uint32_t session_id;
2784         int status;
2785
2786         if (n_tokens != 11) {
2787                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2788                 return;
2789         }
2790
2791         if (strcmp(tokens[0], "pipeline")) {
2792                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
2793                 return;
2794         }
2795
2796         p = pipeline_find(obj, tokens[1]);
2797         if (!p || !p->ctl) {
2798                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2799                 return;
2800         }
2801
2802         if (strcmp(tokens[2], "mirror")) {
2803                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mirror");
2804                 return;
2805         }
2806
2807         if (strcmp(tokens[3], "session")) {
2808                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "session");
2809                 return;
2810         }
2811
2812         if (parser_read_uint32(&session_id, tokens[4])) {
2813                 snprintf(out, out_size, MSG_ARG_INVALID, "session_id");
2814                 return;
2815         }
2816
2817         if (strcmp(tokens[5], "port")) {
2818                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
2819                 return;
2820         }
2821
2822         if (parser_read_uint32(&params.port_id, tokens[6])) {
2823                 snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
2824                 return;
2825         }
2826
2827         if (strcmp(tokens[7], "clone")) {
2828                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "clone");
2829                 return;
2830         }
2831
2832         if (!strcmp(tokens[8], "fast"))
2833                 params.fast_clone = 1;
2834         else if (!strcmp(tokens[8], "slow"))
2835                 params.fast_clone = 0;
2836         else {
2837                 snprintf(out, out_size, MSG_ARG_INVALID, "clone");
2838                 return;
2839         }
2840
2841         if (strcmp(tokens[9], "truncate")) {
2842                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "truncate");
2843                 return;
2844         }
2845
2846         if (parser_read_uint32(&params.truncation_length, tokens[10])) {
2847                 snprintf(out, out_size, MSG_ARG_INVALID, "truncation_length");
2848                 return;
2849         }
2850
2851         status = rte_swx_ctl_pipeline_mirroring_session_set(p->p, session_id, &params);
2852         if (status) {
2853                 snprintf(out, out_size, "Command failed!\n");
2854                 return;
2855         }
2856 }
2857
2858 static const char cmd_thread_pipeline_enable_help[] =
2859 "thread <thread_id> pipeline <pipeline_name> enable\n";
2860
2861 static void
2862 cmd_thread_pipeline_enable(char **tokens,
2863         uint32_t n_tokens,
2864         char *out,
2865         size_t out_size,
2866         void *obj)
2867 {
2868         char *pipeline_name;
2869         struct pipeline *p;
2870         uint32_t thread_id;
2871         int status;
2872
2873         if (n_tokens != 5) {
2874                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2875                 return;
2876         }
2877
2878         if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
2879                 snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
2880                 return;
2881         }
2882
2883         if (strcmp(tokens[2], "pipeline") != 0) {
2884                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
2885                 return;
2886         }
2887
2888         pipeline_name = tokens[3];
2889         p = pipeline_find(obj, pipeline_name);
2890         if (!p || !p->ctl) {
2891                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2892                 return;
2893         }
2894
2895         if (strcmp(tokens[4], "enable") != 0) {
2896                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
2897                 return;
2898         }
2899
2900         status = thread_pipeline_enable(thread_id, obj, pipeline_name);
2901         if (status) {
2902                 snprintf(out, out_size, MSG_CMD_FAIL, "thread pipeline enable");
2903                 return;
2904         }
2905 }
2906
2907 static const char cmd_thread_pipeline_disable_help[] =
2908 "thread <thread_id> pipeline <pipeline_name> disable\n";
2909
2910 static void
2911 cmd_thread_pipeline_disable(char **tokens,
2912         uint32_t n_tokens,
2913         char *out,
2914         size_t out_size,
2915         void *obj)
2916 {
2917         struct pipeline *p;
2918         char *pipeline_name;
2919         uint32_t thread_id;
2920         int status;
2921
2922         if (n_tokens != 5) {
2923                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2924                 return;
2925         }
2926
2927         if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
2928                 snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
2929                 return;
2930         }
2931
2932         if (strcmp(tokens[2], "pipeline") != 0) {
2933                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
2934                 return;
2935         }
2936
2937         pipeline_name = tokens[3];
2938         p = pipeline_find(obj, pipeline_name);
2939         if (!p || !p->ctl) {
2940                 snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2941                 return;
2942         }
2943
2944         if (strcmp(tokens[4], "disable") != 0) {
2945                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
2946                 return;
2947         }
2948
2949         status = thread_pipeline_disable(thread_id, obj, pipeline_name);
2950         if (status) {
2951                 snprintf(out, out_size, MSG_CMD_FAIL,
2952                         "thread pipeline disable");
2953                 return;
2954         }
2955 }
2956
2957 static void
2958 cmd_help(char **tokens,
2959          uint32_t n_tokens,
2960          char *out,
2961          size_t out_size,
2962          void *arg __rte_unused)
2963 {
2964         tokens++;
2965         n_tokens--;
2966
2967         if (n_tokens == 0) {
2968                 snprintf(out, out_size,
2969                         "Type 'help <command>' for command details.\n\n"
2970                         "List of commands:\n"
2971                         "\tmempool\n"
2972                         "\tlink\n"
2973                         "\ttap\n"
2974                         "\tpipeline create\n"
2975                         "\tpipeline port in\n"
2976                         "\tpipeline port out\n"
2977                         "\tpipeline build\n"
2978                         "\tpipeline table add\n"
2979                         "\tpipeline table delete\n"
2980                         "\tpipeline table default\n"
2981                         "\tpipeline table show\n"
2982                         "\tpipeline selector group add\n"
2983                         "\tpipeline selector group delete\n"
2984                         "\tpipeline selector group member add\n"
2985                         "\tpipeline selector group member delete\n"
2986                         "\tpipeline selector show\n"
2987                         "\tpipeline learner default\n"
2988                         "\tpipeline commit\n"
2989                         "\tpipeline abort\n"
2990                         "\tpipeline regrd\n"
2991                         "\tpipeline regwr\n"
2992                         "\tpipeline meter profile add\n"
2993                         "\tpipeline meter profile delete\n"
2994                         "\tpipeline meter reset\n"
2995                         "\tpipeline meter set\n"
2996                         "\tpipeline meter stats\n"
2997                         "\tpipeline stats\n"
2998                         "\tpipeline mirror\n"
2999                         "\tpipeline mirror session\n"
3000                         "\tthread pipeline enable\n"
3001                         "\tthread pipeline disable\n\n");
3002                 return;
3003         }
3004
3005         if (strcmp(tokens[0], "mempool") == 0) {
3006                 snprintf(out, out_size, "\n%s\n", cmd_mempool_help);
3007                 return;
3008         }
3009
3010         if (strcmp(tokens[0], "link") == 0) {
3011                 snprintf(out, out_size, "\n%s\n", cmd_link_help);
3012                 return;
3013         }
3014
3015         if (strcmp(tokens[0], "ring") == 0) {
3016                 snprintf(out, out_size, "\n%s\n", cmd_ring_help);
3017                 return;
3018         }
3019
3020         if (strcmp(tokens[0], "tap") == 0) {
3021                 snprintf(out, out_size, "\n%s\n", cmd_tap_help);
3022                 return;
3023         }
3024
3025         if ((strcmp(tokens[0], "pipeline") == 0) &&
3026                 (n_tokens == 2) && (strcmp(tokens[1], "create") == 0)) {
3027                 snprintf(out, out_size, "\n%s\n", cmd_pipeline_create_help);
3028                 return;
3029         }
3030
3031         if ((strcmp(tokens[0], "pipeline") == 0) &&
3032                 (n_tokens == 3) && (strcmp(tokens[1], "port") == 0)) {
3033                 if (strcmp(tokens[2], "in") == 0) {
3034                         snprintf(out, out_size, "\n%s\n",
3035                                 cmd_pipeline_port_in_help);
3036                         return;
3037                 }
3038
3039                 if (strcmp(tokens[2], "out") == 0) {
3040                         snprintf(out, out_size, "\n%s\n",
3041                                 cmd_pipeline_port_out_help);
3042                         return;
3043                 }
3044         }
3045
3046         if ((strcmp(tokens[0], "pipeline") == 0) &&
3047                 (n_tokens == 2) && (strcmp(tokens[1], "build") == 0)) {
3048                 snprintf(out, out_size, "\n%s\n", cmd_pipeline_build_help);
3049                 return;
3050         }
3051
3052         if ((strcmp(tokens[0], "pipeline") == 0) &&
3053                 (n_tokens == 3) &&
3054                 (strcmp(tokens[1], "table") == 0) &&
3055                 (strcmp(tokens[2], "add") == 0)) {
3056                 snprintf(out, out_size, "\n%s\n",
3057                         cmd_pipeline_table_add_help);
3058                 return;
3059         }
3060
3061         if ((strcmp(tokens[0], "pipeline") == 0) &&
3062                 (n_tokens == 3) &&
3063                 (strcmp(tokens[1], "table") == 0) &&
3064                 (strcmp(tokens[2], "delete") == 0)) {
3065                 snprintf(out, out_size, "\n%s\n",
3066                         cmd_pipeline_table_delete_help);
3067                 return;
3068         }
3069
3070         if ((strcmp(tokens[0], "pipeline") == 0) &&
3071                 (n_tokens == 3) &&
3072                 (strcmp(tokens[1], "table") == 0) &&
3073                 (strcmp(tokens[2], "default") == 0)) {
3074                 snprintf(out, out_size, "\n%s\n",
3075                         cmd_pipeline_table_default_help);
3076                 return;
3077         }
3078
3079         if ((strcmp(tokens[0], "pipeline") == 0) &&
3080                 (n_tokens == 3) &&
3081                 (strcmp(tokens[1], "table") == 0) &&
3082                 (strcmp(tokens[2], "show") == 0)) {
3083                 snprintf(out, out_size, "\n%s\n",
3084                         cmd_pipeline_table_show_help);
3085                 return;
3086         }
3087
3088         if ((strcmp(tokens[0], "pipeline") == 0) &&
3089                 (n_tokens == 4) &&
3090                 (strcmp(tokens[1], "selector") == 0) &&
3091                 (strcmp(tokens[2], "group") == 0) &&
3092                 (strcmp(tokens[3], "add") == 0)) {
3093                 snprintf(out, out_size, "\n%s\n",
3094                         cmd_pipeline_selector_group_add_help);
3095                 return;
3096         }
3097
3098         if ((strcmp(tokens[0], "pipeline") == 0) &&
3099                 (n_tokens == 4) &&
3100                 (strcmp(tokens[1], "selector") == 0) &&
3101                 (strcmp(tokens[2], "group") == 0) &&
3102                 (strcmp(tokens[3], "delete") == 0)) {
3103                 snprintf(out, out_size, "\n%s\n",
3104                         cmd_pipeline_selector_group_delete_help);
3105                 return;
3106         }
3107
3108         if ((strcmp(tokens[0], "pipeline") == 0) &&
3109                 (n_tokens == 5) &&
3110                 (strcmp(tokens[1], "selector") == 0) &&
3111                 (strcmp(tokens[2], "group") == 0) &&
3112                 (strcmp(tokens[3], "member") == 0) &&
3113                 (strcmp(tokens[4], "add") == 0)) {
3114                 snprintf(out, out_size, "\n%s\n",
3115                         cmd_pipeline_selector_group_member_add_help);
3116                 return;
3117         }
3118
3119         if ((strcmp(tokens[0], "pipeline") == 0) &&
3120                 (n_tokens == 5) &&
3121                 (strcmp(tokens[1], "selector") == 0) &&
3122                 (strcmp(tokens[2], "group") == 0) &&
3123                 (strcmp(tokens[3], "member") == 0) &&
3124                 (strcmp(tokens[4], "delete") == 0)) {
3125                 snprintf(out, out_size, "\n%s\n",
3126                         cmd_pipeline_selector_group_member_delete_help);
3127                 return;
3128         }
3129
3130         if ((strcmp(tokens[0], "pipeline") == 0) &&
3131                 (n_tokens == 3) &&
3132                 (strcmp(tokens[1], "selector") == 0) &&
3133                 (strcmp(tokens[2], "show") == 0)) {
3134                 snprintf(out, out_size, "\n%s\n",
3135                         cmd_pipeline_selector_show_help);
3136                 return;
3137         }
3138
3139         if ((strcmp(tokens[0], "pipeline") == 0) &&
3140                 (n_tokens == 3) &&
3141                 (strcmp(tokens[1], "learner") == 0) &&
3142                 (strcmp(tokens[2], "default") == 0)) {
3143                 snprintf(out, out_size, "\n%s\n",
3144                         cmd_pipeline_learner_default_help);
3145                 return;
3146         }
3147
3148         if ((strcmp(tokens[0], "pipeline") == 0) &&
3149                 (n_tokens == 2) &&
3150                 (strcmp(tokens[1], "commit") == 0)) {
3151                 snprintf(out, out_size, "\n%s\n",
3152                         cmd_pipeline_commit_help);
3153                 return;
3154         }
3155
3156         if ((strcmp(tokens[0], "pipeline") == 0) &&
3157                 (n_tokens == 2) &&
3158                 (strcmp(tokens[1], "abort") == 0)) {
3159                 snprintf(out, out_size, "\n%s\n",
3160                         cmd_pipeline_abort_help);
3161                 return;
3162         }
3163
3164         if ((strcmp(tokens[0], "pipeline") == 0) &&
3165                 (n_tokens == 2) && (strcmp(tokens[1], "regrd") == 0)) {
3166                 snprintf(out, out_size, "\n%s\n", cmd_pipeline_regrd_help);
3167                 return;
3168         }
3169
3170         if ((strcmp(tokens[0], "pipeline") == 0) &&
3171                 (n_tokens == 2) && (strcmp(tokens[1], "regwr") == 0)) {
3172                 snprintf(out, out_size, "\n%s\n", cmd_pipeline_regwr_help);
3173                 return;
3174         }
3175
3176         if (!strcmp(tokens[0], "pipeline") &&
3177                 (n_tokens == 4) && !strcmp(tokens[1], "meter")
3178                 && !strcmp(tokens[2], "profile")
3179                 && !strcmp(tokens[3], "add")) {
3180                 snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_profile_add_help);
3181                 return;
3182         }
3183
3184         if (!strcmp(tokens[0], "pipeline") &&
3185                 (n_tokens == 4) && !strcmp(tokens[1], "meter")
3186                 && !strcmp(tokens[2], "profile")
3187                 && !strcmp(tokens[3], "delete")) {
3188                 snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_profile_delete_help);
3189                 return;
3190         }
3191
3192         if (!strcmp(tokens[0], "pipeline") &&
3193                 (n_tokens == 3) && !strcmp(tokens[1], "meter")
3194                 && !strcmp(tokens[2], "reset")) {
3195                 snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_reset_help);
3196                 return;
3197         }
3198
3199         if (!strcmp(tokens[0], "pipeline") &&
3200                 (n_tokens == 3) && !strcmp(tokens[1], "meter")
3201                 && !strcmp(tokens[2], "set")) {
3202                 snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_set_help);
3203                 return;
3204         }
3205
3206         if (!strcmp(tokens[0], "pipeline") &&
3207                 (n_tokens == 3) && !strcmp(tokens[1], "meter")
3208                 && !strcmp(tokens[2], "stats")) {
3209                 snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_stats_help);
3210                 return;
3211         }
3212
3213         if ((strcmp(tokens[0], "pipeline") == 0) &&
3214                 (n_tokens == 2) && (strcmp(tokens[1], "stats") == 0)) {
3215                 snprintf(out, out_size, "\n%s\n", cmd_pipeline_stats_help);
3216                 return;
3217         }
3218
3219         if (!strcmp(tokens[0], "pipeline") &&
3220                 (n_tokens == 2) && !strcmp(tokens[1], "mirror")) {
3221                 snprintf(out, out_size, "\n%s\n", cmd_pipeline_mirror_help);
3222                 return;
3223         }
3224
3225         if (!strcmp(tokens[0], "pipeline") &&
3226                 (n_tokens == 3) && !strcmp(tokens[1], "mirror")
3227                 && !strcmp(tokens[2], "session")) {
3228                 snprintf(out, out_size, "\n%s\n", cmd_pipeline_mirror_session_help);
3229                 return;
3230         }
3231
3232         if ((n_tokens == 3) &&
3233                 (strcmp(tokens[0], "thread") == 0) &&
3234                 (strcmp(tokens[1], "pipeline") == 0)) {
3235                 if (strcmp(tokens[2], "enable") == 0) {
3236                         snprintf(out, out_size, "\n%s\n",
3237                                 cmd_thread_pipeline_enable_help);
3238                         return;
3239                 }
3240
3241                 if (strcmp(tokens[2], "disable") == 0) {
3242                         snprintf(out, out_size, "\n%s\n",
3243                                 cmd_thread_pipeline_disable_help);
3244                         return;
3245                 }
3246         }
3247
3248         snprintf(out, out_size, "Invalid command\n");
3249 }
3250
3251 void
3252 cli_process(char *in, char *out, size_t out_size, void *obj)
3253 {
3254         char *tokens[CMD_MAX_TOKENS];
3255         uint32_t n_tokens = RTE_DIM(tokens);
3256         int status;
3257
3258         if (is_comment(in))
3259                 return;
3260
3261         status = parse_tokenize_string(in, tokens, &n_tokens);
3262         if (status) {
3263                 snprintf(out, out_size, MSG_ARG_TOO_MANY, "");
3264                 return;
3265         }
3266
3267         if (n_tokens == 0)
3268                 return;
3269
3270         if (strcmp(tokens[0], "help") == 0) {
3271                 cmd_help(tokens, n_tokens, out, out_size, obj);
3272                 return;
3273         }
3274
3275         if (strcmp(tokens[0], "mempool") == 0) {
3276                 cmd_mempool(tokens, n_tokens, out, out_size, obj);
3277                 return;
3278         }
3279
3280         if (strcmp(tokens[0], "link") == 0) {
3281                 if ((n_tokens >= 2) && (strcmp(tokens[1], "show") == 0)) {
3282                         cmd_link_show(tokens, n_tokens, out, out_size, obj);
3283                         return;
3284                 }
3285
3286                 cmd_link(tokens, n_tokens, out, out_size, obj);
3287                 return;
3288         }
3289
3290         if (strcmp(tokens[0], "ring") == 0) {
3291                 cmd_ring(tokens, n_tokens, out, out_size, obj);
3292                 return;
3293         }
3294
3295         if (strcmp(tokens[0], "tap") == 0) {
3296                 cmd_tap(tokens, n_tokens, out, out_size, obj);
3297                 return;
3298         }
3299
3300         if (strcmp(tokens[0], "pipeline") == 0) {
3301                 if ((n_tokens >= 3) &&
3302                         (strcmp(tokens[2], "create") == 0)) {
3303                         cmd_pipeline_create(tokens, n_tokens, out, out_size,
3304                                 obj);
3305                         return;
3306                 }
3307
3308                 if ((n_tokens >= 4) &&
3309                         (strcmp(tokens[2], "port") == 0) &&
3310                         (strcmp(tokens[3], "in") == 0)) {
3311                         cmd_pipeline_port_in(tokens, n_tokens, out, out_size,
3312                                 obj);
3313                         return;
3314                 }
3315
3316                 if ((n_tokens >= 4) &&
3317                         (strcmp(tokens[2], "port") == 0) &&
3318                         (strcmp(tokens[3], "out") == 0)) {
3319                         cmd_pipeline_port_out(tokens, n_tokens, out, out_size,
3320                                 obj);
3321                         return;
3322                 }
3323
3324                 if ((n_tokens >= 3) &&
3325                         (strcmp(tokens[2], "build") == 0)) {
3326                         cmd_pipeline_build(tokens, n_tokens, out, out_size,
3327                                 obj);
3328                         return;
3329                 }
3330
3331                 if ((n_tokens >= 5) &&
3332                         (strcmp(tokens[2], "table") == 0) &&
3333                         (strcmp(tokens[4], "add") == 0)) {
3334                         cmd_pipeline_table_add(tokens, n_tokens, out,
3335                                 out_size, obj);
3336                         return;
3337                 }
3338
3339                 if ((n_tokens >= 5) &&
3340                         (strcmp(tokens[2], "table") == 0) &&
3341                         (strcmp(tokens[4], "delete") == 0)) {
3342                         cmd_pipeline_table_delete(tokens, n_tokens, out,
3343                                 out_size, obj);
3344                         return;
3345                 }
3346
3347                 if ((n_tokens >= 5) &&
3348                         (strcmp(tokens[2], "table") == 0) &&
3349                         (strcmp(tokens[4], "default") == 0)) {
3350                         cmd_pipeline_table_default(tokens, n_tokens, out,
3351                                 out_size, obj);
3352                         return;
3353                 }
3354
3355                 if ((n_tokens >= 5) &&
3356                         (strcmp(tokens[2], "table") == 0) &&
3357                         (strcmp(tokens[4], "show") == 0)) {
3358                         cmd_pipeline_table_show(tokens, n_tokens, out,
3359                                 out_size, obj);
3360                         return;
3361                 }
3362
3363                 if ((n_tokens >= 6) &&
3364                         (strcmp(tokens[2], "selector") == 0) &&
3365                         (strcmp(tokens[4], "group") == 0) &&
3366                         (strcmp(tokens[5], "add") == 0)) {
3367                         cmd_pipeline_selector_group_add(tokens, n_tokens, out,
3368                                 out_size, obj);
3369                         return;
3370                 }
3371
3372                 if ((n_tokens >= 6) &&
3373                         (strcmp(tokens[2], "selector") == 0) &&
3374                         (strcmp(tokens[4], "group") == 0) &&
3375                         (strcmp(tokens[5], "delete") == 0)) {
3376                         cmd_pipeline_selector_group_delete(tokens, n_tokens, out,
3377                                 out_size, obj);
3378                         return;
3379                 }
3380
3381                 if ((n_tokens >= 7) &&
3382                         (strcmp(tokens[2], "selector") == 0) &&
3383                         (strcmp(tokens[4], "group") == 0) &&
3384                         (strcmp(tokens[5], "member") == 0) &&
3385                         (strcmp(tokens[6], "add") == 0)) {
3386                         cmd_pipeline_selector_group_member_add(tokens, n_tokens, out,
3387                                 out_size, obj);
3388                         return;
3389                 }
3390
3391                 if ((n_tokens >= 7) &&
3392                         (strcmp(tokens[2], "selector") == 0) &&
3393                         (strcmp(tokens[4], "group") == 0) &&
3394                         (strcmp(tokens[5], "member") == 0) &&
3395                         (strcmp(tokens[6], "delete") == 0)) {
3396                         cmd_pipeline_selector_group_member_delete(tokens, n_tokens, out,
3397                                 out_size, obj);
3398                         return;
3399                 }
3400
3401                 if ((n_tokens >= 5) &&
3402                         (strcmp(tokens[2], "selector") == 0) &&
3403                         (strcmp(tokens[4], "show") == 0)) {
3404                         cmd_pipeline_selector_show(tokens, n_tokens, out,
3405                                 out_size, obj);
3406                         return;
3407                 }
3408
3409                 if ((n_tokens >= 5) &&
3410                         (strcmp(tokens[2], "learner") == 0) &&
3411                         (strcmp(tokens[4], "default") == 0)) {
3412                         cmd_pipeline_learner_default(tokens, n_tokens, out,
3413                                 out_size, obj);
3414                         return;
3415                 }
3416
3417                 if ((n_tokens >= 3) &&
3418                         (strcmp(tokens[2], "commit") == 0)) {
3419                         cmd_pipeline_commit(tokens, n_tokens, out,
3420                                 out_size, obj);
3421                         return;
3422                 }
3423
3424                 if ((n_tokens >= 3) &&
3425                         (strcmp(tokens[2], "abort") == 0)) {
3426                         cmd_pipeline_abort(tokens, n_tokens, out,
3427                                 out_size, obj);
3428                         return;
3429                 }
3430
3431                 if ((n_tokens >= 3) &&
3432                         (strcmp(tokens[2], "regrd") == 0)) {
3433                         cmd_pipeline_regrd(tokens, n_tokens, out, out_size, obj);
3434                         return;
3435                 }
3436
3437                 if ((n_tokens >= 3) &&
3438                         (strcmp(tokens[2], "regwr") == 0)) {
3439                         cmd_pipeline_regwr(tokens, n_tokens, out, out_size, obj);
3440                         return;
3441                 }
3442
3443                 if ((n_tokens >= 6) &&
3444                         (strcmp(tokens[2], "meter") == 0) &&
3445                         (strcmp(tokens[3], "profile") == 0) &&
3446                         (strcmp(tokens[5], "add") == 0)) {
3447                         cmd_pipeline_meter_profile_add(tokens, n_tokens, out, out_size, obj);
3448                         return;
3449                 }
3450
3451                 if ((n_tokens >= 6) &&
3452                         (strcmp(tokens[2], "meter") == 0) &&
3453                         (strcmp(tokens[3], "profile") == 0) &&
3454                         (strcmp(tokens[5], "delete") == 0)) {
3455                         cmd_pipeline_meter_profile_delete(tokens, n_tokens, out, out_size, obj);
3456                         return;
3457                 }
3458
3459                 if ((n_tokens >= 9) &&
3460                         (strcmp(tokens[2], "meter") == 0) &&
3461                         (strcmp(tokens[8], "reset") == 0)) {
3462                         cmd_pipeline_meter_reset(tokens, n_tokens, out, out_size, obj);
3463                         return;
3464                 }
3465
3466                 if ((n_tokens >= 9) &&
3467                         (strcmp(tokens[2], "meter") == 0) &&
3468                         (strcmp(tokens[8], "set") == 0)) {
3469                         cmd_pipeline_meter_set(tokens, n_tokens, out, out_size, obj);
3470                         return;
3471                 }
3472
3473                 if ((n_tokens >= 9) &&
3474                         (strcmp(tokens[2], "meter") == 0) &&
3475                         (strcmp(tokens[8], "stats") == 0)) {
3476                         cmd_pipeline_meter_stats(tokens, n_tokens, out, out_size, obj);
3477                         return;
3478                 }
3479
3480                 if ((n_tokens >= 3) &&
3481                         (strcmp(tokens[2], "stats") == 0)) {
3482                         cmd_pipeline_stats(tokens, n_tokens, out, out_size,
3483                                 obj);
3484                         return;
3485                 }
3486
3487                 if ((n_tokens >= 4) &&
3488                         (strcmp(tokens[2], "mirror") == 0) &&
3489                         (strcmp(tokens[3], "slots") == 0)) {
3490                         cmd_pipeline_mirror(tokens, n_tokens, out, out_size, obj);
3491                         return;
3492                 }
3493
3494                 if ((n_tokens >= 4) &&
3495                         (strcmp(tokens[2], "mirror") == 0) &&
3496                         (strcmp(tokens[3], "session") == 0)) {
3497                         cmd_pipeline_mirror_session(tokens, n_tokens, out, out_size, obj);
3498                         return;
3499                 }
3500         }
3501
3502         if (strcmp(tokens[0], "thread") == 0) {
3503                 if ((n_tokens >= 5) &&
3504                         (strcmp(tokens[4], "enable") == 0)) {
3505                         cmd_thread_pipeline_enable(tokens, n_tokens,
3506                                 out, out_size, obj);
3507                         return;
3508                 }
3509
3510                 if ((n_tokens >= 5) &&
3511                         (strcmp(tokens[4], "disable") == 0)) {
3512                         cmd_thread_pipeline_disable(tokens, n_tokens,
3513                                 out, out_size, obj);
3514                         return;
3515                 }
3516         }
3517
3518         snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
3519 }
3520
3521 int
3522 cli_script_process(const char *file_name,
3523         size_t msg_in_len_max,
3524         size_t msg_out_len_max,
3525         void *obj)
3526 {
3527         char *msg_in = NULL, *msg_out = NULL;
3528         FILE *f = NULL;
3529
3530         /* Check input arguments */
3531         if ((file_name == NULL) ||
3532                 (strlen(file_name) == 0) ||
3533                 (msg_in_len_max == 0) ||
3534                 (msg_out_len_max == 0))
3535                 return -EINVAL;
3536
3537         msg_in = malloc(msg_in_len_max + 1);
3538         msg_out = malloc(msg_out_len_max + 1);
3539         if ((msg_in == NULL) ||
3540                 (msg_out == NULL)) {
3541                 free(msg_out);
3542                 free(msg_in);
3543                 return -ENOMEM;
3544         }
3545
3546         /* Open input file */
3547         f = fopen(file_name, "r");
3548         if (f == NULL) {
3549                 free(msg_out);
3550                 free(msg_in);
3551                 return -EIO;
3552         }
3553
3554         /* Read file */
3555         for ( ; ; ) {
3556                 if (fgets(msg_in, msg_in_len_max + 1, f) == NULL)
3557                         break;
3558
3559                 printf("%s", msg_in);
3560                 msg_out[0] = 0;
3561
3562                 cli_process(msg_in,
3563                         msg_out,
3564                         msg_out_len_max,
3565                         obj);
3566
3567                 if (strlen(msg_out))
3568                         printf("%s", msg_out);
3569         }
3570
3571         /* Close file */
3572         fclose(f);
3573         free(msg_out);
3574         free(msg_in);
3575         return 0;
3576 }