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