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