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