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