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