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