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