New upstream version 18.08
[deb_dpdk.git] / drivers / net / softnic / rte_eth_softnic_cli.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2010-2018 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_cycles.h>
12
13 #include "rte_eth_softnic_internals.h"
14 #include "parser.h"
15
16 #ifndef CMD_MAX_TOKENS
17 #define CMD_MAX_TOKENS     256
18 #endif
19
20 #define MSG_OUT_OF_MEMORY   "Not enough memory.\n"
21 #define MSG_CMD_UNKNOWN     "Unknown command \"%s\".\n"
22 #define MSG_CMD_UNIMPLEM    "Command \"%s\" not implemented.\n"
23 #define MSG_ARG_NOT_ENOUGH  "Not enough arguments for command \"%s\".\n"
24 #define MSG_ARG_TOO_MANY    "Too many arguments for command \"%s\".\n"
25 #define MSG_ARG_MISMATCH    "Wrong number of arguments for command \"%s\".\n"
26 #define MSG_ARG_NOT_FOUND   "Argument \"%s\" not found.\n"
27 #define MSG_ARG_INVALID     "Invalid value for argument \"%s\".\n"
28 #define MSG_FILE_ERR        "Error in file \"%s\" at line %u.\n"
29 #define MSG_FILE_NOT_ENOUGH "Not enough rules in file \"%s\".\n"
30 #define MSG_CMD_FAIL        "Command \"%s\" failed.\n"
31
32 static int
33 is_comment(char *in)
34 {
35         if ((strlen(in) && index("!#%;", in[0])) ||
36                 (strncmp(in, "//", 2) == 0) ||
37                 (strncmp(in, "--", 2) == 0))
38                 return 1;
39
40         return 0;
41 }
42
43 /**
44  * mempool <mempool_name>
45  *  buffer <buffer_size>
46  *  pool <pool_size>
47  *  cache <cache_size>
48  */
49 static void
50 cmd_mempool(struct pmd_internals *softnic,
51         char **tokens,
52         uint32_t n_tokens,
53         char *out,
54         size_t out_size)
55 {
56         struct softnic_mempool_params p;
57         char *name;
58         struct softnic_mempool *mempool;
59
60         if (n_tokens != 8) {
61                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
62                 return;
63         }
64
65         name = tokens[1];
66
67         if (strcmp(tokens[2], "buffer") != 0) {
68                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "buffer");
69                 return;
70         }
71
72         if (softnic_parser_read_uint32(&p.buffer_size, tokens[3]) != 0) {
73                 snprintf(out, out_size, MSG_ARG_INVALID, "buffer_size");
74                 return;
75         }
76
77         if (strcmp(tokens[4], "pool") != 0) {
78                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pool");
79                 return;
80         }
81
82         if (softnic_parser_read_uint32(&p.pool_size, tokens[5]) != 0) {
83                 snprintf(out, out_size, MSG_ARG_INVALID, "pool_size");
84                 return;
85         }
86
87         if (strcmp(tokens[6], "cache") != 0) {
88                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cache");
89                 return;
90         }
91
92         if (softnic_parser_read_uint32(&p.cache_size, tokens[7]) != 0) {
93                 snprintf(out, out_size, MSG_ARG_INVALID, "cache_size");
94                 return;
95         }
96
97         mempool = softnic_mempool_create(softnic, name, &p);
98         if (mempool == NULL) {
99                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
100                 return;
101         }
102 }
103
104 /**
105  * link <link_name>
106  *    dev <device_name> | port <port_id>
107  */
108 static void
109 cmd_link(struct pmd_internals *softnic,
110         char **tokens,
111         uint32_t n_tokens,
112         char *out,
113         size_t out_size)
114 {
115         struct softnic_link_params p;
116         struct softnic_link *link;
117         char *name;
118
119         memset(&p, 0, sizeof(p));
120
121         if (n_tokens != 4) {
122                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
123                 return;
124         }
125         name = tokens[1];
126
127         if (strcmp(tokens[2], "dev") == 0) {
128                 p.dev_name = tokens[3];
129         } else if (strcmp(tokens[2], "port") == 0) {
130                 p.dev_name = NULL;
131
132                 if (softnic_parser_read_uint16(&p.port_id, tokens[3]) != 0) {
133                         snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
134                         return;
135                 }
136         } else {
137                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "dev or port");
138                 return;
139         }
140
141         link = softnic_link_create(softnic, name, &p);
142         if (link == NULL) {
143                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
144                 return;
145         }
146 }
147
148 /**
149  * swq <swq_name>
150  *  size <size>
151  */
152 static void
153 cmd_swq(struct pmd_internals *softnic,
154         char **tokens,
155         uint32_t n_tokens,
156         char *out,
157         size_t out_size)
158 {
159         struct softnic_swq_params p;
160         char *name;
161         struct softnic_swq *swq;
162
163         if (n_tokens != 4) {
164                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
165                 return;
166         }
167
168         name = tokens[1];
169
170         if (strcmp(tokens[2], "size") != 0) {
171                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
172                 return;
173         }
174
175         if (softnic_parser_read_uint32(&p.size, tokens[3]) != 0) {
176                 snprintf(out, out_size, MSG_ARG_INVALID, "size");
177                 return;
178         }
179
180         swq = softnic_swq_create(softnic, name, &p);
181         if (swq == NULL) {
182                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
183                 return;
184         }
185 }
186
187 /**
188  * tmgr shaper profile
189  *  id <profile_id>
190  *  rate <tb_rate> size <tb_size>
191  *  adj <packet_length_adjust>
192  */
193 static void
194 cmd_tmgr_shaper_profile(struct pmd_internals *softnic,
195         char **tokens,
196         uint32_t n_tokens,
197         char *out,
198         size_t out_size)
199 {
200         struct rte_tm_shaper_params sp;
201         struct rte_tm_error error;
202         uint32_t shaper_profile_id;
203         uint16_t port_id;
204         int status;
205
206         memset(&sp, 0, sizeof(struct rte_tm_shaper_params));
207
208         if (n_tokens != 11) {
209                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
210                 return;
211         }
212
213         if (strcmp(tokens[1], "shaper") != 0) {
214                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "shaper");
215                 return;
216         }
217
218         if (strcmp(tokens[2], "profile") != 0) {
219                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
220                 return;
221         }
222
223         if (strcmp(tokens[3], "id") != 0) {
224                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "id");
225                 return;
226         }
227
228         if (softnic_parser_read_uint32(&shaper_profile_id, tokens[4]) != 0) {
229                 snprintf(out, out_size, MSG_ARG_INVALID, "profile_id");
230                 return;
231         }
232
233         if (strcmp(tokens[5], "rate") != 0) {
234                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rate");
235                 return;
236         }
237
238         if (softnic_parser_read_uint64(&sp.peak.rate, tokens[6]) != 0) {
239                 snprintf(out, out_size, MSG_ARG_INVALID, "tb_rate");
240                 return;
241         }
242
243         if (strcmp(tokens[7], "size") != 0) {
244                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
245                 return;
246         }
247
248         if (softnic_parser_read_uint64(&sp.peak.size, tokens[8]) != 0) {
249                 snprintf(out, out_size, MSG_ARG_INVALID, "tb_size");
250                 return;
251         }
252
253         if (strcmp(tokens[9], "adj") != 0) {
254                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "adj");
255                 return;
256         }
257
258         if (softnic_parser_read_int32(&sp.pkt_length_adjust, tokens[10]) != 0) {
259                 snprintf(out, out_size, MSG_ARG_INVALID, "packet_length_adjust");
260                 return;
261         }
262
263         status = rte_eth_dev_get_port_by_name(softnic->params.name, &port_id);
264         if (status)
265                 return;
266
267         status = rte_tm_shaper_profile_add(port_id, shaper_profile_id, &sp, &error);
268         if (status != 0) {
269                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
270                 return;
271         }
272 }
273
274 /**
275  * tmgr shared shaper
276  *  id <shared_shaper_id>
277  *  profile <shaper_profile_id>
278  */
279 static void
280 cmd_tmgr_shared_shaper(struct pmd_internals *softnic,
281         char **tokens,
282         uint32_t n_tokens,
283         char *out,
284         size_t out_size)
285 {
286         struct rte_tm_error error;
287         uint32_t shared_shaper_id, shaper_profile_id;
288         uint16_t port_id;
289         int status;
290
291         if (n_tokens != 7) {
292                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
293                 return;
294         }
295
296         if (strcmp(tokens[1], "shared") != 0) {
297                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "shared");
298                 return;
299         }
300
301         if (strcmp(tokens[2], "shaper") != 0) {
302                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "shaper");
303                 return;
304         }
305
306         if (strcmp(tokens[3], "id") != 0) {
307                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "id");
308                 return;
309         }
310
311         if (softnic_parser_read_uint32(&shared_shaper_id, tokens[4]) != 0) {
312                 snprintf(out, out_size, MSG_ARG_INVALID, "shared_shaper_id");
313                 return;
314         }
315
316         if (strcmp(tokens[5], "profile") != 0) {
317                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
318                 return;
319         }
320
321         if (softnic_parser_read_uint32(&shaper_profile_id, tokens[6]) != 0) {
322                 snprintf(out, out_size, MSG_ARG_INVALID, "shaper_profile_id");
323                 return;
324         }
325
326         status = rte_eth_dev_get_port_by_name(softnic->params.name, &port_id);
327         if (status)
328                 return;
329
330         status = rte_tm_shared_shaper_add_update(port_id,
331                 shared_shaper_id,
332                 shaper_profile_id,
333                 &error);
334         if (status != 0) {
335                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
336                 return;
337         }
338 }
339
340 /**
341  * tmgr node
342  *   id <node_id>
343  *   parent <parent_node_id | none>
344  *   priority <priority>
345  *   weight <weight>
346  *   [shaper profile <shaper_profile_id>]
347  *   [shared shaper <shared_shaper_id>]
348  *   [nonleaf sp <n_sp_priorities>]
349  */
350 static void
351 cmd_tmgr_node(struct pmd_internals *softnic,
352         char **tokens,
353         uint32_t n_tokens,
354         char *out,
355         size_t out_size)
356 {
357         struct rte_tm_error error;
358         struct rte_tm_node_params np;
359         uint32_t node_id, parent_node_id, priority, weight, shared_shaper_id;
360         uint16_t port_id;
361         int status;
362
363         memset(&np, 0, sizeof(struct rte_tm_node_params));
364         np.shaper_profile_id = RTE_TM_SHAPER_PROFILE_ID_NONE;
365         np.nonleaf.n_sp_priorities = 1;
366
367         if (n_tokens < 10) {
368                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
369                 return;
370         }
371
372         if (strcmp(tokens[1], "node") != 0) {
373                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "node");
374                 return;
375         }
376
377         if (strcmp(tokens[2], "id") != 0) {
378                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "id");
379                 return;
380         }
381
382         if (softnic_parser_read_uint32(&node_id, tokens[3]) != 0) {
383                 snprintf(out, out_size, MSG_ARG_INVALID, "node_id");
384                 return;
385         }
386
387         if (strcmp(tokens[4], "parent") != 0) {
388                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "parent");
389                 return;
390         }
391
392         if (strcmp(tokens[5], "none") == 0)
393                 parent_node_id = RTE_TM_NODE_ID_NULL;
394         else {
395                 if (softnic_parser_read_uint32(&parent_node_id, tokens[5]) != 0) {
396                         snprintf(out, out_size, MSG_ARG_INVALID, "parent_node_id");
397                         return;
398                 }
399         }
400
401         if (strcmp(tokens[6], "priority") != 0) {
402                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "priority");
403                 return;
404         }
405
406         if (softnic_parser_read_uint32(&priority, tokens[7]) != 0) {
407                 snprintf(out, out_size, MSG_ARG_INVALID, "priority");
408                 return;
409         }
410
411         if (strcmp(tokens[8], "weight") != 0) {
412                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "weight");
413                 return;
414         }
415
416         if (softnic_parser_read_uint32(&weight, tokens[9]) != 0) {
417                 snprintf(out, out_size, MSG_ARG_INVALID, "weight");
418                 return;
419         }
420
421         tokens += 10;
422         n_tokens -= 10;
423
424         if (n_tokens >= 2 &&
425                 (strcmp(tokens[0], "shaper") == 0) &&
426                 (strcmp(tokens[1], "profile") == 0)) {
427                 if (n_tokens < 3) {
428                         snprintf(out, out_size, MSG_ARG_MISMATCH, "tmgr node");
429                         return;
430                 }
431
432                 if (strcmp(tokens[2], "none") == 0) {
433                         np.shaper_profile_id = RTE_TM_SHAPER_PROFILE_ID_NONE;
434                 } else {
435                         if (softnic_parser_read_uint32(&np.shaper_profile_id, tokens[2]) != 0) {
436                                 snprintf(out, out_size, MSG_ARG_INVALID, "shaper_profile_id");
437                                 return;
438                         }
439                 }
440
441                 tokens += 3;
442                 n_tokens -= 3;
443         } /* shaper profile */
444
445         if (n_tokens >= 2 &&
446                 (strcmp(tokens[0], "shared") == 0) &&
447                 (strcmp(tokens[1], "shaper") == 0)) {
448                 if (n_tokens < 3) {
449                         snprintf(out, out_size, MSG_ARG_MISMATCH, "tmgr node");
450                         return;
451                 }
452
453                 if (softnic_parser_read_uint32(&shared_shaper_id, tokens[2]) != 0) {
454                         snprintf(out, out_size, MSG_ARG_INVALID, "shared_shaper_id");
455                         return;
456                 }
457
458                 np.shared_shaper_id = &shared_shaper_id;
459                 np.n_shared_shapers = 1;
460
461                 tokens += 3;
462                 n_tokens -= 3;
463         } /* shared shaper */
464
465         if (n_tokens >= 2 &&
466                 (strcmp(tokens[0], "nonleaf") == 0) &&
467                 (strcmp(tokens[1], "sp") == 0)) {
468                 if (n_tokens < 3) {
469                         snprintf(out, out_size, MSG_ARG_MISMATCH, "tmgr node");
470                         return;
471                 }
472
473                 if (softnic_parser_read_uint32(&np.nonleaf.n_sp_priorities, tokens[2]) != 0) {
474                         snprintf(out, out_size, MSG_ARG_INVALID, "n_sp_priorities");
475                         return;
476                 }
477
478                 tokens += 3;
479                 n_tokens -= 3;
480         } /* nonleaf sp <n_sp_priorities> */
481
482         if (n_tokens) {
483                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
484                 return;
485         }
486
487         status = rte_eth_dev_get_port_by_name(softnic->params.name, &port_id);
488         if (status != 0)
489                 return;
490
491         status = rte_tm_node_add(port_id,
492                 node_id,
493                 parent_node_id,
494                 priority,
495                 weight,
496                 RTE_TM_NODE_LEVEL_ID_ANY,
497                 &np,
498                 &error);
499         if (status != 0) {
500                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
501                 return;
502         }
503 }
504
505 static uint32_t
506 root_node_id(uint32_t n_spp,
507         uint32_t n_pps)
508 {
509         uint32_t n_queues = n_spp * n_pps * RTE_SCHED_QUEUES_PER_PIPE;
510         uint32_t n_tc = n_spp * n_pps * RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE;
511         uint32_t n_pipes = n_spp * n_pps;
512
513         return n_queues + n_tc + n_pipes + n_spp;
514 }
515
516 static uint32_t
517 subport_node_id(uint32_t n_spp,
518         uint32_t n_pps,
519         uint32_t subport_id)
520 {
521         uint32_t n_pipes = n_spp * n_pps;
522         uint32_t n_tc = n_pipes * RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE;
523         uint32_t n_queues = n_pipes * RTE_SCHED_QUEUES_PER_PIPE;
524
525         return n_queues + n_tc + n_pipes + subport_id;
526 }
527
528 static uint32_t
529 pipe_node_id(uint32_t n_spp,
530         uint32_t n_pps,
531         uint32_t subport_id,
532         uint32_t pipe_id)
533 {
534         uint32_t n_pipes = n_spp * n_pps;
535         uint32_t n_tc = n_pipes * RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE;
536         uint32_t n_queues = n_pipes * RTE_SCHED_QUEUES_PER_PIPE;
537
538         return n_queues +
539                 n_tc +
540                 pipe_id +
541                 subport_id * n_pps;
542 }
543
544 static uint32_t
545 tc_node_id(uint32_t n_spp,
546         uint32_t n_pps,
547         uint32_t subport_id,
548         uint32_t pipe_id,
549         uint32_t tc_id)
550 {
551         uint32_t n_pipes = n_spp * n_pps;
552         uint32_t n_queues = n_pipes * RTE_SCHED_QUEUES_PER_PIPE;
553
554         return n_queues +
555                 tc_id +
556                 (pipe_id + subport_id * n_pps) * RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE;
557 }
558
559 static uint32_t
560 queue_node_id(uint32_t n_spp __rte_unused,
561         uint32_t n_pps,
562         uint32_t subport_id,
563         uint32_t pipe_id,
564         uint32_t tc_id,
565         uint32_t queue_id)
566 {
567         return queue_id +
568                 tc_id * RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE +
569                 (pipe_id + subport_id * n_pps) * RTE_SCHED_QUEUES_PER_PIPE;
570 }
571
572 struct tmgr_hierarchy_default_params {
573         uint32_t n_spp; /**< Number of subports per port. */
574         uint32_t n_pps; /**< Number of pipes per subport. */
575
576         struct {
577                 uint32_t port;
578                 uint32_t subport;
579                 uint32_t pipe;
580                 uint32_t tc[RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE];
581         } shaper_profile_id;
582
583         struct {
584                 uint32_t tc[RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE];
585                 uint32_t tc_valid[RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE];
586         } shared_shaper_id;
587
588         struct {
589                 uint32_t queue[RTE_SCHED_QUEUES_PER_PIPE];
590         } weight;
591 };
592
593 static int
594 tmgr_hierarchy_default(struct pmd_internals *softnic,
595         struct tmgr_hierarchy_default_params *params)
596 {
597         struct rte_tm_node_params root_node_params = {
598                 .shaper_profile_id = params->shaper_profile_id.port,
599                 .nonleaf = {
600                         .n_sp_priorities = 1,
601                 },
602         };
603
604         struct rte_tm_node_params subport_node_params = {
605                 .shaper_profile_id = params->shaper_profile_id.subport,
606                 .nonleaf = {
607                         .n_sp_priorities = 1,
608                 },
609         };
610
611         struct rte_tm_node_params pipe_node_params = {
612                 .shaper_profile_id = params->shaper_profile_id.pipe,
613                 .nonleaf = {
614                         .n_sp_priorities = RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE,
615                 },
616         };
617
618         struct rte_tm_node_params tc_node_params[] = {
619                 [0] = {
620                         .shaper_profile_id = params->shaper_profile_id.tc[0],
621                         .shared_shaper_id = &params->shared_shaper_id.tc[0],
622                         .n_shared_shapers =
623                                 (&params->shared_shaper_id.tc_valid[0]) ? 1 : 0,
624                         .nonleaf = {
625                                 .n_sp_priorities = 1,
626                         },
627                 },
628
629                 [1] = {
630                         .shaper_profile_id = params->shaper_profile_id.tc[1],
631                         .shared_shaper_id = &params->shared_shaper_id.tc[1],
632                         .n_shared_shapers =
633                                 (&params->shared_shaper_id.tc_valid[1]) ? 1 : 0,
634                         .nonleaf = {
635                                 .n_sp_priorities = 1,
636                         },
637                 },
638
639                 [2] = {
640                         .shaper_profile_id = params->shaper_profile_id.tc[2],
641                         .shared_shaper_id = &params->shared_shaper_id.tc[2],
642                         .n_shared_shapers =
643                                 (&params->shared_shaper_id.tc_valid[2]) ? 1 : 0,
644                         .nonleaf = {
645                                 .n_sp_priorities = 1,
646                         },
647                 },
648
649                 [3] = {
650                         .shaper_profile_id = params->shaper_profile_id.tc[3],
651                         .shared_shaper_id = &params->shared_shaper_id.tc[3],
652                         .n_shared_shapers =
653                                 (&params->shared_shaper_id.tc_valid[3]) ? 1 : 0,
654                         .nonleaf = {
655                                 .n_sp_priorities = 1,
656                         },
657                 },
658         };
659
660         struct rte_tm_node_params queue_node_params = {
661                 .shaper_profile_id = RTE_TM_SHAPER_PROFILE_ID_NONE,
662         };
663
664         struct rte_tm_error error;
665         uint32_t n_spp = params->n_spp, n_pps = params->n_pps, s;
666         int status;
667         uint16_t port_id;
668
669         status = rte_eth_dev_get_port_by_name(softnic->params.name, &port_id);
670         if (status)
671                 return -1;
672
673         /* Hierarchy level 0: Root node */
674         status = rte_tm_node_add(port_id,
675                 root_node_id(n_spp, n_pps),
676                 RTE_TM_NODE_ID_NULL,
677                 0,
678                 1,
679                 RTE_TM_NODE_LEVEL_ID_ANY,
680                 &root_node_params,
681                 &error);
682         if (status)
683                 return -1;
684
685         /* Hierarchy level 1: Subport nodes */
686         for (s = 0; s < params->n_spp; s++) {
687                 uint32_t p;
688
689                 status = rte_tm_node_add(port_id,
690                         subport_node_id(n_spp, n_pps, s),
691                         root_node_id(n_spp, n_pps),
692                         0,
693                         1,
694                         RTE_TM_NODE_LEVEL_ID_ANY,
695                         &subport_node_params,
696                         &error);
697                 if (status)
698                         return -1;
699
700                 /* Hierarchy level 2: Pipe nodes */
701                 for (p = 0; p < params->n_pps; p++) {
702                         uint32_t t;
703
704                         status = rte_tm_node_add(port_id,
705                                 pipe_node_id(n_spp, n_pps, s, p),
706                                 subport_node_id(n_spp, n_pps, s),
707                                 0,
708                                 1,
709                                 RTE_TM_NODE_LEVEL_ID_ANY,
710                                 &pipe_node_params,
711                                 &error);
712                         if (status)
713                                 return -1;
714
715                         /* Hierarchy level 3: Traffic class nodes */
716                         for (t = 0; t < RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE; t++) {
717                                 uint32_t q;
718
719                                 status = rte_tm_node_add(port_id,
720                                         tc_node_id(n_spp, n_pps, s, p, t),
721                                         pipe_node_id(n_spp, n_pps, s, p),
722                                         t,
723                                         1,
724                                         RTE_TM_NODE_LEVEL_ID_ANY,
725                                         &tc_node_params[t],
726                                         &error);
727                                 if (status)
728                                         return -1;
729
730                                 /* Hierarchy level 4: Queue nodes */
731                                 for (q = 0; q < RTE_SCHED_QUEUES_PER_TRAFFIC_CLASS; q++) {
732                                         status = rte_tm_node_add(port_id,
733                                                 queue_node_id(n_spp, n_pps, s, p, t, q),
734                                                 tc_node_id(n_spp, n_pps, s, p, t),
735                                                 0,
736                                                 params->weight.queue[q],
737                                                 RTE_TM_NODE_LEVEL_ID_ANY,
738                                                 &queue_node_params,
739                                                 &error);
740                                         if (status)
741                                                 return -1;
742                                 } /* Queue */
743                         } /* TC */
744                 } /* Pipe */
745         } /* Subport */
746
747         return 0;
748 }
749
750
751 /**
752  * tmgr hierarchy-default
753  *  spp <n_subports_per_port>
754  *  pps <n_pipes_per_subport>
755  *  shaper profile
756  *   port <profile_id>
757  *   subport <profile_id>
758  *   pipe <profile_id>
759  *   tc0 <profile_id>
760  *   tc1 <profile_id>
761  *   tc2 <profile_id>
762  *   tc3 <profile_id>
763  *  shared shaper
764  *   tc0 <id | none>
765  *   tc1 <id | none>
766  *   tc2 <id | none>
767  *   tc3 <id | none>
768  *  weight
769  *   queue  <q0> ... <q15>
770  */
771 static void
772 cmd_tmgr_hierarchy_default(struct pmd_internals *softnic,
773         char **tokens,
774         uint32_t n_tokens,
775         char *out,
776         size_t out_size)
777 {
778         struct tmgr_hierarchy_default_params p;
779         int i, status;
780
781         memset(&p, 0, sizeof(p));
782
783         if (n_tokens != 50) {
784                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
785                 return;
786         }
787
788         if (strcmp(tokens[1], "hierarchy-default") != 0) {
789                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "hierarchy-default");
790                 return;
791         }
792
793         if (strcmp(tokens[2], "spp") != 0) {
794                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "spp");
795                 return;
796         }
797
798         if (softnic_parser_read_uint32(&p.n_spp, tokens[3]) != 0) {
799                 snprintf(out, out_size, MSG_ARG_INVALID, "n_subports_per_port");
800                 return;
801         }
802
803         if (strcmp(tokens[4], "pps") != 0) {
804                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pps");
805                 return;
806         }
807
808         if (softnic_parser_read_uint32(&p.n_pps, tokens[5]) != 0) {
809                 snprintf(out, out_size, MSG_ARG_INVALID, "n_pipes_per_subport");
810                 return;
811         }
812
813         /* Shaper profile */
814
815         if (strcmp(tokens[6], "shaper") != 0) {
816                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "shaper");
817                 return;
818         }
819
820         if (strcmp(tokens[7], "profile") != 0) {
821                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
822                 return;
823         }
824
825         if (strcmp(tokens[8], "port") != 0) {
826                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
827                 return;
828         }
829
830         if (softnic_parser_read_uint32(&p.shaper_profile_id.port, tokens[9]) != 0) {
831                 snprintf(out, out_size, MSG_ARG_INVALID, "port profile id");
832                 return;
833         }
834
835         if (strcmp(tokens[10], "subport") != 0) {
836                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "subport");
837                 return;
838         }
839
840         if (softnic_parser_read_uint32(&p.shaper_profile_id.subport, tokens[11]) != 0) {
841                 snprintf(out, out_size, MSG_ARG_INVALID, "subport profile id");
842                 return;
843         }
844
845         if (strcmp(tokens[12], "pipe") != 0) {
846                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipe");
847                 return;
848         }
849
850         if (softnic_parser_read_uint32(&p.shaper_profile_id.pipe, tokens[13]) != 0) {
851                 snprintf(out, out_size, MSG_ARG_INVALID, "pipe_profile_id");
852                 return;
853         }
854
855         if (strcmp(tokens[14], "tc0") != 0) {
856                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc0");
857                 return;
858         }
859
860         if (softnic_parser_read_uint32(&p.shaper_profile_id.tc[0], tokens[15]) != 0) {
861                 snprintf(out, out_size, MSG_ARG_INVALID, "tc0 profile id");
862                 return;
863         }
864
865         if (strcmp(tokens[16], "tc1") != 0) {
866                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc1");
867                 return;
868         }
869
870         if (softnic_parser_read_uint32(&p.shaper_profile_id.tc[1], tokens[17]) != 0) {
871                 snprintf(out, out_size, MSG_ARG_INVALID, "tc1 profile id");
872                 return;
873         }
874
875         if (strcmp(tokens[18], "tc2") != 0) {
876                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc2");
877                 return;
878         }
879
880         if (softnic_parser_read_uint32(&p.shaper_profile_id.tc[2], tokens[19]) != 0) {
881                 snprintf(out, out_size, MSG_ARG_INVALID, "tc2 profile id");
882                 return;
883         }
884
885         if (strcmp(tokens[20], "tc3") != 0) {
886                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc3");
887                 return;
888         }
889
890         if (softnic_parser_read_uint32(&p.shaper_profile_id.tc[3], tokens[21]) != 0) {
891                 snprintf(out, out_size, MSG_ARG_INVALID, "tc3 profile id");
892                 return;
893         }
894
895         /* Shared shaper */
896
897         if (strcmp(tokens[22], "shared") != 0) {
898                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "shared");
899                 return;
900         }
901
902         if (strcmp(tokens[23], "shaper") != 0) {
903                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "shaper");
904                 return;
905         }
906
907         if (strcmp(tokens[24], "tc0") != 0) {
908                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc0");
909                 return;
910         }
911
912         if (strcmp(tokens[25], "none") == 0)
913                 p.shared_shaper_id.tc_valid[0] = 0;
914         else {
915                 if (softnic_parser_read_uint32(&p.shared_shaper_id.tc[0], tokens[25]) != 0) {
916                         snprintf(out, out_size, MSG_ARG_INVALID, "shared shaper tc0");
917                         return;
918                 }
919
920                 p.shared_shaper_id.tc_valid[0] = 1;
921         }
922
923         if (strcmp(tokens[26], "tc1") != 0) {
924                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc1");
925                 return;
926         }
927
928         if (strcmp(tokens[27], "none") == 0)
929                 p.shared_shaper_id.tc_valid[1] = 0;
930         else {
931                 if (softnic_parser_read_uint32(&p.shared_shaper_id.tc[1], tokens[27]) != 0) {
932                         snprintf(out, out_size, MSG_ARG_INVALID, "shared shaper tc1");
933                         return;
934                 }
935
936                 p.shared_shaper_id.tc_valid[1] = 1;
937         }
938
939         if (strcmp(tokens[28], "tc2") != 0) {
940                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc2");
941                 return;
942         }
943
944         if (strcmp(tokens[29], "none") == 0)
945                 p.shared_shaper_id.tc_valid[2] = 0;
946         else {
947                 if (softnic_parser_read_uint32(&p.shared_shaper_id.tc[2], tokens[29]) != 0) {
948                         snprintf(out, out_size, MSG_ARG_INVALID, "shared shaper tc2");
949                         return;
950                 }
951
952                 p.shared_shaper_id.tc_valid[2] = 1;
953         }
954
955         if (strcmp(tokens[30], "tc3") != 0) {
956                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc3");
957                 return;
958         }
959
960         if (strcmp(tokens[31], "none") == 0)
961                 p.shared_shaper_id.tc_valid[3] = 0;
962         else {
963                 if (softnic_parser_read_uint32(&p.shared_shaper_id.tc[3], tokens[31]) != 0) {
964                         snprintf(out, out_size, MSG_ARG_INVALID, "shared shaper tc3");
965                         return;
966                 }
967
968                 p.shared_shaper_id.tc_valid[3] = 1;
969         }
970
971         /* Weight */
972
973         if (strcmp(tokens[32], "weight") != 0) {
974                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "weight");
975                 return;
976         }
977
978         if (strcmp(tokens[33], "queue") != 0) {
979                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "queue");
980                 return;
981         }
982
983         for (i = 0; i < 16; i++) {
984                 if (softnic_parser_read_uint32(&p.weight.queue[i], tokens[34 + i]) != 0) {
985                         snprintf(out, out_size, MSG_ARG_INVALID, "weight queue");
986                         return;
987                 }
988         }
989
990         status = tmgr_hierarchy_default(softnic, &p);
991         if (status != 0) {
992                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
993                 return;
994         }
995 }
996
997 /**
998  * tmgr hierarchy commit
999  */
1000 static void
1001 cmd_tmgr_hierarchy_commit(struct pmd_internals *softnic,
1002         char **tokens,
1003         uint32_t n_tokens,
1004         char *out,
1005         size_t out_size)
1006 {
1007         struct rte_tm_error error;
1008         uint16_t port_id;
1009         int status;
1010
1011         if (n_tokens != 3) {
1012                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1013                 return;
1014         }
1015
1016         if (strcmp(tokens[1], "hierarchy") != 0) {
1017                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "hierarchy");
1018                 return;
1019         }
1020
1021         if (strcmp(tokens[2], "commit") != 0) {
1022                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "commit");
1023                 return;
1024         }
1025
1026         status = rte_eth_dev_get_port_by_name(softnic->params.name, &port_id);
1027         if (status != 0)
1028                 return;
1029
1030         status = rte_tm_hierarchy_commit(port_id, 1, &error);
1031         if (status) {
1032                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
1033                 return;
1034         }
1035 }
1036
1037 /**
1038  * tmgr <tmgr_name>
1039  */
1040 static void
1041 cmd_tmgr(struct pmd_internals *softnic,
1042         char **tokens,
1043         uint32_t n_tokens,
1044         char *out,
1045         size_t out_size)
1046 {
1047         char *name;
1048         struct softnic_tmgr_port *tmgr_port;
1049
1050         if (n_tokens != 2) {
1051                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1052                 return;
1053         }
1054
1055         name = tokens[1];
1056
1057         tmgr_port = softnic_tmgr_port_create(softnic, name);
1058         if (tmgr_port == NULL) {
1059                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
1060                 return;
1061         }
1062 }
1063
1064 /**
1065  * tap <tap_name>
1066  */
1067 static void
1068 cmd_tap(struct pmd_internals *softnic,
1069         char **tokens,
1070         uint32_t n_tokens,
1071         char *out,
1072         size_t out_size)
1073 {
1074         char *name;
1075         struct softnic_tap *tap;
1076
1077         if (n_tokens != 2) {
1078                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1079                 return;
1080         }
1081
1082         name = tokens[1];
1083
1084         tap = softnic_tap_create(softnic, name);
1085         if (tap == NULL) {
1086                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
1087                 return;
1088         }
1089 }
1090
1091 /**
1092  * port in action profile <profile_name>
1093  *  [filter match | mismatch offset <key_offset> mask <key_mask> key <key_value> port <port_id>]
1094  *  [balance offset <key_offset> mask <key_mask> port <port_id0> ... <port_id15>]
1095  */
1096 static void
1097 cmd_port_in_action_profile(struct pmd_internals *softnic,
1098         char **tokens,
1099         uint32_t n_tokens,
1100         char *out,
1101         size_t out_size)
1102 {
1103         struct softnic_port_in_action_profile_params p;
1104         struct softnic_port_in_action_profile *ap;
1105         char *name;
1106         uint32_t t0;
1107
1108         memset(&p, 0, sizeof(p));
1109
1110         if (n_tokens < 5) {
1111                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1112                 return;
1113         }
1114
1115         if (strcmp(tokens[1], "in") != 0) {
1116                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
1117                 return;
1118         }
1119
1120         if (strcmp(tokens[2], "action") != 0) {
1121                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "action");
1122                 return;
1123         }
1124
1125         if (strcmp(tokens[3], "profile") != 0) {
1126                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
1127                 return;
1128         }
1129
1130         name = tokens[4];
1131
1132         t0 = 5;
1133
1134         if (t0 < n_tokens &&
1135                 (strcmp(tokens[t0], "filter") == 0)) {
1136                 uint32_t size;
1137
1138                 if (n_tokens < t0 + 10) {
1139                         snprintf(out, out_size, MSG_ARG_MISMATCH, "port in action profile filter");
1140                         return;
1141                 }
1142
1143                 if (strcmp(tokens[t0 + 1], "match") == 0) {
1144                         p.fltr.filter_on_match = 1;
1145                 } else if (strcmp(tokens[t0 + 1], "mismatch") == 0) {
1146                         p.fltr.filter_on_match = 0;
1147                 } else {
1148                         snprintf(out, out_size, MSG_ARG_INVALID, "match or mismatch");
1149                         return;
1150                 }
1151
1152                 if (strcmp(tokens[t0 + 2], "offset") != 0) {
1153                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
1154                         return;
1155                 }
1156
1157                 if (softnic_parser_read_uint32(&p.fltr.key_offset,
1158                         tokens[t0 + 3]) != 0) {
1159                         snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
1160                         return;
1161                 }
1162
1163                 if (strcmp(tokens[t0 + 4], "mask") != 0) {
1164                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mask");
1165                         return;
1166                 }
1167
1168                 size = RTE_PORT_IN_ACTION_FLTR_KEY_SIZE;
1169                 if ((softnic_parse_hex_string(tokens[t0 + 5],
1170                         p.fltr.key_mask, &size) != 0) ||
1171                         size != RTE_PORT_IN_ACTION_FLTR_KEY_SIZE) {
1172                         snprintf(out, out_size, MSG_ARG_INVALID, "key_mask");
1173                         return;
1174                 }
1175
1176                 if (strcmp(tokens[t0 + 6], "key") != 0) {
1177                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "key");
1178                         return;
1179                 }
1180
1181                 size = RTE_PORT_IN_ACTION_FLTR_KEY_SIZE;
1182                 if ((softnic_parse_hex_string(tokens[t0 + 7],
1183                         p.fltr.key, &size) != 0) ||
1184                         size != RTE_PORT_IN_ACTION_FLTR_KEY_SIZE) {
1185                         snprintf(out, out_size, MSG_ARG_INVALID, "key_value");
1186                         return;
1187                 }
1188
1189                 if (strcmp(tokens[t0 + 8], "port") != 0) {
1190                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
1191                         return;
1192                 }
1193
1194                 if (softnic_parser_read_uint32(&p.fltr.port_id,
1195                         tokens[t0 + 9]) != 0) {
1196                         snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
1197                         return;
1198                 }
1199
1200                 p.action_mask |= 1LLU << RTE_PORT_IN_ACTION_FLTR;
1201                 t0 += 10;
1202         } /* filter */
1203
1204         if (t0 < n_tokens &&
1205                 (strcmp(tokens[t0], "balance") == 0)) {
1206                 uint32_t i;
1207
1208                 if (n_tokens < t0 + 22) {
1209                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1210                                 "port in action profile balance");
1211                         return;
1212                 }
1213
1214                 if (strcmp(tokens[t0 + 1], "offset") != 0) {
1215                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
1216                         return;
1217                 }
1218
1219                 if (softnic_parser_read_uint32(&p.lb.key_offset,
1220                         tokens[t0 + 2]) != 0) {
1221                         snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
1222                         return;
1223                 }
1224
1225                 if (strcmp(tokens[t0 + 3], "mask") != 0) {
1226                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mask");
1227                         return;
1228                 }
1229
1230                 p.lb.key_size = RTE_PORT_IN_ACTION_LB_KEY_SIZE_MAX;
1231                 if (softnic_parse_hex_string(tokens[t0 + 4],
1232                         p.lb.key_mask, &p.lb.key_size) != 0) {
1233                         snprintf(out, out_size, MSG_ARG_INVALID, "key_mask");
1234                         return;
1235                 }
1236
1237                 if (strcmp(tokens[t0 + 5], "port") != 0) {
1238                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
1239                         return;
1240                 }
1241
1242                 for (i = 0; i < 16; i++)
1243                         if (softnic_parser_read_uint32(&p.lb.port_id[i],
1244                         tokens[t0 + 6 + i]) != 0) {
1245                                 snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
1246                                 return;
1247                         }
1248
1249                 p.action_mask |= 1LLU << RTE_PORT_IN_ACTION_LB;
1250                 t0 += 22;
1251         } /* balance */
1252
1253         if (t0 < n_tokens) {
1254                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1255                 return;
1256         }
1257
1258         ap = softnic_port_in_action_profile_create(softnic, name, &p);
1259         if (ap == NULL) {
1260                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
1261                 return;
1262         }
1263 }
1264
1265 /**
1266  * table action profile <profile_name>
1267  *  ipv4 | ipv6
1268  *  offset <ip_offset>
1269  *  fwd
1270  *  [balance offset <key_offset> mask <key_mask> outoffset <out_offset>]
1271  *  [meter srtcm | trtcm
1272  *      tc <n_tc>
1273  *      stats none | pkts | bytes | both]
1274  *  [tm spp <n_subports_per_port> pps <n_pipes_per_subport>]
1275  *  [encap ether | vlan | qinq | mpls | pppoe]
1276  *  [nat src | dst
1277  *      proto udp | tcp]
1278  *  [ttl drop | fwd
1279  *      stats none | pkts]
1280  *  [stats pkts | bytes | both]
1281  *  [time]
1282  */
1283 static void
1284 cmd_table_action_profile(struct pmd_internals *softnic,
1285         char **tokens,
1286         uint32_t n_tokens,
1287         char *out,
1288         size_t out_size)
1289 {
1290         struct softnic_table_action_profile_params p;
1291         struct softnic_table_action_profile *ap;
1292         char *name;
1293         uint32_t t0;
1294
1295         memset(&p, 0, sizeof(p));
1296
1297         if (n_tokens < 8) {
1298                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1299                 return;
1300         }
1301
1302         if (strcmp(tokens[1], "action") != 0) {
1303                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "action");
1304                 return;
1305         }
1306
1307         if (strcmp(tokens[2], "profile") != 0) {
1308                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
1309                 return;
1310         }
1311
1312         name = tokens[3];
1313
1314         if (strcmp(tokens[4], "ipv4") == 0) {
1315                 p.common.ip_version = 1;
1316         } else if (strcmp(tokens[4], "ipv6") == 0) {
1317                 p.common.ip_version = 0;
1318         } else {
1319                 snprintf(out, out_size, MSG_ARG_INVALID, "ipv4 or ipv6");
1320                 return;
1321         }
1322
1323         if (strcmp(tokens[5], "offset") != 0) {
1324                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
1325                 return;
1326         }
1327
1328         if (softnic_parser_read_uint32(&p.common.ip_offset,
1329                 tokens[6]) != 0) {
1330                 snprintf(out, out_size, MSG_ARG_INVALID, "ip_offset");
1331                 return;
1332         }
1333
1334         if (strcmp(tokens[7], "fwd") != 0) {
1335                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "fwd");
1336                 return;
1337         }
1338
1339         p.action_mask |= 1LLU << RTE_TABLE_ACTION_FWD;
1340
1341         t0 = 8;
1342         if (t0 < n_tokens &&
1343                 (strcmp(tokens[t0], "balance") == 0)) {
1344                 if (n_tokens < t0 + 7) {
1345                         snprintf(out, out_size, MSG_ARG_MISMATCH, "table action profile balance");
1346                         return;
1347                 }
1348
1349                 if (strcmp(tokens[t0 + 1], "offset") != 0) {
1350                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
1351                         return;
1352                 }
1353
1354                 if (softnic_parser_read_uint32(&p.lb.key_offset,
1355                         tokens[t0 + 2]) != 0) {
1356                         snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
1357                         return;
1358                 }
1359
1360                 if (strcmp(tokens[t0 + 3], "mask") != 0) {
1361                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mask");
1362                         return;
1363                 }
1364
1365                 p.lb.key_size = RTE_PORT_IN_ACTION_LB_KEY_SIZE_MAX;
1366                 if (softnic_parse_hex_string(tokens[t0 + 4],
1367                         p.lb.key_mask, &p.lb.key_size) != 0) {
1368                         snprintf(out, out_size, MSG_ARG_INVALID, "key_mask");
1369                         return;
1370                 }
1371
1372                 if (strcmp(tokens[t0 + 5], "outoffset") != 0) {
1373                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "outoffset");
1374                         return;
1375                 }
1376
1377                 if (softnic_parser_read_uint32(&p.lb.out_offset,
1378                         tokens[t0 + 6]) != 0) {
1379                         snprintf(out, out_size, MSG_ARG_INVALID, "out_offset");
1380                         return;
1381                 }
1382
1383                 p.action_mask |= 1LLU << RTE_TABLE_ACTION_LB;
1384                 t0 += 7;
1385         } /* balance */
1386
1387         if (t0 < n_tokens &&
1388                 (strcmp(tokens[t0], "meter") == 0)) {
1389                 if (n_tokens < t0 + 6) {
1390                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1391                                 "table action profile meter");
1392                         return;
1393                 }
1394
1395                 if (strcmp(tokens[t0 + 1], "srtcm") == 0) {
1396                         p.mtr.alg = RTE_TABLE_ACTION_METER_SRTCM;
1397                 } else if (strcmp(tokens[t0 + 1], "trtcm") == 0) {
1398                         p.mtr.alg = RTE_TABLE_ACTION_METER_TRTCM;
1399                 } else {
1400                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1401                                 "srtcm or trtcm");
1402                         return;
1403                 }
1404
1405                 if (strcmp(tokens[t0 + 2], "tc") != 0) {
1406                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc");
1407                         return;
1408                 }
1409
1410                 if (softnic_parser_read_uint32(&p.mtr.n_tc,
1411                         tokens[t0 + 3]) != 0) {
1412                         snprintf(out, out_size, MSG_ARG_INVALID, "n_tc");
1413                         return;
1414                 }
1415
1416                 if (strcmp(tokens[t0 + 4], "stats") != 0) {
1417                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
1418                         return;
1419                 }
1420
1421                 if (strcmp(tokens[t0 + 5], "none") == 0) {
1422                         p.mtr.n_packets_enabled = 0;
1423                         p.mtr.n_bytes_enabled = 0;
1424                 } else if (strcmp(tokens[t0 + 5], "pkts") == 0) {
1425                         p.mtr.n_packets_enabled = 1;
1426                         p.mtr.n_bytes_enabled = 0;
1427                 } else if (strcmp(tokens[t0 + 5], "bytes") == 0) {
1428                         p.mtr.n_packets_enabled = 0;
1429                         p.mtr.n_bytes_enabled = 1;
1430                 } else if (strcmp(tokens[t0 + 5], "both") == 0) {
1431                         p.mtr.n_packets_enabled = 1;
1432                         p.mtr.n_bytes_enabled = 1;
1433                 } else {
1434                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1435                                 "none or pkts or bytes or both");
1436                         return;
1437                 }
1438
1439                 p.action_mask |= 1LLU << RTE_TABLE_ACTION_MTR;
1440                 t0 += 6;
1441         } /* meter */
1442
1443         if (t0 < n_tokens &&
1444                 (strcmp(tokens[t0], "tm") == 0)) {
1445                 if (n_tokens < t0 + 5) {
1446                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1447                                 "table action profile tm");
1448                         return;
1449                 }
1450
1451                 if (strcmp(tokens[t0 + 1], "spp") != 0) {
1452                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "spp");
1453                         return;
1454                 }
1455
1456                 if (softnic_parser_read_uint32(&p.tm.n_subports_per_port,
1457                         tokens[t0 + 2]) != 0) {
1458                         snprintf(out, out_size, MSG_ARG_INVALID,
1459                                 "n_subports_per_port");
1460                         return;
1461                 }
1462
1463                 if (strcmp(tokens[t0 + 3], "pps") != 0) {
1464                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pps");
1465                         return;
1466                 }
1467
1468                 if (softnic_parser_read_uint32(&p.tm.n_pipes_per_subport,
1469                         tokens[t0 + 4]) != 0) {
1470                         snprintf(out, out_size, MSG_ARG_INVALID,
1471                                 "n_pipes_per_subport");
1472                         return;
1473                 }
1474
1475                 p.action_mask |= 1LLU << RTE_TABLE_ACTION_TM;
1476                 t0 += 5;
1477         } /* tm */
1478
1479         if (t0 < n_tokens &&
1480                 (strcmp(tokens[t0], "encap") == 0)) {
1481                 if (n_tokens < t0 + 2) {
1482                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1483                                 "action profile encap");
1484                         return;
1485                 }
1486
1487                 if (strcmp(tokens[t0 + 1], "ether") == 0) {
1488                         p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_ETHER;
1489                 } else if (strcmp(tokens[t0 + 1], "vlan") == 0) {
1490                         p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_VLAN;
1491                 } else if (strcmp(tokens[t0 + 1], "qinq") == 0) {
1492                         p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_QINQ;
1493                 } else if (strcmp(tokens[t0 + 1], "mpls") == 0) {
1494                         p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_MPLS;
1495                 } else if (strcmp(tokens[t0 + 1], "pppoe") == 0) {
1496                         p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_PPPOE;
1497                 } else {
1498                         snprintf(out, out_size, MSG_ARG_MISMATCH, "encap");
1499                         return;
1500                 }
1501
1502                 p.action_mask |= 1LLU << RTE_TABLE_ACTION_ENCAP;
1503                 t0 += 2;
1504         } /* encap */
1505
1506         if (t0 < n_tokens &&
1507                 (strcmp(tokens[t0], "nat") == 0)) {
1508                 if (n_tokens < t0 + 4) {
1509                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1510                                 "table action profile nat");
1511                         return;
1512                 }
1513
1514                 if (strcmp(tokens[t0 + 1], "src") == 0) {
1515                         p.nat.source_nat = 1;
1516                 } else if (strcmp(tokens[t0 + 1], "dst") == 0) {
1517                         p.nat.source_nat = 0;
1518                 } else {
1519                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1520                                 "src or dst");
1521                         return;
1522                 }
1523
1524                 if (strcmp(tokens[t0 + 2], "proto") != 0) {
1525                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "proto");
1526                         return;
1527                 }
1528
1529                 if (strcmp(tokens[t0 + 3], "tcp") == 0) {
1530                         p.nat.proto = 0x06;
1531                 } else if (strcmp(tokens[t0 + 3], "udp") == 0) {
1532                         p.nat.proto = 0x11;
1533                 } else {
1534                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1535                                 "tcp or udp");
1536                         return;
1537                 }
1538
1539                 p.action_mask |= 1LLU << RTE_TABLE_ACTION_NAT;
1540                 t0 += 4;
1541         } /* nat */
1542
1543         if (t0 < n_tokens &&
1544                 (strcmp(tokens[t0], "ttl") == 0)) {
1545                 if (n_tokens < t0 + 4) {
1546                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1547                                 "table action profile ttl");
1548                         return;
1549                 }
1550
1551                 if (strcmp(tokens[t0 + 1], "drop") == 0) {
1552                         p.ttl.drop = 1;
1553                 } else if (strcmp(tokens[t0 + 1], "fwd") == 0) {
1554                         p.ttl.drop = 0;
1555                 } else {
1556                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1557                                 "drop or fwd");
1558                         return;
1559                 }
1560
1561                 if (strcmp(tokens[t0 + 2], "stats") != 0) {
1562                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
1563                         return;
1564                 }
1565
1566                 if (strcmp(tokens[t0 + 3], "none") == 0) {
1567                         p.ttl.n_packets_enabled = 0;
1568                 } else if (strcmp(tokens[t0 + 3], "pkts") == 0) {
1569                         p.ttl.n_packets_enabled = 1;
1570                 } else {
1571                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1572                                 "none or pkts");
1573                         return;
1574                 }
1575
1576                 p.action_mask |= 1LLU << RTE_TABLE_ACTION_TTL;
1577                 t0 += 4;
1578         } /* ttl */
1579
1580         if (t0 < n_tokens &&
1581                 (strcmp(tokens[t0], "stats") == 0)) {
1582                 if (n_tokens < t0 + 2) {
1583                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1584                                 "table action profile stats");
1585                         return;
1586                 }
1587
1588                 if (strcmp(tokens[t0 + 1], "pkts") == 0) {
1589                         p.stats.n_packets_enabled = 1;
1590                         p.stats.n_bytes_enabled = 0;
1591                 } else if (strcmp(tokens[t0 + 1], "bytes") == 0) {
1592                         p.stats.n_packets_enabled = 0;
1593                         p.stats.n_bytes_enabled = 1;
1594                 } else if (strcmp(tokens[t0 + 1], "both") == 0) {
1595                         p.stats.n_packets_enabled = 1;
1596                         p.stats.n_bytes_enabled = 1;
1597                 } else {
1598                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1599                                 "pkts or bytes or both");
1600                         return;
1601                 }
1602
1603                 p.action_mask |= 1LLU << RTE_TABLE_ACTION_STATS;
1604                 t0 += 2;
1605         } /* stats */
1606
1607         if (t0 < n_tokens &&
1608                 (strcmp(tokens[t0], "time") == 0)) {
1609                 p.action_mask |= 1LLU << RTE_TABLE_ACTION_TIME;
1610                 t0 += 1;
1611         } /* time */
1612
1613         if (t0 < n_tokens) {
1614                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1615                 return;
1616         }
1617
1618         ap = softnic_table_action_profile_create(softnic, name, &p);
1619         if (ap == NULL) {
1620                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
1621                 return;
1622         }
1623 }
1624
1625 /**
1626  * pipeline <pipeline_name>
1627  *  period <timer_period_ms>
1628  *  offset_port_id <offset_port_id>
1629  */
1630 static void
1631 cmd_pipeline(struct pmd_internals *softnic,
1632         char **tokens,
1633         uint32_t n_tokens,
1634         char *out,
1635         size_t out_size)
1636 {
1637         struct pipeline_params p;
1638         char *name;
1639         struct pipeline *pipeline;
1640
1641         if (n_tokens != 6) {
1642                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1643                 return;
1644         }
1645
1646         name = tokens[1];
1647
1648         if (strcmp(tokens[2], "period") != 0) {
1649                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "period");
1650                 return;
1651         }
1652
1653         if (softnic_parser_read_uint32(&p.timer_period_ms,
1654                 tokens[3]) != 0) {
1655                 snprintf(out, out_size, MSG_ARG_INVALID, "timer_period_ms");
1656                 return;
1657         }
1658
1659         if (strcmp(tokens[4], "offset_port_id") != 0) {
1660                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset_port_id");
1661                 return;
1662         }
1663
1664         if (softnic_parser_read_uint32(&p.offset_port_id,
1665                 tokens[5]) != 0) {
1666                 snprintf(out, out_size, MSG_ARG_INVALID, "offset_port_id");
1667                 return;
1668         }
1669
1670         pipeline = softnic_pipeline_create(softnic, name, &p);
1671         if (pipeline == NULL) {
1672                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
1673                 return;
1674         }
1675 }
1676
1677 /**
1678  * pipeline <pipeline_name> port in
1679  *  bsz <burst_size>
1680  *  link <link_name> rxq <queue_id>
1681  *  | swq <swq_name>
1682  *  | tmgr <tmgr_name>
1683  *  | tap <tap_name> mempool <mempool_name> mtu <mtu>
1684  *  | source mempool <mempool_name> file <file_name> bpp <n_bytes_per_pkt>
1685  *  [action <port_in_action_profile_name>]
1686  *  [disabled]
1687  */
1688 static void
1689 cmd_pipeline_port_in(struct pmd_internals *softnic,
1690         char **tokens,
1691         uint32_t n_tokens,
1692         char *out,
1693         size_t out_size)
1694 {
1695         struct softnic_port_in_params p;
1696         char *pipeline_name;
1697         uint32_t t0;
1698         int enabled, status;
1699
1700         if (n_tokens < 7) {
1701                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1702                 return;
1703         }
1704
1705         pipeline_name = tokens[1];
1706
1707         if (strcmp(tokens[2], "port") != 0) {
1708                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
1709                 return;
1710         }
1711
1712         if (strcmp(tokens[3], "in") != 0) {
1713                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
1714                 return;
1715         }
1716
1717         if (strcmp(tokens[4], "bsz") != 0) {
1718                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
1719                 return;
1720         }
1721
1722         if (softnic_parser_read_uint32(&p.burst_size, tokens[5]) != 0) {
1723                 snprintf(out, out_size, MSG_ARG_INVALID, "burst_size");
1724                 return;
1725         }
1726
1727         t0 = 6;
1728
1729         if (strcmp(tokens[t0], "link") == 0) {
1730                 if (n_tokens < t0 + 4) {
1731                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1732                                 "pipeline port in link");
1733                         return;
1734                 }
1735
1736                 p.type = PORT_IN_RXQ;
1737
1738                 p.dev_name = tokens[t0 + 1];
1739
1740                 if (strcmp(tokens[t0 + 2], "rxq") != 0) {
1741                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq");
1742                         return;
1743                 }
1744
1745                 if (softnic_parser_read_uint16(&p.rxq.queue_id,
1746                         tokens[t0 + 3]) != 0) {
1747                         snprintf(out, out_size, MSG_ARG_INVALID,
1748                                 "queue_id");
1749                         return;
1750                 }
1751                 t0 += 4;
1752         } else if (strcmp(tokens[t0], "swq") == 0) {
1753                 if (n_tokens < t0 + 2) {
1754                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1755                                 "pipeline port in swq");
1756                         return;
1757                 }
1758
1759                 p.type = PORT_IN_SWQ;
1760
1761                 p.dev_name = tokens[t0 + 1];
1762
1763                 t0 += 2;
1764         } else if (strcmp(tokens[t0], "tmgr") == 0) {
1765                 if (n_tokens < t0 + 2) {
1766                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1767                                 "pipeline port in tmgr");
1768                         return;
1769                 }
1770
1771                 p.type = PORT_IN_TMGR;
1772
1773                 p.dev_name = tokens[t0 + 1];
1774
1775                 t0 += 2;
1776         } else if (strcmp(tokens[t0], "tap") == 0) {
1777                 if (n_tokens < t0 + 6) {
1778                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1779                                 "pipeline port in tap");
1780                         return;
1781                 }
1782
1783                 p.type = PORT_IN_TAP;
1784
1785                 p.dev_name = tokens[t0 + 1];
1786
1787                 if (strcmp(tokens[t0 + 2], "mempool") != 0) {
1788                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1789                                 "mempool");
1790                         return;
1791                 }
1792
1793                 p.tap.mempool_name = tokens[t0 + 3];
1794
1795                 if (strcmp(tokens[t0 + 4], "mtu") != 0) {
1796                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1797                                 "mtu");
1798                         return;
1799                 }
1800
1801                 if (softnic_parser_read_uint32(&p.tap.mtu,
1802                         tokens[t0 + 5]) != 0) {
1803                         snprintf(out, out_size, MSG_ARG_INVALID, "mtu");
1804                         return;
1805                 }
1806
1807                 t0 += 6;
1808         } else if (strcmp(tokens[t0], "source") == 0) {
1809                 if (n_tokens < t0 + 6) {
1810                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1811                                 "pipeline port in source");
1812                         return;
1813                 }
1814
1815                 p.type = PORT_IN_SOURCE;
1816
1817                 p.dev_name = NULL;
1818
1819                 if (strcmp(tokens[t0 + 1], "mempool") != 0) {
1820                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1821                                 "mempool");
1822                         return;
1823                 }
1824
1825                 p.source.mempool_name = tokens[t0 + 2];
1826
1827                 if (strcmp(tokens[t0 + 3], "file") != 0) {
1828                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1829                                 "file");
1830                         return;
1831                 }
1832
1833                 p.source.file_name = tokens[t0 + 4];
1834
1835                 if (strcmp(tokens[t0 + 5], "bpp") != 0) {
1836                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1837                                 "bpp");
1838                         return;
1839                 }
1840
1841                 if (softnic_parser_read_uint32(&p.source.n_bytes_per_pkt,
1842                         tokens[t0 + 6]) != 0) {
1843                         snprintf(out, out_size, MSG_ARG_INVALID,
1844                                 "n_bytes_per_pkt");
1845                         return;
1846                 }
1847
1848                 t0 += 7;
1849         } else {
1850                 snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
1851                 return;
1852         }
1853
1854         p.action_profile_name = NULL;
1855         if (n_tokens > t0 &&
1856                 (strcmp(tokens[t0], "action") == 0)) {
1857                 if (n_tokens < t0 + 2) {
1858                         snprintf(out, out_size, MSG_ARG_MISMATCH, "action");
1859                         return;
1860                 }
1861
1862                 p.action_profile_name = tokens[t0 + 1];
1863
1864                 t0 += 2;
1865         }
1866
1867         enabled = 1;
1868         if (n_tokens > t0 &&
1869                 (strcmp(tokens[t0], "disabled") == 0)) {
1870                 enabled = 0;
1871
1872                 t0 += 1;
1873         }
1874
1875         if (n_tokens != t0) {
1876                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1877                 return;
1878         }
1879
1880         status = softnic_pipeline_port_in_create(softnic,
1881                 pipeline_name,
1882                 &p,
1883                 enabled);
1884         if (status) {
1885                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
1886                 return;
1887         }
1888 }
1889
1890 /**
1891  * pipeline <pipeline_name> port out
1892  *  bsz <burst_size>
1893  *  link <link_name> txq <txq_id>
1894  *  | swq <swq_name>
1895  *  | tmgr <tmgr_name>
1896  *  | tap <tap_name>
1897  *  | sink [file <file_name> pkts <max_n_pkts>]
1898  */
1899 static void
1900 cmd_pipeline_port_out(struct pmd_internals *softnic,
1901         char **tokens,
1902         uint32_t n_tokens,
1903         char *out,
1904         size_t out_size)
1905 {
1906         struct softnic_port_out_params p;
1907         char *pipeline_name;
1908         int status;
1909
1910         memset(&p, 0, sizeof(p));
1911
1912         if (n_tokens < 7) {
1913                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1914                 return;
1915         }
1916
1917         pipeline_name = tokens[1];
1918
1919         if (strcmp(tokens[2], "port") != 0) {
1920                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
1921                 return;
1922         }
1923
1924         if (strcmp(tokens[3], "out") != 0) {
1925                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "out");
1926                 return;
1927         }
1928
1929         if (strcmp(tokens[4], "bsz") != 0) {
1930                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
1931                 return;
1932         }
1933
1934         if (softnic_parser_read_uint32(&p.burst_size, tokens[5]) != 0) {
1935                 snprintf(out, out_size, MSG_ARG_INVALID, "burst_size");
1936                 return;
1937         }
1938
1939         if (strcmp(tokens[6], "link") == 0) {
1940                 if (n_tokens != 10) {
1941                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1942                                 "pipeline port out link");
1943                         return;
1944                 }
1945
1946                 p.type = PORT_OUT_TXQ;
1947
1948                 p.dev_name = tokens[7];
1949
1950                 if (strcmp(tokens[8], "txq") != 0) {
1951                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "txq");
1952                         return;
1953                 }
1954
1955                 if (softnic_parser_read_uint16(&p.txq.queue_id,
1956                         tokens[9]) != 0) {
1957                         snprintf(out, out_size, MSG_ARG_INVALID, "queue_id");
1958                         return;
1959                 }
1960         } else if (strcmp(tokens[6], "swq") == 0) {
1961                 if (n_tokens != 8) {
1962                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1963                                 "pipeline port out swq");
1964                         return;
1965                 }
1966
1967                 p.type = PORT_OUT_SWQ;
1968
1969                 p.dev_name = tokens[7];
1970         } else if (strcmp(tokens[6], "tmgr") == 0) {
1971                 if (n_tokens != 8) {
1972                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1973                                 "pipeline port out tmgr");
1974                         return;
1975                 }
1976
1977                 p.type = PORT_OUT_TMGR;
1978
1979                 p.dev_name = tokens[7];
1980         } else if (strcmp(tokens[6], "tap") == 0) {
1981                 if (n_tokens != 8) {
1982                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1983                                 "pipeline port out tap");
1984                         return;
1985                 }
1986
1987                 p.type = PORT_OUT_TAP;
1988
1989                 p.dev_name = tokens[7];
1990         } else if (strcmp(tokens[6], "sink") == 0) {
1991                 if ((n_tokens != 7) && (n_tokens != 11)) {
1992                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1993                                 "pipeline port out sink");
1994                         return;
1995                 }
1996
1997                 p.type = PORT_OUT_SINK;
1998
1999                 p.dev_name = NULL;
2000
2001                 if (n_tokens == 7) {
2002                         p.sink.file_name = NULL;
2003                         p.sink.max_n_pkts = 0;
2004                 } else {
2005                         if (strcmp(tokens[7], "file") != 0) {
2006                                 snprintf(out, out_size, MSG_ARG_NOT_FOUND,
2007                                         "file");
2008                                 return;
2009                         }
2010
2011                         p.sink.file_name = tokens[8];
2012
2013                         if (strcmp(tokens[9], "pkts") != 0) {
2014                                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pkts");
2015                                 return;
2016                         }
2017
2018                         if (softnic_parser_read_uint32(&p.sink.max_n_pkts,
2019                                 tokens[10]) != 0) {
2020                                 snprintf(out, out_size, MSG_ARG_INVALID, "max_n_pkts");
2021                                 return;
2022                         }
2023                 }
2024         } else {
2025                 snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
2026                 return;
2027         }
2028
2029         status = softnic_pipeline_port_out_create(softnic, pipeline_name, &p);
2030         if (status) {
2031                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
2032                 return;
2033         }
2034 }
2035
2036 /**
2037  * pipeline <pipeline_name> table
2038  *      match
2039  *      acl
2040  *          ipv4 | ipv6
2041  *          offset <ip_header_offset>
2042  *          size <n_rules>
2043  *      | array
2044  *          offset <key_offset>
2045  *          size <n_keys>
2046  *      | hash
2047  *          ext | lru
2048  *          key <key_size>
2049  *          mask <key_mask>
2050  *          offset <key_offset>
2051  *          buckets <n_buckets>
2052  *          size <n_keys>
2053  *      | lpm
2054  *          ipv4 | ipv6
2055  *          offset <ip_header_offset>
2056  *          size <n_rules>
2057  *      | stub
2058  *  [action <table_action_profile_name>]
2059  */
2060 static void
2061 cmd_pipeline_table(struct pmd_internals *softnic,
2062         char **tokens,
2063         uint32_t n_tokens,
2064         char *out,
2065         size_t out_size)
2066 {
2067         uint8_t key_mask[TABLE_RULE_MATCH_SIZE_MAX];
2068         struct softnic_table_params p;
2069         char *pipeline_name;
2070         uint32_t t0;
2071         int status;
2072
2073         if (n_tokens < 5) {
2074                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2075                 return;
2076         }
2077
2078         pipeline_name = tokens[1];
2079
2080         if (strcmp(tokens[2], "table") != 0) {
2081                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
2082                 return;
2083         }
2084
2085         if (strcmp(tokens[3], "match") != 0) {
2086                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "match");
2087                 return;
2088         }
2089
2090         t0 = 4;
2091         if (strcmp(tokens[t0], "acl") == 0) {
2092                 if (n_tokens < t0 + 6) {
2093                         snprintf(out, out_size, MSG_ARG_MISMATCH,
2094                                 "pipeline table acl");
2095                         return;
2096                 }
2097
2098                 p.match_type = TABLE_ACL;
2099
2100                 if (strcmp(tokens[t0 + 1], "ipv4") == 0) {
2101                         p.match.acl.ip_version = 1;
2102                 } else if (strcmp(tokens[t0 + 1], "ipv6") == 0) {
2103                         p.match.acl.ip_version = 0;
2104                 } else {
2105                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
2106                                 "ipv4 or ipv6");
2107                         return;
2108                 }
2109
2110                 if (strcmp(tokens[t0 + 2], "offset") != 0) {
2111                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
2112                         return;
2113                 }
2114
2115                 if (softnic_parser_read_uint32(&p.match.acl.ip_header_offset,
2116                         tokens[t0 + 3]) != 0) {
2117                         snprintf(out, out_size, MSG_ARG_INVALID,
2118                                 "ip_header_offset");
2119                         return;
2120                 }
2121
2122                 if (strcmp(tokens[t0 + 4], "size") != 0) {
2123                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
2124                         return;
2125                 }
2126
2127                 if (softnic_parser_read_uint32(&p.match.acl.n_rules,
2128                         tokens[t0 + 5]) != 0) {
2129                         snprintf(out, out_size, MSG_ARG_INVALID, "n_rules");
2130                         return;
2131                 }
2132
2133                 t0 += 6;
2134         } else if (strcmp(tokens[t0], "array") == 0) {
2135                 if (n_tokens < t0 + 5) {
2136                         snprintf(out, out_size, MSG_ARG_MISMATCH,
2137                                 "pipeline table array");
2138                         return;
2139                 }
2140
2141                 p.match_type = TABLE_ARRAY;
2142
2143                 if (strcmp(tokens[t0 + 1], "offset") != 0) {
2144                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
2145                         return;
2146                 }
2147
2148                 if (softnic_parser_read_uint32(&p.match.array.key_offset,
2149                         tokens[t0 + 2]) != 0) {
2150                         snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
2151                         return;
2152                 }
2153
2154                 if (strcmp(tokens[t0 + 3], "size") != 0) {
2155                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
2156                         return;
2157                 }
2158
2159                 if (softnic_parser_read_uint32(&p.match.array.n_keys,
2160                         tokens[t0 + 4]) != 0) {
2161                         snprintf(out, out_size, MSG_ARG_INVALID, "n_keys");
2162                         return;
2163                 }
2164
2165                 t0 += 5;
2166         } else if (strcmp(tokens[t0], "hash") == 0) {
2167                 uint32_t key_mask_size = TABLE_RULE_MATCH_SIZE_MAX;
2168
2169                 if (n_tokens < t0 + 12) {
2170                         snprintf(out, out_size, MSG_ARG_MISMATCH,
2171                                 "pipeline table hash");
2172                         return;
2173                 }
2174
2175                 p.match_type = TABLE_HASH;
2176
2177                 if (strcmp(tokens[t0 + 1], "ext") == 0) {
2178                         p.match.hash.extendable_bucket = 1;
2179                 } else if (strcmp(tokens[t0 + 1], "lru") == 0) {
2180                         p.match.hash.extendable_bucket = 0;
2181                 } else {
2182                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
2183                                 "ext or lru");
2184                         return;
2185                 }
2186
2187                 if (strcmp(tokens[t0 + 2], "key") != 0) {
2188                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "key");
2189                         return;
2190                 }
2191
2192                 if ((softnic_parser_read_uint32(&p.match.hash.key_size,
2193                         tokens[t0 + 3]) != 0) ||
2194                         p.match.hash.key_size == 0 ||
2195                         p.match.hash.key_size > TABLE_RULE_MATCH_SIZE_MAX) {
2196                         snprintf(out, out_size, MSG_ARG_INVALID, "key_size");
2197                         return;
2198                 }
2199
2200                 if (strcmp(tokens[t0 + 4], "mask") != 0) {
2201                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mask");
2202                         return;
2203                 }
2204
2205                 if ((softnic_parse_hex_string(tokens[t0 + 5],
2206                         key_mask, &key_mask_size) != 0) ||
2207                         key_mask_size != p.match.hash.key_size) {
2208                         snprintf(out, out_size, MSG_ARG_INVALID, "key_mask");
2209                         return;
2210                 }
2211                 p.match.hash.key_mask = key_mask;
2212
2213                 if (strcmp(tokens[t0 + 6], "offset") != 0) {
2214                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
2215                         return;
2216                 }
2217
2218                 if (softnic_parser_read_uint32(&p.match.hash.key_offset,
2219                         tokens[t0 + 7]) != 0) {
2220                         snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
2221                         return;
2222                 }
2223
2224                 if (strcmp(tokens[t0 + 8], "buckets") != 0) {
2225                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "buckets");
2226                         return;
2227                 }
2228
2229                 if (softnic_parser_read_uint32(&p.match.hash.n_buckets,
2230                         tokens[t0 + 9]) != 0) {
2231                         snprintf(out, out_size, MSG_ARG_INVALID, "n_buckets");
2232                         return;
2233                 }
2234
2235                 if (strcmp(tokens[t0 + 10], "size") != 0) {
2236                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
2237                         return;
2238                 }
2239
2240                 if (softnic_parser_read_uint32(&p.match.hash.n_keys,
2241                         tokens[t0 + 11]) != 0) {
2242                         snprintf(out, out_size, MSG_ARG_INVALID, "n_keys");
2243                         return;
2244                 }
2245
2246                 t0 += 12;
2247         } else if (strcmp(tokens[t0], "lpm") == 0) {
2248                 if (n_tokens < t0 + 6) {
2249                         snprintf(out, out_size, MSG_ARG_MISMATCH,
2250                                 "pipeline table lpm");
2251                         return;
2252                 }
2253
2254                 p.match_type = TABLE_LPM;
2255
2256                 if (strcmp(tokens[t0 + 1], "ipv4") == 0) {
2257                         p.match.lpm.key_size = 4;
2258                 } else if (strcmp(tokens[t0 + 1], "ipv6") == 0) {
2259                         p.match.lpm.key_size = 16;
2260                 } else {
2261                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
2262                                 "ipv4 or ipv6");
2263                         return;
2264                 }
2265
2266                 if (strcmp(tokens[t0 + 2], "offset") != 0) {
2267                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
2268                         return;
2269                 }
2270
2271                 if (softnic_parser_read_uint32(&p.match.lpm.key_offset,
2272                         tokens[t0 + 3]) != 0) {
2273                         snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
2274                         return;
2275                 }
2276
2277                 if (strcmp(tokens[t0 + 4], "size") != 0) {
2278                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
2279                         return;
2280                 }
2281
2282                 if (softnic_parser_read_uint32(&p.match.lpm.n_rules,
2283                         tokens[t0 + 5]) != 0) {
2284                         snprintf(out, out_size, MSG_ARG_INVALID, "n_rules");
2285                         return;
2286                 }
2287
2288                 t0 += 6;
2289         } else if (strcmp(tokens[t0], "stub") == 0) {
2290                 p.match_type = TABLE_STUB;
2291
2292                 t0 += 1;
2293         } else {
2294                 snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
2295                 return;
2296         }
2297
2298         p.action_profile_name = NULL;
2299         if (n_tokens > t0 &&
2300                 (strcmp(tokens[t0], "action") == 0)) {
2301                 if (n_tokens < t0 + 2) {
2302                         snprintf(out, out_size, MSG_ARG_MISMATCH, "action");
2303                         return;
2304                 }
2305
2306                 p.action_profile_name = tokens[t0 + 1];
2307
2308                 t0 += 2;
2309         }
2310
2311         if (n_tokens > t0) {
2312                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2313                 return;
2314         }
2315
2316         status = softnic_pipeline_table_create(softnic, pipeline_name, &p);
2317         if (status) {
2318                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
2319                 return;
2320         }
2321 }
2322
2323 /**
2324  * pipeline <pipeline_name> port in <port_id> table <table_id>
2325  */
2326 static void
2327 cmd_pipeline_port_in_table(struct pmd_internals *softnic,
2328         char **tokens,
2329         uint32_t n_tokens,
2330         char *out,
2331         size_t out_size)
2332 {
2333         char *pipeline_name;
2334         uint32_t port_id, table_id;
2335         int status;
2336
2337         if (n_tokens != 7) {
2338                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2339                 return;
2340         }
2341
2342         pipeline_name = tokens[1];
2343
2344         if (strcmp(tokens[2], "port") != 0) {
2345                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
2346                 return;
2347         }
2348
2349         if (strcmp(tokens[3], "in") != 0) {
2350                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
2351                 return;
2352         }
2353
2354         if (softnic_parser_read_uint32(&port_id, tokens[4]) != 0) {
2355                 snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
2356                 return;
2357         }
2358
2359         if (strcmp(tokens[5], "table") != 0) {
2360                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
2361                 return;
2362         }
2363
2364         if (softnic_parser_read_uint32(&table_id, tokens[6]) != 0) {
2365                 snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
2366                 return;
2367         }
2368
2369         status = softnic_pipeline_port_in_connect_to_table(softnic,
2370                 pipeline_name,
2371                 port_id,
2372                 table_id);
2373         if (status) {
2374                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
2375                 return;
2376         }
2377 }
2378
2379 /**
2380  * pipeline <pipeline_name> port in <port_id> stats read [clear]
2381  */
2382
2383 #define MSG_PIPELINE_PORT_IN_STATS                         \
2384         "Pkts in: %" PRIu64 "\n"                           \
2385         "Pkts dropped by AH: %" PRIu64 "\n"                \
2386         "Pkts dropped by other: %" PRIu64 "\n"
2387
2388 static void
2389 cmd_pipeline_port_in_stats(struct pmd_internals *softnic,
2390         char **tokens,
2391         uint32_t n_tokens,
2392         char *out,
2393         size_t out_size)
2394 {
2395         struct rte_pipeline_port_in_stats stats;
2396         char *pipeline_name;
2397         uint32_t port_id;
2398         int clear, status;
2399
2400         if (n_tokens != 7 &&
2401                 n_tokens != 8) {
2402                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2403                 return;
2404         }
2405
2406         pipeline_name = tokens[1];
2407
2408         if (strcmp(tokens[2], "port") != 0) {
2409                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
2410                 return;
2411         }
2412
2413         if (strcmp(tokens[3], "in") != 0) {
2414                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
2415                 return;
2416         }
2417
2418         if (softnic_parser_read_uint32(&port_id, tokens[4]) != 0) {
2419                 snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
2420                 return;
2421         }
2422
2423         if (strcmp(tokens[5], "stats") != 0) {
2424                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
2425                 return;
2426         }
2427
2428         if (strcmp(tokens[6], "read") != 0) {
2429                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "read");
2430                 return;
2431         }
2432
2433         clear = 0;
2434         if (n_tokens == 8) {
2435                 if (strcmp(tokens[7], "clear") != 0) {
2436                         snprintf(out, out_size, MSG_ARG_INVALID, "clear");
2437                         return;
2438                 }
2439
2440                 clear = 1;
2441         }
2442
2443         status = softnic_pipeline_port_in_stats_read(softnic,
2444                 pipeline_name,
2445                 port_id,
2446                 &stats,
2447                 clear);
2448         if (status) {
2449                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
2450                 return;
2451         }
2452
2453         snprintf(out, out_size, MSG_PIPELINE_PORT_IN_STATS,
2454                 stats.stats.n_pkts_in,
2455                 stats.n_pkts_dropped_by_ah,
2456                 stats.stats.n_pkts_drop);
2457 }
2458
2459 /**
2460  * pipeline <pipeline_name> port in <port_id> enable
2461  */
2462 static void
2463 cmd_softnic_pipeline_port_in_enable(struct pmd_internals *softnic,
2464         char **tokens,
2465         uint32_t n_tokens,
2466         char *out,
2467         size_t out_size)
2468 {
2469         char *pipeline_name;
2470         uint32_t port_id;
2471         int status;
2472
2473         if (n_tokens != 6) {
2474                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2475                 return;
2476         }
2477
2478         pipeline_name = tokens[1];
2479
2480         if (strcmp(tokens[2], "port") != 0) {
2481                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
2482                 return;
2483         }
2484
2485         if (strcmp(tokens[3], "in") != 0) {
2486                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
2487                 return;
2488         }
2489
2490         if (softnic_parser_read_uint32(&port_id, tokens[4]) != 0) {
2491                 snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
2492                 return;
2493         }
2494
2495         if (strcmp(tokens[5], "enable") != 0) {
2496                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
2497                 return;
2498         }
2499
2500         status = softnic_pipeline_port_in_enable(softnic, pipeline_name, port_id);
2501         if (status) {
2502                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
2503                 return;
2504         }
2505 }
2506
2507 /**
2508  * pipeline <pipeline_name> port in <port_id> disable
2509  */
2510 static void
2511 cmd_softnic_pipeline_port_in_disable(struct pmd_internals *softnic,
2512         char **tokens,
2513         uint32_t n_tokens,
2514         char *out,
2515         size_t out_size)
2516 {
2517         char *pipeline_name;
2518         uint32_t port_id;
2519         int status;
2520
2521         if (n_tokens != 6) {
2522                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2523                 return;
2524         }
2525
2526         pipeline_name = tokens[1];
2527
2528         if (strcmp(tokens[2], "port") != 0) {
2529                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
2530                 return;
2531         }
2532
2533         if (strcmp(tokens[3], "in") != 0) {
2534                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
2535                 return;
2536         }
2537
2538         if (softnic_parser_read_uint32(&port_id, tokens[4]) != 0) {
2539                 snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
2540                 return;
2541         }
2542
2543         if (strcmp(tokens[5], "disable") != 0) {
2544                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
2545                 return;
2546         }
2547
2548         status = softnic_pipeline_port_in_disable(softnic, pipeline_name, port_id);
2549         if (status) {
2550                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
2551                 return;
2552         }
2553 }
2554
2555 /**
2556  * pipeline <pipeline_name> port out <port_id> stats read [clear]
2557  */
2558 #define MSG_PIPELINE_PORT_OUT_STATS                        \
2559         "Pkts in: %" PRIu64 "\n"                           \
2560         "Pkts dropped by AH: %" PRIu64 "\n"                \
2561         "Pkts dropped by other: %" PRIu64 "\n"
2562
2563 static void
2564 cmd_pipeline_port_out_stats(struct pmd_internals *softnic,
2565         char **tokens,
2566         uint32_t n_tokens,
2567         char *out,
2568         size_t out_size)
2569 {
2570         struct rte_pipeline_port_out_stats stats;
2571         char *pipeline_name;
2572         uint32_t port_id;
2573         int clear, status;
2574
2575         if (n_tokens != 7 &&
2576                 n_tokens != 8) {
2577                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2578                 return;
2579         }
2580
2581         pipeline_name = tokens[1];
2582
2583         if (strcmp(tokens[2], "port") != 0) {
2584                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
2585                 return;
2586         }
2587
2588         if (strcmp(tokens[3], "out") != 0) {
2589                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "out");
2590                 return;
2591         }
2592
2593         if (softnic_parser_read_uint32(&port_id, tokens[4]) != 0) {
2594                 snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
2595                 return;
2596         }
2597
2598         if (strcmp(tokens[5], "stats") != 0) {
2599                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
2600                 return;
2601         }
2602
2603         if (strcmp(tokens[6], "read") != 0) {
2604                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "read");
2605                 return;
2606         }
2607
2608         clear = 0;
2609         if (n_tokens == 8) {
2610                 if (strcmp(tokens[7], "clear") != 0) {
2611                         snprintf(out, out_size, MSG_ARG_INVALID, "clear");
2612                         return;
2613                 }
2614
2615                 clear = 1;
2616         }
2617
2618         status = softnic_pipeline_port_out_stats_read(softnic,
2619                 pipeline_name,
2620                 port_id,
2621                 &stats,
2622                 clear);
2623         if (status) {
2624                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
2625                 return;
2626         }
2627
2628         snprintf(out, out_size, MSG_PIPELINE_PORT_OUT_STATS,
2629                 stats.stats.n_pkts_in,
2630                 stats.n_pkts_dropped_by_ah,
2631                 stats.stats.n_pkts_drop);
2632 }
2633
2634 /**
2635  * pipeline <pipeline_name> table <table_id> stats read [clear]
2636  */
2637 #define MSG_PIPELINE_TABLE_STATS                                     \
2638         "Pkts in: %" PRIu64 "\n"                                     \
2639         "Pkts in with lookup miss: %" PRIu64 "\n"                    \
2640         "Pkts in with lookup hit dropped by AH: %" PRIu64 "\n"       \
2641         "Pkts in with lookup hit dropped by others: %" PRIu64 "\n"   \
2642         "Pkts in with lookup miss dropped by AH: %" PRIu64 "\n"      \
2643         "Pkts in with lookup miss dropped by others: %" PRIu64 "\n"
2644
2645 static void
2646 cmd_pipeline_table_stats(struct pmd_internals *softnic,
2647         char **tokens,
2648         uint32_t n_tokens,
2649         char *out,
2650         size_t out_size)
2651 {
2652         struct rte_pipeline_table_stats stats;
2653         char *pipeline_name;
2654         uint32_t table_id;
2655         int clear, status;
2656
2657         if (n_tokens != 6 &&
2658                 n_tokens != 7) {
2659                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2660                 return;
2661         }
2662
2663         pipeline_name = tokens[1];
2664
2665         if (strcmp(tokens[2], "table") != 0) {
2666                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
2667                 return;
2668         }
2669
2670         if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
2671                 snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
2672                 return;
2673         }
2674
2675         if (strcmp(tokens[4], "stats") != 0) {
2676                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
2677                 return;
2678         }
2679
2680         if (strcmp(tokens[5], "read") != 0) {
2681                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "read");
2682                 return;
2683         }
2684
2685         clear = 0;
2686         if (n_tokens == 7) {
2687                 if (strcmp(tokens[6], "clear") != 0) {
2688                         snprintf(out, out_size, MSG_ARG_INVALID, "clear");
2689                         return;
2690                 }
2691
2692                 clear = 1;
2693         }
2694
2695         status = softnic_pipeline_table_stats_read(softnic,
2696                 pipeline_name,
2697                 table_id,
2698                 &stats,
2699                 clear);
2700         if (status) {
2701                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
2702                 return;
2703         }
2704
2705         snprintf(out, out_size, MSG_PIPELINE_TABLE_STATS,
2706                 stats.stats.n_pkts_in,
2707                 stats.stats.n_pkts_lookup_miss,
2708                 stats.n_pkts_dropped_by_lkp_hit_ah,
2709                 stats.n_pkts_dropped_lkp_hit,
2710                 stats.n_pkts_dropped_by_lkp_miss_ah,
2711                 stats.n_pkts_dropped_lkp_miss);
2712 }
2713
2714 /**
2715  * <match> ::=
2716  *
2717  * match
2718  *    acl
2719  *       priority <priority>
2720  *       ipv4 | ipv6 <sa> <sa_depth> <da> <da_depth>
2721  *       <sp0> <sp1> <dp0> <dp1> <proto>
2722  *    | array <pos>
2723  *    | hash
2724  *       raw <key>
2725  *       | ipv4_5tuple <sa> <da> <sp> <dp> <proto>
2726  *       | ipv6_5tuple <sa> <da> <sp> <dp> <proto>
2727  *       | ipv4_addr <addr>
2728  *       | ipv6_addr <addr>
2729  *       | qinq <svlan> <cvlan>
2730  *    | lpm
2731  *       ipv4 | ipv6 <addr> <depth>
2732  */
2733 struct pkt_key_qinq {
2734         uint16_t ethertype_svlan;
2735         uint16_t svlan;
2736         uint16_t ethertype_cvlan;
2737         uint16_t cvlan;
2738 } __attribute__((__packed__));
2739
2740 struct pkt_key_ipv4_5tuple {
2741         uint8_t time_to_live;
2742         uint8_t proto;
2743         uint16_t hdr_checksum;
2744         uint32_t sa;
2745         uint32_t da;
2746         uint16_t sp;
2747         uint16_t dp;
2748 } __attribute__((__packed__));
2749
2750 struct pkt_key_ipv6_5tuple {
2751         uint16_t payload_length;
2752         uint8_t proto;
2753         uint8_t hop_limit;
2754         uint8_t sa[16];
2755         uint8_t da[16];
2756         uint16_t sp;
2757         uint16_t dp;
2758 } __attribute__((__packed__));
2759
2760 struct pkt_key_ipv4_addr {
2761         uint32_t addr;
2762 } __attribute__((__packed__));
2763
2764 struct pkt_key_ipv6_addr {
2765         uint8_t addr[16];
2766 } __attribute__((__packed__));
2767
2768 static uint32_t
2769 parse_match(char **tokens,
2770         uint32_t n_tokens,
2771         char *out,
2772         size_t out_size,
2773         struct softnic_table_rule_match *m)
2774 {
2775         memset(m, 0, sizeof(*m));
2776
2777         if (n_tokens < 2)
2778                 return 0;
2779
2780         if (strcmp(tokens[0], "match") != 0) {
2781                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "match");
2782                 return 0;
2783         }
2784
2785         if (strcmp(tokens[1], "acl") == 0) {
2786                 if (n_tokens < 14) {
2787                         snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2788                         return 0;
2789                 }
2790
2791                 m->match_type = TABLE_ACL;
2792
2793                 if (strcmp(tokens[2], "priority") != 0) {
2794                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "priority");
2795                         return 0;
2796                 }
2797
2798                 if (softnic_parser_read_uint32(&m->match.acl.priority,
2799                         tokens[3]) != 0) {
2800                         snprintf(out, out_size, MSG_ARG_INVALID, "priority");
2801                         return 0;
2802                 }
2803
2804                 if (strcmp(tokens[4], "ipv4") == 0) {
2805                         struct in_addr saddr, daddr;
2806
2807                         m->match.acl.ip_version = 1;
2808
2809                         if (softnic_parse_ipv4_addr(tokens[5], &saddr) != 0) {
2810                                 snprintf(out, out_size, MSG_ARG_INVALID, "sa");
2811                                 return 0;
2812                         }
2813                         m->match.acl.ipv4.sa = rte_be_to_cpu_32(saddr.s_addr);
2814
2815                         if (softnic_parse_ipv4_addr(tokens[7], &daddr) != 0) {
2816                                 snprintf(out, out_size, MSG_ARG_INVALID, "da");
2817                                 return 0;
2818                         }
2819                         m->match.acl.ipv4.da = rte_be_to_cpu_32(daddr.s_addr);
2820                 } else if (strcmp(tokens[4], "ipv6") == 0) {
2821                         struct in6_addr saddr, daddr;
2822
2823                         m->match.acl.ip_version = 0;
2824
2825                         if (softnic_parse_ipv6_addr(tokens[5], &saddr) != 0) {
2826                                 snprintf(out, out_size, MSG_ARG_INVALID, "sa");
2827                                 return 0;
2828                         }
2829                         memcpy(m->match.acl.ipv6.sa, saddr.s6_addr, 16);
2830
2831                         if (softnic_parse_ipv6_addr(tokens[7], &daddr) != 0) {
2832                                 snprintf(out, out_size, MSG_ARG_INVALID, "da");
2833                                 return 0;
2834                         }
2835                         memcpy(m->match.acl.ipv6.da, daddr.s6_addr, 16);
2836                 } else {
2837                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
2838                                 "ipv4 or ipv6");
2839                         return 0;
2840                 }
2841
2842                 if (softnic_parser_read_uint32(&m->match.acl.sa_depth,
2843                         tokens[6]) != 0) {
2844                         snprintf(out, out_size, MSG_ARG_INVALID, "sa_depth");
2845                         return 0;
2846                 }
2847
2848                 if (softnic_parser_read_uint32(&m->match.acl.da_depth,
2849                         tokens[8]) != 0) {
2850                         snprintf(out, out_size, MSG_ARG_INVALID, "da_depth");
2851                         return 0;
2852                 }
2853
2854                 if (softnic_parser_read_uint16(&m->match.acl.sp0, tokens[9]) != 0) {
2855                         snprintf(out, out_size, MSG_ARG_INVALID, "sp0");
2856                         return 0;
2857                 }
2858
2859                 if (softnic_parser_read_uint16(&m->match.acl.sp1, tokens[10]) != 0) {
2860                         snprintf(out, out_size, MSG_ARG_INVALID, "sp1");
2861                         return 0;
2862                 }
2863
2864                 if (softnic_parser_read_uint16(&m->match.acl.dp0, tokens[11]) != 0) {
2865                         snprintf(out, out_size, MSG_ARG_INVALID, "dp0");
2866                         return 0;
2867                 }
2868
2869                 if (softnic_parser_read_uint16(&m->match.acl.dp1, tokens[12]) != 0) {
2870                         snprintf(out, out_size, MSG_ARG_INVALID, "dp1");
2871                         return 0;
2872                 }
2873
2874                 if (softnic_parser_read_uint8(&m->match.acl.proto, tokens[13]) != 0) {
2875                         snprintf(out, out_size, MSG_ARG_INVALID, "proto");
2876                         return 0;
2877                 }
2878
2879                 m->match.acl.proto_mask = 0xff;
2880
2881                 return 14;
2882         } /* acl */
2883
2884         if (strcmp(tokens[1], "array") == 0) {
2885                 if (n_tokens < 3) {
2886                         snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2887                         return 0;
2888                 }
2889
2890                 m->match_type = TABLE_ARRAY;
2891
2892                 if (softnic_parser_read_uint32(&m->match.array.pos, tokens[2]) != 0) {
2893                         snprintf(out, out_size, MSG_ARG_INVALID, "pos");
2894                         return 0;
2895                 }
2896
2897                 return 3;
2898         } /* array */
2899
2900         if (strcmp(tokens[1], "hash") == 0) {
2901                 if (n_tokens < 3) {
2902                         snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2903                         return 0;
2904                 }
2905
2906                 m->match_type = TABLE_HASH;
2907
2908                 if (strcmp(tokens[2], "raw") == 0) {
2909                         uint32_t key_size = TABLE_RULE_MATCH_SIZE_MAX;
2910
2911                         if (n_tokens < 4) {
2912                                 snprintf(out, out_size, MSG_ARG_MISMATCH,
2913                                         tokens[0]);
2914                                 return 0;
2915                         }
2916
2917                         if (softnic_parse_hex_string(tokens[3],
2918                                 m->match.hash.key, &key_size) != 0) {
2919                                 snprintf(out, out_size, MSG_ARG_INVALID, "key");
2920                                 return 0;
2921                         }
2922
2923                         return 4;
2924                 } /* hash raw */
2925
2926                 if (strcmp(tokens[2], "ipv4_5tuple") == 0) {
2927                         struct pkt_key_ipv4_5tuple *ipv4 =
2928                                 (struct pkt_key_ipv4_5tuple *)m->match.hash.key;
2929                         struct in_addr saddr, daddr;
2930                         uint16_t sp, dp;
2931                         uint8_t proto;
2932
2933                         if (n_tokens < 8) {
2934                                 snprintf(out, out_size, MSG_ARG_MISMATCH,
2935                                         tokens[0]);
2936                                 return 0;
2937                         }
2938
2939                         if (softnic_parse_ipv4_addr(tokens[3], &saddr) != 0) {
2940                                 snprintf(out, out_size, MSG_ARG_INVALID, "sa");
2941                                 return 0;
2942                         }
2943
2944                         if (softnic_parse_ipv4_addr(tokens[4], &daddr) != 0) {
2945                                 snprintf(out, out_size, MSG_ARG_INVALID, "da");
2946                                 return 0;
2947                         }
2948
2949                         if (softnic_parser_read_uint16(&sp, tokens[5]) != 0) {
2950                                 snprintf(out, out_size, MSG_ARG_INVALID, "sp");
2951                                 return 0;
2952                         }
2953
2954                         if (softnic_parser_read_uint16(&dp, tokens[6]) != 0) {
2955                                 snprintf(out, out_size, MSG_ARG_INVALID, "dp");
2956                                 return 0;
2957                         }
2958
2959                         if (softnic_parser_read_uint8(&proto, tokens[7]) != 0) {
2960                                 snprintf(out, out_size, MSG_ARG_INVALID,
2961                                         "proto");
2962                                 return 0;
2963                         }
2964
2965                         ipv4->sa = saddr.s_addr;
2966                         ipv4->da = daddr.s_addr;
2967                         ipv4->sp = rte_cpu_to_be_16(sp);
2968                         ipv4->dp = rte_cpu_to_be_16(dp);
2969                         ipv4->proto = proto;
2970
2971                         return 8;
2972                 } /* hash ipv4_5tuple */
2973
2974                 if (strcmp(tokens[2], "ipv6_5tuple") == 0) {
2975                         struct pkt_key_ipv6_5tuple *ipv6 =
2976                                 (struct pkt_key_ipv6_5tuple *)m->match.hash.key;
2977                         struct in6_addr saddr, daddr;
2978                         uint16_t sp, dp;
2979                         uint8_t proto;
2980
2981                         if (n_tokens < 8) {
2982                                 snprintf(out, out_size, MSG_ARG_MISMATCH,
2983                                         tokens[0]);
2984                                 return 0;
2985                         }
2986
2987                         if (softnic_parse_ipv6_addr(tokens[3], &saddr) != 0) {
2988                                 snprintf(out, out_size, MSG_ARG_INVALID, "sa");
2989                                 return 0;
2990                         }
2991
2992                         if (softnic_parse_ipv6_addr(tokens[4], &daddr) != 0) {
2993                                 snprintf(out, out_size, MSG_ARG_INVALID, "da");
2994                                 return 0;
2995                         }
2996
2997                         if (softnic_parser_read_uint16(&sp, tokens[5]) != 0) {
2998                                 snprintf(out, out_size, MSG_ARG_INVALID, "sp");
2999                                 return 0;
3000                         }
3001
3002                         if (softnic_parser_read_uint16(&dp, tokens[6]) != 0) {
3003                                 snprintf(out, out_size, MSG_ARG_INVALID, "dp");
3004                                 return 0;
3005                         }
3006
3007                         if (softnic_parser_read_uint8(&proto, tokens[7]) != 0) {
3008                                 snprintf(out, out_size, MSG_ARG_INVALID,
3009                                         "proto");
3010                                 return 0;
3011                         }
3012
3013                         memcpy(ipv6->sa, saddr.s6_addr, 16);
3014                         memcpy(ipv6->da, daddr.s6_addr, 16);
3015                         ipv6->sp = rte_cpu_to_be_16(sp);
3016                         ipv6->dp = rte_cpu_to_be_16(dp);
3017                         ipv6->proto = proto;
3018
3019                         return 8;
3020                 } /* hash ipv6_5tuple */
3021
3022                 if (strcmp(tokens[2], "ipv4_addr") == 0) {
3023                         struct pkt_key_ipv4_addr *ipv4_addr =
3024                                 (struct pkt_key_ipv4_addr *)m->match.hash.key;
3025                         struct in_addr addr;
3026
3027                         if (n_tokens < 4) {
3028                                 snprintf(out, out_size, MSG_ARG_MISMATCH,
3029                                         tokens[0]);
3030                                 return 0;
3031                         }
3032
3033                         if (softnic_parse_ipv4_addr(tokens[3], &addr) != 0) {
3034                                 snprintf(out, out_size, MSG_ARG_INVALID,
3035                                         "addr");
3036                                 return 0;
3037                         }
3038
3039                         ipv4_addr->addr = addr.s_addr;
3040
3041                         return 4;
3042                 } /* hash ipv4_addr */
3043
3044                 if (strcmp(tokens[2], "ipv6_addr") == 0) {
3045                         struct pkt_key_ipv6_addr *ipv6_addr =
3046                                 (struct pkt_key_ipv6_addr *)m->match.hash.key;
3047                         struct in6_addr addr;
3048
3049                         if (n_tokens < 4) {
3050                                 snprintf(out, out_size, MSG_ARG_MISMATCH,
3051                                         tokens[0]);
3052                                 return 0;
3053                         }
3054
3055                         if (softnic_parse_ipv6_addr(tokens[3], &addr) != 0) {
3056                                 snprintf(out, out_size, MSG_ARG_INVALID,
3057                                         "addr");
3058                                 return 0;
3059                         }
3060
3061                         memcpy(ipv6_addr->addr, addr.s6_addr, 16);
3062
3063                         return 4;
3064                 } /* hash ipv6_5tuple */
3065
3066                 if (strcmp(tokens[2], "qinq") == 0) {
3067                         struct pkt_key_qinq *qinq =
3068                                 (struct pkt_key_qinq *)m->match.hash.key;
3069                         uint16_t svlan, cvlan;
3070
3071                         if (n_tokens < 5) {
3072                                 snprintf(out, out_size, MSG_ARG_MISMATCH,
3073                                         tokens[0]);
3074                                 return 0;
3075                         }
3076
3077                         if ((softnic_parser_read_uint16(&svlan, tokens[3]) != 0) ||
3078                                 svlan > 0xFFF) {
3079                                 snprintf(out, out_size, MSG_ARG_INVALID,
3080                                         "svlan");
3081                                 return 0;
3082                         }
3083
3084                         if ((softnic_parser_read_uint16(&cvlan, tokens[4]) != 0) ||
3085                                 cvlan > 0xFFF) {
3086                                 snprintf(out, out_size, MSG_ARG_INVALID,
3087                                         "cvlan");
3088                                 return 0;
3089                         }
3090
3091                         qinq->svlan = rte_cpu_to_be_16(svlan);
3092                         qinq->cvlan = rte_cpu_to_be_16(cvlan);
3093
3094                         return 5;
3095                 } /* hash qinq */
3096
3097                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3098                 return 0;
3099         } /* hash */
3100
3101         if (strcmp(tokens[1], "lpm") == 0) {
3102                 if (n_tokens < 5) {
3103                         snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3104                         return 0;
3105                 }
3106
3107                 m->match_type = TABLE_LPM;
3108
3109                 if (strcmp(tokens[2], "ipv4") == 0) {
3110                         struct in_addr addr;
3111
3112                         m->match.lpm.ip_version = 1;
3113
3114                         if (softnic_parse_ipv4_addr(tokens[3], &addr) != 0) {
3115                                 snprintf(out, out_size, MSG_ARG_INVALID,
3116                                         "addr");
3117                                 return 0;
3118                         }
3119
3120                         m->match.lpm.ipv4 = rte_be_to_cpu_32(addr.s_addr);
3121                 } else if (strcmp(tokens[2], "ipv6") == 0) {
3122                         struct in6_addr addr;
3123
3124                         m->match.lpm.ip_version = 0;
3125
3126                         if (softnic_parse_ipv6_addr(tokens[3], &addr) != 0) {
3127                                 snprintf(out, out_size, MSG_ARG_INVALID,
3128                                         "addr");
3129                                 return 0;
3130                         }
3131
3132                         memcpy(m->match.lpm.ipv6, addr.s6_addr, 16);
3133                 } else {
3134                         snprintf(out, out_size, MSG_ARG_MISMATCH,
3135                                 "ipv4 or ipv6");
3136                         return 0;
3137                 }
3138
3139                 if (softnic_parser_read_uint8(&m->match.lpm.depth, tokens[4]) != 0) {
3140                         snprintf(out, out_size, MSG_ARG_INVALID, "depth");
3141                         return 0;
3142                 }
3143
3144                 return 5;
3145         } /* lpm */
3146
3147         snprintf(out, out_size, MSG_ARG_MISMATCH,
3148                 "acl or array or hash or lpm");
3149         return 0;
3150 }
3151
3152 /**
3153  * table_action ::=
3154  *
3155  * action
3156  *    fwd
3157  *       drop
3158  *       | port <port_id>
3159  *       | meta
3160  *       | table <table_id>
3161  *    [balance <out0> ... <out7>]
3162  *    [meter
3163  *       tc0 meter <meter_profile_id> policer g <pa> y <pa> r <pa>
3164  *       [tc1 meter <meter_profile_id> policer g <pa> y <pa> r <pa>
3165  *       tc2 meter <meter_profile_id> policer g <pa> y <pa> r <pa>
3166  *       tc3 meter <meter_profile_id> policer g <pa> y <pa> r <pa>]]
3167  *    [tm subport <subport_id> pipe <pipe_id>]
3168  *    [encap
3169  *       ether <da> <sa>
3170  *       | vlan <da> <sa> <pcp> <dei> <vid>
3171  *       | qinq <da> <sa> <pcp> <dei> <vid> <pcp> <dei> <vid>
3172  *       | mpls unicast | multicast
3173  *          <da> <sa>
3174  *          label0 <label> <tc> <ttl>
3175  *          [label1 <label> <tc> <ttl>
3176  *          [label2 <label> <tc> <ttl>
3177  *          [label3 <label> <tc> <ttl>]]]
3178  *       | pppoe <da> <sa> <session_id>]
3179  *    [nat ipv4 | ipv6 <addr> <port>]
3180  *    [ttl dec | keep]
3181  *    [stats]
3182  *    [time]
3183  *
3184  * where:
3185  *    <pa> ::= g | y | r | drop
3186  */
3187 static uint32_t
3188 parse_table_action_fwd(char **tokens,
3189         uint32_t n_tokens,
3190         struct softnic_table_rule_action *a)
3191 {
3192         if (n_tokens == 0 ||
3193                 (strcmp(tokens[0], "fwd") != 0))
3194                 return 0;
3195
3196         tokens++;
3197         n_tokens--;
3198
3199         if (n_tokens && (strcmp(tokens[0], "drop") == 0)) {
3200                 a->fwd.action = RTE_PIPELINE_ACTION_DROP;
3201                 a->action_mask |= 1 << RTE_TABLE_ACTION_FWD;
3202                 return 1 + 1;
3203         }
3204
3205         if (n_tokens && (strcmp(tokens[0], "port") == 0)) {
3206                 uint32_t id;
3207
3208                 if (n_tokens < 2 ||
3209                         softnic_parser_read_uint32(&id, tokens[1]))
3210                         return 0;
3211
3212                 a->fwd.action = RTE_PIPELINE_ACTION_PORT;
3213                 a->fwd.id = id;
3214                 a->action_mask |= 1 << RTE_TABLE_ACTION_FWD;
3215                 return 1 + 2;
3216         }
3217
3218         if (n_tokens && (strcmp(tokens[0], "meta") == 0)) {
3219                 a->fwd.action = RTE_PIPELINE_ACTION_PORT_META;
3220                 a->action_mask |= 1 << RTE_TABLE_ACTION_FWD;
3221                 return 1 + 1;
3222         }
3223
3224         if (n_tokens && (strcmp(tokens[0], "table") == 0)) {
3225                 uint32_t id;
3226
3227                 if (n_tokens < 2 ||
3228                         softnic_parser_read_uint32(&id, tokens[1]))
3229                         return 0;
3230
3231                 a->fwd.action = RTE_PIPELINE_ACTION_TABLE;
3232                 a->fwd.id = id;
3233                 a->action_mask |= 1 << RTE_TABLE_ACTION_FWD;
3234                 return 1 + 2;
3235         }
3236
3237         return 0;
3238 }
3239
3240 static uint32_t
3241 parse_table_action_balance(char **tokens,
3242         uint32_t n_tokens,
3243         struct softnic_table_rule_action *a)
3244 {
3245         uint32_t i;
3246
3247         if (n_tokens == 0 ||
3248                 (strcmp(tokens[0], "balance") != 0))
3249                 return 0;
3250
3251         tokens++;
3252         n_tokens--;
3253
3254         if (n_tokens < RTE_TABLE_ACTION_LB_TABLE_SIZE)
3255                 return 0;
3256
3257         for (i = 0; i < RTE_TABLE_ACTION_LB_TABLE_SIZE; i++)
3258                 if (softnic_parser_read_uint32(&a->lb.out[i], tokens[i]) != 0)
3259                         return 0;
3260
3261         a->action_mask |= 1 << RTE_TABLE_ACTION_LB;
3262         return 1 + RTE_TABLE_ACTION_LB_TABLE_SIZE;
3263 }
3264
3265 static int
3266 parse_policer_action(char *token, enum rte_table_action_policer *a)
3267 {
3268         if (strcmp(token, "g") == 0) {
3269                 *a = RTE_TABLE_ACTION_POLICER_COLOR_GREEN;
3270                 return 0;
3271         }
3272
3273         if (strcmp(token, "y") == 0) {
3274                 *a = RTE_TABLE_ACTION_POLICER_COLOR_YELLOW;
3275                 return 0;
3276         }
3277
3278         if (strcmp(token, "r") == 0) {
3279                 *a = RTE_TABLE_ACTION_POLICER_COLOR_RED;
3280                 return 0;
3281         }
3282
3283         if (strcmp(token, "drop") == 0) {
3284                 *a = RTE_TABLE_ACTION_POLICER_DROP;
3285                 return 0;
3286         }
3287
3288         return -1;
3289 }
3290
3291 static uint32_t
3292 parse_table_action_meter_tc(char **tokens,
3293         uint32_t n_tokens,
3294         struct rte_table_action_mtr_tc_params *mtr)
3295 {
3296         if (n_tokens < 9 ||
3297                 strcmp(tokens[0], "meter") ||
3298                 softnic_parser_read_uint32(&mtr->meter_profile_id, tokens[1]) ||
3299                 strcmp(tokens[2], "policer") ||
3300                 strcmp(tokens[3], "g") ||
3301                 parse_policer_action(tokens[4], &mtr->policer[e_RTE_METER_GREEN]) ||
3302                 strcmp(tokens[5], "y") ||
3303                 parse_policer_action(tokens[6], &mtr->policer[e_RTE_METER_YELLOW]) ||
3304                 strcmp(tokens[7], "r") ||
3305                 parse_policer_action(tokens[8], &mtr->policer[e_RTE_METER_RED]))
3306                 return 0;
3307
3308         return 9;
3309 }
3310
3311 static uint32_t
3312 parse_table_action_meter(char **tokens,
3313         uint32_t n_tokens,
3314         struct softnic_table_rule_action *a)
3315 {
3316         if (n_tokens == 0 ||
3317                 strcmp(tokens[0], "meter"))
3318                 return 0;
3319
3320         tokens++;
3321         n_tokens--;
3322
3323         if (n_tokens < 10 ||
3324                 strcmp(tokens[0], "tc0") ||
3325                 (parse_table_action_meter_tc(tokens + 1,
3326                         n_tokens - 1,
3327                         &a->mtr.mtr[0]) == 0))
3328                 return 0;
3329
3330         tokens += 10;
3331         n_tokens -= 10;
3332
3333         if (n_tokens == 0 ||
3334                 strcmp(tokens[0], "tc1")) {
3335                 a->mtr.tc_mask = 1;
3336                 a->action_mask |= 1 << RTE_TABLE_ACTION_MTR;
3337                 return 1 + 10;
3338         }
3339
3340         if (n_tokens < 30 ||
3341                 (parse_table_action_meter_tc(tokens + 1,
3342                         n_tokens - 1, &a->mtr.mtr[1]) == 0) ||
3343                 strcmp(tokens[10], "tc2") ||
3344                 (parse_table_action_meter_tc(tokens + 11,
3345                         n_tokens - 11, &a->mtr.mtr[2]) == 0) ||
3346                 strcmp(tokens[20], "tc3") ||
3347                 (parse_table_action_meter_tc(tokens + 21,
3348                         n_tokens - 21, &a->mtr.mtr[3]) == 0))
3349                 return 0;
3350
3351         a->mtr.tc_mask = 0xF;
3352         a->action_mask |= 1 << RTE_TABLE_ACTION_MTR;
3353         return 1 + 10 + 3 * 10;
3354 }
3355
3356 static uint32_t
3357 parse_table_action_tm(char **tokens,
3358         uint32_t n_tokens,
3359         struct softnic_table_rule_action *a)
3360 {
3361         uint32_t subport_id, pipe_id;
3362
3363         if (n_tokens < 5 ||
3364                 strcmp(tokens[0], "tm") ||
3365                 strcmp(tokens[1], "subport") ||
3366                 softnic_parser_read_uint32(&subport_id, tokens[2]) ||
3367                 strcmp(tokens[3], "pipe") ||
3368                 softnic_parser_read_uint32(&pipe_id, tokens[4]))
3369                 return 0;
3370
3371         a->tm.subport_id = subport_id;
3372         a->tm.pipe_id = pipe_id;
3373         a->action_mask |= 1 << RTE_TABLE_ACTION_TM;
3374         return 5;
3375 }
3376
3377 static uint32_t
3378 parse_table_action_encap(char **tokens,
3379         uint32_t n_tokens,
3380         struct softnic_table_rule_action *a)
3381 {
3382         if (n_tokens == 0 ||
3383                 strcmp(tokens[0], "encap"))
3384                 return 0;
3385
3386         tokens++;
3387         n_tokens--;
3388
3389         /* ether */
3390         if (n_tokens && (strcmp(tokens[0], "ether") == 0)) {
3391                 if (n_tokens < 3 ||
3392                         softnic_parse_mac_addr(tokens[1], &a->encap.ether.ether.da) ||
3393                         softnic_parse_mac_addr(tokens[2], &a->encap.ether.ether.sa))
3394                         return 0;
3395
3396                 a->encap.type = RTE_TABLE_ACTION_ENCAP_ETHER;
3397                 a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
3398                 return 1 + 3;
3399         }
3400
3401         /* vlan */
3402         if (n_tokens && (strcmp(tokens[0], "vlan") == 0)) {
3403                 uint32_t pcp, dei, vid;
3404
3405                 if (n_tokens < 6 ||
3406                         softnic_parse_mac_addr(tokens[1], &a->encap.vlan.ether.da) ||
3407                         softnic_parse_mac_addr(tokens[2], &a->encap.vlan.ether.sa) ||
3408                         softnic_parser_read_uint32(&pcp, tokens[3]) ||
3409                         pcp > 0x7 ||
3410                         softnic_parser_read_uint32(&dei, tokens[4]) ||
3411                         dei > 0x1 ||
3412                         softnic_parser_read_uint32(&vid, tokens[5]) ||
3413                         vid > 0xFFF)
3414                         return 0;
3415
3416                 a->encap.vlan.vlan.pcp = pcp & 0x7;
3417                 a->encap.vlan.vlan.dei = dei & 0x1;
3418                 a->encap.vlan.vlan.vid = vid & 0xFFF;
3419                 a->encap.type = RTE_TABLE_ACTION_ENCAP_VLAN;
3420                 a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
3421                 return 1 + 6;
3422         }
3423
3424         /* qinq */
3425         if (n_tokens && (strcmp(tokens[0], "qinq") == 0)) {
3426                 uint32_t svlan_pcp, svlan_dei, svlan_vid;
3427                 uint32_t cvlan_pcp, cvlan_dei, cvlan_vid;
3428
3429                 if (n_tokens < 9 ||
3430                         softnic_parse_mac_addr(tokens[1], &a->encap.qinq.ether.da) ||
3431                         softnic_parse_mac_addr(tokens[2], &a->encap.qinq.ether.sa) ||
3432                         softnic_parser_read_uint32(&svlan_pcp, tokens[3]) ||
3433                         svlan_pcp > 0x7 ||
3434                         softnic_parser_read_uint32(&svlan_dei, tokens[4]) ||
3435                         svlan_dei > 0x1 ||
3436                         softnic_parser_read_uint32(&svlan_vid, tokens[5]) ||
3437                         svlan_vid > 0xFFF ||
3438                         softnic_parser_read_uint32(&cvlan_pcp, tokens[6]) ||
3439                         cvlan_pcp > 0x7 ||
3440                         softnic_parser_read_uint32(&cvlan_dei, tokens[7]) ||
3441                         cvlan_dei > 0x1 ||
3442                         softnic_parser_read_uint32(&cvlan_vid, tokens[8]) ||
3443                         cvlan_vid > 0xFFF)
3444                         return 0;
3445
3446                 a->encap.qinq.svlan.pcp = svlan_pcp & 0x7;
3447                 a->encap.qinq.svlan.dei = svlan_dei & 0x1;
3448                 a->encap.qinq.svlan.vid = svlan_vid & 0xFFF;
3449                 a->encap.qinq.cvlan.pcp = cvlan_pcp & 0x7;
3450                 a->encap.qinq.cvlan.dei = cvlan_dei & 0x1;
3451                 a->encap.qinq.cvlan.vid = cvlan_vid & 0xFFF;
3452                 a->encap.type = RTE_TABLE_ACTION_ENCAP_QINQ;
3453                 a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
3454                 return 1 + 9;
3455         }
3456
3457         /* mpls */
3458         if (n_tokens && (strcmp(tokens[0], "mpls") == 0)) {
3459                 uint32_t label, tc, ttl;
3460
3461                 if (n_tokens < 8)
3462                         return 0;
3463
3464                 if (strcmp(tokens[1], "unicast") == 0)
3465                         a->encap.mpls.unicast = 1;
3466                 else if (strcmp(tokens[1], "multicast") == 0)
3467                         a->encap.mpls.unicast = 0;
3468                 else
3469                         return 0;
3470
3471                 if (softnic_parse_mac_addr(tokens[2], &a->encap.mpls.ether.da) ||
3472                         softnic_parse_mac_addr(tokens[3], &a->encap.mpls.ether.sa) ||
3473                         strcmp(tokens[4], "label0") ||
3474                         softnic_parser_read_uint32(&label, tokens[5]) ||
3475                         label > 0xFFFFF ||
3476                         softnic_parser_read_uint32(&tc, tokens[6]) ||
3477                         tc > 0x7 ||
3478                         softnic_parser_read_uint32(&ttl, tokens[7]) ||
3479                         ttl > 0x3F)
3480                         return 0;
3481
3482                 a->encap.mpls.mpls[0].label = label;
3483                 a->encap.mpls.mpls[0].tc = tc;
3484                 a->encap.mpls.mpls[0].ttl = ttl;
3485
3486                 tokens += 8;
3487                 n_tokens -= 8;
3488
3489                 if (n_tokens == 0 ||
3490                         strcmp(tokens[0], "label1")) {
3491                         a->encap.mpls.mpls_count = 1;
3492                         a->encap.type = RTE_TABLE_ACTION_ENCAP_MPLS;
3493                         a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
3494                         return 1 + 8;
3495                 }
3496
3497                 if (n_tokens < 4 ||
3498                         softnic_parser_read_uint32(&label, tokens[1]) ||
3499                         label > 0xFFFFF ||
3500                         softnic_parser_read_uint32(&tc, tokens[2]) ||
3501                         tc > 0x7 ||
3502                         softnic_parser_read_uint32(&ttl, tokens[3]) ||
3503                         ttl > 0x3F)
3504                         return 0;
3505
3506                 a->encap.mpls.mpls[1].label = label;
3507                 a->encap.mpls.mpls[1].tc = tc;
3508                 a->encap.mpls.mpls[1].ttl = ttl;
3509
3510                 tokens += 4;
3511                 n_tokens -= 4;
3512
3513                 if (n_tokens == 0 ||
3514                         strcmp(tokens[0], "label2")) {
3515                         a->encap.mpls.mpls_count = 2;
3516                         a->encap.type = RTE_TABLE_ACTION_ENCAP_MPLS;
3517                         a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
3518                         return 1 + 8 + 4;
3519                 }
3520
3521                 if (n_tokens < 4 ||
3522                         softnic_parser_read_uint32(&label, tokens[1]) ||
3523                         label > 0xFFFFF ||
3524                         softnic_parser_read_uint32(&tc, tokens[2]) ||
3525                         tc > 0x7 ||
3526                         softnic_parser_read_uint32(&ttl, tokens[3]) ||
3527                         ttl > 0x3F)
3528                         return 0;
3529
3530                 a->encap.mpls.mpls[2].label = label;
3531                 a->encap.mpls.mpls[2].tc = tc;
3532                 a->encap.mpls.mpls[2].ttl = ttl;
3533
3534                 tokens += 4;
3535                 n_tokens -= 4;
3536
3537                 if (n_tokens == 0 ||
3538                         strcmp(tokens[0], "label3")) {
3539                         a->encap.mpls.mpls_count = 3;
3540                         a->encap.type = RTE_TABLE_ACTION_ENCAP_MPLS;
3541                         a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
3542                         return 1 + 8 + 4 + 4;
3543                 }
3544
3545                 if (n_tokens < 4 ||
3546                         softnic_parser_read_uint32(&label, tokens[1]) ||
3547                         label > 0xFFFFF ||
3548                         softnic_parser_read_uint32(&tc, tokens[2]) ||
3549                         tc > 0x7 ||
3550                         softnic_parser_read_uint32(&ttl, tokens[3]) ||
3551                         ttl > 0x3F)
3552                         return 0;
3553
3554                 a->encap.mpls.mpls[3].label = label;
3555                 a->encap.mpls.mpls[3].tc = tc;
3556                 a->encap.mpls.mpls[3].ttl = ttl;
3557
3558                 a->encap.mpls.mpls_count = 4;
3559                 a->encap.type = RTE_TABLE_ACTION_ENCAP_MPLS;
3560                 a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
3561                 return 1 + 8 + 4 + 4 + 4;
3562         }
3563
3564         /* pppoe */
3565         if (n_tokens && (strcmp(tokens[0], "pppoe") == 0)) {
3566                 if (n_tokens < 4 ||
3567                         softnic_parse_mac_addr(tokens[1], &a->encap.pppoe.ether.da) ||
3568                         softnic_parse_mac_addr(tokens[2], &a->encap.pppoe.ether.sa) ||
3569                         softnic_parser_read_uint16(&a->encap.pppoe.pppoe.session_id,
3570                                 tokens[3]))
3571                         return 0;
3572
3573                 a->encap.type = RTE_TABLE_ACTION_ENCAP_PPPOE;
3574                 a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
3575                 return 1 + 4;
3576         }
3577
3578         return 0;
3579 }
3580
3581 static uint32_t
3582 parse_table_action_nat(char **tokens,
3583         uint32_t n_tokens,
3584         struct softnic_table_rule_action *a)
3585 {
3586         if (n_tokens < 4 ||
3587                 strcmp(tokens[0], "nat"))
3588                 return 0;
3589
3590         if (strcmp(tokens[1], "ipv4") == 0) {
3591                 struct in_addr addr;
3592                 uint16_t port;
3593
3594                 if (softnic_parse_ipv4_addr(tokens[2], &addr) ||
3595                         softnic_parser_read_uint16(&port, tokens[3]))
3596                         return 0;
3597
3598                 a->nat.ip_version = 1;
3599                 a->nat.addr.ipv4 = rte_be_to_cpu_32(addr.s_addr);
3600                 a->nat.port = port;
3601                 a->action_mask |= 1 << RTE_TABLE_ACTION_NAT;
3602                 return 4;
3603         }
3604
3605         if (strcmp(tokens[1], "ipv6") == 0) {
3606                 struct in6_addr addr;
3607                 uint16_t port;
3608
3609                 if (softnic_parse_ipv6_addr(tokens[2], &addr) ||
3610                         softnic_parser_read_uint16(&port, tokens[3]))
3611                         return 0;
3612
3613                 a->nat.ip_version = 0;
3614                 memcpy(a->nat.addr.ipv6, addr.s6_addr, 16);
3615                 a->nat.port = port;
3616                 a->action_mask |= 1 << RTE_TABLE_ACTION_NAT;
3617                 return 4;
3618         }
3619
3620         return 0;
3621 }
3622
3623 static uint32_t
3624 parse_table_action_ttl(char **tokens,
3625         uint32_t n_tokens,
3626         struct softnic_table_rule_action *a)
3627 {
3628         if (n_tokens < 2 ||
3629                 strcmp(tokens[0], "ttl"))
3630                 return 0;
3631
3632         if (strcmp(tokens[1], "dec") == 0)
3633                 a->ttl.decrement = 1;
3634         else if (strcmp(tokens[1], "keep") == 0)
3635                 a->ttl.decrement = 0;
3636         else
3637                 return 0;
3638
3639         a->action_mask |= 1 << RTE_TABLE_ACTION_TTL;
3640         return 2;
3641 }
3642
3643 static uint32_t
3644 parse_table_action_stats(char **tokens,
3645         uint32_t n_tokens,
3646         struct softnic_table_rule_action *a)
3647 {
3648         if (n_tokens < 1 ||
3649                 strcmp(tokens[0], "stats"))
3650                 return 0;
3651
3652         a->stats.n_packets = 0;
3653         a->stats.n_bytes = 0;
3654         a->action_mask |= 1 << RTE_TABLE_ACTION_STATS;
3655         return 1;
3656 }
3657
3658 static uint32_t
3659 parse_table_action_time(char **tokens,
3660         uint32_t n_tokens,
3661         struct softnic_table_rule_action *a)
3662 {
3663         if (n_tokens < 1 ||
3664                 strcmp(tokens[0], "time"))
3665                 return 0;
3666
3667         a->time.time = rte_rdtsc();
3668         a->action_mask |= 1 << RTE_TABLE_ACTION_TIME;
3669         return 1;
3670 }
3671
3672 static uint32_t
3673 parse_table_action(char **tokens,
3674         uint32_t n_tokens,
3675         char *out,
3676         size_t out_size,
3677         struct softnic_table_rule_action *a)
3678 {
3679         uint32_t n_tokens0 = n_tokens;
3680
3681         memset(a, 0, sizeof(*a));
3682
3683         if (n_tokens < 2 ||
3684                 strcmp(tokens[0], "action"))
3685                 return 0;
3686
3687         tokens++;
3688         n_tokens--;
3689
3690         if (n_tokens && (strcmp(tokens[0], "fwd") == 0)) {
3691                 uint32_t n;
3692
3693                 n = parse_table_action_fwd(tokens, n_tokens, a);
3694                 if (n == 0) {
3695                         snprintf(out, out_size, MSG_ARG_INVALID,
3696                                 "action fwd");
3697                         return 0;
3698                 }
3699
3700                 tokens += n;
3701                 n_tokens -= n;
3702         }
3703
3704         if (n_tokens && (strcmp(tokens[0], "balance") == 0)) {
3705                 uint32_t n;
3706
3707                 n = parse_table_action_balance(tokens, n_tokens, a);
3708                 if (n == 0) {
3709                         snprintf(out, out_size, MSG_ARG_INVALID,
3710                                 "action balance");
3711                         return 0;
3712                 }
3713
3714                 tokens += n;
3715                 n_tokens -= n;
3716         }
3717
3718         if (n_tokens && (strcmp(tokens[0], "meter") == 0)) {
3719                 uint32_t n;
3720
3721                 n = parse_table_action_meter(tokens, n_tokens, a);
3722                 if (n == 0) {
3723                         snprintf(out, out_size, MSG_ARG_INVALID,
3724                                 "action meter");
3725                         return 0;
3726                 }
3727
3728                 tokens += n;
3729                 n_tokens -= n;
3730         }
3731
3732         if (n_tokens && (strcmp(tokens[0], "tm") == 0)) {
3733                 uint32_t n;
3734
3735                 n = parse_table_action_tm(tokens, n_tokens, a);
3736                 if (n == 0) {
3737                         snprintf(out, out_size, MSG_ARG_INVALID,
3738                                 "action tm");
3739                         return 0;
3740                 }
3741
3742                 tokens += n;
3743                 n_tokens -= n;
3744         }
3745
3746         if (n_tokens && (strcmp(tokens[0], "encap") == 0)) {
3747                 uint32_t n;
3748
3749                 n = parse_table_action_encap(tokens, n_tokens, a);
3750                 if (n == 0) {
3751                         snprintf(out, out_size, MSG_ARG_INVALID,
3752                                 "action encap");
3753                         return 0;
3754                 }
3755
3756                 tokens += n;
3757                 n_tokens -= n;
3758         }
3759
3760         if (n_tokens && (strcmp(tokens[0], "nat") == 0)) {
3761                 uint32_t n;
3762
3763                 n = parse_table_action_nat(tokens, n_tokens, a);
3764                 if (n == 0) {
3765                         snprintf(out, out_size, MSG_ARG_INVALID,
3766                                 "action nat");
3767                         return 0;
3768                 }
3769
3770                 tokens += n;
3771                 n_tokens -= n;
3772         }
3773
3774         if (n_tokens && (strcmp(tokens[0], "ttl") == 0)) {
3775                 uint32_t n;
3776
3777                 n = parse_table_action_ttl(tokens, n_tokens, a);
3778                 if (n == 0) {
3779                         snprintf(out, out_size, MSG_ARG_INVALID,
3780                                 "action ttl");
3781                         return 0;
3782                 }
3783
3784                 tokens += n;
3785                 n_tokens -= n;
3786         }
3787
3788         if (n_tokens && (strcmp(tokens[0], "stats") == 0)) {
3789                 uint32_t n;
3790
3791                 n = parse_table_action_stats(tokens, n_tokens, a);
3792                 if (n == 0) {
3793                         snprintf(out, out_size, MSG_ARG_INVALID,
3794                                 "action stats");
3795                         return 0;
3796                 }
3797
3798                 tokens += n;
3799                 n_tokens -= n;
3800         }
3801
3802         if (n_tokens && (strcmp(tokens[0], "time") == 0)) {
3803                 uint32_t n;
3804
3805                 n = parse_table_action_time(tokens, n_tokens, a);
3806                 if (n == 0) {
3807                         snprintf(out, out_size, MSG_ARG_INVALID,
3808                                 "action time");
3809                         return 0;
3810                 }
3811
3812                 tokens += n;
3813                 n_tokens -= n;
3814         }
3815
3816         if (n_tokens0 - n_tokens == 1) {
3817                 snprintf(out, out_size, MSG_ARG_INVALID, "action");
3818                 return 0;
3819         }
3820
3821         return n_tokens0 - n_tokens;
3822 }
3823
3824 /**
3825  * pipeline <pipeline_name> table <table_id> rule add
3826  *    match <match>
3827  *    action <table_action>
3828  */
3829 static void
3830 cmd_softnic_pipeline_table_rule_add(struct pmd_internals *softnic,
3831         char **tokens,
3832         uint32_t n_tokens,
3833         char *out,
3834         size_t out_size)
3835 {
3836         struct softnic_table_rule_match m;
3837         struct softnic_table_rule_action a;
3838         char *pipeline_name;
3839         void *data;
3840         uint32_t table_id, t0, n_tokens_parsed;
3841         int status;
3842
3843         if (n_tokens < 8) {
3844                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3845                 return;
3846         }
3847
3848         pipeline_name = tokens[1];
3849
3850         if (strcmp(tokens[2], "table") != 0) {
3851                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
3852                 return;
3853         }
3854
3855         if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
3856                 snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
3857                 return;
3858         }
3859
3860         if (strcmp(tokens[4], "rule") != 0) {
3861                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule");
3862                 return;
3863         }
3864
3865         if (strcmp(tokens[5], "add") != 0) {
3866                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
3867                 return;
3868         }
3869
3870         t0 = 6;
3871
3872         /* match */
3873         n_tokens_parsed = parse_match(tokens + t0,
3874                 n_tokens - t0,
3875                 out,
3876                 out_size,
3877                 &m);
3878         if (n_tokens_parsed == 0)
3879                 return;
3880         t0 += n_tokens_parsed;
3881
3882         /* action */
3883         n_tokens_parsed = parse_table_action(tokens + t0,
3884                 n_tokens - t0,
3885                 out,
3886                 out_size,
3887                 &a);
3888         if (n_tokens_parsed == 0)
3889                 return;
3890         t0 += n_tokens_parsed;
3891
3892         if (t0 != n_tokens) {
3893                 snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
3894                 return;
3895         }
3896
3897         status = softnic_pipeline_table_rule_add(softnic,
3898                 pipeline_name,
3899                 table_id,
3900                 &m,
3901                 &a,
3902                 &data);
3903         if (status) {
3904                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
3905                 return;
3906         }
3907 }
3908
3909 /**
3910  * pipeline <pipeline_name> table <table_id> rule add
3911  *    match
3912  *       default
3913  *    action
3914  *       fwd
3915  *          drop
3916  *          | port <port_id>
3917  *          | meta
3918  *          | table <table_id>
3919  */
3920 static void
3921 cmd_softnic_pipeline_table_rule_add_default(struct pmd_internals *softnic,
3922         char **tokens,
3923         uint32_t n_tokens,
3924         char *out,
3925         size_t out_size)
3926 {
3927         struct softnic_table_rule_action action;
3928         void *data;
3929         char *pipeline_name;
3930         uint32_t table_id;
3931         int status;
3932
3933         if (n_tokens != 11 &&
3934                 n_tokens != 12) {
3935                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3936                 return;
3937         }
3938
3939         pipeline_name = tokens[1];
3940
3941         if (strcmp(tokens[2], "table") != 0) {
3942                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
3943                 return;
3944         }
3945
3946         if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
3947                 snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
3948                 return;
3949         }
3950
3951         if (strcmp(tokens[4], "rule") != 0) {
3952                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule");
3953                 return;
3954         }
3955
3956         if (strcmp(tokens[5], "add") != 0) {
3957                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
3958                 return;
3959         }
3960
3961         if (strcmp(tokens[6], "match") != 0) {
3962                 snprintf(out, out_size, MSG_ARG_INVALID, "match");
3963                 return;
3964         }
3965
3966         if (strcmp(tokens[7], "default") != 0) {
3967                 snprintf(out, out_size, MSG_ARG_INVALID, "default");
3968                 return;
3969         }
3970
3971         if (strcmp(tokens[8], "action") != 0) {
3972                 snprintf(out, out_size, MSG_ARG_INVALID, "action");
3973                 return;
3974         }
3975
3976         if (strcmp(tokens[9], "fwd") != 0) {
3977                 snprintf(out, out_size, MSG_ARG_INVALID, "fwd");
3978                 return;
3979         }
3980
3981         action.action_mask = 1 << RTE_TABLE_ACTION_FWD;
3982
3983         if (strcmp(tokens[10], "drop") == 0) {
3984                 if (n_tokens != 11) {
3985                         snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3986                         return;
3987                 }
3988
3989                 action.fwd.action = RTE_PIPELINE_ACTION_DROP;
3990         } else if (strcmp(tokens[10], "port") == 0) {
3991                 uint32_t id;
3992
3993                 if (n_tokens != 12) {
3994                         snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3995                         return;
3996                 }
3997
3998                 if (softnic_parser_read_uint32(&id, tokens[11]) != 0) {
3999                         snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
4000                         return;
4001                 }
4002
4003                 action.fwd.action = RTE_PIPELINE_ACTION_PORT;
4004                 action.fwd.id = id;
4005         } else if (strcmp(tokens[10], "meta") == 0) {
4006                 if (n_tokens != 11) {
4007                         snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4008                         return;
4009                 }
4010
4011                 action.fwd.action = RTE_PIPELINE_ACTION_PORT_META;
4012         } else if (strcmp(tokens[10], "table") == 0) {
4013                 uint32_t id;
4014
4015                 if (n_tokens != 12) {
4016                         snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4017                         return;
4018                 }
4019
4020                 if (softnic_parser_read_uint32(&id, tokens[11]) != 0) {
4021                         snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
4022                         return;
4023                 }
4024
4025                 action.fwd.action = RTE_PIPELINE_ACTION_TABLE;
4026                 action.fwd.id = id;
4027         } else {
4028                 snprintf(out, out_size, MSG_ARG_INVALID,
4029                         "drop or port or meta or table");
4030                 return;
4031         }
4032
4033         status = softnic_pipeline_table_rule_add_default(softnic,
4034                 pipeline_name,
4035                 table_id,
4036                 &action,
4037                 &data);
4038         if (status) {
4039                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
4040                 return;
4041         }
4042 }
4043
4044 /**
4045  * pipeline <pipeline_name> table <table_id> rule add bulk <file_name> <n_rules>
4046  *
4047  * File <file_name>:
4048  * - line format: match <match> action <action>
4049  */
4050 static int
4051 cli_rule_file_process(const char *file_name,
4052         size_t line_len_max,
4053         struct softnic_table_rule_match *m,
4054         struct softnic_table_rule_action *a,
4055         uint32_t *n_rules,
4056         uint32_t *line_number,
4057         char *out,
4058         size_t out_size);
4059
4060 static void
4061 cmd_softnic_pipeline_table_rule_add_bulk(struct pmd_internals *softnic,
4062         char **tokens,
4063         uint32_t n_tokens,
4064         char *out,
4065         size_t out_size)
4066 {
4067         struct softnic_table_rule_match *match;
4068         struct softnic_table_rule_action *action;
4069         void **data;
4070         char *pipeline_name, *file_name;
4071         uint32_t table_id, n_rules, n_rules_parsed, line_number;
4072         int status;
4073
4074         if (n_tokens != 9) {
4075                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4076                 return;
4077         }
4078
4079         pipeline_name = tokens[1];
4080
4081         if (strcmp(tokens[2], "table") != 0) {
4082                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
4083                 return;
4084         }
4085
4086         if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
4087                 snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
4088                 return;
4089         }
4090
4091         if (strcmp(tokens[4], "rule") != 0) {
4092                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule");
4093                 return;
4094         }
4095
4096         if (strcmp(tokens[5], "add") != 0) {
4097                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
4098                 return;
4099         }
4100
4101         if (strcmp(tokens[6], "bulk") != 0) {
4102                 snprintf(out, out_size, MSG_ARG_INVALID, "bulk");
4103                 return;
4104         }
4105
4106         file_name = tokens[7];
4107
4108         if ((softnic_parser_read_uint32(&n_rules, tokens[8]) != 0) ||
4109                 n_rules == 0) {
4110                 snprintf(out, out_size, MSG_ARG_INVALID, "n_rules");
4111                 return;
4112         }
4113
4114         /* Memory allocation. */
4115         match = calloc(n_rules, sizeof(struct softnic_table_rule_match));
4116         action = calloc(n_rules, sizeof(struct softnic_table_rule_action));
4117         data = calloc(n_rules, sizeof(void *));
4118         if (match == NULL ||
4119                 action == NULL ||
4120                 data == NULL) {
4121                 snprintf(out, out_size, MSG_OUT_OF_MEMORY);
4122                 free(data);
4123                 free(action);
4124                 free(match);
4125                 return;
4126         }
4127
4128         /* Load rule file */
4129         n_rules_parsed = n_rules;
4130         status = cli_rule_file_process(file_name,
4131                 1024,
4132                 match,
4133                 action,
4134                 &n_rules_parsed,
4135                 &line_number,
4136                 out,
4137                 out_size);
4138         if (status) {
4139                 snprintf(out, out_size, MSG_FILE_ERR, file_name, line_number);
4140                 free(data);
4141                 free(action);
4142                 free(match);
4143                 return;
4144         }
4145         if (n_rules_parsed != n_rules) {
4146                 snprintf(out, out_size, MSG_FILE_NOT_ENOUGH, file_name);
4147                 free(data);
4148                 free(action);
4149                 free(match);
4150                 return;
4151         }
4152
4153         /* Rule bulk add */
4154         status = softnic_pipeline_table_rule_add_bulk(softnic,
4155                 pipeline_name,
4156                 table_id,
4157                 match,
4158                 action,
4159                 data,
4160                 &n_rules);
4161         if (status) {
4162                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
4163                 free(data);
4164                 free(action);
4165                 free(match);
4166                 return;
4167         }
4168
4169         /* Memory free */
4170         free(data);
4171         free(action);
4172         free(match);
4173 }
4174
4175 /**
4176  * pipeline <pipeline_name> table <table_id> rule delete
4177  *    match <match>
4178  */
4179 static void
4180 cmd_softnic_pipeline_table_rule_delete(struct pmd_internals *softnic,
4181         char **tokens,
4182         uint32_t n_tokens,
4183         char *out,
4184         size_t out_size)
4185 {
4186         struct softnic_table_rule_match m;
4187         char *pipeline_name;
4188         uint32_t table_id, n_tokens_parsed, t0;
4189         int status;
4190
4191         if (n_tokens < 8) {
4192                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4193                 return;
4194         }
4195
4196         pipeline_name = tokens[1];
4197
4198         if (strcmp(tokens[2], "table") != 0) {
4199                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
4200                 return;
4201         }
4202
4203         if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
4204                 snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
4205                 return;
4206         }
4207
4208         if (strcmp(tokens[4], "rule") != 0) {
4209                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule");
4210                 return;
4211         }
4212
4213         if (strcmp(tokens[5], "delete") != 0) {
4214                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
4215                 return;
4216         }
4217
4218         t0 = 6;
4219
4220         /* match */
4221         n_tokens_parsed = parse_match(tokens + t0,
4222                 n_tokens - t0,
4223                 out,
4224                 out_size,
4225                 &m);
4226         if (n_tokens_parsed == 0)
4227                 return;
4228         t0 += n_tokens_parsed;
4229
4230         if (n_tokens != t0) {
4231                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4232                 return;
4233         }
4234
4235         status = softnic_pipeline_table_rule_delete(softnic,
4236                 pipeline_name,
4237                 table_id,
4238                 &m);
4239         if (status) {
4240                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
4241                 return;
4242         }
4243 }
4244
4245 /**
4246  * pipeline <pipeline_name> table <table_id> rule delete
4247  *    match
4248  *       default
4249  */
4250 static void
4251 cmd_softnic_pipeline_table_rule_delete_default(struct pmd_internals *softnic,
4252         char **tokens,
4253         uint32_t n_tokens,
4254         char *out,
4255         size_t out_size)
4256 {
4257         char *pipeline_name;
4258         uint32_t table_id;
4259         int status;
4260
4261         if (n_tokens != 8) {
4262                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4263                 return;
4264         }
4265
4266         pipeline_name = tokens[1];
4267
4268         if (strcmp(tokens[2], "table") != 0) {
4269                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
4270                 return;
4271         }
4272
4273         if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
4274                 snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
4275                 return;
4276         }
4277
4278         if (strcmp(tokens[4], "rule") != 0) {
4279                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule");
4280                 return;
4281         }
4282
4283         if (strcmp(tokens[5], "delete") != 0) {
4284                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
4285                 return;
4286         }
4287
4288         if (strcmp(tokens[6], "match") != 0) {
4289                 snprintf(out, out_size, MSG_ARG_INVALID, "match");
4290                 return;
4291         }
4292
4293         if (strcmp(tokens[7], "default") != 0) {
4294                 snprintf(out, out_size, MSG_ARG_INVALID, "default");
4295                 return;
4296         }
4297
4298         status = softnic_pipeline_table_rule_delete_default(softnic,
4299                 pipeline_name,
4300                 table_id);
4301         if (status) {
4302                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
4303                 return;
4304         }
4305 }
4306
4307 /**
4308  * pipeline <pipeline_name> table <table_id> rule read stats [clear]
4309  */
4310 static void
4311 cmd_softnic_pipeline_table_rule_stats_read(struct pmd_internals *softnic __rte_unused,
4312         char **tokens,
4313         uint32_t n_tokens __rte_unused,
4314         char *out,
4315         size_t out_size)
4316 {
4317         snprintf(out, out_size, MSG_CMD_UNIMPLEM, tokens[0]);
4318 }
4319
4320 /**
4321  * pipeline <pipeline_name> table <table_id> meter profile <meter_profile_id>
4322  *  add srtcm cir <cir> cbs <cbs> ebs <ebs>
4323  *  | trtcm cir <cir> pir <pir> cbs <cbs> pbs <pbs>
4324  */
4325 static void
4326 cmd_pipeline_table_meter_profile_add(struct pmd_internals *softnic,
4327         char **tokens,
4328         uint32_t n_tokens,
4329         char *out,
4330         size_t out_size)
4331 {
4332         struct rte_table_action_meter_profile p;
4333         char *pipeline_name;
4334         uint32_t table_id, meter_profile_id;
4335         int status;
4336
4337         if (n_tokens < 9) {
4338                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4339                 return;
4340         }
4341
4342         pipeline_name = tokens[1];
4343
4344         if (strcmp(tokens[2], "table") != 0) {
4345                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
4346                 return;
4347         }
4348
4349         if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
4350                 snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
4351                 return;
4352         }
4353
4354         if (strcmp(tokens[4], "meter") != 0) {
4355                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
4356                 return;
4357         }
4358
4359         if (strcmp(tokens[5], "profile") != 0) {
4360                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
4361                 return;
4362         }
4363
4364         if (softnic_parser_read_uint32(&meter_profile_id, tokens[6]) != 0) {
4365                 snprintf(out, out_size, MSG_ARG_INVALID, "meter_profile_id");
4366                 return;
4367         }
4368
4369         if (strcmp(tokens[7], "add") != 0) {
4370                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
4371                 return;
4372         }
4373
4374         if (strcmp(tokens[8], "srtcm") == 0) {
4375                 if (n_tokens != 15) {
4376                         snprintf(out, out_size, MSG_ARG_MISMATCH,
4377                                 tokens[0]);
4378                         return;
4379                 }
4380
4381                 p.alg = RTE_TABLE_ACTION_METER_SRTCM;
4382
4383                 if (strcmp(tokens[9], "cir") != 0) {
4384                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cir");
4385                         return;
4386                 }
4387
4388                 if (softnic_parser_read_uint64(&p.srtcm.cir, tokens[10]) != 0) {
4389                         snprintf(out, out_size, MSG_ARG_INVALID, "cir");
4390                         return;
4391                 }
4392
4393                 if (strcmp(tokens[11], "cbs") != 0) {
4394                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cbs");
4395                         return;
4396                 }
4397
4398                 if (softnic_parser_read_uint64(&p.srtcm.cbs, tokens[12]) != 0) {
4399                         snprintf(out, out_size, MSG_ARG_INVALID, "cbs");
4400                         return;
4401                 }
4402
4403                 if (strcmp(tokens[13], "ebs") != 0) {
4404                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "ebs");
4405                         return;
4406                 }
4407
4408                 if (softnic_parser_read_uint64(&p.srtcm.ebs, tokens[14]) != 0) {
4409                         snprintf(out, out_size, MSG_ARG_INVALID, "ebs");
4410                         return;
4411                 }
4412         } else if (strcmp(tokens[8], "trtcm") == 0) {
4413                 if (n_tokens != 17) {
4414                         snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4415                         return;
4416                 }
4417
4418                 p.alg = RTE_TABLE_ACTION_METER_TRTCM;
4419
4420                 if (strcmp(tokens[9], "cir") != 0) {
4421                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cir");
4422                         return;
4423                 }
4424
4425                 if (softnic_parser_read_uint64(&p.trtcm.cir, tokens[10]) != 0) {
4426                         snprintf(out, out_size, MSG_ARG_INVALID, "cir");
4427                         return;
4428                 }
4429
4430                 if (strcmp(tokens[11], "pir") != 0) {
4431                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pir");
4432                         return;
4433                 }
4434
4435                 if (softnic_parser_read_uint64(&p.trtcm.pir, tokens[12]) != 0) {
4436                         snprintf(out, out_size, MSG_ARG_INVALID, "pir");
4437                         return;
4438                 }
4439                 if (strcmp(tokens[13], "cbs") != 0) {
4440                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cbs");
4441                         return;
4442                 }
4443
4444                 if (softnic_parser_read_uint64(&p.trtcm.cbs, tokens[14]) != 0) {
4445                         snprintf(out, out_size, MSG_ARG_INVALID, "cbs");
4446                         return;
4447                 }
4448
4449                 if (strcmp(tokens[15], "pbs") != 0) {
4450                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pbs");
4451                         return;
4452                 }
4453
4454                 if (softnic_parser_read_uint64(&p.trtcm.pbs, tokens[16]) != 0) {
4455                         snprintf(out, out_size, MSG_ARG_INVALID, "pbs");
4456                         return;
4457                 }
4458         } else {
4459                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4460                 return;
4461         }
4462
4463         status = softnic_pipeline_table_mtr_profile_add(softnic,
4464                 pipeline_name,
4465                 table_id,
4466                 meter_profile_id,
4467                 &p);
4468         if (status) {
4469                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
4470                 return;
4471         }
4472 }
4473
4474 /**
4475  * pipeline <pipeline_name> table <table_id>
4476  *  meter profile <meter_profile_id> delete
4477  */
4478 static void
4479 cmd_pipeline_table_meter_profile_delete(struct pmd_internals *softnic,
4480         char **tokens,
4481         uint32_t n_tokens,
4482         char *out,
4483         size_t out_size)
4484 {
4485         char *pipeline_name;
4486         uint32_t table_id, meter_profile_id;
4487         int status;
4488
4489         if (n_tokens != 8) {
4490                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4491                 return;
4492         }
4493
4494         pipeline_name = tokens[1];
4495
4496         if (strcmp(tokens[2], "table") != 0) {
4497                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
4498                 return;
4499         }
4500
4501         if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
4502                 snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
4503                 return;
4504         }
4505
4506         if (strcmp(tokens[4], "meter") != 0) {
4507                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
4508                 return;
4509         }
4510
4511         if (strcmp(tokens[5], "profile") != 0) {
4512                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
4513                 return;
4514         }
4515
4516         if (softnic_parser_read_uint32(&meter_profile_id, tokens[6]) != 0) {
4517                 snprintf(out, out_size, MSG_ARG_INVALID, "meter_profile_id");
4518                 return;
4519         }
4520
4521         if (strcmp(tokens[7], "delete") != 0) {
4522                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
4523                 return;
4524         }
4525
4526         status = softnic_pipeline_table_mtr_profile_delete(softnic,
4527                 pipeline_name,
4528                 table_id,
4529                 meter_profile_id);
4530         if (status) {
4531                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
4532                 return;
4533         }
4534 }
4535
4536 /**
4537  * pipeline <pipeline_name> table <table_id> rule read meter [clear]
4538  */
4539 static void
4540 cmd_pipeline_table_rule_meter_read(struct pmd_internals *softnic __rte_unused,
4541         char **tokens,
4542         uint32_t n_tokens __rte_unused,
4543         char *out,
4544         size_t out_size)
4545 {
4546         snprintf(out, out_size, MSG_CMD_UNIMPLEM, tokens[0]);
4547 }
4548
4549 /**
4550  * pipeline <pipeline_name> table <table_id> dscp <file_name>
4551  *
4552  * File <file_name>:
4553  *  - exactly 64 lines
4554  *  - line format: <tc_id> <tc_queue_id> <color>, with <color> as: g | y | r
4555  */
4556 static int
4557 load_dscp_table(struct rte_table_action_dscp_table *dscp_table,
4558         const char *file_name,
4559         uint32_t *line_number)
4560 {
4561         FILE *f = NULL;
4562         uint32_t dscp, l;
4563
4564         /* Check input arguments */
4565         if (dscp_table == NULL ||
4566                 file_name == NULL ||
4567                 line_number == NULL) {
4568                 if (line_number)
4569                         *line_number = 0;
4570                 return -EINVAL;
4571         }
4572
4573         /* Open input file */
4574         f = fopen(file_name, "r");
4575         if (f == NULL) {
4576                 *line_number = 0;
4577                 return -EINVAL;
4578         }
4579
4580         /* Read file */
4581         for (dscp = 0, l = 1; ; l++) {
4582                 char line[64];
4583                 char *tokens[3];
4584                 enum rte_meter_color color;
4585                 uint32_t tc_id, tc_queue_id, n_tokens = RTE_DIM(tokens);
4586
4587                 if (fgets(line, sizeof(line), f) == NULL)
4588                         break;
4589
4590                 if (is_comment(line))
4591                         continue;
4592
4593                 if (softnic_parse_tokenize_string(line, tokens, &n_tokens)) {
4594                         *line_number = l;
4595                         fclose(f);
4596                         return -EINVAL;
4597                 }
4598
4599                 if (n_tokens == 0)
4600                         continue;
4601
4602                 if (dscp >= RTE_DIM(dscp_table->entry) ||
4603                         n_tokens != RTE_DIM(tokens) ||
4604                         softnic_parser_read_uint32(&tc_id, tokens[0]) ||
4605                         tc_id >= RTE_TABLE_ACTION_TC_MAX ||
4606                         softnic_parser_read_uint32(&tc_queue_id, tokens[1]) ||
4607                         tc_queue_id >= RTE_TABLE_ACTION_TC_QUEUE_MAX ||
4608                         (strlen(tokens[2]) != 1)) {
4609                         *line_number = l;
4610                         fclose(f);
4611                         return -EINVAL;
4612                 }
4613
4614                 switch (tokens[2][0]) {
4615                 case 'g':
4616                 case 'G':
4617                         color = e_RTE_METER_GREEN;
4618                         break;
4619
4620                 case 'y':
4621                 case 'Y':
4622                         color = e_RTE_METER_YELLOW;
4623                         break;
4624
4625                 case 'r':
4626                 case 'R':
4627                         color = e_RTE_METER_RED;
4628                         break;
4629
4630                 default:
4631                         *line_number = l;
4632                         fclose(f);
4633                         return -EINVAL;
4634                 }
4635
4636                 dscp_table->entry[dscp].tc_id = tc_id;
4637                 dscp_table->entry[dscp].tc_queue_id = tc_queue_id;
4638                 dscp_table->entry[dscp].color = color;
4639                 dscp++;
4640         }
4641
4642         /* Close file */
4643         fclose(f);
4644         return 0;
4645 }
4646
4647 static void
4648 cmd_pipeline_table_dscp(struct pmd_internals *softnic,
4649         char **tokens,
4650         uint32_t n_tokens,
4651         char *out,
4652         size_t out_size)
4653 {
4654         struct rte_table_action_dscp_table dscp_table;
4655         char *pipeline_name, *file_name;
4656         uint32_t table_id, line_number;
4657         int status;
4658
4659         if (n_tokens != 6) {
4660                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4661                 return;
4662         }
4663
4664         pipeline_name = tokens[1];
4665
4666         if (strcmp(tokens[2], "table") != 0) {
4667                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
4668                 return;
4669         }
4670
4671         if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
4672                 snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
4673                 return;
4674         }
4675
4676         if (strcmp(tokens[4], "dscp") != 0) {
4677                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "dscp");
4678                 return;
4679         }
4680
4681         file_name = tokens[5];
4682
4683         status = load_dscp_table(&dscp_table, file_name, &line_number);
4684         if (status) {
4685                 snprintf(out, out_size, MSG_FILE_ERR, file_name, line_number);
4686                 return;
4687         }
4688
4689         status = softnic_pipeline_table_dscp_table_update(softnic,
4690                 pipeline_name,
4691                 table_id,
4692                 UINT64_MAX,
4693                 &dscp_table);
4694         if (status) {
4695                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
4696                 return;
4697         }
4698 }
4699
4700 /**
4701  * pipeline <pipeline_name> table <table_id> rule read ttl [clear]
4702  */
4703 static void
4704 cmd_softnic_pipeline_table_rule_ttl_read(struct pmd_internals *softnic __rte_unused,
4705         char **tokens,
4706         uint32_t n_tokens __rte_unused,
4707         char *out,
4708         size_t out_size)
4709 {
4710         snprintf(out, out_size, MSG_CMD_UNIMPLEM, tokens[0]);
4711 }
4712
4713 /**
4714  * thread <thread_id> pipeline <pipeline_name> enable
4715  */
4716 static void
4717 cmd_softnic_thread_pipeline_enable(struct pmd_internals *softnic,
4718         char **tokens,
4719         uint32_t n_tokens,
4720         char *out,
4721         size_t out_size)
4722 {
4723         char *pipeline_name;
4724         uint32_t thread_id;
4725         int status;
4726
4727         if (n_tokens != 5) {
4728                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4729                 return;
4730         }
4731
4732         if (softnic_parser_read_uint32(&thread_id, tokens[1]) != 0) {
4733                 snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
4734                 return;
4735         }
4736
4737         if (strcmp(tokens[2], "pipeline") != 0) {
4738                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
4739                 return;
4740         }
4741
4742         pipeline_name = tokens[3];
4743
4744         if (strcmp(tokens[4], "enable") != 0) {
4745                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
4746                 return;
4747         }
4748
4749         status = softnic_thread_pipeline_enable(softnic, thread_id, pipeline_name);
4750         if (status) {
4751                 snprintf(out, out_size, MSG_CMD_FAIL, "thread pipeline enable");
4752                 return;
4753         }
4754 }
4755
4756 /**
4757  * thread <thread_id> pipeline <pipeline_name> disable
4758  */
4759 static void
4760 cmd_softnic_thread_pipeline_disable(struct pmd_internals *softnic,
4761         char **tokens,
4762         uint32_t n_tokens,
4763         char *out,
4764         size_t out_size)
4765 {
4766         char *pipeline_name;
4767         uint32_t thread_id;
4768         int status;
4769
4770         if (n_tokens != 5) {
4771                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4772                 return;
4773         }
4774
4775         if (softnic_parser_read_uint32(&thread_id, tokens[1]) != 0) {
4776                 snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
4777                 return;
4778         }
4779
4780         if (strcmp(tokens[2], "pipeline") != 0) {
4781                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
4782                 return;
4783         }
4784
4785         pipeline_name = tokens[3];
4786
4787         if (strcmp(tokens[4], "disable") != 0) {
4788                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
4789                 return;
4790         }
4791
4792         status = softnic_thread_pipeline_disable(softnic, thread_id, pipeline_name);
4793         if (status) {
4794                 snprintf(out, out_size, MSG_CMD_FAIL,
4795                         "thread pipeline disable");
4796                 return;
4797         }
4798 }
4799
4800 void
4801 softnic_cli_process(char *in, char *out, size_t out_size, void *arg)
4802 {
4803         char *tokens[CMD_MAX_TOKENS];
4804         uint32_t n_tokens = RTE_DIM(tokens);
4805         struct pmd_internals *softnic = arg;
4806         int status;
4807
4808         if (is_comment(in))
4809                 return;
4810
4811         status = softnic_parse_tokenize_string(in, tokens, &n_tokens);
4812         if (status) {
4813                 snprintf(out, out_size, MSG_ARG_TOO_MANY, "");
4814                 return;
4815         }
4816
4817         if (n_tokens == 0)
4818                 return;
4819
4820         if (strcmp(tokens[0], "mempool") == 0) {
4821                 cmd_mempool(softnic, tokens, n_tokens, out, out_size);
4822                 return;
4823         }
4824
4825         if (strcmp(tokens[0], "link") == 0) {
4826                 cmd_link(softnic, tokens, n_tokens, out, out_size);
4827                 return;
4828         }
4829
4830         if (strcmp(tokens[0], "swq") == 0) {
4831                 cmd_swq(softnic, tokens, n_tokens, out, out_size);
4832                 return;
4833         }
4834
4835         if (strcmp(tokens[0], "tmgr") == 0) {
4836                 if (n_tokens == 2) {
4837                         cmd_tmgr(softnic, tokens, n_tokens, out, out_size);
4838                         return;
4839                 }
4840
4841                 if (n_tokens >= 3 &&
4842                         (strcmp(tokens[1], "shaper") == 0) &&
4843                         (strcmp(tokens[2], "profile") == 0)) {
4844                         cmd_tmgr_shaper_profile(softnic, tokens, n_tokens, out, out_size);
4845                         return;
4846                 }
4847
4848                 if (n_tokens >= 3 &&
4849                         (strcmp(tokens[1], "shared") == 0) &&
4850                         (strcmp(tokens[2], "shaper") == 0)) {
4851                         cmd_tmgr_shared_shaper(softnic, tokens, n_tokens, out, out_size);
4852                         return;
4853                 }
4854
4855                 if (n_tokens >= 2 &&
4856                         (strcmp(tokens[1], "node") == 0)) {
4857                         cmd_tmgr_node(softnic, tokens, n_tokens, out, out_size);
4858                         return;
4859                 }
4860
4861                 if (n_tokens >= 2 &&
4862                         (strcmp(tokens[1], "hierarchy-default") == 0)) {
4863                         cmd_tmgr_hierarchy_default(softnic, tokens, n_tokens, out, out_size);
4864                         return;
4865                 }
4866
4867                 if (n_tokens >= 3 &&
4868                         (strcmp(tokens[1], "hierarchy") == 0) &&
4869                         (strcmp(tokens[2], "commit") == 0)) {
4870                         cmd_tmgr_hierarchy_commit(softnic, tokens, n_tokens, out, out_size);
4871                         return;
4872                 }
4873         }
4874
4875         if (strcmp(tokens[0], "tap") == 0) {
4876                 cmd_tap(softnic, tokens, n_tokens, out, out_size);
4877                 return;
4878         }
4879
4880         if (strcmp(tokens[0], "port") == 0) {
4881                 cmd_port_in_action_profile(softnic, tokens, n_tokens, out, out_size);
4882                 return;
4883         }
4884
4885         if (strcmp(tokens[0], "table") == 0) {
4886                 cmd_table_action_profile(softnic, tokens, n_tokens, out, out_size);
4887                 return;
4888         }
4889
4890         if (strcmp(tokens[0], "pipeline") == 0) {
4891                 if (n_tokens >= 3 &&
4892                         (strcmp(tokens[2], "period") == 0)) {
4893                         cmd_pipeline(softnic, tokens, n_tokens, out, out_size);
4894                         return;
4895                 }
4896
4897                 if (n_tokens >= 5 &&
4898                         (strcmp(tokens[2], "port") == 0) &&
4899                         (strcmp(tokens[3], "in") == 0) &&
4900                         (strcmp(tokens[4], "bsz") == 0)) {
4901                         cmd_pipeline_port_in(softnic, tokens, n_tokens, out, out_size);
4902                         return;
4903                 }
4904
4905                 if (n_tokens >= 5 &&
4906                         (strcmp(tokens[2], "port") == 0) &&
4907                         (strcmp(tokens[3], "out") == 0) &&
4908                         (strcmp(tokens[4], "bsz") == 0)) {
4909                         cmd_pipeline_port_out(softnic, tokens, n_tokens, out, out_size);
4910                         return;
4911                 }
4912
4913                 if (n_tokens >= 4 &&
4914                         (strcmp(tokens[2], "table") == 0) &&
4915                         (strcmp(tokens[3], "match") == 0)) {
4916                         cmd_pipeline_table(softnic, tokens, n_tokens, out, out_size);
4917                         return;
4918                 }
4919
4920                 if (n_tokens >= 6 &&
4921                         (strcmp(tokens[2], "port") == 0) &&
4922                         (strcmp(tokens[3], "in") == 0) &&
4923                         (strcmp(tokens[5], "table") == 0)) {
4924                         cmd_pipeline_port_in_table(softnic, tokens, n_tokens,
4925                                 out, out_size);
4926                         return;
4927                 }
4928
4929                 if (n_tokens >= 6 &&
4930                         (strcmp(tokens[2], "port") == 0) &&
4931                         (strcmp(tokens[3], "in") == 0) &&
4932                         (strcmp(tokens[5], "stats") == 0)) {
4933                         cmd_pipeline_port_in_stats(softnic, tokens, n_tokens,
4934                                 out, out_size);
4935                         return;
4936                 }
4937
4938                 if (n_tokens >= 6 &&
4939                         (strcmp(tokens[2], "port") == 0) &&
4940                         (strcmp(tokens[3], "in") == 0) &&
4941                         (strcmp(tokens[5], "enable") == 0)) {
4942                         cmd_softnic_pipeline_port_in_enable(softnic, tokens, n_tokens,
4943                                 out, out_size);
4944                         return;
4945                 }
4946
4947                 if (n_tokens >= 6 &&
4948                         (strcmp(tokens[2], "port") == 0) &&
4949                         (strcmp(tokens[3], "in") == 0) &&
4950                         (strcmp(tokens[5], "disable") == 0)) {
4951                         cmd_softnic_pipeline_port_in_disable(softnic, tokens, n_tokens,
4952                                 out, out_size);
4953                         return;
4954                 }
4955
4956                 if (n_tokens >= 6 &&
4957                         (strcmp(tokens[2], "port") == 0) &&
4958                         (strcmp(tokens[3], "out") == 0) &&
4959                         (strcmp(tokens[5], "stats") == 0)) {
4960                         cmd_pipeline_port_out_stats(softnic, tokens, n_tokens,
4961                                 out, out_size);
4962                         return;
4963                 }
4964
4965                 if (n_tokens >= 5 &&
4966                         (strcmp(tokens[2], "table") == 0) &&
4967                         (strcmp(tokens[4], "stats") == 0)) {
4968                         cmd_pipeline_table_stats(softnic, tokens, n_tokens,
4969                                 out, out_size);
4970                         return;
4971                 }
4972
4973                 if (n_tokens >= 7 &&
4974                         (strcmp(tokens[2], "table") == 0) &&
4975                         (strcmp(tokens[4], "rule") == 0) &&
4976                         (strcmp(tokens[5], "add") == 0) &&
4977                         (strcmp(tokens[6], "match") == 0)) {
4978                         if (n_tokens >= 8 &&
4979                                 (strcmp(tokens[7], "default") == 0)) {
4980                                 cmd_softnic_pipeline_table_rule_add_default(softnic, tokens,
4981                                         n_tokens, out, out_size);
4982                                 return;
4983                         }
4984
4985                         cmd_softnic_pipeline_table_rule_add(softnic, tokens, n_tokens,
4986                                 out, out_size);
4987                         return;
4988                 }
4989
4990                 if (n_tokens >= 7 &&
4991                         (strcmp(tokens[2], "table") == 0) &&
4992                         (strcmp(tokens[4], "rule") == 0) &&
4993                         (strcmp(tokens[5], "add") == 0) &&
4994                         (strcmp(tokens[6], "bulk") == 0)) {
4995                         cmd_softnic_pipeline_table_rule_add_bulk(softnic, tokens,
4996                                 n_tokens, out, out_size);
4997                         return;
4998                 }
4999
5000                 if (n_tokens >= 7 &&
5001                         (strcmp(tokens[2], "table") == 0) &&
5002                         (strcmp(tokens[4], "rule") == 0) &&
5003                         (strcmp(tokens[5], "delete") == 0) &&
5004                         (strcmp(tokens[6], "match") == 0)) {
5005                         if (n_tokens >= 8 &&
5006                                 (strcmp(tokens[7], "default") == 0)) {
5007                                 cmd_softnic_pipeline_table_rule_delete_default(softnic, tokens,
5008                                         n_tokens, out, out_size);
5009                                 return;
5010                                 }
5011
5012                         cmd_softnic_pipeline_table_rule_delete(softnic, tokens, n_tokens,
5013                                 out, out_size);
5014                         return;
5015                 }
5016
5017                 if (n_tokens >= 7 &&
5018                         (strcmp(tokens[2], "table") == 0) &&
5019                         (strcmp(tokens[4], "rule") == 0) &&
5020                         (strcmp(tokens[5], "read") == 0) &&
5021                         (strcmp(tokens[6], "stats") == 0)) {
5022                         cmd_softnic_pipeline_table_rule_stats_read(softnic, tokens, n_tokens,
5023                                 out, out_size);
5024                         return;
5025                 }
5026
5027                 if (n_tokens >= 8 &&
5028                         (strcmp(tokens[2], "table") == 0) &&
5029                         (strcmp(tokens[4], "meter") == 0) &&
5030                         (strcmp(tokens[5], "profile") == 0) &&
5031                         (strcmp(tokens[7], "add") == 0)) {
5032                         cmd_pipeline_table_meter_profile_add(softnic, tokens, n_tokens,
5033                                 out, out_size);
5034                         return;
5035                 }
5036
5037                 if (n_tokens >= 8 &&
5038                         (strcmp(tokens[2], "table") == 0) &&
5039                         (strcmp(tokens[4], "meter") == 0) &&
5040                         (strcmp(tokens[5], "profile") == 0) &&
5041                         (strcmp(tokens[7], "delete") == 0)) {
5042                         cmd_pipeline_table_meter_profile_delete(softnic, tokens,
5043                                 n_tokens, out, out_size);
5044                         return;
5045                 }
5046
5047                 if (n_tokens >= 7 &&
5048                         (strcmp(tokens[2], "table") == 0) &&
5049                         (strcmp(tokens[4], "rule") == 0) &&
5050                         (strcmp(tokens[5], "read") == 0) &&
5051                         (strcmp(tokens[6], "meter") == 0)) {
5052                         cmd_pipeline_table_rule_meter_read(softnic, tokens, n_tokens,
5053                                 out, out_size);
5054                         return;
5055                 }
5056
5057                 if (n_tokens >= 5 &&
5058                         (strcmp(tokens[2], "table") == 0) &&
5059                         (strcmp(tokens[4], "dscp") == 0)) {
5060                         cmd_pipeline_table_dscp(softnic, tokens, n_tokens,
5061                                 out, out_size);
5062                         return;
5063                 }
5064
5065                 if (n_tokens >= 7 &&
5066                         (strcmp(tokens[2], "table") == 0) &&
5067                         (strcmp(tokens[4], "rule") == 0) &&
5068                         (strcmp(tokens[5], "read") == 0) &&
5069                         (strcmp(tokens[6], "ttl") == 0)) {
5070                         cmd_softnic_pipeline_table_rule_ttl_read(softnic, tokens, n_tokens,
5071                                 out, out_size);
5072                         return;
5073                 }
5074         }
5075
5076         if (strcmp(tokens[0], "thread") == 0) {
5077                 if (n_tokens >= 5 &&
5078                         (strcmp(tokens[4], "enable") == 0)) {
5079                         cmd_softnic_thread_pipeline_enable(softnic, tokens, n_tokens,
5080                                 out, out_size);
5081                         return;
5082                 }
5083
5084                 if (n_tokens >= 5 &&
5085                         (strcmp(tokens[4], "disable") == 0)) {
5086                         cmd_softnic_thread_pipeline_disable(softnic, tokens, n_tokens,
5087                                 out, out_size);
5088                         return;
5089                 }
5090         }
5091
5092         snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
5093 }
5094
5095 int
5096 softnic_cli_script_process(struct pmd_internals *softnic,
5097         const char *file_name,
5098         size_t msg_in_len_max,
5099         size_t msg_out_len_max)
5100 {
5101         char *msg_in = NULL, *msg_out = NULL;
5102         FILE *f = NULL;
5103
5104         /* Check input arguments */
5105         if (file_name == NULL ||
5106                 (strlen(file_name) == 0) ||
5107                 msg_in_len_max == 0 ||
5108                 msg_out_len_max == 0)
5109                 return -EINVAL;
5110
5111         msg_in = malloc(msg_in_len_max + 1);
5112         msg_out = malloc(msg_out_len_max + 1);
5113         if (msg_in == NULL ||
5114                 msg_out == NULL) {
5115                 free(msg_out);
5116                 free(msg_in);
5117                 return -ENOMEM;
5118         }
5119
5120         /* Open input file */
5121         f = fopen(file_name, "r");
5122         if (f == NULL) {
5123                 free(msg_out);
5124                 free(msg_in);
5125                 return -EIO;
5126         }
5127
5128         /* Read file */
5129         for ( ; ; ) {
5130                 if (fgets(msg_in, msg_in_len_max + 1, f) == NULL)
5131                         break;
5132
5133                 printf("%s", msg_in);
5134                 msg_out[0] = 0;
5135
5136                 softnic_cli_process(msg_in,
5137                         msg_out,
5138                         msg_out_len_max,
5139                         softnic);
5140
5141                 if (strlen(msg_out))
5142                         printf("%s", msg_out);
5143         }
5144
5145         /* Close file */
5146         fclose(f);
5147         free(msg_out);
5148         free(msg_in);
5149         return 0;
5150 }
5151
5152 static int
5153 cli_rule_file_process(const char *file_name,
5154         size_t line_len_max,
5155         struct softnic_table_rule_match *m,
5156         struct softnic_table_rule_action *a,
5157         uint32_t *n_rules,
5158         uint32_t *line_number,
5159         char *out,
5160         size_t out_size)
5161 {
5162         FILE *f = NULL;
5163         char *line = NULL;
5164         uint32_t rule_id, line_id;
5165         int status = 0;
5166
5167         /* Check input arguments */
5168         if (file_name == NULL ||
5169                 (strlen(file_name) == 0) ||
5170                 line_len_max == 0) {
5171                 *line_number = 0;
5172                 return -EINVAL;
5173         }
5174
5175         /* Memory allocation */
5176         line = malloc(line_len_max + 1);
5177         if (line == NULL) {
5178                 *line_number = 0;
5179                 return -ENOMEM;
5180         }
5181
5182         /* Open file */
5183         f = fopen(file_name, "r");
5184         if (f == NULL) {
5185                 *line_number = 0;
5186                 free(line);
5187                 return -EIO;
5188         }
5189
5190         /* Read file */
5191         for (line_id = 1, rule_id = 0; rule_id < *n_rules; line_id++) {
5192                 char *tokens[CMD_MAX_TOKENS];
5193                 uint32_t n_tokens, n_tokens_parsed, t0;
5194
5195                 /* Read next line from file. */
5196                 if (fgets(line, line_len_max + 1, f) == NULL)
5197                         break;
5198
5199                 /* Comment. */
5200                 if (is_comment(line))
5201                         continue;
5202
5203                 /* Parse line. */
5204                 n_tokens = RTE_DIM(tokens);
5205                 status = softnic_parse_tokenize_string(line, tokens, &n_tokens);
5206                 if (status) {
5207                         status = -EINVAL;
5208                         break;
5209                 }
5210
5211                 /* Empty line. */
5212                 if (n_tokens == 0)
5213                         continue;
5214                 t0 = 0;
5215
5216                 /* Rule match. */
5217                 n_tokens_parsed = parse_match(tokens + t0,
5218                         n_tokens - t0,
5219                         out,
5220                         out_size,
5221                         &m[rule_id]);
5222                 if (n_tokens_parsed == 0) {
5223                         status = -EINVAL;
5224                         break;
5225                 }
5226                 t0 += n_tokens_parsed;
5227
5228                 /* Rule action. */
5229                 n_tokens_parsed = parse_table_action(tokens + t0,
5230                         n_tokens - t0,
5231                         out,
5232                         out_size,
5233                         &a[rule_id]);
5234                 if (n_tokens_parsed == 0) {
5235                         status = -EINVAL;
5236                         break;
5237                 }
5238                 t0 += n_tokens_parsed;
5239
5240                 /* Line completed. */
5241                 if (t0 < n_tokens) {
5242                         status = -EINVAL;
5243                         break;
5244                 }
5245
5246                 /* Increment rule count */
5247                 rule_id++;
5248         }
5249
5250         /* Close file */
5251         fclose(f);
5252
5253         /* Memory free */
5254         free(line);
5255
5256         *n_rules = rule_id;
5257         *line_number = line_id;
5258         return status;
5259 }