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