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