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