31c37da5c5058197c764cc1d314d91208289fa75
[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  *  [tag]
1283  */
1284 static void
1285 cmd_table_action_profile(struct pmd_internals *softnic,
1286         char **tokens,
1287         uint32_t n_tokens,
1288         char *out,
1289         size_t out_size)
1290 {
1291         struct softnic_table_action_profile_params p;
1292         struct softnic_table_action_profile *ap;
1293         char *name;
1294         uint32_t t0;
1295
1296         memset(&p, 0, sizeof(p));
1297
1298         if (n_tokens < 8) {
1299                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1300                 return;
1301         }
1302
1303         if (strcmp(tokens[1], "action") != 0) {
1304                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "action");
1305                 return;
1306         }
1307
1308         if (strcmp(tokens[2], "profile") != 0) {
1309                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
1310                 return;
1311         }
1312
1313         name = tokens[3];
1314
1315         if (strcmp(tokens[4], "ipv4") == 0) {
1316                 p.common.ip_version = 1;
1317         } else if (strcmp(tokens[4], "ipv6") == 0) {
1318                 p.common.ip_version = 0;
1319         } else {
1320                 snprintf(out, out_size, MSG_ARG_INVALID, "ipv4 or ipv6");
1321                 return;
1322         }
1323
1324         if (strcmp(tokens[5], "offset") != 0) {
1325                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
1326                 return;
1327         }
1328
1329         if (softnic_parser_read_uint32(&p.common.ip_offset,
1330                 tokens[6]) != 0) {
1331                 snprintf(out, out_size, MSG_ARG_INVALID, "ip_offset");
1332                 return;
1333         }
1334
1335         if (strcmp(tokens[7], "fwd") != 0) {
1336                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "fwd");
1337                 return;
1338         }
1339
1340         p.action_mask |= 1LLU << RTE_TABLE_ACTION_FWD;
1341
1342         t0 = 8;
1343         if (t0 < n_tokens &&
1344                 (strcmp(tokens[t0], "balance") == 0)) {
1345                 if (n_tokens < t0 + 7) {
1346                         snprintf(out, out_size, MSG_ARG_MISMATCH, "table action profile balance");
1347                         return;
1348                 }
1349
1350                 if (strcmp(tokens[t0 + 1], "offset") != 0) {
1351                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
1352                         return;
1353                 }
1354
1355                 if (softnic_parser_read_uint32(&p.lb.key_offset,
1356                         tokens[t0 + 2]) != 0) {
1357                         snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
1358                         return;
1359                 }
1360
1361                 if (strcmp(tokens[t0 + 3], "mask") != 0) {
1362                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mask");
1363                         return;
1364                 }
1365
1366                 p.lb.key_size = RTE_PORT_IN_ACTION_LB_KEY_SIZE_MAX;
1367                 if (softnic_parse_hex_string(tokens[t0 + 4],
1368                         p.lb.key_mask, &p.lb.key_size) != 0) {
1369                         snprintf(out, out_size, MSG_ARG_INVALID, "key_mask");
1370                         return;
1371                 }
1372
1373                 if (strcmp(tokens[t0 + 5], "outoffset") != 0) {
1374                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "outoffset");
1375                         return;
1376                 }
1377
1378                 if (softnic_parser_read_uint32(&p.lb.out_offset,
1379                         tokens[t0 + 6]) != 0) {
1380                         snprintf(out, out_size, MSG_ARG_INVALID, "out_offset");
1381                         return;
1382                 }
1383
1384                 p.action_mask |= 1LLU << RTE_TABLE_ACTION_LB;
1385                 t0 += 7;
1386         } /* balance */
1387
1388         if (t0 < n_tokens &&
1389                 (strcmp(tokens[t0], "meter") == 0)) {
1390                 if (n_tokens < t0 + 6) {
1391                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1392                                 "table action profile meter");
1393                         return;
1394                 }
1395
1396                 if (strcmp(tokens[t0 + 1], "srtcm") == 0) {
1397                         p.mtr.alg = RTE_TABLE_ACTION_METER_SRTCM;
1398                 } else if (strcmp(tokens[t0 + 1], "trtcm") == 0) {
1399                         p.mtr.alg = RTE_TABLE_ACTION_METER_TRTCM;
1400                 } else {
1401                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1402                                 "srtcm or trtcm");
1403                         return;
1404                 }
1405
1406                 if (strcmp(tokens[t0 + 2], "tc") != 0) {
1407                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc");
1408                         return;
1409                 }
1410
1411                 if (softnic_parser_read_uint32(&p.mtr.n_tc,
1412                         tokens[t0 + 3]) != 0) {
1413                         snprintf(out, out_size, MSG_ARG_INVALID, "n_tc");
1414                         return;
1415                 }
1416
1417                 if (strcmp(tokens[t0 + 4], "stats") != 0) {
1418                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
1419                         return;
1420                 }
1421
1422                 if (strcmp(tokens[t0 + 5], "none") == 0) {
1423                         p.mtr.n_packets_enabled = 0;
1424                         p.mtr.n_bytes_enabled = 0;
1425                 } else if (strcmp(tokens[t0 + 5], "pkts") == 0) {
1426                         p.mtr.n_packets_enabled = 1;
1427                         p.mtr.n_bytes_enabled = 0;
1428                 } else if (strcmp(tokens[t0 + 5], "bytes") == 0) {
1429                         p.mtr.n_packets_enabled = 0;
1430                         p.mtr.n_bytes_enabled = 1;
1431                 } else if (strcmp(tokens[t0 + 5], "both") == 0) {
1432                         p.mtr.n_packets_enabled = 1;
1433                         p.mtr.n_bytes_enabled = 1;
1434                 } else {
1435                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1436                                 "none or pkts or bytes or both");
1437                         return;
1438                 }
1439
1440                 p.action_mask |= 1LLU << RTE_TABLE_ACTION_MTR;
1441                 t0 += 6;
1442         } /* meter */
1443
1444         if (t0 < n_tokens &&
1445                 (strcmp(tokens[t0], "tm") == 0)) {
1446                 if (n_tokens < t0 + 5) {
1447                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1448                                 "table action profile tm");
1449                         return;
1450                 }
1451
1452                 if (strcmp(tokens[t0 + 1], "spp") != 0) {
1453                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "spp");
1454                         return;
1455                 }
1456
1457                 if (softnic_parser_read_uint32(&p.tm.n_subports_per_port,
1458                         tokens[t0 + 2]) != 0) {
1459                         snprintf(out, out_size, MSG_ARG_INVALID,
1460                                 "n_subports_per_port");
1461                         return;
1462                 }
1463
1464                 if (strcmp(tokens[t0 + 3], "pps") != 0) {
1465                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pps");
1466                         return;
1467                 }
1468
1469                 if (softnic_parser_read_uint32(&p.tm.n_pipes_per_subport,
1470                         tokens[t0 + 4]) != 0) {
1471                         snprintf(out, out_size, MSG_ARG_INVALID,
1472                                 "n_pipes_per_subport");
1473                         return;
1474                 }
1475
1476                 p.action_mask |= 1LLU << RTE_TABLE_ACTION_TM;
1477                 t0 += 5;
1478         } /* tm */
1479
1480         if (t0 < n_tokens &&
1481                 (strcmp(tokens[t0], "encap") == 0)) {
1482                 if (n_tokens < t0 + 2) {
1483                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1484                                 "action profile encap");
1485                         return;
1486                 }
1487
1488                 if (strcmp(tokens[t0 + 1], "ether") == 0) {
1489                         p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_ETHER;
1490                 } else if (strcmp(tokens[t0 + 1], "vlan") == 0) {
1491                         p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_VLAN;
1492                 } else if (strcmp(tokens[t0 + 1], "qinq") == 0) {
1493                         p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_QINQ;
1494                 } else if (strcmp(tokens[t0 + 1], "mpls") == 0) {
1495                         p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_MPLS;
1496                 } else if (strcmp(tokens[t0 + 1], "pppoe") == 0) {
1497                         p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_PPPOE;
1498                 } else {
1499                         snprintf(out, out_size, MSG_ARG_MISMATCH, "encap");
1500                         return;
1501                 }
1502
1503                 p.action_mask |= 1LLU << RTE_TABLE_ACTION_ENCAP;
1504                 t0 += 2;
1505         } /* encap */
1506
1507         if (t0 < n_tokens &&
1508                 (strcmp(tokens[t0], "nat") == 0)) {
1509                 if (n_tokens < t0 + 4) {
1510                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1511                                 "table action profile nat");
1512                         return;
1513                 }
1514
1515                 if (strcmp(tokens[t0 + 1], "src") == 0) {
1516                         p.nat.source_nat = 1;
1517                 } else if (strcmp(tokens[t0 + 1], "dst") == 0) {
1518                         p.nat.source_nat = 0;
1519                 } else {
1520                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1521                                 "src or dst");
1522                         return;
1523                 }
1524
1525                 if (strcmp(tokens[t0 + 2], "proto") != 0) {
1526                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "proto");
1527                         return;
1528                 }
1529
1530                 if (strcmp(tokens[t0 + 3], "tcp") == 0) {
1531                         p.nat.proto = 0x06;
1532                 } else if (strcmp(tokens[t0 + 3], "udp") == 0) {
1533                         p.nat.proto = 0x11;
1534                 } else {
1535                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1536                                 "tcp or udp");
1537                         return;
1538                 }
1539
1540                 p.action_mask |= 1LLU << RTE_TABLE_ACTION_NAT;
1541                 t0 += 4;
1542         } /* nat */
1543
1544         if (t0 < n_tokens &&
1545                 (strcmp(tokens[t0], "ttl") == 0)) {
1546                 if (n_tokens < t0 + 4) {
1547                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1548                                 "table action profile ttl");
1549                         return;
1550                 }
1551
1552                 if (strcmp(tokens[t0 + 1], "drop") == 0) {
1553                         p.ttl.drop = 1;
1554                 } else if (strcmp(tokens[t0 + 1], "fwd") == 0) {
1555                         p.ttl.drop = 0;
1556                 } else {
1557                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1558                                 "drop or fwd");
1559                         return;
1560                 }
1561
1562                 if (strcmp(tokens[t0 + 2], "stats") != 0) {
1563                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
1564                         return;
1565                 }
1566
1567                 if (strcmp(tokens[t0 + 3], "none") == 0) {
1568                         p.ttl.n_packets_enabled = 0;
1569                 } else if (strcmp(tokens[t0 + 3], "pkts") == 0) {
1570                         p.ttl.n_packets_enabled = 1;
1571                 } else {
1572                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1573                                 "none or pkts");
1574                         return;
1575                 }
1576
1577                 p.action_mask |= 1LLU << RTE_TABLE_ACTION_TTL;
1578                 t0 += 4;
1579         } /* ttl */
1580
1581         if (t0 < n_tokens &&
1582                 (strcmp(tokens[t0], "stats") == 0)) {
1583                 if (n_tokens < t0 + 2) {
1584                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1585                                 "table action profile stats");
1586                         return;
1587                 }
1588
1589                 if (strcmp(tokens[t0 + 1], "pkts") == 0) {
1590                         p.stats.n_packets_enabled = 1;
1591                         p.stats.n_bytes_enabled = 0;
1592                 } else if (strcmp(tokens[t0 + 1], "bytes") == 0) {
1593                         p.stats.n_packets_enabled = 0;
1594                         p.stats.n_bytes_enabled = 1;
1595                 } else if (strcmp(tokens[t0 + 1], "both") == 0) {
1596                         p.stats.n_packets_enabled = 1;
1597                         p.stats.n_bytes_enabled = 1;
1598                 } else {
1599                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1600                                 "pkts or bytes or both");
1601                         return;
1602                 }
1603
1604                 p.action_mask |= 1LLU << RTE_TABLE_ACTION_STATS;
1605                 t0 += 2;
1606         } /* stats */
1607
1608         if (t0 < n_tokens &&
1609                 (strcmp(tokens[t0], "time") == 0)) {
1610                 p.action_mask |= 1LLU << RTE_TABLE_ACTION_TIME;
1611                 t0 += 1;
1612         } /* time */
1613
1614         if (t0 < n_tokens &&
1615                 (strcmp(tokens[t0], "tag") == 0)) {
1616                 p.action_mask |= 1LLU << RTE_TABLE_ACTION_TAG;
1617                 t0 += 1;
1618         } /* tag */
1619
1620         if (t0 < n_tokens) {
1621                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1622                 return;
1623         }
1624
1625         ap = softnic_table_action_profile_create(softnic, name, &p);
1626         if (ap == NULL) {
1627                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
1628                 return;
1629         }
1630 }
1631
1632 /**
1633  * pipeline <pipeline_name>
1634  *  period <timer_period_ms>
1635  *  offset_port_id <offset_port_id>
1636  */
1637 static void
1638 cmd_pipeline(struct pmd_internals *softnic,
1639         char **tokens,
1640         uint32_t n_tokens,
1641         char *out,
1642         size_t out_size)
1643 {
1644         struct pipeline_params p;
1645         char *name;
1646         struct pipeline *pipeline;
1647
1648         if (n_tokens != 6) {
1649                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1650                 return;
1651         }
1652
1653         name = tokens[1];
1654
1655         if (strcmp(tokens[2], "period") != 0) {
1656                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "period");
1657                 return;
1658         }
1659
1660         if (softnic_parser_read_uint32(&p.timer_period_ms,
1661                 tokens[3]) != 0) {
1662                 snprintf(out, out_size, MSG_ARG_INVALID, "timer_period_ms");
1663                 return;
1664         }
1665
1666         if (strcmp(tokens[4], "offset_port_id") != 0) {
1667                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset_port_id");
1668                 return;
1669         }
1670
1671         if (softnic_parser_read_uint32(&p.offset_port_id,
1672                 tokens[5]) != 0) {
1673                 snprintf(out, out_size, MSG_ARG_INVALID, "offset_port_id");
1674                 return;
1675         }
1676
1677         pipeline = softnic_pipeline_create(softnic, name, &p);
1678         if (pipeline == NULL) {
1679                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
1680                 return;
1681         }
1682 }
1683
1684 /**
1685  * pipeline <pipeline_name> port in
1686  *  bsz <burst_size>
1687  *  link <link_name> rxq <queue_id>
1688  *  | swq <swq_name>
1689  *  | tmgr <tmgr_name>
1690  *  | tap <tap_name> mempool <mempool_name> mtu <mtu>
1691  *  | source mempool <mempool_name> file <file_name> bpp <n_bytes_per_pkt>
1692  *  [action <port_in_action_profile_name>]
1693  *  [disabled]
1694  */
1695 static void
1696 cmd_pipeline_port_in(struct pmd_internals *softnic,
1697         char **tokens,
1698         uint32_t n_tokens,
1699         char *out,
1700         size_t out_size)
1701 {
1702         struct softnic_port_in_params p;
1703         char *pipeline_name;
1704         uint32_t t0;
1705         int enabled, status;
1706
1707         memset(&p, 0, sizeof(p));
1708
1709         if (n_tokens < 7) {
1710                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1711                 return;
1712         }
1713
1714         pipeline_name = tokens[1];
1715
1716         if (strcmp(tokens[2], "port") != 0) {
1717                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
1718                 return;
1719         }
1720
1721         if (strcmp(tokens[3], "in") != 0) {
1722                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
1723                 return;
1724         }
1725
1726         if (strcmp(tokens[4], "bsz") != 0) {
1727                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
1728                 return;
1729         }
1730
1731         if (softnic_parser_read_uint32(&p.burst_size, tokens[5]) != 0) {
1732                 snprintf(out, out_size, MSG_ARG_INVALID, "burst_size");
1733                 return;
1734         }
1735
1736         t0 = 6;
1737
1738         if (strcmp(tokens[t0], "link") == 0) {
1739                 if (n_tokens < t0 + 4) {
1740                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1741                                 "pipeline port in link");
1742                         return;
1743                 }
1744
1745                 p.type = PORT_IN_RXQ;
1746
1747                 strcpy(p.dev_name, tokens[t0 + 1]);
1748
1749                 if (strcmp(tokens[t0 + 2], "rxq") != 0) {
1750                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq");
1751                         return;
1752                 }
1753
1754                 if (softnic_parser_read_uint16(&p.rxq.queue_id,
1755                         tokens[t0 + 3]) != 0) {
1756                         snprintf(out, out_size, MSG_ARG_INVALID,
1757                                 "queue_id");
1758                         return;
1759                 }
1760                 t0 += 4;
1761         } else if (strcmp(tokens[t0], "swq") == 0) {
1762                 if (n_tokens < t0 + 2) {
1763                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1764                                 "pipeline port in swq");
1765                         return;
1766                 }
1767
1768                 p.type = PORT_IN_SWQ;
1769
1770                 strcpy(p.dev_name, tokens[t0 + 1]);
1771
1772                 t0 += 2;
1773         } else if (strcmp(tokens[t0], "tmgr") == 0) {
1774                 if (n_tokens < t0 + 2) {
1775                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1776                                 "pipeline port in tmgr");
1777                         return;
1778                 }
1779
1780                 p.type = PORT_IN_TMGR;
1781
1782                 strcpy(p.dev_name, tokens[t0 + 1]);
1783
1784                 t0 += 2;
1785         } else if (strcmp(tokens[t0], "tap") == 0) {
1786                 if (n_tokens < t0 + 6) {
1787                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1788                                 "pipeline port in tap");
1789                         return;
1790                 }
1791
1792                 p.type = PORT_IN_TAP;
1793
1794                 strcpy(p.dev_name, tokens[t0 + 1]);
1795
1796                 if (strcmp(tokens[t0 + 2], "mempool") != 0) {
1797                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1798                                 "mempool");
1799                         return;
1800                 }
1801
1802                 p.tap.mempool_name = tokens[t0 + 3];
1803
1804                 if (strcmp(tokens[t0 + 4], "mtu") != 0) {
1805                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1806                                 "mtu");
1807                         return;
1808                 }
1809
1810                 if (softnic_parser_read_uint32(&p.tap.mtu,
1811                         tokens[t0 + 5]) != 0) {
1812                         snprintf(out, out_size, MSG_ARG_INVALID, "mtu");
1813                         return;
1814                 }
1815
1816                 t0 += 6;
1817         } else if (strcmp(tokens[t0], "source") == 0) {
1818                 if (n_tokens < t0 + 6) {
1819                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1820                                 "pipeline port in source");
1821                         return;
1822                 }
1823
1824                 p.type = PORT_IN_SOURCE;
1825
1826                 if (strcmp(tokens[t0 + 1], "mempool") != 0) {
1827                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1828                                 "mempool");
1829                         return;
1830                 }
1831
1832                 p.source.mempool_name = tokens[t0 + 2];
1833
1834                 if (strcmp(tokens[t0 + 3], "file") != 0) {
1835                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1836                                 "file");
1837                         return;
1838                 }
1839
1840                 p.source.file_name = tokens[t0 + 4];
1841
1842                 if (strcmp(tokens[t0 + 5], "bpp") != 0) {
1843                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1844                                 "bpp");
1845                         return;
1846                 }
1847
1848                 if (softnic_parser_read_uint32(&p.source.n_bytes_per_pkt,
1849                         tokens[t0 + 6]) != 0) {
1850                         snprintf(out, out_size, MSG_ARG_INVALID,
1851                                 "n_bytes_per_pkt");
1852                         return;
1853                 }
1854
1855                 t0 += 7;
1856         } else {
1857                 snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
1858                 return;
1859         }
1860
1861         if (n_tokens > t0 &&
1862                 (strcmp(tokens[t0], "action") == 0)) {
1863                 if (n_tokens < t0 + 2) {
1864                         snprintf(out, out_size, MSG_ARG_MISMATCH, "action");
1865                         return;
1866                 }
1867
1868                 strcpy(p.action_profile_name, tokens[t0 + 1]);
1869
1870                 t0 += 2;
1871         }
1872
1873         enabled = 1;
1874         if (n_tokens > t0 &&
1875                 (strcmp(tokens[t0], "disabled") == 0)) {
1876                 enabled = 0;
1877
1878                 t0 += 1;
1879         }
1880
1881         if (n_tokens != t0) {
1882                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1883                 return;
1884         }
1885
1886         status = softnic_pipeline_port_in_create(softnic,
1887                 pipeline_name,
1888                 &p,
1889                 enabled);
1890         if (status) {
1891                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
1892                 return;
1893         }
1894 }
1895
1896 /**
1897  * pipeline <pipeline_name> port out
1898  *  bsz <burst_size>
1899  *  link <link_name> txq <txq_id>
1900  *  | swq <swq_name>
1901  *  | tmgr <tmgr_name>
1902  *  | tap <tap_name>
1903  *  | sink [file <file_name> pkts <max_n_pkts>]
1904  */
1905 static void
1906 cmd_pipeline_port_out(struct pmd_internals *softnic,
1907         char **tokens,
1908         uint32_t n_tokens,
1909         char *out,
1910         size_t out_size)
1911 {
1912         struct softnic_port_out_params p;
1913         char *pipeline_name;
1914         int status;
1915
1916         memset(&p, 0, sizeof(p));
1917
1918         if (n_tokens < 7) {
1919                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1920                 return;
1921         }
1922
1923         pipeline_name = tokens[1];
1924
1925         if (strcmp(tokens[2], "port") != 0) {
1926                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
1927                 return;
1928         }
1929
1930         if (strcmp(tokens[3], "out") != 0) {
1931                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "out");
1932                 return;
1933         }
1934
1935         if (strcmp(tokens[4], "bsz") != 0) {
1936                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
1937                 return;
1938         }
1939
1940         if (softnic_parser_read_uint32(&p.burst_size, tokens[5]) != 0) {
1941                 snprintf(out, out_size, MSG_ARG_INVALID, "burst_size");
1942                 return;
1943         }
1944
1945         if (strcmp(tokens[6], "link") == 0) {
1946                 if (n_tokens != 10) {
1947                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1948                                 "pipeline port out link");
1949                         return;
1950                 }
1951
1952                 p.type = PORT_OUT_TXQ;
1953
1954                 strcpy(p.dev_name, tokens[7]);
1955
1956                 if (strcmp(tokens[8], "txq") != 0) {
1957                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "txq");
1958                         return;
1959                 }
1960
1961                 if (softnic_parser_read_uint16(&p.txq.queue_id,
1962                         tokens[9]) != 0) {
1963                         snprintf(out, out_size, MSG_ARG_INVALID, "queue_id");
1964                         return;
1965                 }
1966         } else if (strcmp(tokens[6], "swq") == 0) {
1967                 if (n_tokens != 8) {
1968                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1969                                 "pipeline port out swq");
1970                         return;
1971                 }
1972
1973                 p.type = PORT_OUT_SWQ;
1974
1975                 strcpy(p.dev_name, tokens[7]);
1976         } else if (strcmp(tokens[6], "tmgr") == 0) {
1977                 if (n_tokens != 8) {
1978                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1979                                 "pipeline port out tmgr");
1980                         return;
1981                 }
1982
1983                 p.type = PORT_OUT_TMGR;
1984
1985                 strcpy(p.dev_name, tokens[7]);
1986         } else if (strcmp(tokens[6], "tap") == 0) {
1987                 if (n_tokens != 8) {
1988                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1989                                 "pipeline port out tap");
1990                         return;
1991                 }
1992
1993                 p.type = PORT_OUT_TAP;
1994
1995                 strcpy(p.dev_name, tokens[7]);
1996         } else if (strcmp(tokens[6], "sink") == 0) {
1997                 if ((n_tokens != 7) && (n_tokens != 11)) {
1998                         snprintf(out, out_size, MSG_ARG_MISMATCH,
1999                                 "pipeline port out sink");
2000                         return;
2001                 }
2002
2003                 p.type = PORT_OUT_SINK;
2004
2005                 if (n_tokens == 7) {
2006                         p.sink.file_name = NULL;
2007                         p.sink.max_n_pkts = 0;
2008                 } else {
2009                         if (strcmp(tokens[7], "file") != 0) {
2010                                 snprintf(out, out_size, MSG_ARG_NOT_FOUND,
2011                                         "file");
2012                                 return;
2013                         }
2014
2015                         p.sink.file_name = tokens[8];
2016
2017                         if (strcmp(tokens[9], "pkts") != 0) {
2018                                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pkts");
2019                                 return;
2020                         }
2021
2022                         if (softnic_parser_read_uint32(&p.sink.max_n_pkts,
2023                                 tokens[10]) != 0) {
2024                                 snprintf(out, out_size, MSG_ARG_INVALID, "max_n_pkts");
2025                                 return;
2026                         }
2027                 }
2028         } else {
2029                 snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
2030                 return;
2031         }
2032
2033         status = softnic_pipeline_port_out_create(softnic, pipeline_name, &p);
2034         if (status) {
2035                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
2036                 return;
2037         }
2038 }
2039
2040 /**
2041  * pipeline <pipeline_name> table
2042  *      match
2043  *      acl
2044  *          ipv4 | ipv6
2045  *          offset <ip_header_offset>
2046  *          size <n_rules>
2047  *      | array
2048  *          offset <key_offset>
2049  *          size <n_keys>
2050  *      | hash
2051  *          ext | lru
2052  *          key <key_size>
2053  *          mask <key_mask>
2054  *          offset <key_offset>
2055  *          buckets <n_buckets>
2056  *          size <n_keys>
2057  *      | lpm
2058  *          ipv4 | ipv6
2059  *          offset <ip_header_offset>
2060  *          size <n_rules>
2061  *      | stub
2062  *  [action <table_action_profile_name>]
2063  */
2064 static void
2065 cmd_pipeline_table(struct pmd_internals *softnic,
2066         char **tokens,
2067         uint32_t n_tokens,
2068         char *out,
2069         size_t out_size)
2070 {
2071         struct softnic_table_params p;
2072         char *pipeline_name;
2073         uint32_t t0;
2074         int status;
2075
2076         memset(&p, 0, sizeof(p));
2077
2078         if (n_tokens < 5) {
2079                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2080                 return;
2081         }
2082
2083         pipeline_name = tokens[1];
2084
2085         if (strcmp(tokens[2], "table") != 0) {
2086                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
2087                 return;
2088         }
2089
2090         if (strcmp(tokens[3], "match") != 0) {
2091                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "match");
2092                 return;
2093         }
2094
2095         t0 = 4;
2096         if (strcmp(tokens[t0], "acl") == 0) {
2097                 if (n_tokens < t0 + 6) {
2098                         snprintf(out, out_size, MSG_ARG_MISMATCH,
2099                                 "pipeline table acl");
2100                         return;
2101                 }
2102
2103                 p.match_type = TABLE_ACL;
2104
2105                 if (strcmp(tokens[t0 + 1], "ipv4") == 0) {
2106                         p.match.acl.ip_version = 1;
2107                 } else if (strcmp(tokens[t0 + 1], "ipv6") == 0) {
2108                         p.match.acl.ip_version = 0;
2109                 } else {
2110                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
2111                                 "ipv4 or ipv6");
2112                         return;
2113                 }
2114
2115                 if (strcmp(tokens[t0 + 2], "offset") != 0) {
2116                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
2117                         return;
2118                 }
2119
2120                 if (softnic_parser_read_uint32(&p.match.acl.ip_header_offset,
2121                         tokens[t0 + 3]) != 0) {
2122                         snprintf(out, out_size, MSG_ARG_INVALID,
2123                                 "ip_header_offset");
2124                         return;
2125                 }
2126
2127                 if (strcmp(tokens[t0 + 4], "size") != 0) {
2128                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
2129                         return;
2130                 }
2131
2132                 if (softnic_parser_read_uint32(&p.match.acl.n_rules,
2133                         tokens[t0 + 5]) != 0) {
2134                         snprintf(out, out_size, MSG_ARG_INVALID, "n_rules");
2135                         return;
2136                 }
2137
2138                 t0 += 6;
2139         } else if (strcmp(tokens[t0], "array") == 0) {
2140                 if (n_tokens < t0 + 5) {
2141                         snprintf(out, out_size, MSG_ARG_MISMATCH,
2142                                 "pipeline table array");
2143                         return;
2144                 }
2145
2146                 p.match_type = TABLE_ARRAY;
2147
2148                 if (strcmp(tokens[t0 + 1], "offset") != 0) {
2149                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
2150                         return;
2151                 }
2152
2153                 if (softnic_parser_read_uint32(&p.match.array.key_offset,
2154                         tokens[t0 + 2]) != 0) {
2155                         snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
2156                         return;
2157                 }
2158
2159                 if (strcmp(tokens[t0 + 3], "size") != 0) {
2160                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
2161                         return;
2162                 }
2163
2164                 if (softnic_parser_read_uint32(&p.match.array.n_keys,
2165                         tokens[t0 + 4]) != 0) {
2166                         snprintf(out, out_size, MSG_ARG_INVALID, "n_keys");
2167                         return;
2168                 }
2169
2170                 t0 += 5;
2171         } else if (strcmp(tokens[t0], "hash") == 0) {
2172                 uint32_t key_mask_size = TABLE_RULE_MATCH_SIZE_MAX;
2173
2174                 if (n_tokens < t0 + 12) {
2175                         snprintf(out, out_size, MSG_ARG_MISMATCH,
2176                                 "pipeline table hash");
2177                         return;
2178                 }
2179
2180                 p.match_type = TABLE_HASH;
2181
2182                 if (strcmp(tokens[t0 + 1], "ext") == 0) {
2183                         p.match.hash.extendable_bucket = 1;
2184                 } else if (strcmp(tokens[t0 + 1], "lru") == 0) {
2185                         p.match.hash.extendable_bucket = 0;
2186                 } else {
2187                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
2188                                 "ext or lru");
2189                         return;
2190                 }
2191
2192                 if (strcmp(tokens[t0 + 2], "key") != 0) {
2193                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "key");
2194                         return;
2195                 }
2196
2197                 if ((softnic_parser_read_uint32(&p.match.hash.key_size,
2198                         tokens[t0 + 3]) != 0) ||
2199                         p.match.hash.key_size == 0 ||
2200                         p.match.hash.key_size > TABLE_RULE_MATCH_SIZE_MAX) {
2201                         snprintf(out, out_size, MSG_ARG_INVALID, "key_size");
2202                         return;
2203                 }
2204
2205                 if (strcmp(tokens[t0 + 4], "mask") != 0) {
2206                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mask");
2207                         return;
2208                 }
2209
2210                 if ((softnic_parse_hex_string(tokens[t0 + 5],
2211                         p.match.hash.key_mask, &key_mask_size) != 0) ||
2212                         key_mask_size != p.match.hash.key_size) {
2213                         snprintf(out, out_size, MSG_ARG_INVALID, "key_mask");
2214                         return;
2215                 }
2216
2217                 if (strcmp(tokens[t0 + 6], "offset") != 0) {
2218                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
2219                         return;
2220                 }
2221
2222                 if (softnic_parser_read_uint32(&p.match.hash.key_offset,
2223                         tokens[t0 + 7]) != 0) {
2224                         snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
2225                         return;
2226                 }
2227
2228                 if (strcmp(tokens[t0 + 8], "buckets") != 0) {
2229                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "buckets");
2230                         return;
2231                 }
2232
2233                 if (softnic_parser_read_uint32(&p.match.hash.n_buckets,
2234                         tokens[t0 + 9]) != 0) {
2235                         snprintf(out, out_size, MSG_ARG_INVALID, "n_buckets");
2236                         return;
2237                 }
2238
2239                 if (strcmp(tokens[t0 + 10], "size") != 0) {
2240                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
2241                         return;
2242                 }
2243
2244                 if (softnic_parser_read_uint32(&p.match.hash.n_keys,
2245                         tokens[t0 + 11]) != 0) {
2246                         snprintf(out, out_size, MSG_ARG_INVALID, "n_keys");
2247                         return;
2248                 }
2249
2250                 t0 += 12;
2251         } else if (strcmp(tokens[t0], "lpm") == 0) {
2252                 if (n_tokens < t0 + 6) {
2253                         snprintf(out, out_size, MSG_ARG_MISMATCH,
2254                                 "pipeline table lpm");
2255                         return;
2256                 }
2257
2258                 p.match_type = TABLE_LPM;
2259
2260                 if (strcmp(tokens[t0 + 1], "ipv4") == 0) {
2261                         p.match.lpm.key_size = 4;
2262                 } else if (strcmp(tokens[t0 + 1], "ipv6") == 0) {
2263                         p.match.lpm.key_size = 16;
2264                 } else {
2265                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
2266                                 "ipv4 or ipv6");
2267                         return;
2268                 }
2269
2270                 if (strcmp(tokens[t0 + 2], "offset") != 0) {
2271                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
2272                         return;
2273                 }
2274
2275                 if (softnic_parser_read_uint32(&p.match.lpm.key_offset,
2276                         tokens[t0 + 3]) != 0) {
2277                         snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
2278                         return;
2279                 }
2280
2281                 if (strcmp(tokens[t0 + 4], "size") != 0) {
2282                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
2283                         return;
2284                 }
2285
2286                 if (softnic_parser_read_uint32(&p.match.lpm.n_rules,
2287                         tokens[t0 + 5]) != 0) {
2288                         snprintf(out, out_size, MSG_ARG_INVALID, "n_rules");
2289                         return;
2290                 }
2291
2292                 t0 += 6;
2293         } else if (strcmp(tokens[t0], "stub") == 0) {
2294                 p.match_type = TABLE_STUB;
2295
2296                 t0 += 1;
2297         } else {
2298                 snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
2299                 return;
2300         }
2301
2302         if (n_tokens > t0 &&
2303                 (strcmp(tokens[t0], "action") == 0)) {
2304                 if (n_tokens < t0 + 2) {
2305                         snprintf(out, out_size, MSG_ARG_MISMATCH, "action");
2306                         return;
2307                 }
2308
2309                 strcpy(p.action_profile_name, tokens[t0 + 1]);
2310
2311                 t0 += 2;
2312         }
2313
2314         if (n_tokens > t0) {
2315                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2316                 return;
2317         }
2318
2319         status = softnic_pipeline_table_create(softnic, pipeline_name, &p);
2320         if (status) {
2321                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
2322                 return;
2323         }
2324 }
2325
2326 /**
2327  * pipeline <pipeline_name> port in <port_id> table <table_id>
2328  */
2329 static void
2330 cmd_pipeline_port_in_table(struct pmd_internals *softnic,
2331         char **tokens,
2332         uint32_t n_tokens,
2333         char *out,
2334         size_t out_size)
2335 {
2336         char *pipeline_name;
2337         uint32_t port_id, table_id;
2338         int status;
2339
2340         if (n_tokens != 7) {
2341                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2342                 return;
2343         }
2344
2345         pipeline_name = tokens[1];
2346
2347         if (strcmp(tokens[2], "port") != 0) {
2348                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
2349                 return;
2350         }
2351
2352         if (strcmp(tokens[3], "in") != 0) {
2353                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
2354                 return;
2355         }
2356
2357         if (softnic_parser_read_uint32(&port_id, tokens[4]) != 0) {
2358                 snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
2359                 return;
2360         }
2361
2362         if (strcmp(tokens[5], "table") != 0) {
2363                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
2364                 return;
2365         }
2366
2367         if (softnic_parser_read_uint32(&table_id, tokens[6]) != 0) {
2368                 snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
2369                 return;
2370         }
2371
2372         status = softnic_pipeline_port_in_connect_to_table(softnic,
2373                 pipeline_name,
2374                 port_id,
2375                 table_id);
2376         if (status) {
2377                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
2378                 return;
2379         }
2380 }
2381
2382 /**
2383  * pipeline <pipeline_name> port in <port_id> stats read [clear]
2384  */
2385
2386 #define MSG_PIPELINE_PORT_IN_STATS                         \
2387         "Pkts in: %" PRIu64 "\n"                           \
2388         "Pkts dropped by AH: %" PRIu64 "\n"                \
2389         "Pkts dropped by other: %" PRIu64 "\n"
2390
2391 static void
2392 cmd_pipeline_port_in_stats(struct pmd_internals *softnic,
2393         char **tokens,
2394         uint32_t n_tokens,
2395         char *out,
2396         size_t out_size)
2397 {
2398         struct rte_pipeline_port_in_stats stats;
2399         char *pipeline_name;
2400         uint32_t port_id;
2401         int clear, status;
2402
2403         if (n_tokens != 7 &&
2404                 n_tokens != 8) {
2405                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2406                 return;
2407         }
2408
2409         pipeline_name = tokens[1];
2410
2411         if (strcmp(tokens[2], "port") != 0) {
2412                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
2413                 return;
2414         }
2415
2416         if (strcmp(tokens[3], "in") != 0) {
2417                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
2418                 return;
2419         }
2420
2421         if (softnic_parser_read_uint32(&port_id, tokens[4]) != 0) {
2422                 snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
2423                 return;
2424         }
2425
2426         if (strcmp(tokens[5], "stats") != 0) {
2427                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
2428                 return;
2429         }
2430
2431         if (strcmp(tokens[6], "read") != 0) {
2432                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "read");
2433                 return;
2434         }
2435
2436         clear = 0;
2437         if (n_tokens == 8) {
2438                 if (strcmp(tokens[7], "clear") != 0) {
2439                         snprintf(out, out_size, MSG_ARG_INVALID, "clear");
2440                         return;
2441                 }
2442
2443                 clear = 1;
2444         }
2445
2446         status = softnic_pipeline_port_in_stats_read(softnic,
2447                 pipeline_name,
2448                 port_id,
2449                 &stats,
2450                 clear);
2451         if (status) {
2452                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
2453                 return;
2454         }
2455
2456         snprintf(out, out_size, MSG_PIPELINE_PORT_IN_STATS,
2457                 stats.stats.n_pkts_in,
2458                 stats.n_pkts_dropped_by_ah,
2459                 stats.stats.n_pkts_drop);
2460 }
2461
2462 /**
2463  * pipeline <pipeline_name> port in <port_id> enable
2464  */
2465 static void
2466 cmd_softnic_pipeline_port_in_enable(struct pmd_internals *softnic,
2467         char **tokens,
2468         uint32_t n_tokens,
2469         char *out,
2470         size_t out_size)
2471 {
2472         char *pipeline_name;
2473         uint32_t port_id;
2474         int status;
2475
2476         if (n_tokens != 6) {
2477                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2478                 return;
2479         }
2480
2481         pipeline_name = tokens[1];
2482
2483         if (strcmp(tokens[2], "port") != 0) {
2484                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
2485                 return;
2486         }
2487
2488         if (strcmp(tokens[3], "in") != 0) {
2489                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
2490                 return;
2491         }
2492
2493         if (softnic_parser_read_uint32(&port_id, tokens[4]) != 0) {
2494                 snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
2495                 return;
2496         }
2497
2498         if (strcmp(tokens[5], "enable") != 0) {
2499                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
2500                 return;
2501         }
2502
2503         status = softnic_pipeline_port_in_enable(softnic, pipeline_name, port_id);
2504         if (status) {
2505                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
2506                 return;
2507         }
2508 }
2509
2510 /**
2511  * pipeline <pipeline_name> port in <port_id> disable
2512  */
2513 static void
2514 cmd_softnic_pipeline_port_in_disable(struct pmd_internals *softnic,
2515         char **tokens,
2516         uint32_t n_tokens,
2517         char *out,
2518         size_t out_size)
2519 {
2520         char *pipeline_name;
2521         uint32_t port_id;
2522         int status;
2523
2524         if (n_tokens != 6) {
2525                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2526                 return;
2527         }
2528
2529         pipeline_name = tokens[1];
2530
2531         if (strcmp(tokens[2], "port") != 0) {
2532                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
2533                 return;
2534         }
2535
2536         if (strcmp(tokens[3], "in") != 0) {
2537                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
2538                 return;
2539         }
2540
2541         if (softnic_parser_read_uint32(&port_id, tokens[4]) != 0) {
2542                 snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
2543                 return;
2544         }
2545
2546         if (strcmp(tokens[5], "disable") != 0) {
2547                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
2548                 return;
2549         }
2550
2551         status = softnic_pipeline_port_in_disable(softnic, pipeline_name, port_id);
2552         if (status) {
2553                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
2554                 return;
2555         }
2556 }
2557
2558 /**
2559  * pipeline <pipeline_name> port out <port_id> stats read [clear]
2560  */
2561 #define MSG_PIPELINE_PORT_OUT_STATS                        \
2562         "Pkts in: %" PRIu64 "\n"                           \
2563         "Pkts dropped by AH: %" PRIu64 "\n"                \
2564         "Pkts dropped by other: %" PRIu64 "\n"
2565
2566 static void
2567 cmd_pipeline_port_out_stats(struct pmd_internals *softnic,
2568         char **tokens,
2569         uint32_t n_tokens,
2570         char *out,
2571         size_t out_size)
2572 {
2573         struct rte_pipeline_port_out_stats stats;
2574         char *pipeline_name;
2575         uint32_t port_id;
2576         int clear, status;
2577
2578         if (n_tokens != 7 &&
2579                 n_tokens != 8) {
2580                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2581                 return;
2582         }
2583
2584         pipeline_name = tokens[1];
2585
2586         if (strcmp(tokens[2], "port") != 0) {
2587                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
2588                 return;
2589         }
2590
2591         if (strcmp(tokens[3], "out") != 0) {
2592                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "out");
2593                 return;
2594         }
2595
2596         if (softnic_parser_read_uint32(&port_id, tokens[4]) != 0) {
2597                 snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
2598                 return;
2599         }
2600
2601         if (strcmp(tokens[5], "stats") != 0) {
2602                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
2603                 return;
2604         }
2605
2606         if (strcmp(tokens[6], "read") != 0) {
2607                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "read");
2608                 return;
2609         }
2610
2611         clear = 0;
2612         if (n_tokens == 8) {
2613                 if (strcmp(tokens[7], "clear") != 0) {
2614                         snprintf(out, out_size, MSG_ARG_INVALID, "clear");
2615                         return;
2616                 }
2617
2618                 clear = 1;
2619         }
2620
2621         status = softnic_pipeline_port_out_stats_read(softnic,
2622                 pipeline_name,
2623                 port_id,
2624                 &stats,
2625                 clear);
2626         if (status) {
2627                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
2628                 return;
2629         }
2630
2631         snprintf(out, out_size, MSG_PIPELINE_PORT_OUT_STATS,
2632                 stats.stats.n_pkts_in,
2633                 stats.n_pkts_dropped_by_ah,
2634                 stats.stats.n_pkts_drop);
2635 }
2636
2637 /**
2638  * pipeline <pipeline_name> table <table_id> stats read [clear]
2639  */
2640 #define MSG_PIPELINE_TABLE_STATS                                     \
2641         "Pkts in: %" PRIu64 "\n"                                     \
2642         "Pkts in with lookup miss: %" PRIu64 "\n"                    \
2643         "Pkts in with lookup hit dropped by AH: %" PRIu64 "\n"       \
2644         "Pkts in with lookup hit dropped by others: %" PRIu64 "\n"   \
2645         "Pkts in with lookup miss dropped by AH: %" PRIu64 "\n"      \
2646         "Pkts in with lookup miss dropped by others: %" PRIu64 "\n"
2647
2648 static void
2649 cmd_pipeline_table_stats(struct pmd_internals *softnic,
2650         char **tokens,
2651         uint32_t n_tokens,
2652         char *out,
2653         size_t out_size)
2654 {
2655         struct rte_pipeline_table_stats stats;
2656         char *pipeline_name;
2657         uint32_t table_id;
2658         int clear, status;
2659
2660         if (n_tokens != 6 &&
2661                 n_tokens != 7) {
2662                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2663                 return;
2664         }
2665
2666         pipeline_name = tokens[1];
2667
2668         if (strcmp(tokens[2], "table") != 0) {
2669                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
2670                 return;
2671         }
2672
2673         if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
2674                 snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
2675                 return;
2676         }
2677
2678         if (strcmp(tokens[4], "stats") != 0) {
2679                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
2680                 return;
2681         }
2682
2683         if (strcmp(tokens[5], "read") != 0) {
2684                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "read");
2685                 return;
2686         }
2687
2688         clear = 0;
2689         if (n_tokens == 7) {
2690                 if (strcmp(tokens[6], "clear") != 0) {
2691                         snprintf(out, out_size, MSG_ARG_INVALID, "clear");
2692                         return;
2693                 }
2694
2695                 clear = 1;
2696         }
2697
2698         status = softnic_pipeline_table_stats_read(softnic,
2699                 pipeline_name,
2700                 table_id,
2701                 &stats,
2702                 clear);
2703         if (status) {
2704                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
2705                 return;
2706         }
2707
2708         snprintf(out, out_size, MSG_PIPELINE_TABLE_STATS,
2709                 stats.stats.n_pkts_in,
2710                 stats.stats.n_pkts_lookup_miss,
2711                 stats.n_pkts_dropped_by_lkp_hit_ah,
2712                 stats.n_pkts_dropped_lkp_hit,
2713                 stats.n_pkts_dropped_by_lkp_miss_ah,
2714                 stats.n_pkts_dropped_lkp_miss);
2715 }
2716
2717 /**
2718  * <match> ::=
2719  *
2720  * match
2721  *    acl
2722  *       priority <priority>
2723  *       ipv4 | ipv6 <sa> <sa_depth> <da> <da_depth>
2724  *       <sp0> <sp1> <dp0> <dp1> <proto>
2725  *    | array <pos>
2726  *    | hash
2727  *       raw <key>
2728  *       | ipv4_5tuple <sa> <da> <sp> <dp> <proto>
2729  *       | ipv6_5tuple <sa> <da> <sp> <dp> <proto>
2730  *       | ipv4_addr <addr>
2731  *       | ipv6_addr <addr>
2732  *       | qinq <svlan> <cvlan>
2733  *    | lpm
2734  *       ipv4 | ipv6 <addr> <depth>
2735  */
2736 struct pkt_key_qinq {
2737         uint16_t ethertype_svlan;
2738         uint16_t svlan;
2739         uint16_t ethertype_cvlan;
2740         uint16_t cvlan;
2741 } __attribute__((__packed__));
2742
2743 struct pkt_key_ipv4_5tuple {
2744         uint8_t time_to_live;
2745         uint8_t proto;
2746         uint16_t hdr_checksum;
2747         uint32_t sa;
2748         uint32_t da;
2749         uint16_t sp;
2750         uint16_t dp;
2751 } __attribute__((__packed__));
2752
2753 struct pkt_key_ipv6_5tuple {
2754         uint16_t payload_length;
2755         uint8_t proto;
2756         uint8_t hop_limit;
2757         uint8_t sa[16];
2758         uint8_t da[16];
2759         uint16_t sp;
2760         uint16_t dp;
2761 } __attribute__((__packed__));
2762
2763 struct pkt_key_ipv4_addr {
2764         uint32_t addr;
2765 } __attribute__((__packed__));
2766
2767 struct pkt_key_ipv6_addr {
2768         uint8_t addr[16];
2769 } __attribute__((__packed__));
2770
2771 static uint32_t
2772 parse_match(char **tokens,
2773         uint32_t n_tokens,
2774         char *out,
2775         size_t out_size,
2776         struct softnic_table_rule_match *m)
2777 {
2778         memset(m, 0, sizeof(*m));
2779
2780         if (n_tokens < 2)
2781                 return 0;
2782
2783         if (strcmp(tokens[0], "match") != 0) {
2784                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "match");
2785                 return 0;
2786         }
2787
2788         if (strcmp(tokens[1], "acl") == 0) {
2789                 if (n_tokens < 14) {
2790                         snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2791                         return 0;
2792                 }
2793
2794                 m->match_type = TABLE_ACL;
2795
2796                 if (strcmp(tokens[2], "priority") != 0) {
2797                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "priority");
2798                         return 0;
2799                 }
2800
2801                 if (softnic_parser_read_uint32(&m->match.acl.priority,
2802                         tokens[3]) != 0) {
2803                         snprintf(out, out_size, MSG_ARG_INVALID, "priority");
2804                         return 0;
2805                 }
2806
2807                 if (strcmp(tokens[4], "ipv4") == 0) {
2808                         struct in_addr saddr, daddr;
2809
2810                         m->match.acl.ip_version = 1;
2811
2812                         if (softnic_parse_ipv4_addr(tokens[5], &saddr) != 0) {
2813                                 snprintf(out, out_size, MSG_ARG_INVALID, "sa");
2814                                 return 0;
2815                         }
2816                         m->match.acl.ipv4.sa = rte_be_to_cpu_32(saddr.s_addr);
2817
2818                         if (softnic_parse_ipv4_addr(tokens[7], &daddr) != 0) {
2819                                 snprintf(out, out_size, MSG_ARG_INVALID, "da");
2820                                 return 0;
2821                         }
2822                         m->match.acl.ipv4.da = rte_be_to_cpu_32(daddr.s_addr);
2823                 } else if (strcmp(tokens[4], "ipv6") == 0) {
2824                         struct in6_addr saddr, daddr;
2825
2826                         m->match.acl.ip_version = 0;
2827
2828                         if (softnic_parse_ipv6_addr(tokens[5], &saddr) != 0) {
2829                                 snprintf(out, out_size, MSG_ARG_INVALID, "sa");
2830                                 return 0;
2831                         }
2832                         memcpy(m->match.acl.ipv6.sa, saddr.s6_addr, 16);
2833
2834                         if (softnic_parse_ipv6_addr(tokens[7], &daddr) != 0) {
2835                                 snprintf(out, out_size, MSG_ARG_INVALID, "da");
2836                                 return 0;
2837                         }
2838                         memcpy(m->match.acl.ipv6.da, daddr.s6_addr, 16);
2839                 } else {
2840                         snprintf(out, out_size, MSG_ARG_NOT_FOUND,
2841                                 "ipv4 or ipv6");
2842                         return 0;
2843                 }
2844
2845                 if (softnic_parser_read_uint32(&m->match.acl.sa_depth,
2846                         tokens[6]) != 0) {
2847                         snprintf(out, out_size, MSG_ARG_INVALID, "sa_depth");
2848                         return 0;
2849                 }
2850
2851                 if (softnic_parser_read_uint32(&m->match.acl.da_depth,
2852                         tokens[8]) != 0) {
2853                         snprintf(out, out_size, MSG_ARG_INVALID, "da_depth");
2854                         return 0;
2855                 }
2856
2857                 if (softnic_parser_read_uint16(&m->match.acl.sp0, tokens[9]) != 0) {
2858                         snprintf(out, out_size, MSG_ARG_INVALID, "sp0");
2859                         return 0;
2860                 }
2861
2862                 if (softnic_parser_read_uint16(&m->match.acl.sp1, tokens[10]) != 0) {
2863                         snprintf(out, out_size, MSG_ARG_INVALID, "sp1");
2864                         return 0;
2865                 }
2866
2867                 if (softnic_parser_read_uint16(&m->match.acl.dp0, tokens[11]) != 0) {
2868                         snprintf(out, out_size, MSG_ARG_INVALID, "dp0");
2869                         return 0;
2870                 }
2871
2872                 if (softnic_parser_read_uint16(&m->match.acl.dp1, tokens[12]) != 0) {
2873                         snprintf(out, out_size, MSG_ARG_INVALID, "dp1");
2874                         return 0;
2875                 }
2876
2877                 if (softnic_parser_read_uint8(&m->match.acl.proto, tokens[13]) != 0) {
2878                         snprintf(out, out_size, MSG_ARG_INVALID, "proto");
2879                         return 0;
2880                 }
2881
2882                 m->match.acl.proto_mask = 0xff;
2883
2884                 return 14;
2885         } /* acl */
2886
2887         if (strcmp(tokens[1], "array") == 0) {
2888                 if (n_tokens < 3) {
2889                         snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2890                         return 0;
2891                 }
2892
2893                 m->match_type = TABLE_ARRAY;
2894
2895                 if (softnic_parser_read_uint32(&m->match.array.pos, tokens[2]) != 0) {
2896                         snprintf(out, out_size, MSG_ARG_INVALID, "pos");
2897                         return 0;
2898                 }
2899
2900                 return 3;
2901         } /* array */
2902
2903         if (strcmp(tokens[1], "hash") == 0) {
2904                 if (n_tokens < 3) {
2905                         snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2906                         return 0;
2907                 }
2908
2909                 m->match_type = TABLE_HASH;
2910
2911                 if (strcmp(tokens[2], "raw") == 0) {
2912                         uint32_t key_size = TABLE_RULE_MATCH_SIZE_MAX;
2913
2914                         if (n_tokens < 4) {
2915                                 snprintf(out, out_size, MSG_ARG_MISMATCH,
2916                                         tokens[0]);
2917                                 return 0;
2918                         }
2919
2920                         if (softnic_parse_hex_string(tokens[3],
2921                                 m->match.hash.key, &key_size) != 0) {
2922                                 snprintf(out, out_size, MSG_ARG_INVALID, "key");
2923                                 return 0;
2924                         }
2925
2926                         return 4;
2927                 } /* hash raw */
2928
2929                 if (strcmp(tokens[2], "ipv4_5tuple") == 0) {
2930                         struct pkt_key_ipv4_5tuple *ipv4 =
2931                                 (struct pkt_key_ipv4_5tuple *)m->match.hash.key;
2932                         struct in_addr saddr, daddr;
2933                         uint16_t sp, dp;
2934                         uint8_t proto;
2935
2936                         if (n_tokens < 8) {
2937                                 snprintf(out, out_size, MSG_ARG_MISMATCH,
2938                                         tokens[0]);
2939                                 return 0;
2940                         }
2941
2942                         if (softnic_parse_ipv4_addr(tokens[3], &saddr) != 0) {
2943                                 snprintf(out, out_size, MSG_ARG_INVALID, "sa");
2944                                 return 0;
2945                         }
2946
2947                         if (softnic_parse_ipv4_addr(tokens[4], &daddr) != 0) {
2948                                 snprintf(out, out_size, MSG_ARG_INVALID, "da");
2949                                 return 0;
2950                         }
2951
2952                         if (softnic_parser_read_uint16(&sp, tokens[5]) != 0) {
2953                                 snprintf(out, out_size, MSG_ARG_INVALID, "sp");
2954                                 return 0;
2955                         }
2956
2957                         if (softnic_parser_read_uint16(&dp, tokens[6]) != 0) {
2958                                 snprintf(out, out_size, MSG_ARG_INVALID, "dp");
2959                                 return 0;
2960                         }
2961
2962                         if (softnic_parser_read_uint8(&proto, tokens[7]) != 0) {
2963                                 snprintf(out, out_size, MSG_ARG_INVALID,
2964                                         "proto");
2965                                 return 0;
2966                         }
2967
2968                         ipv4->sa = saddr.s_addr;
2969                         ipv4->da = daddr.s_addr;
2970                         ipv4->sp = rte_cpu_to_be_16(sp);
2971                         ipv4->dp = rte_cpu_to_be_16(dp);
2972                         ipv4->proto = proto;
2973
2974                         return 8;
2975                 } /* hash ipv4_5tuple */
2976
2977                 if (strcmp(tokens[2], "ipv6_5tuple") == 0) {
2978                         struct pkt_key_ipv6_5tuple *ipv6 =
2979                                 (struct pkt_key_ipv6_5tuple *)m->match.hash.key;
2980                         struct in6_addr saddr, daddr;
2981                         uint16_t sp, dp;
2982                         uint8_t proto;
2983
2984                         if (n_tokens < 8) {
2985                                 snprintf(out, out_size, MSG_ARG_MISMATCH,
2986                                         tokens[0]);
2987                                 return 0;
2988                         }
2989
2990                         if (softnic_parse_ipv6_addr(tokens[3], &saddr) != 0) {
2991                                 snprintf(out, out_size, MSG_ARG_INVALID, "sa");
2992                                 return 0;
2993                         }
2994
2995                         if (softnic_parse_ipv6_addr(tokens[4], &daddr) != 0) {
2996                                 snprintf(out, out_size, MSG_ARG_INVALID, "da");
2997                                 return 0;
2998                         }
2999
3000                         if (softnic_parser_read_uint16(&sp, tokens[5]) != 0) {
3001                                 snprintf(out, out_size, MSG_ARG_INVALID, "sp");
3002                                 return 0;
3003                         }
3004
3005                         if (softnic_parser_read_uint16(&dp, tokens[6]) != 0) {
3006                                 snprintf(out, out_size, MSG_ARG_INVALID, "dp");
3007                                 return 0;
3008                         }
3009
3010                         if (softnic_parser_read_uint8(&proto, tokens[7]) != 0) {
3011                                 snprintf(out, out_size, MSG_ARG_INVALID,
3012                                         "proto");
3013                                 return 0;
3014                         }
3015
3016                         memcpy(ipv6->sa, saddr.s6_addr, 16);
3017                         memcpy(ipv6->da, daddr.s6_addr, 16);
3018                         ipv6->sp = rte_cpu_to_be_16(sp);
3019                         ipv6->dp = rte_cpu_to_be_16(dp);
3020                         ipv6->proto = proto;
3021
3022                         return 8;
3023                 } /* hash ipv6_5tuple */
3024
3025                 if (strcmp(tokens[2], "ipv4_addr") == 0) {
3026                         struct pkt_key_ipv4_addr *ipv4_addr =
3027                                 (struct pkt_key_ipv4_addr *)m->match.hash.key;
3028                         struct in_addr addr;
3029
3030                         if (n_tokens < 4) {
3031                                 snprintf(out, out_size, MSG_ARG_MISMATCH,
3032                                         tokens[0]);
3033                                 return 0;
3034                         }
3035
3036                         if (softnic_parse_ipv4_addr(tokens[3], &addr) != 0) {
3037                                 snprintf(out, out_size, MSG_ARG_INVALID,
3038                                         "addr");
3039                                 return 0;
3040                         }
3041
3042                         ipv4_addr->addr = addr.s_addr;
3043
3044                         return 4;
3045                 } /* hash ipv4_addr */
3046
3047                 if (strcmp(tokens[2], "ipv6_addr") == 0) {
3048                         struct pkt_key_ipv6_addr *ipv6_addr =
3049                                 (struct pkt_key_ipv6_addr *)m->match.hash.key;
3050                         struct in6_addr addr;
3051
3052                         if (n_tokens < 4) {
3053                                 snprintf(out, out_size, MSG_ARG_MISMATCH,
3054                                         tokens[0]);
3055                                 return 0;
3056                         }
3057
3058                         if (softnic_parse_ipv6_addr(tokens[3], &addr) != 0) {
3059                                 snprintf(out, out_size, MSG_ARG_INVALID,
3060                                         "addr");
3061                                 return 0;
3062                         }
3063
3064                         memcpy(ipv6_addr->addr, addr.s6_addr, 16);
3065
3066                         return 4;
3067                 } /* hash ipv6_5tuple */
3068
3069                 if (strcmp(tokens[2], "qinq") == 0) {
3070                         struct pkt_key_qinq *qinq =
3071                                 (struct pkt_key_qinq *)m->match.hash.key;
3072                         uint16_t svlan, cvlan;
3073
3074                         if (n_tokens < 5) {
3075                                 snprintf(out, out_size, MSG_ARG_MISMATCH,
3076                                         tokens[0]);
3077                                 return 0;
3078                         }
3079
3080                         if ((softnic_parser_read_uint16(&svlan, tokens[3]) != 0) ||
3081                                 svlan > 0xFFF) {
3082                                 snprintf(out, out_size, MSG_ARG_INVALID,
3083                                         "svlan");
3084                                 return 0;
3085                         }
3086
3087                         if ((softnic_parser_read_uint16(&cvlan, tokens[4]) != 0) ||
3088                                 cvlan > 0xFFF) {
3089                                 snprintf(out, out_size, MSG_ARG_INVALID,
3090                                         "cvlan");
3091                                 return 0;
3092                         }
3093
3094                         qinq->svlan = rte_cpu_to_be_16(svlan);
3095                         qinq->cvlan = rte_cpu_to_be_16(cvlan);
3096
3097                         return 5;
3098                 } /* hash qinq */
3099
3100                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3101                 return 0;
3102         } /* hash */
3103
3104         if (strcmp(tokens[1], "lpm") == 0) {
3105                 if (n_tokens < 5) {
3106                         snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3107                         return 0;
3108                 }
3109
3110                 m->match_type = TABLE_LPM;
3111
3112                 if (strcmp(tokens[2], "ipv4") == 0) {
3113                         struct in_addr addr;
3114
3115                         m->match.lpm.ip_version = 1;
3116
3117                         if (softnic_parse_ipv4_addr(tokens[3], &addr) != 0) {
3118                                 snprintf(out, out_size, MSG_ARG_INVALID,
3119                                         "addr");
3120                                 return 0;
3121                         }
3122
3123                         m->match.lpm.ipv4 = rte_be_to_cpu_32(addr.s_addr);
3124                 } else if (strcmp(tokens[2], "ipv6") == 0) {
3125                         struct in6_addr addr;
3126
3127                         m->match.lpm.ip_version = 0;
3128
3129                         if (softnic_parse_ipv6_addr(tokens[3], &addr) != 0) {
3130                                 snprintf(out, out_size, MSG_ARG_INVALID,
3131                                         "addr");
3132                                 return 0;
3133                         }
3134
3135                         memcpy(m->match.lpm.ipv6, addr.s6_addr, 16);
3136                 } else {
3137                         snprintf(out, out_size, MSG_ARG_MISMATCH,
3138                                 "ipv4 or ipv6");
3139                         return 0;
3140                 }
3141
3142                 if (softnic_parser_read_uint8(&m->match.lpm.depth, tokens[4]) != 0) {
3143                         snprintf(out, out_size, MSG_ARG_INVALID, "depth");
3144                         return 0;
3145                 }
3146
3147                 return 5;
3148         } /* lpm */
3149
3150         snprintf(out, out_size, MSG_ARG_MISMATCH,
3151                 "acl or array or hash or lpm");
3152         return 0;
3153 }
3154
3155 /**
3156  * table_action ::=
3157  *
3158  * action
3159  *    fwd
3160  *       drop
3161  *       | port <port_id>
3162  *       | meta
3163  *       | table <table_id>
3164  *    [balance <out0> ... <out7>]
3165  *    [meter
3166  *       tc0 meter <meter_profile_id> policer g <pa> y <pa> r <pa>
3167  *       [tc1 meter <meter_profile_id> policer g <pa> y <pa> r <pa>
3168  *       tc2 meter <meter_profile_id> policer g <pa> y <pa> r <pa>
3169  *       tc3 meter <meter_profile_id> policer g <pa> y <pa> r <pa>]]
3170  *    [tm subport <subport_id> pipe <pipe_id>]
3171  *    [encap
3172  *       ether <da> <sa>
3173  *       | vlan <da> <sa> <pcp> <dei> <vid>
3174  *       | qinq <da> <sa> <pcp> <dei> <vid> <pcp> <dei> <vid>
3175  *       | mpls unicast | multicast
3176  *          <da> <sa>
3177  *          label0 <label> <tc> <ttl>
3178  *          [label1 <label> <tc> <ttl>
3179  *          [label2 <label> <tc> <ttl>
3180  *          [label3 <label> <tc> <ttl>]]]
3181  *       | pppoe <da> <sa> <session_id>]
3182  *    [nat ipv4 | ipv6 <addr> <port>]
3183  *    [ttl dec | keep]
3184  *    [stats]
3185  *    [time]
3186  *    [tag <tag>]
3187  *
3188  * where:
3189  *    <pa> ::= g | y | r | drop
3190  */
3191 static uint32_t
3192 parse_table_action_fwd(char **tokens,
3193         uint32_t n_tokens,
3194         struct softnic_table_rule_action *a)
3195 {
3196         if (n_tokens == 0 ||
3197                 (strcmp(tokens[0], "fwd") != 0))
3198                 return 0;
3199
3200         tokens++;
3201         n_tokens--;
3202
3203         if (n_tokens && (strcmp(tokens[0], "drop") == 0)) {
3204                 a->fwd.action = RTE_PIPELINE_ACTION_DROP;
3205                 a->action_mask |= 1 << RTE_TABLE_ACTION_FWD;
3206                 return 1 + 1;
3207         }
3208
3209         if (n_tokens && (strcmp(tokens[0], "port") == 0)) {
3210                 uint32_t id;
3211
3212                 if (n_tokens < 2 ||
3213                         softnic_parser_read_uint32(&id, tokens[1]))
3214                         return 0;
3215
3216                 a->fwd.action = RTE_PIPELINE_ACTION_PORT;
3217                 a->fwd.id = id;
3218                 a->action_mask |= 1 << RTE_TABLE_ACTION_FWD;
3219                 return 1 + 2;
3220         }
3221
3222         if (n_tokens && (strcmp(tokens[0], "meta") == 0)) {
3223                 a->fwd.action = RTE_PIPELINE_ACTION_PORT_META;
3224                 a->action_mask |= 1 << RTE_TABLE_ACTION_FWD;
3225                 return 1 + 1;
3226         }
3227
3228         if (n_tokens && (strcmp(tokens[0], "table") == 0)) {
3229                 uint32_t id;
3230
3231                 if (n_tokens < 2 ||
3232                         softnic_parser_read_uint32(&id, tokens[1]))
3233                         return 0;
3234
3235                 a->fwd.action = RTE_PIPELINE_ACTION_TABLE;
3236                 a->fwd.id = id;
3237                 a->action_mask |= 1 << RTE_TABLE_ACTION_FWD;
3238                 return 1 + 2;
3239         }
3240
3241         return 0;
3242 }
3243
3244 static uint32_t
3245 parse_table_action_balance(char **tokens,
3246         uint32_t n_tokens,
3247         struct softnic_table_rule_action *a)
3248 {
3249         uint32_t i;
3250
3251         if (n_tokens == 0 ||
3252                 (strcmp(tokens[0], "balance") != 0))
3253                 return 0;
3254
3255         tokens++;
3256         n_tokens--;
3257
3258         if (n_tokens < RTE_TABLE_ACTION_LB_TABLE_SIZE)
3259                 return 0;
3260
3261         for (i = 0; i < RTE_TABLE_ACTION_LB_TABLE_SIZE; i++)
3262                 if (softnic_parser_read_uint32(&a->lb.out[i], tokens[i]) != 0)
3263                         return 0;
3264
3265         a->action_mask |= 1 << RTE_TABLE_ACTION_LB;
3266         return 1 + RTE_TABLE_ACTION_LB_TABLE_SIZE;
3267 }
3268
3269 static int
3270 parse_policer_action(char *token, enum rte_table_action_policer *a)
3271 {
3272         if (strcmp(token, "g") == 0) {
3273                 *a = RTE_TABLE_ACTION_POLICER_COLOR_GREEN;
3274                 return 0;
3275         }
3276
3277         if (strcmp(token, "y") == 0) {
3278                 *a = RTE_TABLE_ACTION_POLICER_COLOR_YELLOW;
3279                 return 0;
3280         }
3281
3282         if (strcmp(token, "r") == 0) {
3283                 *a = RTE_TABLE_ACTION_POLICER_COLOR_RED;
3284                 return 0;
3285         }
3286
3287         if (strcmp(token, "drop") == 0) {
3288                 *a = RTE_TABLE_ACTION_POLICER_DROP;
3289                 return 0;
3290         }
3291
3292         return -1;
3293 }
3294
3295 static uint32_t
3296 parse_table_action_meter_tc(char **tokens,
3297         uint32_t n_tokens,
3298         struct rte_table_action_mtr_tc_params *mtr)
3299 {
3300         if (n_tokens < 9 ||
3301                 strcmp(tokens[0], "meter") ||
3302                 softnic_parser_read_uint32(&mtr->meter_profile_id, tokens[1]) ||
3303                 strcmp(tokens[2], "policer") ||
3304                 strcmp(tokens[3], "g") ||
3305                 parse_policer_action(tokens[4], &mtr->policer[e_RTE_METER_GREEN]) ||
3306                 strcmp(tokens[5], "y") ||
3307                 parse_policer_action(tokens[6], &mtr->policer[e_RTE_METER_YELLOW]) ||
3308                 strcmp(tokens[7], "r") ||
3309                 parse_policer_action(tokens[8], &mtr->policer[e_RTE_METER_RED]))
3310                 return 0;
3311
3312         return 9;
3313 }
3314
3315 static uint32_t
3316 parse_table_action_meter(char **tokens,
3317         uint32_t n_tokens,
3318         struct softnic_table_rule_action *a)
3319 {
3320         if (n_tokens == 0 ||
3321                 strcmp(tokens[0], "meter"))
3322                 return 0;
3323
3324         tokens++;
3325         n_tokens--;
3326
3327         if (n_tokens < 10 ||
3328                 strcmp(tokens[0], "tc0") ||
3329                 (parse_table_action_meter_tc(tokens + 1,
3330                         n_tokens - 1,
3331                         &a->mtr.mtr[0]) == 0))
3332                 return 0;
3333
3334         tokens += 10;
3335         n_tokens -= 10;
3336
3337         if (n_tokens == 0 ||
3338                 strcmp(tokens[0], "tc1")) {
3339                 a->mtr.tc_mask = 1;
3340                 a->action_mask |= 1 << RTE_TABLE_ACTION_MTR;
3341                 return 1 + 10;
3342         }
3343
3344         if (n_tokens < 30 ||
3345                 (parse_table_action_meter_tc(tokens + 1,
3346                         n_tokens - 1, &a->mtr.mtr[1]) == 0) ||
3347                 strcmp(tokens[10], "tc2") ||
3348                 (parse_table_action_meter_tc(tokens + 11,
3349                         n_tokens - 11, &a->mtr.mtr[2]) == 0) ||
3350                 strcmp(tokens[20], "tc3") ||
3351                 (parse_table_action_meter_tc(tokens + 21,
3352                         n_tokens - 21, &a->mtr.mtr[3]) == 0))
3353                 return 0;
3354
3355         a->mtr.tc_mask = 0xF;
3356         a->action_mask |= 1 << RTE_TABLE_ACTION_MTR;
3357         return 1 + 10 + 3 * 10;
3358 }
3359
3360 static uint32_t
3361 parse_table_action_tm(char **tokens,
3362         uint32_t n_tokens,
3363         struct softnic_table_rule_action *a)
3364 {
3365         uint32_t subport_id, pipe_id;
3366
3367         if (n_tokens < 5 ||
3368                 strcmp(tokens[0], "tm") ||
3369                 strcmp(tokens[1], "subport") ||
3370                 softnic_parser_read_uint32(&subport_id, tokens[2]) ||
3371                 strcmp(tokens[3], "pipe") ||
3372                 softnic_parser_read_uint32(&pipe_id, tokens[4]))
3373                 return 0;
3374
3375         a->tm.subport_id = subport_id;
3376         a->tm.pipe_id = pipe_id;
3377         a->action_mask |= 1 << RTE_TABLE_ACTION_TM;
3378         return 5;
3379 }
3380
3381 static uint32_t
3382 parse_table_action_encap(char **tokens,
3383         uint32_t n_tokens,
3384         struct softnic_table_rule_action *a)
3385 {
3386         if (n_tokens == 0 ||
3387                 strcmp(tokens[0], "encap"))
3388                 return 0;
3389
3390         tokens++;
3391         n_tokens--;
3392
3393         /* ether */
3394         if (n_tokens && (strcmp(tokens[0], "ether") == 0)) {
3395                 if (n_tokens < 3 ||
3396                         softnic_parse_mac_addr(tokens[1], &a->encap.ether.ether.da) ||
3397                         softnic_parse_mac_addr(tokens[2], &a->encap.ether.ether.sa))
3398                         return 0;
3399
3400                 a->encap.type = RTE_TABLE_ACTION_ENCAP_ETHER;
3401                 a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
3402                 return 1 + 3;
3403         }
3404
3405         /* vlan */
3406         if (n_tokens && (strcmp(tokens[0], "vlan") == 0)) {
3407                 uint32_t pcp, dei, vid;
3408
3409                 if (n_tokens < 6 ||
3410                         softnic_parse_mac_addr(tokens[1], &a->encap.vlan.ether.da) ||
3411                         softnic_parse_mac_addr(tokens[2], &a->encap.vlan.ether.sa) ||
3412                         softnic_parser_read_uint32(&pcp, tokens[3]) ||
3413                         pcp > 0x7 ||
3414                         softnic_parser_read_uint32(&dei, tokens[4]) ||
3415                         dei > 0x1 ||
3416                         softnic_parser_read_uint32(&vid, tokens[5]) ||
3417                         vid > 0xFFF)
3418                         return 0;
3419
3420                 a->encap.vlan.vlan.pcp = pcp & 0x7;
3421                 a->encap.vlan.vlan.dei = dei & 0x1;
3422                 a->encap.vlan.vlan.vid = vid & 0xFFF;
3423                 a->encap.type = RTE_TABLE_ACTION_ENCAP_VLAN;
3424                 a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
3425                 return 1 + 6;
3426         }
3427
3428         /* qinq */
3429         if (n_tokens && (strcmp(tokens[0], "qinq") == 0)) {
3430                 uint32_t svlan_pcp, svlan_dei, svlan_vid;
3431                 uint32_t cvlan_pcp, cvlan_dei, cvlan_vid;
3432
3433                 if (n_tokens < 9 ||
3434                         softnic_parse_mac_addr(tokens[1], &a->encap.qinq.ether.da) ||
3435                         softnic_parse_mac_addr(tokens[2], &a->encap.qinq.ether.sa) ||
3436                         softnic_parser_read_uint32(&svlan_pcp, tokens[3]) ||
3437                         svlan_pcp > 0x7 ||
3438                         softnic_parser_read_uint32(&svlan_dei, tokens[4]) ||
3439                         svlan_dei > 0x1 ||
3440                         softnic_parser_read_uint32(&svlan_vid, tokens[5]) ||
3441                         svlan_vid > 0xFFF ||
3442                         softnic_parser_read_uint32(&cvlan_pcp, tokens[6]) ||
3443                         cvlan_pcp > 0x7 ||
3444                         softnic_parser_read_uint32(&cvlan_dei, tokens[7]) ||
3445                         cvlan_dei > 0x1 ||
3446                         softnic_parser_read_uint32(&cvlan_vid, tokens[8]) ||
3447                         cvlan_vid > 0xFFF)
3448                         return 0;
3449
3450                 a->encap.qinq.svlan.pcp = svlan_pcp & 0x7;
3451                 a->encap.qinq.svlan.dei = svlan_dei & 0x1;
3452                 a->encap.qinq.svlan.vid = svlan_vid & 0xFFF;
3453                 a->encap.qinq.cvlan.pcp = cvlan_pcp & 0x7;
3454                 a->encap.qinq.cvlan.dei = cvlan_dei & 0x1;
3455                 a->encap.qinq.cvlan.vid = cvlan_vid & 0xFFF;
3456                 a->encap.type = RTE_TABLE_ACTION_ENCAP_QINQ;
3457                 a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
3458                 return 1 + 9;
3459         }
3460
3461         /* mpls */
3462         if (n_tokens && (strcmp(tokens[0], "mpls") == 0)) {
3463                 uint32_t label, tc, ttl;
3464
3465                 if (n_tokens < 8)
3466                         return 0;
3467
3468                 if (strcmp(tokens[1], "unicast") == 0)
3469                         a->encap.mpls.unicast = 1;
3470                 else if (strcmp(tokens[1], "multicast") == 0)
3471                         a->encap.mpls.unicast = 0;
3472                 else
3473                         return 0;
3474
3475                 if (softnic_parse_mac_addr(tokens[2], &a->encap.mpls.ether.da) ||
3476                         softnic_parse_mac_addr(tokens[3], &a->encap.mpls.ether.sa) ||
3477                         strcmp(tokens[4], "label0") ||
3478                         softnic_parser_read_uint32(&label, tokens[5]) ||
3479                         label > 0xFFFFF ||
3480                         softnic_parser_read_uint32(&tc, tokens[6]) ||
3481                         tc > 0x7 ||
3482                         softnic_parser_read_uint32(&ttl, tokens[7]) ||
3483                         ttl > 0x3F)
3484                         return 0;
3485
3486                 a->encap.mpls.mpls[0].label = label;
3487                 a->encap.mpls.mpls[0].tc = tc;
3488                 a->encap.mpls.mpls[0].ttl = ttl;
3489
3490                 tokens += 8;
3491                 n_tokens -= 8;
3492
3493                 if (n_tokens == 0 ||
3494                         strcmp(tokens[0], "label1")) {
3495                         a->encap.mpls.mpls_count = 1;
3496                         a->encap.type = RTE_TABLE_ACTION_ENCAP_MPLS;
3497                         a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
3498                         return 1 + 8;
3499                 }
3500
3501                 if (n_tokens < 4 ||
3502                         softnic_parser_read_uint32(&label, tokens[1]) ||
3503                         label > 0xFFFFF ||
3504                         softnic_parser_read_uint32(&tc, tokens[2]) ||
3505                         tc > 0x7 ||
3506                         softnic_parser_read_uint32(&ttl, tokens[3]) ||
3507                         ttl > 0x3F)
3508                         return 0;
3509
3510                 a->encap.mpls.mpls[1].label = label;
3511                 a->encap.mpls.mpls[1].tc = tc;
3512                 a->encap.mpls.mpls[1].ttl = ttl;
3513
3514                 tokens += 4;
3515                 n_tokens -= 4;
3516
3517                 if (n_tokens == 0 ||
3518                         strcmp(tokens[0], "label2")) {
3519                         a->encap.mpls.mpls_count = 2;
3520                         a->encap.type = RTE_TABLE_ACTION_ENCAP_MPLS;
3521                         a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
3522                         return 1 + 8 + 4;
3523                 }
3524
3525                 if (n_tokens < 4 ||
3526                         softnic_parser_read_uint32(&label, tokens[1]) ||
3527                         label > 0xFFFFF ||
3528                         softnic_parser_read_uint32(&tc, tokens[2]) ||
3529                         tc > 0x7 ||
3530                         softnic_parser_read_uint32(&ttl, tokens[3]) ||
3531                         ttl > 0x3F)
3532                         return 0;
3533
3534                 a->encap.mpls.mpls[2].label = label;
3535                 a->encap.mpls.mpls[2].tc = tc;
3536                 a->encap.mpls.mpls[2].ttl = ttl;
3537
3538                 tokens += 4;
3539                 n_tokens -= 4;
3540
3541                 if (n_tokens == 0 ||
3542                         strcmp(tokens[0], "label3")) {
3543                         a->encap.mpls.mpls_count = 3;
3544                         a->encap.type = RTE_TABLE_ACTION_ENCAP_MPLS;
3545                         a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
3546                         return 1 + 8 + 4 + 4;
3547                 }
3548
3549                 if (n_tokens < 4 ||
3550                         softnic_parser_read_uint32(&label, tokens[1]) ||
3551                         label > 0xFFFFF ||
3552                         softnic_parser_read_uint32(&tc, tokens[2]) ||
3553                         tc > 0x7 ||
3554                         softnic_parser_read_uint32(&ttl, tokens[3]) ||
3555                         ttl > 0x3F)
3556                         return 0;
3557
3558                 a->encap.mpls.mpls[3].label = label;
3559                 a->encap.mpls.mpls[3].tc = tc;
3560                 a->encap.mpls.mpls[3].ttl = ttl;
3561
3562                 a->encap.mpls.mpls_count = 4;
3563                 a->encap.type = RTE_TABLE_ACTION_ENCAP_MPLS;
3564                 a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
3565                 return 1 + 8 + 4 + 4 + 4;
3566         }
3567
3568         /* pppoe */
3569         if (n_tokens && (strcmp(tokens[0], "pppoe") == 0)) {
3570                 if (n_tokens < 4 ||
3571                         softnic_parse_mac_addr(tokens[1], &a->encap.pppoe.ether.da) ||
3572                         softnic_parse_mac_addr(tokens[2], &a->encap.pppoe.ether.sa) ||
3573                         softnic_parser_read_uint16(&a->encap.pppoe.pppoe.session_id,
3574                                 tokens[3]))
3575                         return 0;
3576
3577                 a->encap.type = RTE_TABLE_ACTION_ENCAP_PPPOE;
3578                 a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
3579                 return 1 + 4;
3580         }
3581
3582         return 0;
3583 }
3584
3585 static uint32_t
3586 parse_table_action_nat(char **tokens,
3587         uint32_t n_tokens,
3588         struct softnic_table_rule_action *a)
3589 {
3590         if (n_tokens < 4 ||
3591                 strcmp(tokens[0], "nat"))
3592                 return 0;
3593
3594         if (strcmp(tokens[1], "ipv4") == 0) {
3595                 struct in_addr addr;
3596                 uint16_t port;
3597
3598                 if (softnic_parse_ipv4_addr(tokens[2], &addr) ||
3599                         softnic_parser_read_uint16(&port, tokens[3]))
3600                         return 0;
3601
3602                 a->nat.ip_version = 1;
3603                 a->nat.addr.ipv4 = rte_be_to_cpu_32(addr.s_addr);
3604                 a->nat.port = port;
3605                 a->action_mask |= 1 << RTE_TABLE_ACTION_NAT;
3606                 return 4;
3607         }
3608
3609         if (strcmp(tokens[1], "ipv6") == 0) {
3610                 struct in6_addr addr;
3611                 uint16_t port;
3612
3613                 if (softnic_parse_ipv6_addr(tokens[2], &addr) ||
3614                         softnic_parser_read_uint16(&port, tokens[3]))
3615                         return 0;
3616
3617                 a->nat.ip_version = 0;
3618                 memcpy(a->nat.addr.ipv6, addr.s6_addr, 16);
3619                 a->nat.port = port;
3620                 a->action_mask |= 1 << RTE_TABLE_ACTION_NAT;
3621                 return 4;
3622         }
3623
3624         return 0;
3625 }
3626
3627 static uint32_t
3628 parse_table_action_ttl(char **tokens,
3629         uint32_t n_tokens,
3630         struct softnic_table_rule_action *a)
3631 {
3632         if (n_tokens < 2 ||
3633                 strcmp(tokens[0], "ttl"))
3634                 return 0;
3635
3636         if (strcmp(tokens[1], "dec") == 0)
3637                 a->ttl.decrement = 1;
3638         else if (strcmp(tokens[1], "keep") == 0)
3639                 a->ttl.decrement = 0;
3640         else
3641                 return 0;
3642
3643         a->action_mask |= 1 << RTE_TABLE_ACTION_TTL;
3644         return 2;
3645 }
3646
3647 static uint32_t
3648 parse_table_action_stats(char **tokens,
3649         uint32_t n_tokens,
3650         struct softnic_table_rule_action *a)
3651 {
3652         if (n_tokens < 1 ||
3653                 strcmp(tokens[0], "stats"))
3654                 return 0;
3655
3656         a->stats.n_packets = 0;
3657         a->stats.n_bytes = 0;
3658         a->action_mask |= 1 << RTE_TABLE_ACTION_STATS;
3659         return 1;
3660 }
3661
3662 static uint32_t
3663 parse_table_action_time(char **tokens,
3664         uint32_t n_tokens,
3665         struct softnic_table_rule_action *a)
3666 {
3667         if (n_tokens < 1 ||
3668                 strcmp(tokens[0], "time"))
3669                 return 0;
3670
3671         a->time.time = rte_rdtsc();
3672         a->action_mask |= 1 << RTE_TABLE_ACTION_TIME;
3673         return 1;
3674 }
3675
3676 static uint32_t
3677 parse_table_action_tag(char **tokens,
3678         uint32_t n_tokens,
3679         struct softnic_table_rule_action *a)
3680 {
3681         if (n_tokens < 2 ||
3682                 strcmp(tokens[0], "tag"))
3683                 return 0;
3684
3685         if (softnic_parser_read_uint32(&a->tag.tag, tokens[1]))
3686                 return 0;
3687
3688         a->action_mask |= 1 << RTE_TABLE_ACTION_TAG;
3689         return 2;
3690 }
3691
3692 static uint32_t
3693 parse_table_action(char **tokens,
3694         uint32_t n_tokens,
3695         char *out,
3696         size_t out_size,
3697         struct softnic_table_rule_action *a)
3698 {
3699         uint32_t n_tokens0 = n_tokens;
3700
3701         memset(a, 0, sizeof(*a));
3702
3703         if (n_tokens < 2 ||
3704                 strcmp(tokens[0], "action"))
3705                 return 0;
3706
3707         tokens++;
3708         n_tokens--;
3709
3710         if (n_tokens && (strcmp(tokens[0], "fwd") == 0)) {
3711                 uint32_t n;
3712
3713                 n = parse_table_action_fwd(tokens, n_tokens, a);
3714                 if (n == 0) {
3715                         snprintf(out, out_size, MSG_ARG_INVALID,
3716                                 "action fwd");
3717                         return 0;
3718                 }
3719
3720                 tokens += n;
3721                 n_tokens -= n;
3722         }
3723
3724         if (n_tokens && (strcmp(tokens[0], "balance") == 0)) {
3725                 uint32_t n;
3726
3727                 n = parse_table_action_balance(tokens, n_tokens, a);
3728                 if (n == 0) {
3729                         snprintf(out, out_size, MSG_ARG_INVALID,
3730                                 "action balance");
3731                         return 0;
3732                 }
3733
3734                 tokens += n;
3735                 n_tokens -= n;
3736         }
3737
3738         if (n_tokens && (strcmp(tokens[0], "meter") == 0)) {
3739                 uint32_t n;
3740
3741                 n = parse_table_action_meter(tokens, n_tokens, a);
3742                 if (n == 0) {
3743                         snprintf(out, out_size, MSG_ARG_INVALID,
3744                                 "action meter");
3745                         return 0;
3746                 }
3747
3748                 tokens += n;
3749                 n_tokens -= n;
3750         }
3751
3752         if (n_tokens && (strcmp(tokens[0], "tm") == 0)) {
3753                 uint32_t n;
3754
3755                 n = parse_table_action_tm(tokens, n_tokens, a);
3756                 if (n == 0) {
3757                         snprintf(out, out_size, MSG_ARG_INVALID,
3758                                 "action tm");
3759                         return 0;
3760                 }
3761
3762                 tokens += n;
3763                 n_tokens -= n;
3764         }
3765
3766         if (n_tokens && (strcmp(tokens[0], "encap") == 0)) {
3767                 uint32_t n;
3768
3769                 n = parse_table_action_encap(tokens, n_tokens, a);
3770                 if (n == 0) {
3771                         snprintf(out, out_size, MSG_ARG_INVALID,
3772                                 "action encap");
3773                         return 0;
3774                 }
3775
3776                 tokens += n;
3777                 n_tokens -= n;
3778         }
3779
3780         if (n_tokens && (strcmp(tokens[0], "nat") == 0)) {
3781                 uint32_t n;
3782
3783                 n = parse_table_action_nat(tokens, n_tokens, a);
3784                 if (n == 0) {
3785                         snprintf(out, out_size, MSG_ARG_INVALID,
3786                                 "action nat");
3787                         return 0;
3788                 }
3789
3790                 tokens += n;
3791                 n_tokens -= n;
3792         }
3793
3794         if (n_tokens && (strcmp(tokens[0], "ttl") == 0)) {
3795                 uint32_t n;
3796
3797                 n = parse_table_action_ttl(tokens, n_tokens, a);
3798                 if (n == 0) {
3799                         snprintf(out, out_size, MSG_ARG_INVALID,
3800                                 "action ttl");
3801                         return 0;
3802                 }
3803
3804                 tokens += n;
3805                 n_tokens -= n;
3806         }
3807
3808         if (n_tokens && (strcmp(tokens[0], "stats") == 0)) {
3809                 uint32_t n;
3810
3811                 n = parse_table_action_stats(tokens, n_tokens, a);
3812                 if (n == 0) {
3813                         snprintf(out, out_size, MSG_ARG_INVALID,
3814                                 "action stats");
3815                         return 0;
3816                 }
3817
3818                 tokens += n;
3819                 n_tokens -= n;
3820         }
3821
3822         if (n_tokens && (strcmp(tokens[0], "time") == 0)) {
3823                 uint32_t n;
3824
3825                 n = parse_table_action_time(tokens, n_tokens, a);
3826                 if (n == 0) {
3827                         snprintf(out, out_size, MSG_ARG_INVALID,
3828                                 "action time");
3829                         return 0;
3830                 }
3831
3832                 tokens += n;
3833                 n_tokens -= n;
3834         }
3835
3836         if (n_tokens && (strcmp(tokens[0], "tag") == 0)) {
3837                 uint32_t n;
3838
3839                 n = parse_table_action_tag(tokens, n_tokens, a);
3840                 if (n == 0) {
3841                         snprintf(out, out_size, MSG_ARG_INVALID,
3842                                 "action tag");
3843                         return 0;
3844                 }
3845
3846                 tokens += n;
3847                 n_tokens -= n;
3848         }
3849
3850         if (n_tokens0 - n_tokens == 1) {
3851                 snprintf(out, out_size, MSG_ARG_INVALID, "action");
3852                 return 0;
3853         }
3854
3855         return n_tokens0 - n_tokens;
3856 }
3857
3858 /**
3859  * pipeline <pipeline_name> table <table_id> rule add
3860  *    match <match>
3861  *    action <table_action>
3862  */
3863 static void
3864 cmd_softnic_pipeline_table_rule_add(struct pmd_internals *softnic,
3865         char **tokens,
3866         uint32_t n_tokens,
3867         char *out,
3868         size_t out_size)
3869 {
3870         struct softnic_table_rule_match m;
3871         struct softnic_table_rule_action a;
3872         char *pipeline_name;
3873         void *data;
3874         uint32_t table_id, t0, n_tokens_parsed;
3875         int status;
3876
3877         if (n_tokens < 8) {
3878                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3879                 return;
3880         }
3881
3882         pipeline_name = tokens[1];
3883
3884         if (strcmp(tokens[2], "table") != 0) {
3885                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
3886                 return;
3887         }
3888
3889         if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
3890                 snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
3891                 return;
3892         }
3893
3894         if (strcmp(tokens[4], "rule") != 0) {
3895                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule");
3896                 return;
3897         }
3898
3899         if (strcmp(tokens[5], "add") != 0) {
3900                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
3901                 return;
3902         }
3903
3904         t0 = 6;
3905
3906         /* match */
3907         n_tokens_parsed = parse_match(tokens + t0,
3908                 n_tokens - t0,
3909                 out,
3910                 out_size,
3911                 &m);
3912         if (n_tokens_parsed == 0)
3913                 return;
3914         t0 += n_tokens_parsed;
3915
3916         /* action */
3917         n_tokens_parsed = parse_table_action(tokens + t0,
3918                 n_tokens - t0,
3919                 out,
3920                 out_size,
3921                 &a);
3922         if (n_tokens_parsed == 0)
3923                 return;
3924         t0 += n_tokens_parsed;
3925
3926         if (t0 != n_tokens) {
3927                 snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
3928                 return;
3929         }
3930
3931         status = softnic_pipeline_table_rule_add(softnic,
3932                 pipeline_name,
3933                 table_id,
3934                 &m,
3935                 &a,
3936                 &data);
3937         if (status) {
3938                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
3939                 return;
3940         }
3941 }
3942
3943 /**
3944  * pipeline <pipeline_name> table <table_id> rule add
3945  *    match
3946  *       default
3947  *    action
3948  *       fwd
3949  *          drop
3950  *          | port <port_id>
3951  *          | meta
3952  *          | table <table_id>
3953  */
3954 static void
3955 cmd_softnic_pipeline_table_rule_add_default(struct pmd_internals *softnic,
3956         char **tokens,
3957         uint32_t n_tokens,
3958         char *out,
3959         size_t out_size)
3960 {
3961         struct softnic_table_rule_action action;
3962         void *data;
3963         char *pipeline_name;
3964         uint32_t table_id;
3965         int status;
3966
3967         if (n_tokens != 11 &&
3968                 n_tokens != 12) {
3969                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3970                 return;
3971         }
3972
3973         pipeline_name = tokens[1];
3974
3975         if (strcmp(tokens[2], "table") != 0) {
3976                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
3977                 return;
3978         }
3979
3980         if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
3981                 snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
3982                 return;
3983         }
3984
3985         if (strcmp(tokens[4], "rule") != 0) {
3986                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule");
3987                 return;
3988         }
3989
3990         if (strcmp(tokens[5], "add") != 0) {
3991                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
3992                 return;
3993         }
3994
3995         if (strcmp(tokens[6], "match") != 0) {
3996                 snprintf(out, out_size, MSG_ARG_INVALID, "match");
3997                 return;
3998         }
3999
4000         if (strcmp(tokens[7], "default") != 0) {
4001                 snprintf(out, out_size, MSG_ARG_INVALID, "default");
4002                 return;
4003         }
4004
4005         if (strcmp(tokens[8], "action") != 0) {
4006                 snprintf(out, out_size, MSG_ARG_INVALID, "action");
4007                 return;
4008         }
4009
4010         if (strcmp(tokens[9], "fwd") != 0) {
4011                 snprintf(out, out_size, MSG_ARG_INVALID, "fwd");
4012                 return;
4013         }
4014
4015         action.action_mask = 1 << RTE_TABLE_ACTION_FWD;
4016
4017         if (strcmp(tokens[10], "drop") == 0) {
4018                 if (n_tokens != 11) {
4019                         snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4020                         return;
4021                 }
4022
4023                 action.fwd.action = RTE_PIPELINE_ACTION_DROP;
4024         } else if (strcmp(tokens[10], "port") == 0) {
4025                 uint32_t id;
4026
4027                 if (n_tokens != 12) {
4028                         snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4029                         return;
4030                 }
4031
4032                 if (softnic_parser_read_uint32(&id, tokens[11]) != 0) {
4033                         snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
4034                         return;
4035                 }
4036
4037                 action.fwd.action = RTE_PIPELINE_ACTION_PORT;
4038                 action.fwd.id = id;
4039         } else if (strcmp(tokens[10], "meta") == 0) {
4040                 if (n_tokens != 11) {
4041                         snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4042                         return;
4043                 }
4044
4045                 action.fwd.action = RTE_PIPELINE_ACTION_PORT_META;
4046         } else if (strcmp(tokens[10], "table") == 0) {
4047                 uint32_t id;
4048
4049                 if (n_tokens != 12) {
4050                         snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4051                         return;
4052                 }
4053
4054                 if (softnic_parser_read_uint32(&id, tokens[11]) != 0) {
4055                         snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
4056                         return;
4057                 }
4058
4059                 action.fwd.action = RTE_PIPELINE_ACTION_TABLE;
4060                 action.fwd.id = id;
4061         } else {
4062                 snprintf(out, out_size, MSG_ARG_INVALID,
4063                         "drop or port or meta or table");
4064                 return;
4065         }
4066
4067         status = softnic_pipeline_table_rule_add_default(softnic,
4068                 pipeline_name,
4069                 table_id,
4070                 &action,
4071                 &data);
4072         if (status) {
4073                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
4074                 return;
4075         }
4076 }
4077
4078 /**
4079  * pipeline <pipeline_name> table <table_id> rule add bulk <file_name> <n_rules>
4080  *
4081  * File <file_name>:
4082  * - line format: match <match> action <action>
4083  */
4084 static int
4085 cli_rule_file_process(const char *file_name,
4086         size_t line_len_max,
4087         struct softnic_table_rule_match *m,
4088         struct softnic_table_rule_action *a,
4089         uint32_t *n_rules,
4090         uint32_t *line_number,
4091         char *out,
4092         size_t out_size);
4093
4094 static void
4095 cmd_softnic_pipeline_table_rule_add_bulk(struct pmd_internals *softnic,
4096         char **tokens,
4097         uint32_t n_tokens,
4098         char *out,
4099         size_t out_size)
4100 {
4101         struct softnic_table_rule_match *match;
4102         struct softnic_table_rule_action *action;
4103         void **data;
4104         char *pipeline_name, *file_name;
4105         uint32_t table_id, n_rules, n_rules_parsed, line_number;
4106         int status;
4107
4108         if (n_tokens != 9) {
4109                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4110                 return;
4111         }
4112
4113         pipeline_name = tokens[1];
4114
4115         if (strcmp(tokens[2], "table") != 0) {
4116                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
4117                 return;
4118         }
4119
4120         if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
4121                 snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
4122                 return;
4123         }
4124
4125         if (strcmp(tokens[4], "rule") != 0) {
4126                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule");
4127                 return;
4128         }
4129
4130         if (strcmp(tokens[5], "add") != 0) {
4131                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
4132                 return;
4133         }
4134
4135         if (strcmp(tokens[6], "bulk") != 0) {
4136                 snprintf(out, out_size, MSG_ARG_INVALID, "bulk");
4137                 return;
4138         }
4139
4140         file_name = tokens[7];
4141
4142         if ((softnic_parser_read_uint32(&n_rules, tokens[8]) != 0) ||
4143                 n_rules == 0) {
4144                 snprintf(out, out_size, MSG_ARG_INVALID, "n_rules");
4145                 return;
4146         }
4147
4148         /* Memory allocation. */
4149         match = calloc(n_rules, sizeof(struct softnic_table_rule_match));
4150         action = calloc(n_rules, sizeof(struct softnic_table_rule_action));
4151         data = calloc(n_rules, sizeof(void *));
4152         if (match == NULL ||
4153                 action == NULL ||
4154                 data == NULL) {
4155                 snprintf(out, out_size, MSG_OUT_OF_MEMORY);
4156                 free(data);
4157                 free(action);
4158                 free(match);
4159                 return;
4160         }
4161
4162         /* Load rule file */
4163         n_rules_parsed = n_rules;
4164         status = cli_rule_file_process(file_name,
4165                 1024,
4166                 match,
4167                 action,
4168                 &n_rules_parsed,
4169                 &line_number,
4170                 out,
4171                 out_size);
4172         if (status) {
4173                 snprintf(out, out_size, MSG_FILE_ERR, file_name, line_number);
4174                 free(data);
4175                 free(action);
4176                 free(match);
4177                 return;
4178         }
4179         if (n_rules_parsed != n_rules) {
4180                 snprintf(out, out_size, MSG_FILE_NOT_ENOUGH, file_name);
4181                 free(data);
4182                 free(action);
4183                 free(match);
4184                 return;
4185         }
4186
4187         /* Rule bulk add */
4188         status = softnic_pipeline_table_rule_add_bulk(softnic,
4189                 pipeline_name,
4190                 table_id,
4191                 match,
4192                 action,
4193                 data,
4194                 &n_rules);
4195         if (status) {
4196                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
4197                 free(data);
4198                 free(action);
4199                 free(match);
4200                 return;
4201         }
4202
4203         /* Memory free */
4204         free(data);
4205         free(action);
4206         free(match);
4207 }
4208
4209 /**
4210  * pipeline <pipeline_name> table <table_id> rule delete
4211  *    match <match>
4212  */
4213 static void
4214 cmd_softnic_pipeline_table_rule_delete(struct pmd_internals *softnic,
4215         char **tokens,
4216         uint32_t n_tokens,
4217         char *out,
4218         size_t out_size)
4219 {
4220         struct softnic_table_rule_match m;
4221         char *pipeline_name;
4222         uint32_t table_id, n_tokens_parsed, t0;
4223         int status;
4224
4225         if (n_tokens < 8) {
4226                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4227                 return;
4228         }
4229
4230         pipeline_name = tokens[1];
4231
4232         if (strcmp(tokens[2], "table") != 0) {
4233                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
4234                 return;
4235         }
4236
4237         if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
4238                 snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
4239                 return;
4240         }
4241
4242         if (strcmp(tokens[4], "rule") != 0) {
4243                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule");
4244                 return;
4245         }
4246
4247         if (strcmp(tokens[5], "delete") != 0) {
4248                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
4249                 return;
4250         }
4251
4252         t0 = 6;
4253
4254         /* match */
4255         n_tokens_parsed = parse_match(tokens + t0,
4256                 n_tokens - t0,
4257                 out,
4258                 out_size,
4259                 &m);
4260         if (n_tokens_parsed == 0)
4261                 return;
4262         t0 += n_tokens_parsed;
4263
4264         if (n_tokens != t0) {
4265                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4266                 return;
4267         }
4268
4269         status = softnic_pipeline_table_rule_delete(softnic,
4270                 pipeline_name,
4271                 table_id,
4272                 &m);
4273         if (status) {
4274                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
4275                 return;
4276         }
4277 }
4278
4279 /**
4280  * pipeline <pipeline_name> table <table_id> rule delete
4281  *    match
4282  *       default
4283  */
4284 static void
4285 cmd_softnic_pipeline_table_rule_delete_default(struct pmd_internals *softnic,
4286         char **tokens,
4287         uint32_t n_tokens,
4288         char *out,
4289         size_t out_size)
4290 {
4291         char *pipeline_name;
4292         uint32_t table_id;
4293         int status;
4294
4295         if (n_tokens != 8) {
4296                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4297                 return;
4298         }
4299
4300         pipeline_name = tokens[1];
4301
4302         if (strcmp(tokens[2], "table") != 0) {
4303                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
4304                 return;
4305         }
4306
4307         if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
4308                 snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
4309                 return;
4310         }
4311
4312         if (strcmp(tokens[4], "rule") != 0) {
4313                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule");
4314                 return;
4315         }
4316
4317         if (strcmp(tokens[5], "delete") != 0) {
4318                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
4319                 return;
4320         }
4321
4322         if (strcmp(tokens[6], "match") != 0) {
4323                 snprintf(out, out_size, MSG_ARG_INVALID, "match");
4324                 return;
4325         }
4326
4327         if (strcmp(tokens[7], "default") != 0) {
4328                 snprintf(out, out_size, MSG_ARG_INVALID, "default");
4329                 return;
4330         }
4331
4332         status = softnic_pipeline_table_rule_delete_default(softnic,
4333                 pipeline_name,
4334                 table_id);
4335         if (status) {
4336                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
4337                 return;
4338         }
4339 }
4340
4341 /**
4342  * pipeline <pipeline_name> table <table_id> rule read stats [clear]
4343  */
4344 static void
4345 cmd_softnic_pipeline_table_rule_stats_read(struct pmd_internals *softnic __rte_unused,
4346         char **tokens,
4347         uint32_t n_tokens __rte_unused,
4348         char *out,
4349         size_t out_size)
4350 {
4351         snprintf(out, out_size, MSG_CMD_UNIMPLEM, tokens[0]);
4352 }
4353
4354 /**
4355  * pipeline <pipeline_name> table <table_id> meter profile <meter_profile_id>
4356  *  add srtcm cir <cir> cbs <cbs> ebs <ebs>
4357  *  | trtcm cir <cir> pir <pir> cbs <cbs> pbs <pbs>
4358  */
4359 static void
4360 cmd_pipeline_table_meter_profile_add(struct pmd_internals *softnic,
4361         char **tokens,
4362         uint32_t n_tokens,
4363         char *out,
4364         size_t out_size)
4365 {
4366         struct rte_table_action_meter_profile p;
4367         char *pipeline_name;
4368         uint32_t table_id, meter_profile_id;
4369         int status;
4370
4371         if (n_tokens < 9) {
4372                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4373                 return;
4374         }
4375
4376         pipeline_name = tokens[1];
4377
4378         if (strcmp(tokens[2], "table") != 0) {
4379                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
4380                 return;
4381         }
4382
4383         if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
4384                 snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
4385                 return;
4386         }
4387
4388         if (strcmp(tokens[4], "meter") != 0) {
4389                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
4390                 return;
4391         }
4392
4393         if (strcmp(tokens[5], "profile") != 0) {
4394                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
4395                 return;
4396         }
4397
4398         if (softnic_parser_read_uint32(&meter_profile_id, tokens[6]) != 0) {
4399                 snprintf(out, out_size, MSG_ARG_INVALID, "meter_profile_id");
4400                 return;
4401         }
4402
4403         if (strcmp(tokens[7], "add") != 0) {
4404                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
4405                 return;
4406         }
4407
4408         if (strcmp(tokens[8], "srtcm") == 0) {
4409                 if (n_tokens != 15) {
4410                         snprintf(out, out_size, MSG_ARG_MISMATCH,
4411                                 tokens[0]);
4412                         return;
4413                 }
4414
4415                 p.alg = RTE_TABLE_ACTION_METER_SRTCM;
4416
4417                 if (strcmp(tokens[9], "cir") != 0) {
4418                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cir");
4419                         return;
4420                 }
4421
4422                 if (softnic_parser_read_uint64(&p.srtcm.cir, tokens[10]) != 0) {
4423                         snprintf(out, out_size, MSG_ARG_INVALID, "cir");
4424                         return;
4425                 }
4426
4427                 if (strcmp(tokens[11], "cbs") != 0) {
4428                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cbs");
4429                         return;
4430                 }
4431
4432                 if (softnic_parser_read_uint64(&p.srtcm.cbs, tokens[12]) != 0) {
4433                         snprintf(out, out_size, MSG_ARG_INVALID, "cbs");
4434                         return;
4435                 }
4436
4437                 if (strcmp(tokens[13], "ebs") != 0) {
4438                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "ebs");
4439                         return;
4440                 }
4441
4442                 if (softnic_parser_read_uint64(&p.srtcm.ebs, tokens[14]) != 0) {
4443                         snprintf(out, out_size, MSG_ARG_INVALID, "ebs");
4444                         return;
4445                 }
4446         } else if (strcmp(tokens[8], "trtcm") == 0) {
4447                 if (n_tokens != 17) {
4448                         snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4449                         return;
4450                 }
4451
4452                 p.alg = RTE_TABLE_ACTION_METER_TRTCM;
4453
4454                 if (strcmp(tokens[9], "cir") != 0) {
4455                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cir");
4456                         return;
4457                 }
4458
4459                 if (softnic_parser_read_uint64(&p.trtcm.cir, tokens[10]) != 0) {
4460                         snprintf(out, out_size, MSG_ARG_INVALID, "cir");
4461                         return;
4462                 }
4463
4464                 if (strcmp(tokens[11], "pir") != 0) {
4465                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pir");
4466                         return;
4467                 }
4468
4469                 if (softnic_parser_read_uint64(&p.trtcm.pir, tokens[12]) != 0) {
4470                         snprintf(out, out_size, MSG_ARG_INVALID, "pir");
4471                         return;
4472                 }
4473                 if (strcmp(tokens[13], "cbs") != 0) {
4474                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cbs");
4475                         return;
4476                 }
4477
4478                 if (softnic_parser_read_uint64(&p.trtcm.cbs, tokens[14]) != 0) {
4479                         snprintf(out, out_size, MSG_ARG_INVALID, "cbs");
4480                         return;
4481                 }
4482
4483                 if (strcmp(tokens[15], "pbs") != 0) {
4484                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pbs");
4485                         return;
4486                 }
4487
4488                 if (softnic_parser_read_uint64(&p.trtcm.pbs, tokens[16]) != 0) {
4489                         snprintf(out, out_size, MSG_ARG_INVALID, "pbs");
4490                         return;
4491                 }
4492         } else {
4493                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4494                 return;
4495         }
4496
4497         status = softnic_pipeline_table_mtr_profile_add(softnic,
4498                 pipeline_name,
4499                 table_id,
4500                 meter_profile_id,
4501                 &p);
4502         if (status) {
4503                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
4504                 return;
4505         }
4506 }
4507
4508 /**
4509  * pipeline <pipeline_name> table <table_id>
4510  *  meter profile <meter_profile_id> delete
4511  */
4512 static void
4513 cmd_pipeline_table_meter_profile_delete(struct pmd_internals *softnic,
4514         char **tokens,
4515         uint32_t n_tokens,
4516         char *out,
4517         size_t out_size)
4518 {
4519         char *pipeline_name;
4520         uint32_t table_id, meter_profile_id;
4521         int status;
4522
4523         if (n_tokens != 8) {
4524                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4525                 return;
4526         }
4527
4528         pipeline_name = tokens[1];
4529
4530         if (strcmp(tokens[2], "table") != 0) {
4531                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
4532                 return;
4533         }
4534
4535         if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
4536                 snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
4537                 return;
4538         }
4539
4540         if (strcmp(tokens[4], "meter") != 0) {
4541                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
4542                 return;
4543         }
4544
4545         if (strcmp(tokens[5], "profile") != 0) {
4546                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
4547                 return;
4548         }
4549
4550         if (softnic_parser_read_uint32(&meter_profile_id, tokens[6]) != 0) {
4551                 snprintf(out, out_size, MSG_ARG_INVALID, "meter_profile_id");
4552                 return;
4553         }
4554
4555         if (strcmp(tokens[7], "delete") != 0) {
4556                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
4557                 return;
4558         }
4559
4560         status = softnic_pipeline_table_mtr_profile_delete(softnic,
4561                 pipeline_name,
4562                 table_id,
4563                 meter_profile_id);
4564         if (status) {
4565                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
4566                 return;
4567         }
4568 }
4569
4570 /**
4571  * pipeline <pipeline_name> table <table_id> rule read meter [clear]
4572  */
4573 static void
4574 cmd_pipeline_table_rule_meter_read(struct pmd_internals *softnic __rte_unused,
4575         char **tokens,
4576         uint32_t n_tokens __rte_unused,
4577         char *out,
4578         size_t out_size)
4579 {
4580         snprintf(out, out_size, MSG_CMD_UNIMPLEM, tokens[0]);
4581 }
4582
4583 /**
4584  * pipeline <pipeline_name> table <table_id> dscp <file_name>
4585  *
4586  * File <file_name>:
4587  *  - exactly 64 lines
4588  *  - line format: <tc_id> <tc_queue_id> <color>, with <color> as: g | y | r
4589  */
4590 static int
4591 load_dscp_table(struct rte_table_action_dscp_table *dscp_table,
4592         const char *file_name,
4593         uint32_t *line_number)
4594 {
4595         FILE *f = NULL;
4596         uint32_t dscp, l;
4597
4598         /* Check input arguments */
4599         if (dscp_table == NULL ||
4600                 file_name == NULL ||
4601                 line_number == NULL) {
4602                 if (line_number)
4603                         *line_number = 0;
4604                 return -EINVAL;
4605         }
4606
4607         /* Open input file */
4608         f = fopen(file_name, "r");
4609         if (f == NULL) {
4610                 *line_number = 0;
4611                 return -EINVAL;
4612         }
4613
4614         /* Read file */
4615         for (dscp = 0, l = 1; ; l++) {
4616                 char line[64];
4617                 char *tokens[3];
4618                 enum rte_meter_color color;
4619                 uint32_t tc_id, tc_queue_id, n_tokens = RTE_DIM(tokens);
4620
4621                 if (fgets(line, sizeof(line), f) == NULL)
4622                         break;
4623
4624                 if (is_comment(line))
4625                         continue;
4626
4627                 if (softnic_parse_tokenize_string(line, tokens, &n_tokens)) {
4628                         *line_number = l;
4629                         fclose(f);
4630                         return -EINVAL;
4631                 }
4632
4633                 if (n_tokens == 0)
4634                         continue;
4635
4636                 if (dscp >= RTE_DIM(dscp_table->entry) ||
4637                         n_tokens != RTE_DIM(tokens) ||
4638                         softnic_parser_read_uint32(&tc_id, tokens[0]) ||
4639                         tc_id >= RTE_TABLE_ACTION_TC_MAX ||
4640                         softnic_parser_read_uint32(&tc_queue_id, tokens[1]) ||
4641                         tc_queue_id >= RTE_TABLE_ACTION_TC_QUEUE_MAX ||
4642                         (strlen(tokens[2]) != 1)) {
4643                         *line_number = l;
4644                         fclose(f);
4645                         return -EINVAL;
4646                 }
4647
4648                 switch (tokens[2][0]) {
4649                 case 'g':
4650                 case 'G':
4651                         color = e_RTE_METER_GREEN;
4652                         break;
4653
4654                 case 'y':
4655                 case 'Y':
4656                         color = e_RTE_METER_YELLOW;
4657                         break;
4658
4659                 case 'r':
4660                 case 'R':
4661                         color = e_RTE_METER_RED;
4662                         break;
4663
4664                 default:
4665                         *line_number = l;
4666                         fclose(f);
4667                         return -EINVAL;
4668                 }
4669
4670                 dscp_table->entry[dscp].tc_id = tc_id;
4671                 dscp_table->entry[dscp].tc_queue_id = tc_queue_id;
4672                 dscp_table->entry[dscp].color = color;
4673                 dscp++;
4674         }
4675
4676         /* Close file */
4677         fclose(f);
4678         return 0;
4679 }
4680
4681 static void
4682 cmd_pipeline_table_dscp(struct pmd_internals *softnic,
4683         char **tokens,
4684         uint32_t n_tokens,
4685         char *out,
4686         size_t out_size)
4687 {
4688         struct rte_table_action_dscp_table dscp_table;
4689         char *pipeline_name, *file_name;
4690         uint32_t table_id, line_number;
4691         int status;
4692
4693         if (n_tokens != 6) {
4694                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4695                 return;
4696         }
4697
4698         pipeline_name = tokens[1];
4699
4700         if (strcmp(tokens[2], "table") != 0) {
4701                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
4702                 return;
4703         }
4704
4705         if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
4706                 snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
4707                 return;
4708         }
4709
4710         if (strcmp(tokens[4], "dscp") != 0) {
4711                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "dscp");
4712                 return;
4713         }
4714
4715         file_name = tokens[5];
4716
4717         status = load_dscp_table(&dscp_table, file_name, &line_number);
4718         if (status) {
4719                 snprintf(out, out_size, MSG_FILE_ERR, file_name, line_number);
4720                 return;
4721         }
4722
4723         status = softnic_pipeline_table_dscp_table_update(softnic,
4724                 pipeline_name,
4725                 table_id,
4726                 UINT64_MAX,
4727                 &dscp_table);
4728         if (status) {
4729                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
4730                 return;
4731         }
4732 }
4733
4734 /**
4735  * pipeline <pipeline_name> table <table_id> rule read ttl [clear]
4736  */
4737 static void
4738 cmd_softnic_pipeline_table_rule_ttl_read(struct pmd_internals *softnic __rte_unused,
4739         char **tokens,
4740         uint32_t n_tokens __rte_unused,
4741         char *out,
4742         size_t out_size)
4743 {
4744         snprintf(out, out_size, MSG_CMD_UNIMPLEM, tokens[0]);
4745 }
4746
4747 /**
4748  * thread <thread_id> pipeline <pipeline_name> enable
4749  */
4750 static void
4751 cmd_softnic_thread_pipeline_enable(struct pmd_internals *softnic,
4752         char **tokens,
4753         uint32_t n_tokens,
4754         char *out,
4755         size_t out_size)
4756 {
4757         char *pipeline_name;
4758         uint32_t thread_id;
4759         int status;
4760
4761         if (n_tokens != 5) {
4762                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4763                 return;
4764         }
4765
4766         if (softnic_parser_read_uint32(&thread_id, tokens[1]) != 0) {
4767                 snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
4768                 return;
4769         }
4770
4771         if (strcmp(tokens[2], "pipeline") != 0) {
4772                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
4773                 return;
4774         }
4775
4776         pipeline_name = tokens[3];
4777
4778         if (strcmp(tokens[4], "enable") != 0) {
4779                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
4780                 return;
4781         }
4782
4783         status = softnic_thread_pipeline_enable(softnic, thread_id, pipeline_name);
4784         if (status) {
4785                 snprintf(out, out_size, MSG_CMD_FAIL, "thread pipeline enable");
4786                 return;
4787         }
4788 }
4789
4790 /**
4791  * thread <thread_id> pipeline <pipeline_name> disable
4792  */
4793 static void
4794 cmd_softnic_thread_pipeline_disable(struct pmd_internals *softnic,
4795         char **tokens,
4796         uint32_t n_tokens,
4797         char *out,
4798         size_t out_size)
4799 {
4800         char *pipeline_name;
4801         uint32_t thread_id;
4802         int status;
4803
4804         if (n_tokens != 5) {
4805                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4806                 return;
4807         }
4808
4809         if (softnic_parser_read_uint32(&thread_id, tokens[1]) != 0) {
4810                 snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
4811                 return;
4812         }
4813
4814         if (strcmp(tokens[2], "pipeline") != 0) {
4815                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
4816                 return;
4817         }
4818
4819         pipeline_name = tokens[3];
4820
4821         if (strcmp(tokens[4], "disable") != 0) {
4822                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
4823                 return;
4824         }
4825
4826         status = softnic_thread_pipeline_disable(softnic, thread_id, pipeline_name);
4827         if (status) {
4828                 snprintf(out, out_size, MSG_CMD_FAIL,
4829                         "thread pipeline disable");
4830                 return;
4831         }
4832 }
4833
4834 /**
4835  * flowapi map
4836  *  group <group_id>
4837  *  ingress | egress
4838  *  pipeline <pipeline_name>
4839  *  table <table_id>
4840  */
4841 static void
4842 cmd_softnic_flowapi_map(struct pmd_internals *softnic,
4843                 char **tokens,
4844                 uint32_t n_tokens,
4845                 char *out,
4846                 size_t out_size)
4847 {
4848         char *pipeline_name;
4849         uint32_t group_id, table_id;
4850         int ingress, status;
4851
4852         if (n_tokens != 9) {
4853                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4854                 return;
4855         }
4856
4857         if (strcmp(tokens[1], "map") != 0) {
4858                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "map");
4859                 return;
4860         }
4861
4862         if (strcmp(tokens[2], "group") != 0) {
4863                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group");
4864                 return;
4865         }
4866
4867         if (softnic_parser_read_uint32(&group_id, tokens[3]) != 0) {
4868                 snprintf(out, out_size, MSG_ARG_INVALID, "group_id");
4869                 return;
4870         }
4871
4872         if (strcmp(tokens[4], "ingress") == 0) {
4873                 ingress = 1;
4874         } else if (strcmp(tokens[4], "egress") == 0) {
4875                 ingress = 0;
4876         } else {
4877                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "ingress | egress");
4878                 return;
4879         }
4880
4881         if (strcmp(tokens[5], "pipeline") != 0) {
4882                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
4883                 return;
4884         }
4885
4886         pipeline_name = tokens[6];
4887
4888         if (strcmp(tokens[7], "table") != 0) {
4889                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
4890                 return;
4891         }
4892
4893         if (softnic_parser_read_uint32(&table_id, tokens[8]) != 0) {
4894                 snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
4895                 return;
4896         }
4897
4898         status = flow_attr_map_set(softnic,
4899                         group_id,
4900                         ingress,
4901                         pipeline_name,
4902                         table_id);
4903         if (status) {
4904                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
4905                 return;
4906         }
4907 }
4908
4909 void
4910 softnic_cli_process(char *in, char *out, size_t out_size, void *arg)
4911 {
4912         char *tokens[CMD_MAX_TOKENS];
4913         uint32_t n_tokens = RTE_DIM(tokens);
4914         struct pmd_internals *softnic = arg;
4915         int status;
4916
4917         if (is_comment(in))
4918                 return;
4919
4920         status = softnic_parse_tokenize_string(in, tokens, &n_tokens);
4921         if (status) {
4922                 snprintf(out, out_size, MSG_ARG_TOO_MANY, "");
4923                 return;
4924         }
4925
4926         if (n_tokens == 0)
4927                 return;
4928
4929         if (strcmp(tokens[0], "mempool") == 0) {
4930                 cmd_mempool(softnic, tokens, n_tokens, out, out_size);
4931                 return;
4932         }
4933
4934         if (strcmp(tokens[0], "link") == 0) {
4935                 cmd_link(softnic, tokens, n_tokens, out, out_size);
4936                 return;
4937         }
4938
4939         if (strcmp(tokens[0], "swq") == 0) {
4940                 cmd_swq(softnic, tokens, n_tokens, out, out_size);
4941                 return;
4942         }
4943
4944         if (strcmp(tokens[0], "tmgr") == 0) {
4945                 if (n_tokens == 2) {
4946                         cmd_tmgr(softnic, tokens, n_tokens, out, out_size);
4947                         return;
4948                 }
4949
4950                 if (n_tokens >= 3 &&
4951                         (strcmp(tokens[1], "shaper") == 0) &&
4952                         (strcmp(tokens[2], "profile") == 0)) {
4953                         cmd_tmgr_shaper_profile(softnic, tokens, n_tokens, out, out_size);
4954                         return;
4955                 }
4956
4957                 if (n_tokens >= 3 &&
4958                         (strcmp(tokens[1], "shared") == 0) &&
4959                         (strcmp(tokens[2], "shaper") == 0)) {
4960                         cmd_tmgr_shared_shaper(softnic, tokens, n_tokens, out, out_size);
4961                         return;
4962                 }
4963
4964                 if (n_tokens >= 2 &&
4965                         (strcmp(tokens[1], "node") == 0)) {
4966                         cmd_tmgr_node(softnic, tokens, n_tokens, out, out_size);
4967                         return;
4968                 }
4969
4970                 if (n_tokens >= 2 &&
4971                         (strcmp(tokens[1], "hierarchy-default") == 0)) {
4972                         cmd_tmgr_hierarchy_default(softnic, tokens, n_tokens, out, out_size);
4973                         return;
4974                 }
4975
4976                 if (n_tokens >= 3 &&
4977                         (strcmp(tokens[1], "hierarchy") == 0) &&
4978                         (strcmp(tokens[2], "commit") == 0)) {
4979                         cmd_tmgr_hierarchy_commit(softnic, tokens, n_tokens, out, out_size);
4980                         return;
4981                 }
4982         }
4983
4984         if (strcmp(tokens[0], "tap") == 0) {
4985                 cmd_tap(softnic, tokens, n_tokens, out, out_size);
4986                 return;
4987         }
4988
4989         if (strcmp(tokens[0], "port") == 0) {
4990                 cmd_port_in_action_profile(softnic, tokens, n_tokens, out, out_size);
4991                 return;
4992         }
4993
4994         if (strcmp(tokens[0], "table") == 0) {
4995                 cmd_table_action_profile(softnic, tokens, n_tokens, out, out_size);
4996                 return;
4997         }
4998
4999         if (strcmp(tokens[0], "pipeline") == 0) {
5000                 if (n_tokens >= 3 &&
5001                         (strcmp(tokens[2], "period") == 0)) {
5002                         cmd_pipeline(softnic, tokens, n_tokens, out, out_size);
5003                         return;
5004                 }
5005
5006                 if (n_tokens >= 5 &&
5007                         (strcmp(tokens[2], "port") == 0) &&
5008                         (strcmp(tokens[3], "in") == 0) &&
5009                         (strcmp(tokens[4], "bsz") == 0)) {
5010                         cmd_pipeline_port_in(softnic, tokens, n_tokens, out, out_size);
5011                         return;
5012                 }
5013
5014                 if (n_tokens >= 5 &&
5015                         (strcmp(tokens[2], "port") == 0) &&
5016                         (strcmp(tokens[3], "out") == 0) &&
5017                         (strcmp(tokens[4], "bsz") == 0)) {
5018                         cmd_pipeline_port_out(softnic, tokens, n_tokens, out, out_size);
5019                         return;
5020                 }
5021
5022                 if (n_tokens >= 4 &&
5023                         (strcmp(tokens[2], "table") == 0) &&
5024                         (strcmp(tokens[3], "match") == 0)) {
5025                         cmd_pipeline_table(softnic, tokens, n_tokens, out, out_size);
5026                         return;
5027                 }
5028
5029                 if (n_tokens >= 6 &&
5030                         (strcmp(tokens[2], "port") == 0) &&
5031                         (strcmp(tokens[3], "in") == 0) &&
5032                         (strcmp(tokens[5], "table") == 0)) {
5033                         cmd_pipeline_port_in_table(softnic, tokens, n_tokens,
5034                                 out, out_size);
5035                         return;
5036                 }
5037
5038                 if (n_tokens >= 6 &&
5039                         (strcmp(tokens[2], "port") == 0) &&
5040                         (strcmp(tokens[3], "in") == 0) &&
5041                         (strcmp(tokens[5], "stats") == 0)) {
5042                         cmd_pipeline_port_in_stats(softnic, tokens, n_tokens,
5043                                 out, out_size);
5044                         return;
5045                 }
5046
5047                 if (n_tokens >= 6 &&
5048                         (strcmp(tokens[2], "port") == 0) &&
5049                         (strcmp(tokens[3], "in") == 0) &&
5050                         (strcmp(tokens[5], "enable") == 0)) {
5051                         cmd_softnic_pipeline_port_in_enable(softnic, tokens, n_tokens,
5052                                 out, out_size);
5053                         return;
5054                 }
5055
5056                 if (n_tokens >= 6 &&
5057                         (strcmp(tokens[2], "port") == 0) &&
5058                         (strcmp(tokens[3], "in") == 0) &&
5059                         (strcmp(tokens[5], "disable") == 0)) {
5060                         cmd_softnic_pipeline_port_in_disable(softnic, tokens, n_tokens,
5061                                 out, out_size);
5062                         return;
5063                 }
5064
5065                 if (n_tokens >= 6 &&
5066                         (strcmp(tokens[2], "port") == 0) &&
5067                         (strcmp(tokens[3], "out") == 0) &&
5068                         (strcmp(tokens[5], "stats") == 0)) {
5069                         cmd_pipeline_port_out_stats(softnic, tokens, n_tokens,
5070                                 out, out_size);
5071                         return;
5072                 }
5073
5074                 if (n_tokens >= 5 &&
5075                         (strcmp(tokens[2], "table") == 0) &&
5076                         (strcmp(tokens[4], "stats") == 0)) {
5077                         cmd_pipeline_table_stats(softnic, tokens, n_tokens,
5078                                 out, out_size);
5079                         return;
5080                 }
5081
5082                 if (n_tokens >= 7 &&
5083                         (strcmp(tokens[2], "table") == 0) &&
5084                         (strcmp(tokens[4], "rule") == 0) &&
5085                         (strcmp(tokens[5], "add") == 0) &&
5086                         (strcmp(tokens[6], "match") == 0)) {
5087                         if (n_tokens >= 8 &&
5088                                 (strcmp(tokens[7], "default") == 0)) {
5089                                 cmd_softnic_pipeline_table_rule_add_default(softnic, tokens,
5090                                         n_tokens, out, out_size);
5091                                 return;
5092                         }
5093
5094                         cmd_softnic_pipeline_table_rule_add(softnic, tokens, n_tokens,
5095                                 out, out_size);
5096                         return;
5097                 }
5098
5099                 if (n_tokens >= 7 &&
5100                         (strcmp(tokens[2], "table") == 0) &&
5101                         (strcmp(tokens[4], "rule") == 0) &&
5102                         (strcmp(tokens[5], "add") == 0) &&
5103                         (strcmp(tokens[6], "bulk") == 0)) {
5104                         cmd_softnic_pipeline_table_rule_add_bulk(softnic, tokens,
5105                                 n_tokens, out, out_size);
5106                         return;
5107                 }
5108
5109                 if (n_tokens >= 7 &&
5110                         (strcmp(tokens[2], "table") == 0) &&
5111                         (strcmp(tokens[4], "rule") == 0) &&
5112                         (strcmp(tokens[5], "delete") == 0) &&
5113                         (strcmp(tokens[6], "match") == 0)) {
5114                         if (n_tokens >= 8 &&
5115                                 (strcmp(tokens[7], "default") == 0)) {
5116                                 cmd_softnic_pipeline_table_rule_delete_default(softnic, tokens,
5117                                         n_tokens, out, out_size);
5118                                 return;
5119                                 }
5120
5121                         cmd_softnic_pipeline_table_rule_delete(softnic, tokens, n_tokens,
5122                                 out, out_size);
5123                         return;
5124                 }
5125
5126                 if (n_tokens >= 7 &&
5127                         (strcmp(tokens[2], "table") == 0) &&
5128                         (strcmp(tokens[4], "rule") == 0) &&
5129                         (strcmp(tokens[5], "read") == 0) &&
5130                         (strcmp(tokens[6], "stats") == 0)) {
5131                         cmd_softnic_pipeline_table_rule_stats_read(softnic, tokens, n_tokens,
5132                                 out, out_size);
5133                         return;
5134                 }
5135
5136                 if (n_tokens >= 8 &&
5137                         (strcmp(tokens[2], "table") == 0) &&
5138                         (strcmp(tokens[4], "meter") == 0) &&
5139                         (strcmp(tokens[5], "profile") == 0) &&
5140                         (strcmp(tokens[7], "add") == 0)) {
5141                         cmd_pipeline_table_meter_profile_add(softnic, tokens, n_tokens,
5142                                 out, out_size);
5143                         return;
5144                 }
5145
5146                 if (n_tokens >= 8 &&
5147                         (strcmp(tokens[2], "table") == 0) &&
5148                         (strcmp(tokens[4], "meter") == 0) &&
5149                         (strcmp(tokens[5], "profile") == 0) &&
5150                         (strcmp(tokens[7], "delete") == 0)) {
5151                         cmd_pipeline_table_meter_profile_delete(softnic, tokens,
5152                                 n_tokens, out, out_size);
5153                         return;
5154                 }
5155
5156                 if (n_tokens >= 7 &&
5157                         (strcmp(tokens[2], "table") == 0) &&
5158                         (strcmp(tokens[4], "rule") == 0) &&
5159                         (strcmp(tokens[5], "read") == 0) &&
5160                         (strcmp(tokens[6], "meter") == 0)) {
5161                         cmd_pipeline_table_rule_meter_read(softnic, tokens, n_tokens,
5162                                 out, out_size);
5163                         return;
5164                 }
5165
5166                 if (n_tokens >= 5 &&
5167                         (strcmp(tokens[2], "table") == 0) &&
5168                         (strcmp(tokens[4], "dscp") == 0)) {
5169                         cmd_pipeline_table_dscp(softnic, tokens, n_tokens,
5170                                 out, out_size);
5171                         return;
5172                 }
5173
5174                 if (n_tokens >= 7 &&
5175                         (strcmp(tokens[2], "table") == 0) &&
5176                         (strcmp(tokens[4], "rule") == 0) &&
5177                         (strcmp(tokens[5], "read") == 0) &&
5178                         (strcmp(tokens[6], "ttl") == 0)) {
5179                         cmd_softnic_pipeline_table_rule_ttl_read(softnic, tokens, n_tokens,
5180                                 out, out_size);
5181                         return;
5182                 }
5183         }
5184
5185         if (strcmp(tokens[0], "thread") == 0) {
5186                 if (n_tokens >= 5 &&
5187                         (strcmp(tokens[4], "enable") == 0)) {
5188                         cmd_softnic_thread_pipeline_enable(softnic, tokens, n_tokens,
5189                                 out, out_size);
5190                         return;
5191                 }
5192
5193                 if (n_tokens >= 5 &&
5194                         (strcmp(tokens[4], "disable") == 0)) {
5195                         cmd_softnic_thread_pipeline_disable(softnic, tokens, n_tokens,
5196                                 out, out_size);
5197                         return;
5198                 }
5199         }
5200
5201         if (strcmp(tokens[0], "flowapi") == 0) {
5202                 cmd_softnic_flowapi_map(softnic, tokens, n_tokens, out,
5203                                         out_size);
5204                 return;
5205         }
5206
5207         snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
5208 }
5209
5210 int
5211 softnic_cli_script_process(struct pmd_internals *softnic,
5212         const char *file_name,
5213         size_t msg_in_len_max,
5214         size_t msg_out_len_max)
5215 {
5216         char *msg_in = NULL, *msg_out = NULL;
5217         FILE *f = NULL;
5218
5219         /* Check input arguments */
5220         if (file_name == NULL ||
5221                 (strlen(file_name) == 0) ||
5222                 msg_in_len_max == 0 ||
5223                 msg_out_len_max == 0)
5224                 return -EINVAL;
5225
5226         msg_in = malloc(msg_in_len_max + 1);
5227         msg_out = malloc(msg_out_len_max + 1);
5228         if (msg_in == NULL ||
5229                 msg_out == NULL) {
5230                 free(msg_out);
5231                 free(msg_in);
5232                 return -ENOMEM;
5233         }
5234
5235         /* Open input file */
5236         f = fopen(file_name, "r");
5237         if (f == NULL) {
5238                 free(msg_out);
5239                 free(msg_in);
5240                 return -EIO;
5241         }
5242
5243         /* Read file */
5244         for ( ; ; ) {
5245                 if (fgets(msg_in, msg_in_len_max + 1, f) == NULL)
5246                         break;
5247
5248                 printf("%s", msg_in);
5249                 msg_out[0] = 0;
5250
5251                 softnic_cli_process(msg_in,
5252                         msg_out,
5253                         msg_out_len_max,
5254                         softnic);
5255
5256                 if (strlen(msg_out))
5257                         printf("%s", msg_out);
5258         }
5259
5260         /* Close file */
5261         fclose(f);
5262         free(msg_out);
5263         free(msg_in);
5264         return 0;
5265 }
5266
5267 static int
5268 cli_rule_file_process(const char *file_name,
5269         size_t line_len_max,
5270         struct softnic_table_rule_match *m,
5271         struct softnic_table_rule_action *a,
5272         uint32_t *n_rules,
5273         uint32_t *line_number,
5274         char *out,
5275         size_t out_size)
5276 {
5277         FILE *f = NULL;
5278         char *line = NULL;
5279         uint32_t rule_id, line_id;
5280         int status = 0;
5281
5282         /* Check input arguments */
5283         if (file_name == NULL ||
5284                 (strlen(file_name) == 0) ||
5285                 line_len_max == 0) {
5286                 *line_number = 0;
5287                 return -EINVAL;
5288         }
5289
5290         /* Memory allocation */
5291         line = malloc(line_len_max + 1);
5292         if (line == NULL) {
5293                 *line_number = 0;
5294                 return -ENOMEM;
5295         }
5296
5297         /* Open file */
5298         f = fopen(file_name, "r");
5299         if (f == NULL) {
5300                 *line_number = 0;
5301                 free(line);
5302                 return -EIO;
5303         }
5304
5305         /* Read file */
5306         for (line_id = 1, rule_id = 0; rule_id < *n_rules; line_id++) {
5307                 char *tokens[CMD_MAX_TOKENS];
5308                 uint32_t n_tokens, n_tokens_parsed, t0;
5309
5310                 /* Read next line from file. */
5311                 if (fgets(line, line_len_max + 1, f) == NULL)
5312                         break;
5313
5314                 /* Comment. */
5315                 if (is_comment(line))
5316                         continue;
5317
5318                 /* Parse line. */
5319                 n_tokens = RTE_DIM(tokens);
5320                 status = softnic_parse_tokenize_string(line, tokens, &n_tokens);
5321                 if (status) {
5322                         status = -EINVAL;
5323                         break;
5324                 }
5325
5326                 /* Empty line. */
5327                 if (n_tokens == 0)
5328                         continue;
5329                 t0 = 0;
5330
5331                 /* Rule match. */
5332                 n_tokens_parsed = parse_match(tokens + t0,
5333                         n_tokens - t0,
5334                         out,
5335                         out_size,
5336                         &m[rule_id]);
5337                 if (n_tokens_parsed == 0) {
5338                         status = -EINVAL;
5339                         break;
5340                 }
5341                 t0 += n_tokens_parsed;
5342
5343                 /* Rule action. */
5344                 n_tokens_parsed = parse_table_action(tokens + t0,
5345                         n_tokens - t0,
5346                         out,
5347                         out_size,
5348                         &a[rule_id]);
5349                 if (n_tokens_parsed == 0) {
5350                         status = -EINVAL;
5351                         break;
5352                 }
5353                 t0 += n_tokens_parsed;
5354
5355                 /* Line completed. */
5356                 if (t0 < n_tokens) {
5357                         status = -EINVAL;
5358                         break;
5359                 }
5360
5361                 /* Increment rule count */
5362                 rule_id++;
5363         }
5364
5365         /* Close file */
5366         fclose(f);
5367
5368         /* Memory free */
5369         free(line);
5370
5371         *n_rules = rule_id;
5372         *line_number = line_id;
5373         return status;
5374 }