net/softnic: fix useless address check
[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                         if (p->cipher_auth.cipher_iv.val)
4486                                 free(p->cipher_auth.cipher_iv.val);
4487                         if (p->cipher_auth.cipher_iv_update.val)
4488                                 free(p->cipher_auth.cipher_iv_update.val);
4489                         break;
4490                 case RTE_CRYPTO_SYM_XFORM_AUTH:
4491                         if (p->cipher_auth.auth_iv.val)
4492                                 free(p->cipher_auth.cipher_iv.val);
4493                         if (p->cipher_auth.auth_iv_update.val)
4494                                 free(p->cipher_auth.cipher_iv_update.val);
4495                         break;
4496                 case RTE_CRYPTO_SYM_XFORM_AEAD:
4497                         if (p->aead.iv.val)
4498                                 free(p->aead.iv.val);
4499                         if (p->aead.aad.val)
4500                                 free(p->aead.aad.val);
4501                         break;
4502                 default:
4503                         continue;
4504                 }
4505         }
4506
4507 }
4508
4509 static struct rte_crypto_sym_xform *
4510 parse_table_action_cipher(struct rte_table_action_sym_crypto_params *p,
4511                 uint8_t *key, uint32_t max_key_len, char **tokens,
4512                 uint32_t n_tokens, uint32_t encrypt, uint32_t *used_n_tokens)
4513 {
4514         struct rte_crypto_sym_xform *xform_cipher;
4515         int status;
4516         size_t len;
4517
4518         if (n_tokens < 7 || strcmp(tokens[1], "cipher_algo") ||
4519                         strcmp(tokens[3], "cipher_key") ||
4520                         strcmp(tokens[5], "cipher_iv"))
4521                 return NULL;
4522
4523         xform_cipher = calloc(1, sizeof(*xform_cipher));
4524         if (xform_cipher == NULL)
4525                 return NULL;
4526
4527         xform_cipher->type = RTE_CRYPTO_SYM_XFORM_CIPHER;
4528         xform_cipher->cipher.op = encrypt ? RTE_CRYPTO_CIPHER_OP_ENCRYPT :
4529                         RTE_CRYPTO_CIPHER_OP_DECRYPT;
4530
4531         /* cipher_algo */
4532         status = rte_cryptodev_get_cipher_algo_enum(
4533                         &xform_cipher->cipher.algo, tokens[2]);
4534         if (status < 0)
4535                 goto error_exit;
4536
4537         /* cipher_key */
4538         len = strlen(tokens[4]);
4539         if (len / 2 > max_key_len) {
4540                 status = -ENOMEM;
4541                 goto error_exit;
4542         }
4543
4544         status = softnic_parse_hex_string(tokens[4], key, (uint32_t *)&len);
4545         if (status < 0)
4546                 goto error_exit;
4547
4548         xform_cipher->cipher.key.data = key;
4549         xform_cipher->cipher.key.length = (uint16_t)len;
4550
4551         /* cipher_iv */
4552         len = strlen(tokens[6]);
4553
4554         p->cipher_auth.cipher_iv.val = calloc(1, len / 2 + 1);
4555         if (p->cipher_auth.cipher_iv.val == NULL)
4556                 goto error_exit;
4557
4558         status = softnic_parse_hex_string(tokens[6],
4559                         p->cipher_auth.cipher_iv.val,
4560                         (uint32_t *)&len);
4561         if (status < 0)
4562                 goto error_exit;
4563
4564         xform_cipher->cipher.iv.length = (uint16_t)len;
4565         xform_cipher->cipher.iv.offset = RTE_TABLE_ACTION_SYM_CRYPTO_IV_OFFSET;
4566         p->cipher_auth.cipher_iv.length = (uint32_t)len;
4567         *used_n_tokens = 7;
4568
4569         return xform_cipher;
4570
4571 error_exit:
4572         if (p->cipher_auth.cipher_iv.val) {
4573                 free(p->cipher_auth.cipher_iv.val);
4574                 p->cipher_auth.cipher_iv.val = NULL;
4575         }
4576
4577         free(xform_cipher);
4578
4579         return NULL;
4580 }
4581
4582 static struct rte_crypto_sym_xform *
4583 parse_table_action_cipher_auth(struct rte_table_action_sym_crypto_params *p,
4584                 uint8_t *key, uint32_t max_key_len, char **tokens,
4585                 uint32_t n_tokens, uint32_t encrypt, uint32_t *used_n_tokens)
4586 {
4587         struct rte_crypto_sym_xform *xform_cipher;
4588         struct rte_crypto_sym_xform *xform_auth;
4589         int status;
4590         size_t len;
4591
4592         if (n_tokens < 13 ||
4593                         strcmp(tokens[7], "auth_algo") ||
4594                         strcmp(tokens[9], "auth_key") ||
4595                         strcmp(tokens[11], "digest_size"))
4596                 return NULL;
4597
4598         xform_auth = calloc(1, sizeof(*xform_auth));
4599         if (xform_auth == NULL)
4600                 return NULL;
4601
4602         xform_auth->type = RTE_CRYPTO_SYM_XFORM_AUTH;
4603         xform_auth->auth.op = encrypt ? RTE_CRYPTO_AUTH_OP_GENERATE :
4604                         RTE_CRYPTO_AUTH_OP_VERIFY;
4605
4606         /* auth_algo */
4607         status = rte_cryptodev_get_auth_algo_enum(&xform_auth->auth.algo,
4608                         tokens[8]);
4609         if (status < 0)
4610                 goto error_exit;
4611
4612         /* auth_key */
4613         len = strlen(tokens[10]);
4614         if (len / 2 > max_key_len) {
4615                 status = -ENOMEM;
4616                 goto error_exit;
4617         }
4618
4619         status = softnic_parse_hex_string(tokens[10], key, (uint32_t *)&len);
4620         if (status < 0)
4621                 goto error_exit;
4622
4623         xform_auth->auth.key.data = key;
4624         xform_auth->auth.key.length = (uint16_t)len;
4625
4626         key += xform_auth->auth.key.length;
4627         max_key_len -= xform_auth->auth.key.length;
4628
4629         if (strcmp(tokens[11], "digest_size"))
4630                 goto error_exit;
4631
4632         status = softnic_parser_read_uint16(&xform_auth->auth.digest_length,
4633                         tokens[12]);
4634         if (status < 0)
4635                 goto error_exit;
4636
4637         xform_cipher = parse_table_action_cipher(p, key, max_key_len, tokens, 7,
4638                         encrypt, used_n_tokens);
4639         if (xform_cipher == NULL)
4640                 goto error_exit;
4641
4642         *used_n_tokens += 6;
4643
4644         if (encrypt) {
4645                 xform_cipher->next = xform_auth;
4646                 return xform_cipher;
4647         } else {
4648                 xform_auth->next = xform_cipher;
4649                 return xform_auth;
4650         }
4651
4652 error_exit:
4653         if (p->cipher_auth.auth_iv.val) {
4654                 free(p->cipher_auth.auth_iv.val);
4655                 p->cipher_auth.auth_iv.val = 0;
4656         }
4657
4658         free(xform_auth);
4659
4660         return NULL;
4661 }
4662
4663 static struct rte_crypto_sym_xform *
4664 parse_table_action_aead(struct rte_table_action_sym_crypto_params *p,
4665                 uint8_t *key, uint32_t max_key_len, char **tokens,
4666                 uint32_t n_tokens, uint32_t encrypt, uint32_t *used_n_tokens)
4667 {
4668         struct rte_crypto_sym_xform *xform_aead;
4669         int status;
4670         size_t len;
4671
4672         if (n_tokens < 11 || strcmp(tokens[1], "aead_algo") ||
4673                         strcmp(tokens[3], "aead_key") ||
4674                         strcmp(tokens[5], "aead_iv") ||
4675                         strcmp(tokens[7], "aead_aad") ||
4676                         strcmp(tokens[9], "digest_size"))
4677                 return NULL;
4678
4679         xform_aead = calloc(1, sizeof(*xform_aead));
4680         if (xform_aead == NULL)
4681                 return NULL;
4682
4683         xform_aead->type = RTE_CRYPTO_SYM_XFORM_AEAD;
4684         xform_aead->aead.op = encrypt ? RTE_CRYPTO_AEAD_OP_ENCRYPT :
4685                         RTE_CRYPTO_AEAD_OP_DECRYPT;
4686
4687         /* aead_algo */
4688         status = rte_cryptodev_get_aead_algo_enum(&xform_aead->aead.algo,
4689                         tokens[2]);
4690         if (status < 0)
4691                 goto error_exit;
4692
4693         /* aead_key */
4694         len = strlen(tokens[4]);
4695         if (len / 2 > max_key_len) {
4696                 status = -ENOMEM;
4697                 goto error_exit;
4698         }
4699
4700         status = softnic_parse_hex_string(tokens[4], key, (uint32_t *)&len);
4701         if (status < 0)
4702                 goto error_exit;
4703
4704         xform_aead->aead.key.data = key;
4705         xform_aead->aead.key.length = (uint16_t)len;
4706
4707         /* aead_iv */
4708         len = strlen(tokens[6]);
4709         p->aead.iv.val = calloc(1, len / 2 + 1);
4710         if (p->aead.iv.val == NULL)
4711                 goto error_exit;
4712
4713         status = softnic_parse_hex_string(tokens[6], p->aead.iv.val,
4714                         (uint32_t *)&len);
4715         if (status < 0)
4716                 goto error_exit;
4717
4718         xform_aead->aead.iv.length = (uint16_t)len;
4719         xform_aead->aead.iv.offset = RTE_TABLE_ACTION_SYM_CRYPTO_IV_OFFSET;
4720         p->aead.iv.length = (uint32_t)len;
4721
4722         /* aead_aad */
4723         len = strlen(tokens[8]);
4724         p->aead.aad.val = calloc(1, len / 2 + 1);
4725         if (p->aead.aad.val == NULL)
4726                 goto error_exit;
4727
4728         status = softnic_parse_hex_string(tokens[8], p->aead.aad.val, (uint32_t *)&len);
4729         if (status < 0)
4730                 goto error_exit;
4731
4732         xform_aead->aead.aad_length = (uint16_t)len;
4733         p->aead.aad.length = (uint32_t)len;
4734
4735         /* digest_size */
4736         status = softnic_parser_read_uint16(&xform_aead->aead.digest_length,
4737                         tokens[10]);
4738         if (status < 0)
4739                 goto error_exit;
4740
4741         *used_n_tokens = 11;
4742
4743         return xform_aead;
4744
4745 error_exit:
4746         if (p->aead.iv.val) {
4747                 free(p->aead.iv.val);
4748                 p->aead.iv.val = NULL;
4749         }
4750         if (p->aead.aad.val) {
4751                 free(p->aead.aad.val);
4752                 p->aead.aad.val = NULL;
4753         }
4754
4755         free(xform_aead);
4756
4757         return NULL;
4758 }
4759
4760
4761 static uint32_t
4762 parse_table_action_sym_crypto(char **tokens,
4763         uint32_t n_tokens,
4764         struct softnic_table_rule_action *a)
4765 {
4766         struct rte_table_action_sym_crypto_params *p = &a->sym_crypto;
4767         struct rte_crypto_sym_xform *xform = NULL;
4768         uint8_t *key = a->sym_crypto_key;
4769         uint32_t max_key_len = SYM_CRYPTO_MAX_KEY_SIZE;
4770         uint32_t used_n_tokens;
4771         uint32_t encrypt;
4772         int status;
4773
4774         if ((n_tokens < 12) ||
4775                 strcmp(tokens[0], "sym_crypto") ||
4776                 strcmp(tokens[2], "type"))
4777                 return 0;
4778
4779         memset(p, 0, sizeof(*p));
4780
4781         if (strcmp(tokens[1], "encrypt") == 0)
4782                 encrypt = 1;
4783         else
4784                 encrypt = 0;
4785
4786         status = softnic_parser_read_uint32(&p->data_offset, tokens[n_tokens - 1]);
4787         if (status < 0)
4788                 return 0;
4789
4790         if (strcmp(tokens[3], "cipher") == 0) {
4791                 tokens += 3;
4792                 n_tokens -= 3;
4793
4794                 xform = parse_table_action_cipher(p, key, max_key_len, tokens,
4795                                 n_tokens, encrypt, &used_n_tokens);
4796         } else if (strcmp(tokens[3], "cipher_auth") == 0) {
4797                 tokens += 3;
4798                 n_tokens -= 3;
4799
4800                 xform = parse_table_action_cipher_auth(p, key, max_key_len,
4801                                 tokens, n_tokens, encrypt, &used_n_tokens);
4802         } else if (strcmp(tokens[3], "aead") == 0) {
4803                 tokens += 3;
4804                 n_tokens -= 3;
4805
4806                 xform = parse_table_action_aead(p, key, max_key_len, tokens,
4807                                 n_tokens, encrypt, &used_n_tokens);
4808         }
4809
4810         if (xform == NULL)
4811                 return 0;
4812
4813         p->xform = xform;
4814
4815         if (strcmp(tokens[used_n_tokens], "data_offset")) {
4816                 parse_free_sym_crypto_param_data(p);
4817                 return 0;
4818         }
4819
4820         a->action_mask |= 1 << RTE_TABLE_ACTION_SYM_CRYPTO;
4821
4822         return used_n_tokens + 5;
4823 }
4824
4825 static uint32_t
4826 parse_table_action_tag(char **tokens,
4827         uint32_t n_tokens,
4828         struct softnic_table_rule_action *a)
4829 {
4830         if (n_tokens < 2 ||
4831                 strcmp(tokens[0], "tag"))
4832                 return 0;
4833
4834         if (softnic_parser_read_uint32(&a->tag.tag, tokens[1]))
4835                 return 0;
4836
4837         a->action_mask |= 1 << RTE_TABLE_ACTION_TAG;
4838         return 2;
4839 }
4840
4841 static uint32_t
4842 parse_table_action_decap(char **tokens,
4843         uint32_t n_tokens,
4844         struct softnic_table_rule_action *a)
4845 {
4846         if (n_tokens < 2 ||
4847                 strcmp(tokens[0], "decap"))
4848                 return 0;
4849
4850         if (softnic_parser_read_uint16(&a->decap.n, tokens[1]))
4851                 return 0;
4852
4853         a->action_mask |= 1 << RTE_TABLE_ACTION_DECAP;
4854         return 2;
4855 }
4856
4857 static uint32_t
4858 parse_table_action(char **tokens,
4859         uint32_t n_tokens,
4860         char *out,
4861         size_t out_size,
4862         struct softnic_table_rule_action *a)
4863 {
4864         uint32_t n_tokens0 = n_tokens;
4865
4866         memset(a, 0, sizeof(*a));
4867
4868         if (n_tokens < 2 ||
4869                 strcmp(tokens[0], "action"))
4870                 return 0;
4871
4872         tokens++;
4873         n_tokens--;
4874
4875         if (n_tokens && (strcmp(tokens[0], "fwd") == 0)) {
4876                 uint32_t n;
4877
4878                 n = parse_table_action_fwd(tokens, n_tokens, a);
4879                 if (n == 0) {
4880                         snprintf(out, out_size, MSG_ARG_INVALID,
4881                                 "action fwd");
4882                         return 0;
4883                 }
4884
4885                 tokens += n;
4886                 n_tokens -= n;
4887         }
4888
4889         if (n_tokens && (strcmp(tokens[0], "balance") == 0)) {
4890                 uint32_t n;
4891
4892                 n = parse_table_action_balance(tokens, n_tokens, a);
4893                 if (n == 0) {
4894                         snprintf(out, out_size, MSG_ARG_INVALID,
4895                                 "action balance");
4896                         return 0;
4897                 }
4898
4899                 tokens += n;
4900                 n_tokens -= n;
4901         }
4902
4903         if (n_tokens && (strcmp(tokens[0], "meter") == 0)) {
4904                 uint32_t n;
4905
4906                 n = parse_table_action_meter(tokens, n_tokens, a);
4907                 if (n == 0) {
4908                         snprintf(out, out_size, MSG_ARG_INVALID,
4909                                 "action meter");
4910                         return 0;
4911                 }
4912
4913                 tokens += n;
4914                 n_tokens -= n;
4915         }
4916
4917         if (n_tokens && (strcmp(tokens[0], "tm") == 0)) {
4918                 uint32_t n;
4919
4920                 n = parse_table_action_tm(tokens, n_tokens, a);
4921                 if (n == 0) {
4922                         snprintf(out, out_size, MSG_ARG_INVALID,
4923                                 "action tm");
4924                         return 0;
4925                 }
4926
4927                 tokens += n;
4928                 n_tokens -= n;
4929         }
4930
4931         if (n_tokens && (strcmp(tokens[0], "encap") == 0)) {
4932                 uint32_t n;
4933
4934                 n = parse_table_action_encap(tokens, n_tokens, a);
4935                 if (n == 0) {
4936                         snprintf(out, out_size, MSG_ARG_INVALID,
4937                                 "action encap");
4938                         return 0;
4939                 }
4940
4941                 tokens += n;
4942                 n_tokens -= n;
4943         }
4944
4945         if (n_tokens && (strcmp(tokens[0], "nat") == 0)) {
4946                 uint32_t n;
4947
4948                 n = parse_table_action_nat(tokens, n_tokens, a);
4949                 if (n == 0) {
4950                         snprintf(out, out_size, MSG_ARG_INVALID,
4951                                 "action nat");
4952                         return 0;
4953                 }
4954
4955                 tokens += n;
4956                 n_tokens -= n;
4957         }
4958
4959         if (n_tokens && (strcmp(tokens[0], "ttl") == 0)) {
4960                 uint32_t n;
4961
4962                 n = parse_table_action_ttl(tokens, n_tokens, a);
4963                 if (n == 0) {
4964                         snprintf(out, out_size, MSG_ARG_INVALID,
4965                                 "action ttl");
4966                         return 0;
4967                 }
4968
4969                 tokens += n;
4970                 n_tokens -= n;
4971         }
4972
4973         if (n_tokens && (strcmp(tokens[0], "stats") == 0)) {
4974                 uint32_t n;
4975
4976                 n = parse_table_action_stats(tokens, n_tokens, a);
4977                 if (n == 0) {
4978                         snprintf(out, out_size, MSG_ARG_INVALID,
4979                                 "action stats");
4980                         return 0;
4981                 }
4982
4983                 tokens += n;
4984                 n_tokens -= n;
4985         }
4986
4987         if (n_tokens && (strcmp(tokens[0], "time") == 0)) {
4988                 uint32_t n;
4989
4990                 n = parse_table_action_time(tokens, n_tokens, a);
4991                 if (n == 0) {
4992                         snprintf(out, out_size, MSG_ARG_INVALID,
4993                                 "action time");
4994                         return 0;
4995                 }
4996
4997                 tokens += n;
4998                 n_tokens -= n;
4999         }
5000
5001         if (n_tokens && (strcmp(tokens[0], "tag") == 0)) {
5002                 uint32_t n;
5003
5004                 n = parse_table_action_tag(tokens, n_tokens, a);
5005                 if (n == 0) {
5006                         snprintf(out, out_size, MSG_ARG_INVALID,
5007                                 "action tag");
5008                         return 0;
5009                 }
5010
5011                 tokens += n;
5012                 n_tokens -= n;
5013         }
5014
5015         if (n_tokens && (strcmp(tokens[0], "decap") == 0)) {
5016                 uint32_t n;
5017
5018                 n = parse_table_action_decap(tokens, n_tokens, a);
5019                 if (n == 0) {
5020                         snprintf(out, out_size, MSG_ARG_INVALID,
5021                                 "action decap");
5022                         return 0;
5023                 }
5024
5025                 tokens += n;
5026                 n_tokens -= n;
5027         }
5028
5029         if (n_tokens && (strcmp(tokens[0], "sym_crypto") == 0)) {
5030                 uint32_t n;
5031
5032                 n = parse_table_action_sym_crypto(tokens, n_tokens, a);
5033                 if (n == 0) {
5034                         snprintf(out, out_size, MSG_ARG_INVALID,
5035                                 "action sym_crypto");
5036                 }
5037
5038                 tokens += n;
5039                 n_tokens -= n;
5040         }
5041
5042         if (n_tokens0 - n_tokens == 1) {
5043                 snprintf(out, out_size, MSG_ARG_INVALID, "action");
5044                 return 0;
5045         }
5046
5047         return n_tokens0 - n_tokens;
5048 }
5049
5050 /**
5051  * pipeline <pipeline_name> table <table_id> rule add
5052  *    match <match>
5053  *    action <table_action>
5054  */
5055 static void
5056 cmd_softnic_pipeline_table_rule_add(struct pmd_internals *softnic,
5057         char **tokens,
5058         uint32_t n_tokens,
5059         char *out,
5060         size_t out_size)
5061 {
5062         struct softnic_table_rule_match m;
5063         struct softnic_table_rule_action a;
5064         char *pipeline_name;
5065         void *data;
5066         uint32_t table_id, t0, n_tokens_parsed;
5067         int status;
5068
5069         if (n_tokens < 8) {
5070                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5071                 return;
5072         }
5073
5074         pipeline_name = tokens[1];
5075
5076         if (strcmp(tokens[2], "table") != 0) {
5077                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
5078                 return;
5079         }
5080
5081         if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
5082                 snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
5083                 return;
5084         }
5085
5086         if (strcmp(tokens[4], "rule") != 0) {
5087                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule");
5088                 return;
5089         }
5090
5091         if (strcmp(tokens[5], "add") != 0) {
5092                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
5093                 return;
5094         }
5095
5096         t0 = 6;
5097
5098         /* match */
5099         n_tokens_parsed = parse_match(tokens + t0,
5100                 n_tokens - t0,
5101                 out,
5102                 out_size,
5103                 &m);
5104         if (n_tokens_parsed == 0)
5105                 return;
5106         t0 += n_tokens_parsed;
5107
5108         /* action */
5109         n_tokens_parsed = parse_table_action(tokens + t0,
5110                 n_tokens - t0,
5111                 out,
5112                 out_size,
5113                 &a);
5114         if (n_tokens_parsed == 0)
5115                 return;
5116         t0 += n_tokens_parsed;
5117
5118         if (t0 != n_tokens) {
5119                 snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
5120                 return;
5121         }
5122
5123         status = softnic_pipeline_table_rule_add(softnic,
5124                 pipeline_name,
5125                 table_id,
5126                 &m,
5127                 &a,
5128                 &data);
5129         if (status) {
5130                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
5131                 return;
5132         }
5133 }
5134
5135 /**
5136  * pipeline <pipeline_name> table <table_id> rule add
5137  *    match
5138  *       default
5139  *    action
5140  *       fwd
5141  *          drop
5142  *          | port <port_id>
5143  *          | meta
5144  *          | table <table_id>
5145  */
5146 static void
5147 cmd_softnic_pipeline_table_rule_add_default(struct pmd_internals *softnic,
5148         char **tokens,
5149         uint32_t n_tokens,
5150         char *out,
5151         size_t out_size)
5152 {
5153         struct softnic_table_rule_action action;
5154         void *data;
5155         char *pipeline_name;
5156         uint32_t table_id;
5157         int status;
5158
5159         if (n_tokens != 11 &&
5160                 n_tokens != 12) {
5161                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5162                 return;
5163         }
5164
5165         pipeline_name = tokens[1];
5166
5167         if (strcmp(tokens[2], "table") != 0) {
5168                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
5169                 return;
5170         }
5171
5172         if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
5173                 snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
5174                 return;
5175         }
5176
5177         if (strcmp(tokens[4], "rule") != 0) {
5178                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule");
5179                 return;
5180         }
5181
5182         if (strcmp(tokens[5], "add") != 0) {
5183                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
5184                 return;
5185         }
5186
5187         if (strcmp(tokens[6], "match") != 0) {
5188                 snprintf(out, out_size, MSG_ARG_INVALID, "match");
5189                 return;
5190         }
5191
5192         if (strcmp(tokens[7], "default") != 0) {
5193                 snprintf(out, out_size, MSG_ARG_INVALID, "default");
5194                 return;
5195         }
5196
5197         if (strcmp(tokens[8], "action") != 0) {
5198                 snprintf(out, out_size, MSG_ARG_INVALID, "action");
5199                 return;
5200         }
5201
5202         if (strcmp(tokens[9], "fwd") != 0) {
5203                 snprintf(out, out_size, MSG_ARG_INVALID, "fwd");
5204                 return;
5205         }
5206
5207         action.action_mask = 1 << RTE_TABLE_ACTION_FWD;
5208
5209         if (strcmp(tokens[10], "drop") == 0) {
5210                 if (n_tokens != 11) {
5211                         snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5212                         return;
5213                 }
5214
5215                 action.fwd.action = RTE_PIPELINE_ACTION_DROP;
5216         } else if (strcmp(tokens[10], "port") == 0) {
5217                 uint32_t id;
5218
5219                 if (n_tokens != 12) {
5220                         snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5221                         return;
5222                 }
5223
5224                 if (softnic_parser_read_uint32(&id, tokens[11]) != 0) {
5225                         snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
5226                         return;
5227                 }
5228
5229                 action.fwd.action = RTE_PIPELINE_ACTION_PORT;
5230                 action.fwd.id = id;
5231         } else if (strcmp(tokens[10], "meta") == 0) {
5232                 if (n_tokens != 11) {
5233                         snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5234                         return;
5235                 }
5236
5237                 action.fwd.action = RTE_PIPELINE_ACTION_PORT_META;
5238         } else if (strcmp(tokens[10], "table") == 0) {
5239                 uint32_t id;
5240
5241                 if (n_tokens != 12) {
5242                         snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5243                         return;
5244                 }
5245
5246                 if (softnic_parser_read_uint32(&id, tokens[11]) != 0) {
5247                         snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
5248                         return;
5249                 }
5250
5251                 action.fwd.action = RTE_PIPELINE_ACTION_TABLE;
5252                 action.fwd.id = id;
5253         } else {
5254                 snprintf(out, out_size, MSG_ARG_INVALID,
5255                         "drop or port or meta or table");
5256                 return;
5257         }
5258
5259         status = softnic_pipeline_table_rule_add_default(softnic,
5260                 pipeline_name,
5261                 table_id,
5262                 &action,
5263                 &data);
5264         if (status) {
5265                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
5266                 return;
5267         }
5268 }
5269
5270 /**
5271  * pipeline <pipeline_name> table <table_id> rule add bulk <file_name> <n_rules>
5272  *
5273  * File <file_name>:
5274  * - line format: match <match> action <action>
5275  */
5276 static int
5277 cli_rule_file_process(const char *file_name,
5278         size_t line_len_max,
5279         struct softnic_table_rule_match *m,
5280         struct softnic_table_rule_action *a,
5281         uint32_t *n_rules,
5282         uint32_t *line_number,
5283         char *out,
5284         size_t out_size);
5285
5286 static void
5287 cmd_softnic_pipeline_table_rule_add_bulk(struct pmd_internals *softnic,
5288         char **tokens,
5289         uint32_t n_tokens,
5290         char *out,
5291         size_t out_size)
5292 {
5293         struct softnic_table_rule_match *match;
5294         struct softnic_table_rule_action *action;
5295         void **data;
5296         char *pipeline_name, *file_name;
5297         uint32_t table_id, n_rules, n_rules_parsed, line_number;
5298         int status;
5299
5300         if (n_tokens != 9) {
5301                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5302                 return;
5303         }
5304
5305         pipeline_name = tokens[1];
5306
5307         if (strcmp(tokens[2], "table") != 0) {
5308                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
5309                 return;
5310         }
5311
5312         if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
5313                 snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
5314                 return;
5315         }
5316
5317         if (strcmp(tokens[4], "rule") != 0) {
5318                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule");
5319                 return;
5320         }
5321
5322         if (strcmp(tokens[5], "add") != 0) {
5323                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
5324                 return;
5325         }
5326
5327         if (strcmp(tokens[6], "bulk") != 0) {
5328                 snprintf(out, out_size, MSG_ARG_INVALID, "bulk");
5329                 return;
5330         }
5331
5332         file_name = tokens[7];
5333
5334         if ((softnic_parser_read_uint32(&n_rules, tokens[8]) != 0) ||
5335                 n_rules == 0) {
5336                 snprintf(out, out_size, MSG_ARG_INVALID, "n_rules");
5337                 return;
5338         }
5339
5340         /* Memory allocation. */
5341         match = calloc(n_rules, sizeof(struct softnic_table_rule_match));
5342         action = calloc(n_rules, sizeof(struct softnic_table_rule_action));
5343         data = calloc(n_rules, sizeof(void *));
5344         if (match == NULL ||
5345                 action == NULL ||
5346                 data == NULL) {
5347                 snprintf(out, out_size, MSG_OUT_OF_MEMORY);
5348                 free(data);
5349                 free(action);
5350                 free(match);
5351                 return;
5352         }
5353
5354         /* Load rule file */
5355         n_rules_parsed = n_rules;
5356         status = cli_rule_file_process(file_name,
5357                 1024,
5358                 match,
5359                 action,
5360                 &n_rules_parsed,
5361                 &line_number,
5362                 out,
5363                 out_size);
5364         if (status) {
5365                 snprintf(out, out_size, MSG_FILE_ERR, file_name, line_number);
5366                 free(data);
5367                 free(action);
5368                 free(match);
5369                 return;
5370         }
5371         if (n_rules_parsed != n_rules) {
5372                 snprintf(out, out_size, MSG_FILE_NOT_ENOUGH, file_name);
5373                 free(data);
5374                 free(action);
5375                 free(match);
5376                 return;
5377         }
5378
5379         /* Rule bulk add */
5380         status = softnic_pipeline_table_rule_add_bulk(softnic,
5381                 pipeline_name,
5382                 table_id,
5383                 match,
5384                 action,
5385                 data,
5386                 &n_rules);
5387         if (status) {
5388                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
5389                 free(data);
5390                 free(action);
5391                 free(match);
5392                 return;
5393         }
5394
5395         /* Memory free */
5396         free(data);
5397         free(action);
5398         free(match);
5399 }
5400
5401 /**
5402  * pipeline <pipeline_name> table <table_id> rule delete
5403  *    match <match>
5404  */
5405 static void
5406 cmd_softnic_pipeline_table_rule_delete(struct pmd_internals *softnic,
5407         char **tokens,
5408         uint32_t n_tokens,
5409         char *out,
5410         size_t out_size)
5411 {
5412         struct softnic_table_rule_match m;
5413         char *pipeline_name;
5414         uint32_t table_id, n_tokens_parsed, t0;
5415         int status;
5416
5417         if (n_tokens < 8) {
5418                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5419                 return;
5420         }
5421
5422         pipeline_name = tokens[1];
5423
5424         if (strcmp(tokens[2], "table") != 0) {
5425                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
5426                 return;
5427         }
5428
5429         if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
5430                 snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
5431                 return;
5432         }
5433
5434         if (strcmp(tokens[4], "rule") != 0) {
5435                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule");
5436                 return;
5437         }
5438
5439         if (strcmp(tokens[5], "delete") != 0) {
5440                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
5441                 return;
5442         }
5443
5444         t0 = 6;
5445
5446         /* match */
5447         n_tokens_parsed = parse_match(tokens + t0,
5448                 n_tokens - t0,
5449                 out,
5450                 out_size,
5451                 &m);
5452         if (n_tokens_parsed == 0)
5453                 return;
5454         t0 += n_tokens_parsed;
5455
5456         if (n_tokens != t0) {
5457                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5458                 return;
5459         }
5460
5461         status = softnic_pipeline_table_rule_delete(softnic,
5462                 pipeline_name,
5463                 table_id,
5464                 &m);
5465         if (status) {
5466                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
5467                 return;
5468         }
5469 }
5470
5471 /**
5472  * pipeline <pipeline_name> table <table_id> rule delete
5473  *    match
5474  *       default
5475  */
5476 static void
5477 cmd_softnic_pipeline_table_rule_delete_default(struct pmd_internals *softnic,
5478         char **tokens,
5479         uint32_t n_tokens,
5480         char *out,
5481         size_t out_size)
5482 {
5483         char *pipeline_name;
5484         uint32_t table_id;
5485         int status;
5486
5487         if (n_tokens != 8) {
5488                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5489                 return;
5490         }
5491
5492         pipeline_name = tokens[1];
5493
5494         if (strcmp(tokens[2], "table") != 0) {
5495                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
5496                 return;
5497         }
5498
5499         if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
5500                 snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
5501                 return;
5502         }
5503
5504         if (strcmp(tokens[4], "rule") != 0) {
5505                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule");
5506                 return;
5507         }
5508
5509         if (strcmp(tokens[5], "delete") != 0) {
5510                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
5511                 return;
5512         }
5513
5514         if (strcmp(tokens[6], "match") != 0) {
5515                 snprintf(out, out_size, MSG_ARG_INVALID, "match");
5516                 return;
5517         }
5518
5519         if (strcmp(tokens[7], "default") != 0) {
5520                 snprintf(out, out_size, MSG_ARG_INVALID, "default");
5521                 return;
5522         }
5523
5524         status = softnic_pipeline_table_rule_delete_default(softnic,
5525                 pipeline_name,
5526                 table_id);
5527         if (status) {
5528                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
5529                 return;
5530         }
5531 }
5532
5533 /**
5534  * pipeline <pipeline_name> table <table_id> rule read stats [clear]
5535  */
5536 static void
5537 cmd_softnic_pipeline_table_rule_stats_read(struct pmd_internals *softnic __rte_unused,
5538         char **tokens,
5539         uint32_t n_tokens __rte_unused,
5540         char *out,
5541         size_t out_size)
5542 {
5543         snprintf(out, out_size, MSG_CMD_UNIMPLEM, tokens[0]);
5544 }
5545
5546 /**
5547  * pipeline <pipeline_name> table <table_id> meter profile <meter_profile_id>
5548  *  add srtcm cir <cir> cbs <cbs> ebs <ebs>
5549  *  | trtcm cir <cir> pir <pir> cbs <cbs> pbs <pbs>
5550  */
5551 static void
5552 cmd_pipeline_table_meter_profile_add(struct pmd_internals *softnic,
5553         char **tokens,
5554         uint32_t n_tokens,
5555         char *out,
5556         size_t out_size)
5557 {
5558         struct rte_table_action_meter_profile p;
5559         char *pipeline_name;
5560         uint32_t table_id, meter_profile_id;
5561         int status;
5562
5563         if (n_tokens < 9) {
5564                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5565                 return;
5566         }
5567
5568         pipeline_name = tokens[1];
5569
5570         if (strcmp(tokens[2], "table") != 0) {
5571                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
5572                 return;
5573         }
5574
5575         if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
5576                 snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
5577                 return;
5578         }
5579
5580         if (strcmp(tokens[4], "meter") != 0) {
5581                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
5582                 return;
5583         }
5584
5585         if (strcmp(tokens[5], "profile") != 0) {
5586                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
5587                 return;
5588         }
5589
5590         if (softnic_parser_read_uint32(&meter_profile_id, tokens[6]) != 0) {
5591                 snprintf(out, out_size, MSG_ARG_INVALID, "meter_profile_id");
5592                 return;
5593         }
5594
5595         if (strcmp(tokens[7], "add") != 0) {
5596                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
5597                 return;
5598         }
5599
5600         if (strcmp(tokens[8], "srtcm") == 0) {
5601                 if (n_tokens != 15) {
5602                         snprintf(out, out_size, MSG_ARG_MISMATCH,
5603                                 tokens[0]);
5604                         return;
5605                 }
5606
5607                 p.alg = RTE_TABLE_ACTION_METER_SRTCM;
5608
5609                 if (strcmp(tokens[9], "cir") != 0) {
5610                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cir");
5611                         return;
5612                 }
5613
5614                 if (softnic_parser_read_uint64(&p.srtcm.cir, tokens[10]) != 0) {
5615                         snprintf(out, out_size, MSG_ARG_INVALID, "cir");
5616                         return;
5617                 }
5618
5619                 if (strcmp(tokens[11], "cbs") != 0) {
5620                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cbs");
5621                         return;
5622                 }
5623
5624                 if (softnic_parser_read_uint64(&p.srtcm.cbs, tokens[12]) != 0) {
5625                         snprintf(out, out_size, MSG_ARG_INVALID, "cbs");
5626                         return;
5627                 }
5628
5629                 if (strcmp(tokens[13], "ebs") != 0) {
5630                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "ebs");
5631                         return;
5632                 }
5633
5634                 if (softnic_parser_read_uint64(&p.srtcm.ebs, tokens[14]) != 0) {
5635                         snprintf(out, out_size, MSG_ARG_INVALID, "ebs");
5636                         return;
5637                 }
5638         } else if (strcmp(tokens[8], "trtcm") == 0) {
5639                 if (n_tokens != 17) {
5640                         snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5641                         return;
5642                 }
5643
5644                 p.alg = RTE_TABLE_ACTION_METER_TRTCM;
5645
5646                 if (strcmp(tokens[9], "cir") != 0) {
5647                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cir");
5648                         return;
5649                 }
5650
5651                 if (softnic_parser_read_uint64(&p.trtcm.cir, tokens[10]) != 0) {
5652                         snprintf(out, out_size, MSG_ARG_INVALID, "cir");
5653                         return;
5654                 }
5655
5656                 if (strcmp(tokens[11], "pir") != 0) {
5657                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pir");
5658                         return;
5659                 }
5660
5661                 if (softnic_parser_read_uint64(&p.trtcm.pir, tokens[12]) != 0) {
5662                         snprintf(out, out_size, MSG_ARG_INVALID, "pir");
5663                         return;
5664                 }
5665                 if (strcmp(tokens[13], "cbs") != 0) {
5666                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cbs");
5667                         return;
5668                 }
5669
5670                 if (softnic_parser_read_uint64(&p.trtcm.cbs, tokens[14]) != 0) {
5671                         snprintf(out, out_size, MSG_ARG_INVALID, "cbs");
5672                         return;
5673                 }
5674
5675                 if (strcmp(tokens[15], "pbs") != 0) {
5676                         snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pbs");
5677                         return;
5678                 }
5679
5680                 if (softnic_parser_read_uint64(&p.trtcm.pbs, tokens[16]) != 0) {
5681                         snprintf(out, out_size, MSG_ARG_INVALID, "pbs");
5682                         return;
5683                 }
5684         } else {
5685                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5686                 return;
5687         }
5688
5689         status = softnic_pipeline_table_mtr_profile_add(softnic,
5690                 pipeline_name,
5691                 table_id,
5692                 meter_profile_id,
5693                 &p);
5694         if (status) {
5695                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
5696                 return;
5697         }
5698 }
5699
5700 /**
5701  * pipeline <pipeline_name> table <table_id>
5702  *  meter profile <meter_profile_id> delete
5703  */
5704 static void
5705 cmd_pipeline_table_meter_profile_delete(struct pmd_internals *softnic,
5706         char **tokens,
5707         uint32_t n_tokens,
5708         char *out,
5709         size_t out_size)
5710 {
5711         char *pipeline_name;
5712         uint32_t table_id, meter_profile_id;
5713         int status;
5714
5715         if (n_tokens != 8) {
5716                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5717                 return;
5718         }
5719
5720         pipeline_name = tokens[1];
5721
5722         if (strcmp(tokens[2], "table") != 0) {
5723                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
5724                 return;
5725         }
5726
5727         if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
5728                 snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
5729                 return;
5730         }
5731
5732         if (strcmp(tokens[4], "meter") != 0) {
5733                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
5734                 return;
5735         }
5736
5737         if (strcmp(tokens[5], "profile") != 0) {
5738                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
5739                 return;
5740         }
5741
5742         if (softnic_parser_read_uint32(&meter_profile_id, tokens[6]) != 0) {
5743                 snprintf(out, out_size, MSG_ARG_INVALID, "meter_profile_id");
5744                 return;
5745         }
5746
5747         if (strcmp(tokens[7], "delete") != 0) {
5748                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
5749                 return;
5750         }
5751
5752         status = softnic_pipeline_table_mtr_profile_delete(softnic,
5753                 pipeline_name,
5754                 table_id,
5755                 meter_profile_id);
5756         if (status) {
5757                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
5758                 return;
5759         }
5760 }
5761
5762 /**
5763  * pipeline <pipeline_name> table <table_id> rule read meter [clear]
5764  */
5765 static void
5766 cmd_pipeline_table_rule_meter_read(struct pmd_internals *softnic __rte_unused,
5767         char **tokens,
5768         uint32_t n_tokens __rte_unused,
5769         char *out,
5770         size_t out_size)
5771 {
5772         snprintf(out, out_size, MSG_CMD_UNIMPLEM, tokens[0]);
5773 }
5774
5775 /**
5776  * pipeline <pipeline_name> table <table_id> dscp <file_name>
5777  *
5778  * File <file_name>:
5779  *  - exactly 64 lines
5780  *  - line format: <tc_id> <tc_queue_id> <color>, with <color> as: g | y | r
5781  */
5782 static int
5783 load_dscp_table(struct rte_table_action_dscp_table *dscp_table,
5784         const char *file_name,
5785         uint32_t *line_number)
5786 {
5787         FILE *f = NULL;
5788         uint32_t dscp, l;
5789
5790         /* Check input arguments */
5791         if (dscp_table == NULL ||
5792                 file_name == NULL ||
5793                 line_number == NULL) {
5794                 if (line_number)
5795                         *line_number = 0;
5796                 return -EINVAL;
5797         }
5798
5799         /* Open input file */
5800         f = fopen(file_name, "r");
5801         if (f == NULL) {
5802                 *line_number = 0;
5803                 return -EINVAL;
5804         }
5805
5806         /* Read file */
5807         for (dscp = 0, l = 1; ; l++) {
5808                 char line[64];
5809                 char *tokens[3];
5810                 enum rte_color color;
5811                 uint32_t tc_id, tc_queue_id, n_tokens = RTE_DIM(tokens);
5812
5813                 if (fgets(line, sizeof(line), f) == NULL)
5814                         break;
5815
5816                 if (is_comment(line))
5817                         continue;
5818
5819                 if (softnic_parse_tokenize_string(line, tokens, &n_tokens)) {
5820                         *line_number = l;
5821                         fclose(f);
5822                         return -EINVAL;
5823                 }
5824
5825                 if (n_tokens == 0)
5826                         continue;
5827
5828                 if (dscp >= RTE_DIM(dscp_table->entry) ||
5829                         n_tokens != RTE_DIM(tokens) ||
5830                         softnic_parser_read_uint32(&tc_id, tokens[0]) ||
5831                         tc_id >= RTE_TABLE_ACTION_TC_MAX ||
5832                         softnic_parser_read_uint32(&tc_queue_id, tokens[1]) ||
5833                         tc_queue_id >= RTE_TABLE_ACTION_TC_QUEUE_MAX ||
5834                         (strlen(tokens[2]) != 1)) {
5835                         *line_number = l;
5836                         fclose(f);
5837                         return -EINVAL;
5838                 }
5839
5840                 switch (tokens[2][0]) {
5841                 case 'g':
5842                 case 'G':
5843                         color = RTE_COLOR_GREEN;
5844                         break;
5845
5846                 case 'y':
5847                 case 'Y':
5848                         color = RTE_COLOR_YELLOW;
5849                         break;
5850
5851                 case 'r':
5852                 case 'R':
5853                         color = RTE_COLOR_RED;
5854                         break;
5855
5856                 default:
5857                         *line_number = l;
5858                         fclose(f);
5859                         return -EINVAL;
5860                 }
5861
5862                 dscp_table->entry[dscp].tc_id = tc_id;
5863                 dscp_table->entry[dscp].tc_queue_id = tc_queue_id;
5864                 dscp_table->entry[dscp].color = color;
5865                 dscp++;
5866         }
5867
5868         /* Close file */
5869         fclose(f);
5870         return 0;
5871 }
5872
5873 static void
5874 cmd_pipeline_table_dscp(struct pmd_internals *softnic,
5875         char **tokens,
5876         uint32_t n_tokens,
5877         char *out,
5878         size_t out_size)
5879 {
5880         struct rte_table_action_dscp_table dscp_table;
5881         char *pipeline_name, *file_name;
5882         uint32_t table_id, line_number;
5883         int status;
5884
5885         if (n_tokens != 6) {
5886                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5887                 return;
5888         }
5889
5890         pipeline_name = tokens[1];
5891
5892         if (strcmp(tokens[2], "table") != 0) {
5893                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
5894                 return;
5895         }
5896
5897         if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
5898                 snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
5899                 return;
5900         }
5901
5902         if (strcmp(tokens[4], "dscp") != 0) {
5903                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "dscp");
5904                 return;
5905         }
5906
5907         file_name = tokens[5];
5908
5909         status = load_dscp_table(&dscp_table, file_name, &line_number);
5910         if (status) {
5911                 snprintf(out, out_size, MSG_FILE_ERR, file_name, line_number);
5912                 return;
5913         }
5914
5915         status = softnic_pipeline_table_dscp_table_update(softnic,
5916                 pipeline_name,
5917                 table_id,
5918                 UINT64_MAX,
5919                 &dscp_table);
5920         if (status) {
5921                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
5922                 return;
5923         }
5924 }
5925
5926 /**
5927  * pipeline <pipeline_name> table <table_id> rule read ttl [clear]
5928  */
5929 static void
5930 cmd_softnic_pipeline_table_rule_ttl_read(struct pmd_internals *softnic __rte_unused,
5931         char **tokens,
5932         uint32_t n_tokens __rte_unused,
5933         char *out,
5934         size_t out_size)
5935 {
5936         snprintf(out, out_size, MSG_CMD_UNIMPLEM, tokens[0]);
5937 }
5938
5939 /**
5940  * thread <thread_id> pipeline <pipeline_name> enable
5941  */
5942 static void
5943 cmd_softnic_thread_pipeline_enable(struct pmd_internals *softnic,
5944         char **tokens,
5945         uint32_t n_tokens,
5946         char *out,
5947         size_t out_size)
5948 {
5949         char *pipeline_name;
5950         uint32_t thread_id;
5951         int status;
5952
5953         if (n_tokens != 5) {
5954                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5955                 return;
5956         }
5957
5958         if (softnic_parser_read_uint32(&thread_id, tokens[1]) != 0) {
5959                 snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
5960                 return;
5961         }
5962
5963         if (strcmp(tokens[2], "pipeline") != 0) {
5964                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
5965                 return;
5966         }
5967
5968         pipeline_name = tokens[3];
5969
5970         if (strcmp(tokens[4], "enable") != 0) {
5971                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
5972                 return;
5973         }
5974
5975         status = softnic_thread_pipeline_enable(softnic, thread_id, pipeline_name);
5976         if (status) {
5977                 snprintf(out, out_size, MSG_CMD_FAIL, "thread pipeline enable");
5978                 return;
5979         }
5980 }
5981
5982 /**
5983  * thread <thread_id> pipeline <pipeline_name> disable
5984  */
5985 static void
5986 cmd_softnic_thread_pipeline_disable(struct pmd_internals *softnic,
5987         char **tokens,
5988         uint32_t n_tokens,
5989         char *out,
5990         size_t out_size)
5991 {
5992         char *pipeline_name;
5993         uint32_t thread_id;
5994         int status;
5995
5996         if (n_tokens != 5) {
5997                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5998                 return;
5999         }
6000
6001         if (softnic_parser_read_uint32(&thread_id, tokens[1]) != 0) {
6002                 snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
6003                 return;
6004         }
6005
6006         if (strcmp(tokens[2], "pipeline") != 0) {
6007                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
6008                 return;
6009         }
6010
6011         pipeline_name = tokens[3];
6012
6013         if (strcmp(tokens[4], "disable") != 0) {
6014                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
6015                 return;
6016         }
6017
6018         status = softnic_thread_pipeline_disable(softnic, thread_id, pipeline_name);
6019         if (status) {
6020                 snprintf(out, out_size, MSG_CMD_FAIL,
6021                         "thread pipeline disable");
6022                 return;
6023         }
6024 }
6025
6026 /**
6027  * flowapi map
6028  *  group <group_id>
6029  *  ingress | egress
6030  *  pipeline <pipeline_name>
6031  *  table <table_id>
6032  */
6033 static void
6034 cmd_softnic_flowapi_map(struct pmd_internals *softnic,
6035                 char **tokens,
6036                 uint32_t n_tokens,
6037                 char *out,
6038                 size_t out_size)
6039 {
6040         char *pipeline_name;
6041         uint32_t group_id, table_id;
6042         int ingress, status;
6043
6044         if (n_tokens != 9) {
6045                 snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
6046                 return;
6047         }
6048
6049         if (strcmp(tokens[1], "map") != 0) {
6050                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "map");
6051                 return;
6052         }
6053
6054         if (strcmp(tokens[2], "group") != 0) {
6055                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group");
6056                 return;
6057         }
6058
6059         if (softnic_parser_read_uint32(&group_id, tokens[3]) != 0) {
6060                 snprintf(out, out_size, MSG_ARG_INVALID, "group_id");
6061                 return;
6062         }
6063
6064         if (strcmp(tokens[4], "ingress") == 0) {
6065                 ingress = 1;
6066         } else if (strcmp(tokens[4], "egress") == 0) {
6067                 ingress = 0;
6068         } else {
6069                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "ingress | egress");
6070                 return;
6071         }
6072
6073         if (strcmp(tokens[5], "pipeline") != 0) {
6074                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
6075                 return;
6076         }
6077
6078         pipeline_name = tokens[6];
6079
6080         if (strcmp(tokens[7], "table") != 0) {
6081                 snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
6082                 return;
6083         }
6084
6085         if (softnic_parser_read_uint32(&table_id, tokens[8]) != 0) {
6086                 snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
6087                 return;
6088         }
6089
6090         status = flow_attr_map_set(softnic,
6091                         group_id,
6092                         ingress,
6093                         pipeline_name,
6094                         table_id);
6095         if (status) {
6096                 snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
6097                 return;
6098         }
6099 }
6100
6101 void
6102 softnic_cli_process(char *in, char *out, size_t out_size, void *arg)
6103 {
6104         char *tokens[CMD_MAX_TOKENS];
6105         uint32_t n_tokens = RTE_DIM(tokens);
6106         struct pmd_internals *softnic = arg;
6107         int status;
6108
6109         if (is_comment(in))
6110                 return;
6111
6112         status = softnic_parse_tokenize_string(in, tokens, &n_tokens);
6113         if (status) {
6114                 snprintf(out, out_size, MSG_ARG_TOO_MANY, "");
6115                 return;
6116         }
6117
6118         if (n_tokens == 0)
6119                 return;
6120
6121         if (strcmp(tokens[0], "mempool") == 0) {
6122                 cmd_mempool(softnic, tokens, n_tokens, out, out_size);
6123                 return;
6124         }
6125
6126         if (strcmp(tokens[0], "link") == 0) {
6127                 cmd_link(softnic, tokens, n_tokens, out, out_size);
6128                 return;
6129         }
6130
6131         if (strcmp(tokens[0], "swq") == 0) {
6132                 cmd_swq(softnic, tokens, n_tokens, out, out_size);
6133                 return;
6134         }
6135
6136         if (strcmp(tokens[0], "tmgr") == 0) {
6137                 if (n_tokens == 2) {
6138                         cmd_tmgr(softnic, tokens, n_tokens, out, out_size);
6139                         return;
6140                 }
6141
6142                 if (n_tokens >= 3 &&
6143                         (strcmp(tokens[1], "shaper") == 0) &&
6144                         (strcmp(tokens[2], "profile") == 0)) {
6145                         cmd_tmgr_shaper_profile(softnic, tokens, n_tokens, out, out_size);
6146                         return;
6147                 }
6148
6149                 if (n_tokens >= 3 &&
6150                         (strcmp(tokens[1], "shared") == 0) &&
6151                         (strcmp(tokens[2], "shaper") == 0)) {
6152                         cmd_tmgr_shared_shaper(softnic, tokens, n_tokens, out, out_size);
6153                         return;
6154                 }
6155
6156                 if (n_tokens >= 2 &&
6157                         (strcmp(tokens[1], "node") == 0)) {
6158                         cmd_tmgr_node(softnic, tokens, n_tokens, out, out_size);
6159                         return;
6160                 }
6161
6162                 if (n_tokens >= 2 &&
6163                         (strcmp(tokens[1], "hierarchy-default") == 0)) {
6164                         cmd_tmgr_hierarchy_default(softnic, tokens, n_tokens, out, out_size);
6165                         return;
6166                 }
6167
6168                 if (n_tokens >= 3 &&
6169                         (strcmp(tokens[1], "hierarchy") == 0) &&
6170                         (strcmp(tokens[2], "commit") == 0)) {
6171                         cmd_tmgr_hierarchy_commit(softnic, tokens, n_tokens, out, out_size);
6172                         return;
6173                 }
6174         }
6175
6176         if (strcmp(tokens[0], "tap") == 0) {
6177                 cmd_tap(softnic, tokens, n_tokens, out, out_size);
6178                 return;
6179         }
6180
6181         if (strcmp(tokens[0], "cryptodev") == 0) {
6182                 cmd_cryptodev(softnic, tokens, n_tokens, out, out_size);
6183                 return;
6184         }
6185
6186         if (strcmp(tokens[0], "port") == 0) {
6187                 cmd_port_in_action_profile(softnic, tokens, n_tokens, out, out_size);
6188                 return;
6189         }
6190
6191         if (strcmp(tokens[0], "table") == 0) {
6192                 cmd_table_action_profile(softnic, tokens, n_tokens, out, out_size);
6193                 return;
6194         }
6195
6196         if (strcmp(tokens[0], "pipeline") == 0) {
6197                 if (n_tokens >= 3 &&
6198                         (strcmp(tokens[2], "period") == 0)) {
6199                         cmd_pipeline(softnic, tokens, n_tokens, out, out_size);
6200                         return;
6201                 }
6202
6203                 if (n_tokens >= 5 &&
6204                         (strcmp(tokens[2], "port") == 0) &&
6205                         (strcmp(tokens[3], "in") == 0) &&
6206                         (strcmp(tokens[4], "bsz") == 0)) {
6207                         cmd_pipeline_port_in(softnic, tokens, n_tokens, out, out_size);
6208                         return;
6209                 }
6210
6211                 if (n_tokens >= 5 &&
6212                         (strcmp(tokens[2], "port") == 0) &&
6213                         (strcmp(tokens[3], "out") == 0) &&
6214                         (strcmp(tokens[4], "bsz") == 0)) {
6215                         cmd_pipeline_port_out(softnic, tokens, n_tokens, out, out_size);
6216                         return;
6217                 }
6218
6219                 if (n_tokens >= 4 &&
6220                         (strcmp(tokens[2], "table") == 0) &&
6221                         (strcmp(tokens[3], "match") == 0)) {
6222                         cmd_pipeline_table(softnic, tokens, n_tokens, out, out_size);
6223                         return;
6224                 }
6225
6226                 if (n_tokens >= 6 &&
6227                         (strcmp(tokens[2], "port") == 0) &&
6228                         (strcmp(tokens[3], "in") == 0) &&
6229                         (strcmp(tokens[5], "table") == 0)) {
6230                         cmd_pipeline_port_in_table(softnic, tokens, n_tokens,
6231                                 out, out_size);
6232                         return;
6233                 }
6234
6235                 if (n_tokens >= 6 &&
6236                         (strcmp(tokens[2], "port") == 0) &&
6237                         (strcmp(tokens[3], "in") == 0) &&
6238                         (strcmp(tokens[5], "stats") == 0)) {
6239                         cmd_pipeline_port_in_stats(softnic, tokens, n_tokens,
6240                                 out, out_size);
6241                         return;
6242                 }
6243
6244                 if (n_tokens >= 6 &&
6245                         (strcmp(tokens[2], "port") == 0) &&
6246                         (strcmp(tokens[3], "in") == 0) &&
6247                         (strcmp(tokens[5], "enable") == 0)) {
6248                         cmd_softnic_pipeline_port_in_enable(softnic, tokens, n_tokens,
6249                                 out, out_size);
6250                         return;
6251                 }
6252
6253                 if (n_tokens >= 6 &&
6254                         (strcmp(tokens[2], "port") == 0) &&
6255                         (strcmp(tokens[3], "in") == 0) &&
6256                         (strcmp(tokens[5], "disable") == 0)) {
6257                         cmd_softnic_pipeline_port_in_disable(softnic, tokens, n_tokens,
6258                                 out, out_size);
6259                         return;
6260                 }
6261
6262                 if (n_tokens >= 6 &&
6263                         (strcmp(tokens[2], "port") == 0) &&
6264                         (strcmp(tokens[3], "out") == 0) &&
6265                         (strcmp(tokens[5], "stats") == 0)) {
6266                         cmd_pipeline_port_out_stats(softnic, tokens, n_tokens,
6267                                 out, out_size);
6268                         return;
6269                 }
6270
6271                 if (n_tokens >= 5 &&
6272                         (strcmp(tokens[2], "table") == 0) &&
6273                         (strcmp(tokens[4], "stats") == 0)) {
6274                         cmd_pipeline_table_stats(softnic, tokens, n_tokens,
6275                                 out, out_size);
6276                         return;
6277                 }
6278
6279                 if (n_tokens >= 7 &&
6280                         (strcmp(tokens[2], "table") == 0) &&
6281                         (strcmp(tokens[4], "rule") == 0) &&
6282                         (strcmp(tokens[5], "add") == 0) &&
6283                         (strcmp(tokens[6], "match") == 0)) {
6284                         if (n_tokens >= 8 &&
6285                                 (strcmp(tokens[7], "default") == 0)) {
6286                                 cmd_softnic_pipeline_table_rule_add_default(softnic, tokens,
6287                                         n_tokens, out, out_size);
6288                                 return;
6289                         }
6290
6291                         cmd_softnic_pipeline_table_rule_add(softnic, tokens, n_tokens,
6292                                 out, out_size);
6293                         return;
6294                 }
6295
6296                 if (n_tokens >= 7 &&
6297                         (strcmp(tokens[2], "table") == 0) &&
6298                         (strcmp(tokens[4], "rule") == 0) &&
6299                         (strcmp(tokens[5], "add") == 0) &&
6300                         (strcmp(tokens[6], "bulk") == 0)) {
6301                         cmd_softnic_pipeline_table_rule_add_bulk(softnic, tokens,
6302                                 n_tokens, out, out_size);
6303                         return;
6304                 }
6305
6306                 if (n_tokens >= 7 &&
6307                         (strcmp(tokens[2], "table") == 0) &&
6308                         (strcmp(tokens[4], "rule") == 0) &&
6309                         (strcmp(tokens[5], "delete") == 0) &&
6310                         (strcmp(tokens[6], "match") == 0)) {
6311                         if (n_tokens >= 8 &&
6312                                 (strcmp(tokens[7], "default") == 0)) {
6313                                 cmd_softnic_pipeline_table_rule_delete_default(softnic, tokens,
6314                                         n_tokens, out, out_size);
6315                                 return;
6316                                 }
6317
6318                         cmd_softnic_pipeline_table_rule_delete(softnic, tokens, n_tokens,
6319                                 out, out_size);
6320                         return;
6321                 }
6322
6323                 if (n_tokens >= 7 &&
6324                         (strcmp(tokens[2], "table") == 0) &&
6325                         (strcmp(tokens[4], "rule") == 0) &&
6326                         (strcmp(tokens[5], "read") == 0) &&
6327                         (strcmp(tokens[6], "stats") == 0)) {
6328                         cmd_softnic_pipeline_table_rule_stats_read(softnic, tokens, n_tokens,
6329                                 out, out_size);
6330                         return;
6331                 }
6332
6333                 if (n_tokens >= 8 &&
6334                         (strcmp(tokens[2], "table") == 0) &&
6335                         (strcmp(tokens[4], "meter") == 0) &&
6336                         (strcmp(tokens[5], "profile") == 0) &&
6337                         (strcmp(tokens[7], "add") == 0)) {
6338                         cmd_pipeline_table_meter_profile_add(softnic, tokens, n_tokens,
6339                                 out, out_size);
6340                         return;
6341                 }
6342
6343                 if (n_tokens >= 8 &&
6344                         (strcmp(tokens[2], "table") == 0) &&
6345                         (strcmp(tokens[4], "meter") == 0) &&
6346                         (strcmp(tokens[5], "profile") == 0) &&
6347                         (strcmp(tokens[7], "delete") == 0)) {
6348                         cmd_pipeline_table_meter_profile_delete(softnic, tokens,
6349                                 n_tokens, out, out_size);
6350                         return;
6351                 }
6352
6353                 if (n_tokens >= 7 &&
6354                         (strcmp(tokens[2], "table") == 0) &&
6355                         (strcmp(tokens[4], "rule") == 0) &&
6356                         (strcmp(tokens[5], "read") == 0) &&
6357                         (strcmp(tokens[6], "meter") == 0)) {
6358                         cmd_pipeline_table_rule_meter_read(softnic, tokens, n_tokens,
6359                                 out, out_size);
6360                         return;
6361                 }
6362
6363                 if (n_tokens >= 5 &&
6364                         (strcmp(tokens[2], "table") == 0) &&
6365                         (strcmp(tokens[4], "dscp") == 0)) {
6366                         cmd_pipeline_table_dscp(softnic, tokens, n_tokens,
6367                                 out, out_size);
6368                         return;
6369                 }
6370
6371                 if (n_tokens >= 7 &&
6372                         (strcmp(tokens[2], "table") == 0) &&
6373                         (strcmp(tokens[4], "rule") == 0) &&
6374                         (strcmp(tokens[5], "read") == 0) &&
6375                         (strcmp(tokens[6], "ttl") == 0)) {
6376                         cmd_softnic_pipeline_table_rule_ttl_read(softnic, tokens, n_tokens,
6377                                 out, out_size);
6378                         return;
6379                 }
6380         }
6381
6382         if (strcmp(tokens[0], "thread") == 0) {
6383                 if (n_tokens >= 5 &&
6384                         (strcmp(tokens[4], "enable") == 0)) {
6385                         cmd_softnic_thread_pipeline_enable(softnic, tokens, n_tokens,
6386                                 out, out_size);
6387                         return;
6388                 }
6389
6390                 if (n_tokens >= 5 &&
6391                         (strcmp(tokens[4], "disable") == 0)) {
6392                         cmd_softnic_thread_pipeline_disable(softnic, tokens, n_tokens,
6393                                 out, out_size);
6394                         return;
6395                 }
6396         }
6397
6398         if (strcmp(tokens[0], "flowapi") == 0) {
6399                 cmd_softnic_flowapi_map(softnic, tokens, n_tokens, out,
6400                                         out_size);
6401                 return;
6402         }
6403
6404         snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
6405 }
6406
6407 int
6408 softnic_cli_script_process(struct pmd_internals *softnic,
6409         const char *file_name,
6410         size_t msg_in_len_max,
6411         size_t msg_out_len_max)
6412 {
6413         char *msg_in = NULL, *msg_out = NULL;
6414         FILE *f = NULL;
6415
6416         /* Check input arguments */
6417         if (file_name == NULL ||
6418                 (strlen(file_name) == 0) ||
6419                 msg_in_len_max == 0 ||
6420                 msg_out_len_max == 0)
6421                 return -EINVAL;
6422
6423         msg_in = malloc(msg_in_len_max + 1);
6424         msg_out = malloc(msg_out_len_max + 1);
6425         if (msg_in == NULL ||
6426                 msg_out == NULL) {
6427                 free(msg_out);
6428                 free(msg_in);
6429                 return -ENOMEM;
6430         }
6431
6432         /* Open input file */
6433         f = fopen(file_name, "r");
6434         if (f == NULL) {
6435                 free(msg_out);
6436                 free(msg_in);
6437                 return -EIO;
6438         }
6439
6440         /* Read file */
6441         for ( ; ; ) {
6442                 if (fgets(msg_in, msg_in_len_max + 1, f) == NULL)
6443                         break;
6444
6445                 printf("%s", msg_in);
6446                 msg_out[0] = 0;
6447
6448                 softnic_cli_process(msg_in,
6449                         msg_out,
6450                         msg_out_len_max,
6451                         softnic);
6452
6453                 if (strlen(msg_out))
6454                         printf("%s", msg_out);
6455         }
6456
6457         /* Close file */
6458         fclose(f);
6459         free(msg_out);
6460         free(msg_in);
6461         return 0;
6462 }
6463
6464 static int
6465 cli_rule_file_process(const char *file_name,
6466         size_t line_len_max,
6467         struct softnic_table_rule_match *m,
6468         struct softnic_table_rule_action *a,
6469         uint32_t *n_rules,
6470         uint32_t *line_number,
6471         char *out,
6472         size_t out_size)
6473 {
6474         FILE *f = NULL;
6475         char *line = NULL;
6476         uint32_t rule_id, line_id;
6477         int status = 0;
6478
6479         /* Check input arguments */
6480         if (file_name == NULL ||
6481                 (strlen(file_name) == 0) ||
6482                 line_len_max == 0) {
6483                 *line_number = 0;
6484                 return -EINVAL;
6485         }
6486
6487         /* Memory allocation */
6488         line = malloc(line_len_max + 1);
6489         if (line == NULL) {
6490                 *line_number = 0;
6491                 return -ENOMEM;
6492         }
6493
6494         /* Open file */
6495         f = fopen(file_name, "r");
6496         if (f == NULL) {
6497                 *line_number = 0;
6498                 free(line);
6499                 return -EIO;
6500         }
6501
6502         /* Read file */
6503         for (line_id = 1, rule_id = 0; rule_id < *n_rules; line_id++) {
6504                 char *tokens[CMD_MAX_TOKENS];
6505                 uint32_t n_tokens, n_tokens_parsed, t0;
6506
6507                 /* Read next line from file. */
6508                 if (fgets(line, line_len_max + 1, f) == NULL)
6509                         break;
6510
6511                 /* Comment. */
6512                 if (is_comment(line))
6513                         continue;
6514
6515                 /* Parse line. */
6516                 n_tokens = RTE_DIM(tokens);
6517                 status = softnic_parse_tokenize_string(line, tokens, &n_tokens);
6518                 if (status) {
6519                         status = -EINVAL;
6520                         break;
6521                 }
6522
6523                 /* Empty line. */
6524                 if (n_tokens == 0)
6525                         continue;
6526                 t0 = 0;
6527
6528                 /* Rule match. */
6529                 n_tokens_parsed = parse_match(tokens + t0,
6530                         n_tokens - t0,
6531                         out,
6532                         out_size,
6533                         &m[rule_id]);
6534                 if (n_tokens_parsed == 0) {
6535                         status = -EINVAL;
6536                         break;
6537                 }
6538                 t0 += n_tokens_parsed;
6539
6540                 /* Rule action. */
6541                 n_tokens_parsed = parse_table_action(tokens + t0,
6542                         n_tokens - t0,
6543                         out,
6544                         out_size,
6545                         &a[rule_id]);
6546                 if (n_tokens_parsed == 0) {
6547                         status = -EINVAL;
6548                         break;
6549                 }
6550                 t0 += n_tokens_parsed;
6551
6552                 /* Line completed. */
6553                 if (t0 < n_tokens) {
6554                         status = -EINVAL;
6555                         break;
6556                 }
6557
6558                 /* Increment rule count */
6559                 rule_id++;
6560         }
6561
6562         /* Close file */
6563         fclose(f);
6564
6565         /* Memory free */
6566         free(line);
6567
6568         *n_rules = rule_id;
6569         *line_number = line_id;
6570         return status;
6571 }