xref: /dpdk/drivers/net/softnic/rte_eth_softnic_cli.c (revision 089e5ed727a15da2729cfee9b63533dd120bd04c)
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 #include <rte_string_fns.h>
13 #include <rte_cryptodev.h>
14 
15 #include "rte_eth_softnic_internals.h"
16 #include "parser.h"
17 
18 #ifndef CMD_MAX_TOKENS
19 #define CMD_MAX_TOKENS     256
20 #endif
21 
22 #define MSG_OUT_OF_MEMORY   "Not enough memory.\n"
23 #define MSG_CMD_UNKNOWN     "Unknown command \"%s\".\n"
24 #define MSG_CMD_UNIMPLEM    "Command \"%s\" not implemented.\n"
25 #define MSG_ARG_NOT_ENOUGH  "Not enough arguments for command \"%s\".\n"
26 #define MSG_ARG_TOO_MANY    "Too many arguments for command \"%s\".\n"
27 #define MSG_ARG_MISMATCH    "Wrong number of arguments for command \"%s\".\n"
28 #define MSG_ARG_NOT_FOUND   "Argument \"%s\" not found.\n"
29 #define MSG_ARG_INVALID     "Invalid value for argument \"%s\".\n"
30 #define MSG_FILE_ERR        "Error in file \"%s\" at line %u.\n"
31 #define MSG_FILE_NOT_ENOUGH "Not enough rules in file \"%s\".\n"
32 #define MSG_CMD_FAIL        "Command \"%s\" failed.\n"
33 
34 static int
35 is_comment(char *in)
36 {
37 	if ((strlen(in) && index("!#%;", in[0])) ||
38 		(strncmp(in, "//", 2) == 0) ||
39 		(strncmp(in, "--", 2) == 0))
40 		return 1;
41 
42 	return 0;
43 }
44 
45 /**
46  * mempool <mempool_name>
47  *  buffer <buffer_size>
48  *  pool <pool_size>
49  *  cache <cache_size>
50  */
51 static void
52 cmd_mempool(struct pmd_internals *softnic,
53 	char **tokens,
54 	uint32_t n_tokens,
55 	char *out,
56 	size_t out_size)
57 {
58 	struct softnic_mempool_params p;
59 	char *name;
60 	struct softnic_mempool *mempool;
61 
62 	if (n_tokens != 8) {
63 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
64 		return;
65 	}
66 
67 	name = tokens[1];
68 
69 	if (strcmp(tokens[2], "buffer") != 0) {
70 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "buffer");
71 		return;
72 	}
73 
74 	if (softnic_parser_read_uint32(&p.buffer_size, tokens[3]) != 0) {
75 		snprintf(out, out_size, MSG_ARG_INVALID, "buffer_size");
76 		return;
77 	}
78 
79 	if (strcmp(tokens[4], "pool") != 0) {
80 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pool");
81 		return;
82 	}
83 
84 	if (softnic_parser_read_uint32(&p.pool_size, tokens[5]) != 0) {
85 		snprintf(out, out_size, MSG_ARG_INVALID, "pool_size");
86 		return;
87 	}
88 
89 	if (strcmp(tokens[6], "cache") != 0) {
90 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cache");
91 		return;
92 	}
93 
94 	if (softnic_parser_read_uint32(&p.cache_size, tokens[7]) != 0) {
95 		snprintf(out, out_size, MSG_ARG_INVALID, "cache_size");
96 		return;
97 	}
98 
99 	mempool = softnic_mempool_create(softnic, name, &p);
100 	if (mempool == NULL) {
101 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
102 		return;
103 	}
104 }
105 
106 /**
107  * link <link_name>
108  *    dev <device_name> | port <port_id>
109  */
110 static void
111 cmd_link(struct pmd_internals *softnic,
112 	char **tokens,
113 	uint32_t n_tokens,
114 	char *out,
115 	size_t out_size)
116 {
117 	struct softnic_link_params p;
118 	struct softnic_link *link;
119 	char *name;
120 
121 	memset(&p, 0, sizeof(p));
122 
123 	if (n_tokens != 4) {
124 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
125 		return;
126 	}
127 	name = tokens[1];
128 
129 	if (strcmp(tokens[2], "dev") == 0) {
130 		p.dev_name = tokens[3];
131 	} else if (strcmp(tokens[2], "port") == 0) {
132 		p.dev_name = NULL;
133 
134 		if (softnic_parser_read_uint16(&p.port_id, tokens[3]) != 0) {
135 			snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
136 			return;
137 		}
138 	} else {
139 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "dev or port");
140 		return;
141 	}
142 
143 	link = softnic_link_create(softnic, name, &p);
144 	if (link == NULL) {
145 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
146 		return;
147 	}
148 }
149 
150 /**
151  * swq <swq_name>
152  *  size <size>
153  */
154 static void
155 cmd_swq(struct pmd_internals *softnic,
156 	char **tokens,
157 	uint32_t n_tokens,
158 	char *out,
159 	size_t out_size)
160 {
161 	struct softnic_swq_params p;
162 	char *name;
163 	struct softnic_swq *swq;
164 
165 	if (n_tokens != 4) {
166 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
167 		return;
168 	}
169 
170 	name = tokens[1];
171 
172 	if (strcmp(tokens[2], "size") != 0) {
173 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
174 		return;
175 	}
176 
177 	if (softnic_parser_read_uint32(&p.size, tokens[3]) != 0) {
178 		snprintf(out, out_size, MSG_ARG_INVALID, "size");
179 		return;
180 	}
181 
182 	swq = softnic_swq_create(softnic, name, &p);
183 	if (swq == NULL) {
184 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
185 		return;
186 	}
187 }
188 
189 /**
190  * tmgr shaper profile
191  *  id <profile_id>
192  *  rate <tb_rate> size <tb_size>
193  *  adj <packet_length_adjust>
194  */
195 static void
196 cmd_tmgr_shaper_profile(struct pmd_internals *softnic,
197 	char **tokens,
198 	uint32_t n_tokens,
199 	char *out,
200 	size_t out_size)
201 {
202 	struct rte_tm_shaper_params sp;
203 	struct rte_tm_error error;
204 	uint32_t shaper_profile_id;
205 	uint16_t port_id;
206 	int status;
207 
208 	memset(&sp, 0, sizeof(struct rte_tm_shaper_params));
209 
210 	if (n_tokens != 11) {
211 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
212 		return;
213 	}
214 
215 	if (strcmp(tokens[1], "shaper") != 0) {
216 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "shaper");
217 		return;
218 	}
219 
220 	if (strcmp(tokens[2], "profile") != 0) {
221 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
222 		return;
223 	}
224 
225 	if (strcmp(tokens[3], "id") != 0) {
226 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "id");
227 		return;
228 	}
229 
230 	if (softnic_parser_read_uint32(&shaper_profile_id, tokens[4]) != 0) {
231 		snprintf(out, out_size, MSG_ARG_INVALID, "profile_id");
232 		return;
233 	}
234 
235 	if (strcmp(tokens[5], "rate") != 0) {
236 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rate");
237 		return;
238 	}
239 
240 	if (softnic_parser_read_uint64(&sp.peak.rate, tokens[6]) != 0) {
241 		snprintf(out, out_size, MSG_ARG_INVALID, "tb_rate");
242 		return;
243 	}
244 
245 	if (strcmp(tokens[7], "size") != 0) {
246 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
247 		return;
248 	}
249 
250 	if (softnic_parser_read_uint64(&sp.peak.size, tokens[8]) != 0) {
251 		snprintf(out, out_size, MSG_ARG_INVALID, "tb_size");
252 		return;
253 	}
254 
255 	if (strcmp(tokens[9], "adj") != 0) {
256 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "adj");
257 		return;
258 	}
259 
260 	if (softnic_parser_read_int32(&sp.pkt_length_adjust, tokens[10]) != 0) {
261 		snprintf(out, out_size, MSG_ARG_INVALID, "packet_length_adjust");
262 		return;
263 	}
264 
265 	status = rte_eth_dev_get_port_by_name(softnic->params.name, &port_id);
266 	if (status)
267 		return;
268 
269 	status = rte_tm_shaper_profile_add(port_id, shaper_profile_id, &sp, &error);
270 	if (status != 0) {
271 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
272 		return;
273 	}
274 }
275 
276 /**
277  * tmgr shared shaper
278  *  id <shared_shaper_id>
279  *  profile <shaper_profile_id>
280  */
281 static void
282 cmd_tmgr_shared_shaper(struct pmd_internals *softnic,
283 	char **tokens,
284 	uint32_t n_tokens,
285 	char *out,
286 	size_t out_size)
287 {
288 	struct rte_tm_error error;
289 	uint32_t shared_shaper_id, shaper_profile_id;
290 	uint16_t port_id;
291 	int status;
292 
293 	if (n_tokens != 7) {
294 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
295 		return;
296 	}
297 
298 	if (strcmp(tokens[1], "shared") != 0) {
299 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "shared");
300 		return;
301 	}
302 
303 	if (strcmp(tokens[2], "shaper") != 0) {
304 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "shaper");
305 		return;
306 	}
307 
308 	if (strcmp(tokens[3], "id") != 0) {
309 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "id");
310 		return;
311 	}
312 
313 	if (softnic_parser_read_uint32(&shared_shaper_id, tokens[4]) != 0) {
314 		snprintf(out, out_size, MSG_ARG_INVALID, "shared_shaper_id");
315 		return;
316 	}
317 
318 	if (strcmp(tokens[5], "profile") != 0) {
319 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
320 		return;
321 	}
322 
323 	if (softnic_parser_read_uint32(&shaper_profile_id, tokens[6]) != 0) {
324 		snprintf(out, out_size, MSG_ARG_INVALID, "shaper_profile_id");
325 		return;
326 	}
327 
328 	status = rte_eth_dev_get_port_by_name(softnic->params.name, &port_id);
329 	if (status)
330 		return;
331 
332 	status = rte_tm_shared_shaper_add_update(port_id,
333 		shared_shaper_id,
334 		shaper_profile_id,
335 		&error);
336 	if (status != 0) {
337 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
338 		return;
339 	}
340 }
341 
342 /**
343  * tmgr node
344  *   id <node_id>
345  *   parent <parent_node_id | none>
346  *   priority <priority>
347  *   weight <weight>
348  *   [shaper profile <shaper_profile_id>]
349  *   [shared shaper <shared_shaper_id>]
350  *   [nonleaf sp <n_sp_priorities>]
351  */
352 static void
353 cmd_tmgr_node(struct pmd_internals *softnic,
354 	char **tokens,
355 	uint32_t n_tokens,
356 	char *out,
357 	size_t out_size)
358 {
359 	struct rte_tm_error error;
360 	struct rte_tm_node_params np;
361 	uint32_t node_id, parent_node_id, priority, weight, shared_shaper_id;
362 	uint16_t port_id;
363 	int status;
364 
365 	memset(&np, 0, sizeof(struct rte_tm_node_params));
366 	np.shaper_profile_id = RTE_TM_SHAPER_PROFILE_ID_NONE;
367 	np.nonleaf.n_sp_priorities = 1;
368 
369 	if (n_tokens < 10) {
370 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
371 		return;
372 	}
373 
374 	if (strcmp(tokens[1], "node") != 0) {
375 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "node");
376 		return;
377 	}
378 
379 	if (strcmp(tokens[2], "id") != 0) {
380 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "id");
381 		return;
382 	}
383 
384 	if (softnic_parser_read_uint32(&node_id, tokens[3]) != 0) {
385 		snprintf(out, out_size, MSG_ARG_INVALID, "node_id");
386 		return;
387 	}
388 
389 	if (strcmp(tokens[4], "parent") != 0) {
390 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "parent");
391 		return;
392 	}
393 
394 	if (strcmp(tokens[5], "none") == 0)
395 		parent_node_id = RTE_TM_NODE_ID_NULL;
396 	else {
397 		if (softnic_parser_read_uint32(&parent_node_id, tokens[5]) != 0) {
398 			snprintf(out, out_size, MSG_ARG_INVALID, "parent_node_id");
399 			return;
400 		}
401 	}
402 
403 	if (strcmp(tokens[6], "priority") != 0) {
404 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "priority");
405 		return;
406 	}
407 
408 	if (softnic_parser_read_uint32(&priority, tokens[7]) != 0) {
409 		snprintf(out, out_size, MSG_ARG_INVALID, "priority");
410 		return;
411 	}
412 
413 	if (strcmp(tokens[8], "weight") != 0) {
414 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "weight");
415 		return;
416 	}
417 
418 	if (softnic_parser_read_uint32(&weight, tokens[9]) != 0) {
419 		snprintf(out, out_size, MSG_ARG_INVALID, "weight");
420 		return;
421 	}
422 
423 	tokens += 10;
424 	n_tokens -= 10;
425 
426 	if (n_tokens >= 2 &&
427 		(strcmp(tokens[0], "shaper") == 0) &&
428 		(strcmp(tokens[1], "profile") == 0)) {
429 		if (n_tokens < 3) {
430 			snprintf(out, out_size, MSG_ARG_MISMATCH, "tmgr node");
431 			return;
432 		}
433 
434 		if (strcmp(tokens[2], "none") == 0) {
435 			np.shaper_profile_id = RTE_TM_SHAPER_PROFILE_ID_NONE;
436 		} else {
437 			if (softnic_parser_read_uint32(&np.shaper_profile_id, tokens[2]) != 0) {
438 				snprintf(out, out_size, MSG_ARG_INVALID, "shaper_profile_id");
439 				return;
440 			}
441 		}
442 
443 		tokens += 3;
444 		n_tokens -= 3;
445 	} /* shaper profile */
446 
447 	if (n_tokens >= 2 &&
448 		(strcmp(tokens[0], "shared") == 0) &&
449 		(strcmp(tokens[1], "shaper") == 0)) {
450 		if (n_tokens < 3) {
451 			snprintf(out, out_size, MSG_ARG_MISMATCH, "tmgr node");
452 			return;
453 		}
454 
455 		if (softnic_parser_read_uint32(&shared_shaper_id, tokens[2]) != 0) {
456 			snprintf(out, out_size, MSG_ARG_INVALID, "shared_shaper_id");
457 			return;
458 		}
459 
460 		np.shared_shaper_id = &shared_shaper_id;
461 		np.n_shared_shapers = 1;
462 
463 		tokens += 3;
464 		n_tokens -= 3;
465 	} /* shared shaper */
466 
467 	if (n_tokens >= 2 &&
468 		(strcmp(tokens[0], "nonleaf") == 0) &&
469 		(strcmp(tokens[1], "sp") == 0)) {
470 		if (n_tokens < 3) {
471 			snprintf(out, out_size, MSG_ARG_MISMATCH, "tmgr node");
472 			return;
473 		}
474 
475 		if (softnic_parser_read_uint32(&np.nonleaf.n_sp_priorities, tokens[2]) != 0) {
476 			snprintf(out, out_size, MSG_ARG_INVALID, "n_sp_priorities");
477 			return;
478 		}
479 
480 		tokens += 3;
481 		n_tokens -= 3;
482 	} /* nonleaf sp <n_sp_priorities> */
483 
484 	if (n_tokens) {
485 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
486 		return;
487 	}
488 
489 	status = rte_eth_dev_get_port_by_name(softnic->params.name, &port_id);
490 	if (status != 0)
491 		return;
492 
493 	status = rte_tm_node_add(port_id,
494 		node_id,
495 		parent_node_id,
496 		priority,
497 		weight,
498 		RTE_TM_NODE_LEVEL_ID_ANY,
499 		&np,
500 		&error);
501 	if (status != 0) {
502 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
503 		return;
504 	}
505 }
506 
507 static uint32_t
508 root_node_id(uint32_t n_spp,
509 	uint32_t n_pps)
510 {
511 	uint32_t n_queues = n_spp * n_pps * RTE_SCHED_QUEUES_PER_PIPE;
512 	uint32_t n_tc = n_spp * n_pps * RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE;
513 	uint32_t n_pipes = n_spp * n_pps;
514 
515 	return n_queues + n_tc + n_pipes + n_spp;
516 }
517 
518 static uint32_t
519 subport_node_id(uint32_t n_spp,
520 	uint32_t n_pps,
521 	uint32_t subport_id)
522 {
523 	uint32_t n_pipes = n_spp * n_pps;
524 	uint32_t n_tc = n_pipes * RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE;
525 	uint32_t n_queues = n_pipes * RTE_SCHED_QUEUES_PER_PIPE;
526 
527 	return n_queues + n_tc + n_pipes + subport_id;
528 }
529 
530 static uint32_t
531 pipe_node_id(uint32_t n_spp,
532 	uint32_t n_pps,
533 	uint32_t subport_id,
534 	uint32_t pipe_id)
535 {
536 	uint32_t n_pipes = n_spp * n_pps;
537 	uint32_t n_tc = n_pipes * RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE;
538 	uint32_t n_queues = n_pipes * RTE_SCHED_QUEUES_PER_PIPE;
539 
540 	return n_queues +
541 		n_tc +
542 		pipe_id +
543 		subport_id * n_pps;
544 }
545 
546 static uint32_t
547 tc_node_id(uint32_t n_spp,
548 	uint32_t n_pps,
549 	uint32_t subport_id,
550 	uint32_t pipe_id,
551 	uint32_t tc_id)
552 {
553 	uint32_t n_pipes = n_spp * n_pps;
554 	uint32_t n_queues = n_pipes * RTE_SCHED_QUEUES_PER_PIPE;
555 
556 	return n_queues +
557 		tc_id +
558 		(pipe_id + subport_id * n_pps) * RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE;
559 }
560 
561 static uint32_t
562 queue_node_id(uint32_t n_spp __rte_unused,
563 	uint32_t n_pps,
564 	uint32_t subport_id,
565 	uint32_t pipe_id,
566 	uint32_t tc_id,
567 	uint32_t queue_id)
568 {
569 	return queue_id + tc_id +
570 		(pipe_id + subport_id * n_pps) * RTE_SCHED_QUEUES_PER_PIPE;
571 }
572 
573 struct tmgr_hierarchy_default_params {
574 	uint32_t n_spp; /**< Number of subports per port. */
575 	uint32_t n_pps; /**< Number of pipes per subport. */
576 
577 	struct {
578 		uint32_t port;
579 		uint32_t subport;
580 		uint32_t pipe;
581 		uint32_t tc[RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE];
582 	} shaper_profile_id;
583 
584 	struct {
585 		uint32_t tc[RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE];
586 		uint32_t tc_valid[RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE];
587 	} shared_shaper_id;
588 
589 	struct {
590 		uint32_t queue[RTE_SCHED_QUEUES_PER_PIPE];
591 	} weight;
592 };
593 
594 static int
595 tmgr_hierarchy_default(struct pmd_internals *softnic,
596 	struct tmgr_hierarchy_default_params *params)
597 {
598 	struct rte_tm_node_params root_node_params = {
599 		.shaper_profile_id = params->shaper_profile_id.port,
600 		.nonleaf = {
601 			.n_sp_priorities = 1,
602 		},
603 	};
604 
605 	struct rte_tm_node_params subport_node_params = {
606 		.shaper_profile_id = params->shaper_profile_id.subport,
607 		.nonleaf = {
608 			.n_sp_priorities = 1,
609 		},
610 	};
611 
612 	struct rte_tm_node_params pipe_node_params = {
613 		.shaper_profile_id = params->shaper_profile_id.pipe,
614 		.nonleaf = {
615 			.n_sp_priorities = RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE,
616 		},
617 	};
618 
619 	uint32_t *shared_shaper_id =
620 		(uint32_t *)calloc(RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE,
621 			sizeof(uint32_t));
622 
623 	if (shared_shaper_id == NULL)
624 		return -1;
625 
626 	memcpy(shared_shaper_id, params->shared_shaper_id.tc,
627 		sizeof(params->shared_shaper_id.tc));
628 
629 	struct rte_tm_node_params tc_node_params[] = {
630 		[0] = {
631 			.shaper_profile_id = params->shaper_profile_id.tc[0],
632 			.shared_shaper_id = &shared_shaper_id[0],
633 			.n_shared_shapers =
634 				(&params->shared_shaper_id.tc_valid[0]) ? 1 : 0,
635 			.nonleaf = {
636 				.n_sp_priorities = 1,
637 			},
638 		},
639 
640 		[1] = {
641 			.shaper_profile_id = params->shaper_profile_id.tc[1],
642 			.shared_shaper_id = &shared_shaper_id[1],
643 			.n_shared_shapers =
644 				(&params->shared_shaper_id.tc_valid[1]) ? 1 : 0,
645 			.nonleaf = {
646 				.n_sp_priorities = 1,
647 			},
648 		},
649 
650 		[2] = {
651 			.shaper_profile_id = params->shaper_profile_id.tc[2],
652 			.shared_shaper_id = &shared_shaper_id[2],
653 			.n_shared_shapers =
654 				(&params->shared_shaper_id.tc_valid[2]) ? 1 : 0,
655 			.nonleaf = {
656 				.n_sp_priorities = 1,
657 			},
658 		},
659 
660 		[3] = {
661 			.shaper_profile_id = params->shaper_profile_id.tc[3],
662 			.shared_shaper_id = &shared_shaper_id[3],
663 			.n_shared_shapers =
664 				(&params->shared_shaper_id.tc_valid[3]) ? 1 : 0,
665 			.nonleaf = {
666 				.n_sp_priorities = 1,
667 			},
668 		},
669 
670 		[4] = {
671 			.shaper_profile_id = params->shaper_profile_id.tc[4],
672 			.shared_shaper_id = &shared_shaper_id[4],
673 			.n_shared_shapers =
674 				(&params->shared_shaper_id.tc_valid[4]) ? 1 : 0,
675 			.nonleaf = {
676 				.n_sp_priorities = 1,
677 			},
678 		},
679 
680 		[5] = {
681 			.shaper_profile_id = params->shaper_profile_id.tc[5],
682 			.shared_shaper_id = &shared_shaper_id[5],
683 			.n_shared_shapers =
684 				(&params->shared_shaper_id.tc_valid[5]) ? 1 : 0,
685 			.nonleaf = {
686 				.n_sp_priorities = 1,
687 			},
688 		},
689 
690 		[6] = {
691 			.shaper_profile_id = params->shaper_profile_id.tc[6],
692 			.shared_shaper_id = &shared_shaper_id[6],
693 			.n_shared_shapers =
694 				(&params->shared_shaper_id.tc_valid[6]) ? 1 : 0,
695 			.nonleaf = {
696 				.n_sp_priorities = 1,
697 			},
698 		},
699 
700 		[7] = {
701 			.shaper_profile_id = params->shaper_profile_id.tc[7],
702 			.shared_shaper_id = &shared_shaper_id[7],
703 			.n_shared_shapers =
704 				(&params->shared_shaper_id.tc_valid[7]) ? 1 : 0,
705 			.nonleaf = {
706 				.n_sp_priorities = 1,
707 			},
708 		},
709 
710 		[8] = {
711 			.shaper_profile_id = params->shaper_profile_id.tc[8],
712 			.shared_shaper_id = &shared_shaper_id[8],
713 			.n_shared_shapers =
714 				(&params->shared_shaper_id.tc_valid[8]) ? 1 : 0,
715 			.nonleaf = {
716 				.n_sp_priorities = 1,
717 			},
718 		},
719 
720 		[9] = {
721 			.shaper_profile_id = params->shaper_profile_id.tc[9],
722 			.shared_shaper_id = &shared_shaper_id[9],
723 			.n_shared_shapers =
724 				(&params->shared_shaper_id.tc_valid[9]) ? 1 : 0,
725 			.nonleaf = {
726 				.n_sp_priorities = 1,
727 			},
728 		},
729 
730 		[10] = {
731 			.shaper_profile_id = params->shaper_profile_id.tc[10],
732 			.shared_shaper_id = &shared_shaper_id[10],
733 			.n_shared_shapers =
734 				(&params->shared_shaper_id.tc_valid[10]) ? 1 : 0,
735 			.nonleaf = {
736 				.n_sp_priorities = 1,
737 			},
738 		},
739 
740 		[11] = {
741 			.shaper_profile_id = params->shaper_profile_id.tc[11],
742 			.shared_shaper_id = &shared_shaper_id[11],
743 			.n_shared_shapers =
744 				(&params->shared_shaper_id.tc_valid[11]) ? 1 : 0,
745 			.nonleaf = {
746 				.n_sp_priorities = 1,
747 			},
748 		},
749 
750 		[12] = {
751 			.shaper_profile_id = params->shaper_profile_id.tc[12],
752 			.shared_shaper_id = &shared_shaper_id[12],
753 			.n_shared_shapers =
754 				(&params->shared_shaper_id.tc_valid[12]) ? 1 : 0,
755 			.nonleaf = {
756 				.n_sp_priorities = 1,
757 			},
758 		},
759 	};
760 
761 	struct rte_tm_node_params queue_node_params = {
762 		.shaper_profile_id = RTE_TM_SHAPER_PROFILE_ID_NONE,
763 	};
764 
765 	struct rte_tm_error error;
766 	uint32_t n_spp = params->n_spp, n_pps = params->n_pps, s;
767 	int status;
768 	uint16_t port_id;
769 
770 	status = rte_eth_dev_get_port_by_name(softnic->params.name, &port_id);
771 	if (status)
772 		return -1;
773 
774 	/* Hierarchy level 0: Root node */
775 	status = rte_tm_node_add(port_id,
776 		root_node_id(n_spp, n_pps),
777 		RTE_TM_NODE_ID_NULL,
778 		0,
779 		1,
780 		RTE_TM_NODE_LEVEL_ID_ANY,
781 		&root_node_params,
782 		&error);
783 	if (status)
784 		return -1;
785 
786 	/* Hierarchy level 1: Subport nodes */
787 	for (s = 0; s < params->n_spp; s++) {
788 		uint32_t p;
789 
790 		status = rte_tm_node_add(port_id,
791 			subport_node_id(n_spp, n_pps, s),
792 			root_node_id(n_spp, n_pps),
793 			0,
794 			1,
795 			RTE_TM_NODE_LEVEL_ID_ANY,
796 			&subport_node_params,
797 			&error);
798 		if (status)
799 			return -1;
800 
801 		/* Hierarchy level 2: Pipe nodes */
802 		for (p = 0; p < params->n_pps; p++) {
803 			uint32_t t;
804 
805 			status = rte_tm_node_add(port_id,
806 				pipe_node_id(n_spp, n_pps, s, p),
807 				subport_node_id(n_spp, n_pps, s),
808 				0,
809 				1,
810 				RTE_TM_NODE_LEVEL_ID_ANY,
811 				&pipe_node_params,
812 				&error);
813 			if (status)
814 				return -1;
815 
816 			/* Hierarchy level 3: Traffic class nodes */
817 			for (t = 0; t < RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE; t++) {
818 				uint32_t q;
819 
820 				status = rte_tm_node_add(port_id,
821 					tc_node_id(n_spp, n_pps, s, p, t),
822 					pipe_node_id(n_spp, n_pps, s, p),
823 					t,
824 					1,
825 					RTE_TM_NODE_LEVEL_ID_ANY,
826 					&tc_node_params[t],
827 					&error);
828 				if (status)
829 					return -1;
830 
831 				/* Hierarchy level 4: Queue nodes */
832 				if (t < RTE_SCHED_TRAFFIC_CLASS_BE) {
833 					/* Strict-priority traffic class queues */
834 					q = 0;
835 					status = rte_tm_node_add(port_id,
836 						queue_node_id(n_spp, n_pps, s, p, t, q),
837 						tc_node_id(n_spp, n_pps, s, p, t),
838 						0,
839 						params->weight.queue[q],
840 						RTE_TM_NODE_LEVEL_ID_ANY,
841 						&queue_node_params,
842 						&error);
843 					if (status)
844 						return -1;
845 
846 					continue;
847 				}
848 				/* Best-effort traffic class queues */
849 				for (q = 0; q < RTE_SCHED_BE_QUEUES_PER_PIPE; q++) {
850 					status = rte_tm_node_add(port_id,
851 						queue_node_id(n_spp, n_pps, s, p, t, q),
852 						tc_node_id(n_spp, n_pps, s, p, t),
853 						0,
854 						params->weight.queue[q],
855 						RTE_TM_NODE_LEVEL_ID_ANY,
856 						&queue_node_params,
857 						&error);
858 					if (status)
859 						return -1;
860 				}
861 			} /* TC */
862 		} /* Pipe */
863 	} /* Subport */
864 
865 	return 0;
866 }
867 
868 
869 /**
870  * tmgr hierarchy-default
871  *  spp <n_subports_per_port>
872  *  pps <n_pipes_per_subport>
873  *  shaper profile
874  *   port <profile_id>
875  *   subport <profile_id>
876  *   pipe <profile_id>
877  *   tc0 <profile_id>
878  *   tc1 <profile_id>
879  *   tc2 <profile_id>
880  *   tc3 <profile_id>
881  *   tc4 <profile_id>
882  *   tc5 <profile_id>
883  *   tc6 <profile_id>
884  *   tc7 <profile_id>
885  *   tc8 <profile_id>
886  *   tc9 <profile_id>
887  *   tc10 <profile_id>
888  *   tc11 <profile_id>
889  *   tc12 <profile_id>
890  *  shared shaper
891  *   tc0 <id | none>
892  *   tc1 <id | none>
893  *   tc2 <id | none>
894  *   tc3 <id | none>
895  *   tc4 <id | none>
896  *   tc5 <id | none>
897  *   tc6 <id | none>
898  *   tc7 <id | none>
899  *   tc8 <id | none>
900  *   tc9 <id | none>
901  *   tc10 <id | none>
902  *   tc11 <id | none>
903  *   tc12 <id | none>
904  *  weight
905  *   queue  <q12> ... <q15>
906  */
907 static void
908 cmd_tmgr_hierarchy_default(struct pmd_internals *softnic,
909 	char **tokens,
910 	uint32_t n_tokens,
911 	char *out,
912 	size_t out_size)
913 {
914 	struct tmgr_hierarchy_default_params p;
915 	int i, j, status;
916 
917 	memset(&p, 0, sizeof(p));
918 
919 	if (n_tokens != 74) {
920 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
921 		return;
922 	}
923 
924 	if (strcmp(tokens[1], "hierarchy-default") != 0) {
925 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "hierarchy-default");
926 		return;
927 	}
928 
929 	if (strcmp(tokens[2], "spp") != 0) {
930 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "spp");
931 		return;
932 	}
933 
934 	if (softnic_parser_read_uint32(&p.n_spp, tokens[3]) != 0) {
935 		snprintf(out, out_size, MSG_ARG_INVALID, "n_subports_per_port");
936 		return;
937 	}
938 
939 	if (strcmp(tokens[4], "pps") != 0) {
940 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pps");
941 		return;
942 	}
943 
944 	if (softnic_parser_read_uint32(&p.n_pps, tokens[5]) != 0) {
945 		snprintf(out, out_size, MSG_ARG_INVALID, "n_pipes_per_subport");
946 		return;
947 	}
948 
949 	/* Shaper profile */
950 
951 	if (strcmp(tokens[6], "shaper") != 0) {
952 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "shaper");
953 		return;
954 	}
955 
956 	if (strcmp(tokens[7], "profile") != 0) {
957 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
958 		return;
959 	}
960 
961 	if (strcmp(tokens[8], "port") != 0) {
962 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
963 		return;
964 	}
965 
966 	if (softnic_parser_read_uint32(&p.shaper_profile_id.port, tokens[9]) != 0) {
967 		snprintf(out, out_size, MSG_ARG_INVALID, "port profile id");
968 		return;
969 	}
970 
971 	if (strcmp(tokens[10], "subport") != 0) {
972 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "subport");
973 		return;
974 	}
975 
976 	if (softnic_parser_read_uint32(&p.shaper_profile_id.subport, tokens[11]) != 0) {
977 		snprintf(out, out_size, MSG_ARG_INVALID, "subport profile id");
978 		return;
979 	}
980 
981 	if (strcmp(tokens[12], "pipe") != 0) {
982 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipe");
983 		return;
984 	}
985 
986 	if (softnic_parser_read_uint32(&p.shaper_profile_id.pipe, tokens[13]) != 0) {
987 		snprintf(out, out_size, MSG_ARG_INVALID, "pipe_profile_id");
988 		return;
989 	}
990 
991 	if (strcmp(tokens[14], "tc0") != 0) {
992 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc0");
993 		return;
994 	}
995 
996 	if (softnic_parser_read_uint32(&p.shaper_profile_id.tc[0], tokens[15]) != 0) {
997 		snprintf(out, out_size, MSG_ARG_INVALID, "tc0 profile id");
998 		return;
999 	}
1000 
1001 	if (strcmp(tokens[16], "tc1") != 0) {
1002 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc1");
1003 		return;
1004 	}
1005 
1006 	if (softnic_parser_read_uint32(&p.shaper_profile_id.tc[1], tokens[17]) != 0) {
1007 		snprintf(out, out_size, MSG_ARG_INVALID, "tc1 profile id");
1008 		return;
1009 	}
1010 
1011 	if (strcmp(tokens[18], "tc2") != 0) {
1012 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc2");
1013 		return;
1014 	}
1015 
1016 	if (softnic_parser_read_uint32(&p.shaper_profile_id.tc[2], tokens[19]) != 0) {
1017 		snprintf(out, out_size, MSG_ARG_INVALID, "tc2 profile id");
1018 		return;
1019 	}
1020 
1021 	if (strcmp(tokens[20], "tc3") != 0) {
1022 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc3");
1023 		return;
1024 	}
1025 
1026 	if (softnic_parser_read_uint32(&p.shaper_profile_id.tc[3], tokens[21]) != 0) {
1027 		snprintf(out, out_size, MSG_ARG_INVALID, "tc3 profile id");
1028 		return;
1029 	}
1030 
1031 	if (strcmp(tokens[22], "tc4") != 0) {
1032 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc4");
1033 		return;
1034 	}
1035 
1036 	if (softnic_parser_read_uint32(&p.shaper_profile_id.tc[4], tokens[23]) != 0) {
1037 		snprintf(out, out_size, MSG_ARG_INVALID, "tc4 profile id");
1038 		return;
1039 	}
1040 
1041 	if (strcmp(tokens[24], "tc5") != 0) {
1042 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc5");
1043 		return;
1044 	}
1045 
1046 	if (softnic_parser_read_uint32(&p.shaper_profile_id.tc[5], tokens[25]) != 0) {
1047 		snprintf(out, out_size, MSG_ARG_INVALID, "tc5 profile id");
1048 		return;
1049 	}
1050 
1051 	if (strcmp(tokens[26], "tc6") != 0) {
1052 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc6");
1053 		return;
1054 	}
1055 
1056 	if (softnic_parser_read_uint32(&p.shaper_profile_id.tc[6], tokens[27]) != 0) {
1057 		snprintf(out, out_size, MSG_ARG_INVALID, "tc6 profile id");
1058 		return;
1059 	}
1060 
1061 	if (strcmp(tokens[28], "tc7") != 0) {
1062 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc7");
1063 		return;
1064 	}
1065 
1066 	if (softnic_parser_read_uint32(&p.shaper_profile_id.tc[7], tokens[29]) != 0) {
1067 		snprintf(out, out_size, MSG_ARG_INVALID, "tc7 profile id");
1068 		return;
1069 	}
1070 
1071 	if (strcmp(tokens[30], "tc8") != 0) {
1072 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc8");
1073 		return;
1074 	}
1075 
1076 	if (softnic_parser_read_uint32(&p.shaper_profile_id.tc[8], tokens[31]) != 0) {
1077 		snprintf(out, out_size, MSG_ARG_INVALID, "tc8 profile id");
1078 		return;
1079 	}
1080 
1081 	if (strcmp(tokens[32], "tc9") != 0) {
1082 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc9");
1083 		return;
1084 	}
1085 
1086 	if (softnic_parser_read_uint32(&p.shaper_profile_id.tc[9], tokens[33]) != 0) {
1087 		snprintf(out, out_size, MSG_ARG_INVALID, "tc9 profile id");
1088 		return;
1089 	}
1090 
1091 	if (strcmp(tokens[34], "tc10") != 0) {
1092 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc10");
1093 		return;
1094 	}
1095 
1096 	if (softnic_parser_read_uint32(&p.shaper_profile_id.tc[10], tokens[35]) != 0) {
1097 		snprintf(out, out_size, MSG_ARG_INVALID, "tc10 profile id");
1098 		return;
1099 	}
1100 
1101 	if (strcmp(tokens[36], "tc11") != 0) {
1102 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc11");
1103 		return;
1104 	}
1105 
1106 	if (softnic_parser_read_uint32(&p.shaper_profile_id.tc[11], tokens[37]) != 0) {
1107 		snprintf(out, out_size, MSG_ARG_INVALID, "tc11 profile id");
1108 		return;
1109 	}
1110 
1111 	if (strcmp(tokens[38], "tc12") != 0) {
1112 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc12");
1113 		return;
1114 	}
1115 
1116 	if (softnic_parser_read_uint32(&p.shaper_profile_id.tc[12], tokens[39]) != 0) {
1117 		snprintf(out, out_size, MSG_ARG_INVALID, "tc12 profile id");
1118 		return;
1119 	}
1120 
1121 	/* Shared shaper */
1122 
1123 	if (strcmp(tokens[40], "shared") != 0) {
1124 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "shared");
1125 		return;
1126 	}
1127 
1128 	if (strcmp(tokens[41], "shaper") != 0) {
1129 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "shaper");
1130 		return;
1131 	}
1132 
1133 	if (strcmp(tokens[42], "tc0") != 0) {
1134 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc0");
1135 		return;
1136 	}
1137 
1138 	if (strcmp(tokens[43], "none") == 0)
1139 		p.shared_shaper_id.tc_valid[0] = 0;
1140 	else {
1141 		if (softnic_parser_read_uint32(&p.shared_shaper_id.tc[0],
1142 			tokens[43]) != 0) {
1143 			snprintf(out, out_size, MSG_ARG_INVALID, "shared shaper tc0");
1144 			return;
1145 		}
1146 
1147 		p.shared_shaper_id.tc_valid[0] = 1;
1148 	}
1149 
1150 	if (strcmp(tokens[44], "tc1") != 0) {
1151 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc1");
1152 		return;
1153 	}
1154 
1155 	if (strcmp(tokens[45], "none") == 0)
1156 		p.shared_shaper_id.tc_valid[1] = 0;
1157 	else {
1158 		if (softnic_parser_read_uint32(&p.shared_shaper_id.tc[1],
1159 			tokens[45]) != 0) {
1160 			snprintf(out, out_size, MSG_ARG_INVALID, "shared shaper tc1");
1161 			return;
1162 		}
1163 
1164 		p.shared_shaper_id.tc_valid[1] = 1;
1165 	}
1166 
1167 	if (strcmp(tokens[46], "tc2") != 0) {
1168 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc2");
1169 		return;
1170 	}
1171 
1172 	if (strcmp(tokens[47], "none") == 0)
1173 		p.shared_shaper_id.tc_valid[2] = 0;
1174 	else {
1175 		if (softnic_parser_read_uint32(&p.shared_shaper_id.tc[2],
1176 			tokens[47]) != 0) {
1177 			snprintf(out, out_size, MSG_ARG_INVALID, "shared shaper tc2");
1178 			return;
1179 		}
1180 
1181 		p.shared_shaper_id.tc_valid[2] = 1;
1182 	}
1183 
1184 	if (strcmp(tokens[48], "tc3") != 0) {
1185 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc3");
1186 		return;
1187 	}
1188 
1189 	if (strcmp(tokens[49], "none") == 0)
1190 		p.shared_shaper_id.tc_valid[3] = 0;
1191 	else {
1192 		if (softnic_parser_read_uint32(&p.shared_shaper_id.tc[3],
1193 			tokens[49]) != 0) {
1194 			snprintf(out, out_size, MSG_ARG_INVALID, "shared shaper tc3");
1195 			return;
1196 		}
1197 
1198 		p.shared_shaper_id.tc_valid[3] = 1;
1199 	}
1200 
1201 	if (strcmp(tokens[50], "tc4") != 0) {
1202 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc4");
1203 		return;
1204 	}
1205 
1206 	if (strcmp(tokens[51], "none") == 0) {
1207 		p.shared_shaper_id.tc_valid[4] = 0;
1208 	} else {
1209 		if (softnic_parser_read_uint32(&p.shared_shaper_id.tc[4],
1210 			tokens[51]) != 0) {
1211 			snprintf(out, out_size, MSG_ARG_INVALID, "shared shaper tc4");
1212 			return;
1213 		}
1214 
1215 		p.shared_shaper_id.tc_valid[4] = 1;
1216 	}
1217 
1218 	if (strcmp(tokens[52], "tc5") != 0) {
1219 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc5");
1220 		return;
1221 	}
1222 
1223 	if (strcmp(tokens[53], "none") == 0) {
1224 		p.shared_shaper_id.tc_valid[5] = 0;
1225 	} else {
1226 		if (softnic_parser_read_uint32(&p.shared_shaper_id.tc[5],
1227 			tokens[53]) != 0) {
1228 			snprintf(out, out_size, MSG_ARG_INVALID, "shared shaper tc5");
1229 			return;
1230 		}
1231 
1232 		p.shared_shaper_id.tc_valid[5] = 1;
1233 	}
1234 
1235 	if (strcmp(tokens[54], "tc6") != 0) {
1236 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc6");
1237 		return;
1238 	}
1239 
1240 	if (strcmp(tokens[55], "none") == 0) {
1241 		p.shared_shaper_id.tc_valid[6] = 0;
1242 	} else {
1243 		if (softnic_parser_read_uint32(&p.shared_shaper_id.tc[6],
1244 			tokens[55]) != 0) {
1245 			snprintf(out, out_size, MSG_ARG_INVALID, "shared shaper tc6");
1246 			return;
1247 		}
1248 
1249 		p.shared_shaper_id.tc_valid[6] = 1;
1250 	}
1251 
1252 	if (strcmp(tokens[56], "tc7") != 0) {
1253 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc7");
1254 		return;
1255 	}
1256 
1257 	if (strcmp(tokens[57], "none") == 0) {
1258 		p.shared_shaper_id.tc_valid[7] = 0;
1259 	} else {
1260 		if (softnic_parser_read_uint32(&p.shared_shaper_id.tc[7],
1261 			tokens[57]) != 0) {
1262 			snprintf(out, out_size, MSG_ARG_INVALID, "shared shaper tc7");
1263 			return;
1264 		}
1265 
1266 		p.shared_shaper_id.tc_valid[7] = 1;
1267 	}
1268 
1269 	if (strcmp(tokens[58], "tc8") != 0) {
1270 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc8");
1271 		return;
1272 	}
1273 
1274 	if (strcmp(tokens[59], "none") == 0) {
1275 		p.shared_shaper_id.tc_valid[8] = 0;
1276 	} else {
1277 		if (softnic_parser_read_uint32(&p.shared_shaper_id.tc[8],
1278 			tokens[59]) != 0) {
1279 			snprintf(out, out_size, MSG_ARG_INVALID, "shared shaper tc8");
1280 			return;
1281 		}
1282 
1283 		p.shared_shaper_id.tc_valid[8] = 1;
1284 	}
1285 
1286 	if (strcmp(tokens[60], "tc9") != 0) {
1287 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc9");
1288 		return;
1289 	}
1290 
1291 	if (strcmp(tokens[61], "none") == 0) {
1292 		p.shared_shaper_id.tc_valid[9] = 0;
1293 	} else {
1294 		if (softnic_parser_read_uint32(&p.shared_shaper_id.tc[9],
1295 			tokens[61]) != 0) {
1296 			snprintf(out, out_size, MSG_ARG_INVALID, "shared shaper tc9");
1297 			return;
1298 		}
1299 
1300 		p.shared_shaper_id.tc_valid[9] = 1;
1301 	}
1302 
1303 	if (strcmp(tokens[62], "tc10") != 0) {
1304 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc10");
1305 		return;
1306 	}
1307 
1308 	if (strcmp(tokens[63], "none") == 0) {
1309 		p.shared_shaper_id.tc_valid[10] = 0;
1310 	} else {
1311 		if (softnic_parser_read_uint32(&p.shared_shaper_id.tc[10],
1312 			tokens[63]) != 0) {
1313 			snprintf(out, out_size, MSG_ARG_INVALID, "shared shaper tc10");
1314 			return;
1315 		}
1316 
1317 		p.shared_shaper_id.tc_valid[10] = 1;
1318 	}
1319 
1320 	if (strcmp(tokens[64], "tc11") != 0) {
1321 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc11");
1322 		return;
1323 	}
1324 
1325 	if (strcmp(tokens[65], "none") == 0) {
1326 		p.shared_shaper_id.tc_valid[11] = 0;
1327 	} else {
1328 		if (softnic_parser_read_uint32(&p.shared_shaper_id.tc[11],
1329 			tokens[65]) != 0) {
1330 			snprintf(out, out_size, MSG_ARG_INVALID, "shared shaper tc11");
1331 			return;
1332 		}
1333 
1334 		p.shared_shaper_id.tc_valid[11] = 1;
1335 	}
1336 
1337 	if (strcmp(tokens[66], "tc12") != 0) {
1338 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc12");
1339 		return;
1340 	}
1341 
1342 	if (strcmp(tokens[67], "none") == 0) {
1343 		p.shared_shaper_id.tc_valid[12] = 0;
1344 	} else {
1345 		if (softnic_parser_read_uint32(&p.shared_shaper_id.tc[12],
1346 			tokens[67]) != 0) {
1347 			snprintf(out, out_size, MSG_ARG_INVALID, "shared shaper tc12");
1348 			return;
1349 		}
1350 
1351 		p.shared_shaper_id.tc_valid[12] = 1;
1352 	}
1353 
1354 	/* Weight */
1355 
1356 	if (strcmp(tokens[68], "weight") != 0) {
1357 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "weight");
1358 		return;
1359 	}
1360 
1361 	if (strcmp(tokens[69], "queue") != 0) {
1362 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "queue");
1363 		return;
1364 	}
1365 
1366 	for (i = 0, j = 0; i < 16; i++) {
1367 		if (i < RTE_SCHED_TRAFFIC_CLASS_BE) {
1368 			p.weight.queue[i] = 1;
1369 		} else {
1370 			if (softnic_parser_read_uint32(&p.weight.queue[i],
1371 				tokens[70 + j]) != 0) {
1372 				snprintf(out, out_size, MSG_ARG_INVALID, "weight queue");
1373 				return;
1374 			}
1375 			j++;
1376 		}
1377 	}
1378 
1379 	status = tmgr_hierarchy_default(softnic, &p);
1380 	if (status != 0) {
1381 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
1382 		return;
1383 	}
1384 }
1385 
1386 /**
1387  * tmgr hierarchy commit
1388  */
1389 static void
1390 cmd_tmgr_hierarchy_commit(struct pmd_internals *softnic,
1391 	char **tokens,
1392 	uint32_t n_tokens,
1393 	char *out,
1394 	size_t out_size)
1395 {
1396 	struct rte_tm_error error;
1397 	uint16_t port_id;
1398 	int status;
1399 
1400 	if (n_tokens != 3) {
1401 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1402 		return;
1403 	}
1404 
1405 	if (strcmp(tokens[1], "hierarchy") != 0) {
1406 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "hierarchy");
1407 		return;
1408 	}
1409 
1410 	if (strcmp(tokens[2], "commit") != 0) {
1411 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "commit");
1412 		return;
1413 	}
1414 
1415 	status = rte_eth_dev_get_port_by_name(softnic->params.name, &port_id);
1416 	if (status != 0)
1417 		return;
1418 
1419 	status = rte_tm_hierarchy_commit(port_id, 1, &error);
1420 	if (status) {
1421 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
1422 		return;
1423 	}
1424 }
1425 
1426 /**
1427  * tmgr <tmgr_name>
1428  */
1429 static void
1430 cmd_tmgr(struct pmd_internals *softnic,
1431 	char **tokens,
1432 	uint32_t n_tokens,
1433 	char *out,
1434 	size_t out_size)
1435 {
1436 	char *name;
1437 	struct softnic_tmgr_port *tmgr_port;
1438 
1439 	if (n_tokens != 2) {
1440 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1441 		return;
1442 	}
1443 
1444 	name = tokens[1];
1445 
1446 	tmgr_port = softnic_tmgr_port_create(softnic, name);
1447 	if (tmgr_port == NULL) {
1448 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
1449 		return;
1450 	}
1451 }
1452 
1453 /**
1454  * tap <tap_name>
1455  */
1456 static void
1457 cmd_tap(struct pmd_internals *softnic,
1458 	char **tokens,
1459 	uint32_t n_tokens,
1460 	char *out,
1461 	size_t out_size)
1462 {
1463 	char *name;
1464 	struct softnic_tap *tap;
1465 
1466 	if (n_tokens != 2) {
1467 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1468 		return;
1469 	}
1470 
1471 	name = tokens[1];
1472 
1473 	tap = softnic_tap_create(softnic, name);
1474 	if (tap == NULL) {
1475 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
1476 		return;
1477 	}
1478 }
1479 
1480 /**
1481  * cryptodev <tap_name> dev <device_name> | dev_id <device_id>
1482  * queue <n_queues> <queue_size> max_sessions <n_sessions>
1483  **/
1484 
1485 static void
1486 cmd_cryptodev(struct pmd_internals *softnic,
1487 		char **tokens,
1488 		uint32_t n_tokens,
1489 		char *out,
1490 		size_t out_size)
1491 {
1492 	struct softnic_cryptodev_params params;
1493 	char *name;
1494 
1495 	memset(&params, 0, sizeof(params));
1496 	if (n_tokens != 9) {
1497 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1498 		return;
1499 	}
1500 
1501 	name = tokens[1];
1502 
1503 	if (strcmp(tokens[2], "dev") == 0)
1504 		params.dev_name = tokens[3];
1505 	else if (strcmp(tokens[2], "dev_id") == 0) {
1506 		if (softnic_parser_read_uint32(&params.dev_id, tokens[3]) < 0) {
1507 			snprintf(out, out_size,	MSG_ARG_INVALID,
1508 				"dev_id");
1509 			return;
1510 		}
1511 	} else {
1512 		snprintf(out, out_size,	MSG_ARG_INVALID,
1513 			"cryptodev");
1514 		return;
1515 	}
1516 
1517 	if (strcmp(tokens[4], "queue")) {
1518 		snprintf(out, out_size,	MSG_ARG_NOT_FOUND,
1519 			"4");
1520 		return;
1521 	}
1522 
1523 	if (softnic_parser_read_uint32(&params.n_queues, tokens[5]) < 0) {
1524 		snprintf(out, out_size,	MSG_ARG_INVALID,
1525 			"q");
1526 		return;
1527 	}
1528 
1529 	if (softnic_parser_read_uint32(&params.queue_size, tokens[6]) < 0) {
1530 		snprintf(out, out_size,	MSG_ARG_INVALID,
1531 			"queue_size");
1532 		return;
1533 	}
1534 
1535 	if (strcmp(tokens[7], "max_sessions")) {
1536 		snprintf(out, out_size,	MSG_ARG_NOT_FOUND,
1537 			"4");
1538 		return;
1539 	}
1540 
1541 	if (softnic_parser_read_uint32(&params.session_pool_size, tokens[8])
1542 			< 0) {
1543 		snprintf(out, out_size,	MSG_ARG_INVALID,
1544 			"q");
1545 		return;
1546 	}
1547 
1548 	if (softnic_cryptodev_create(softnic, name, &params) == NULL) {
1549 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
1550 		return;
1551 	}
1552 }
1553 
1554 /**
1555  * port in action profile <profile_name>
1556  *  [filter match | mismatch offset <key_offset> mask <key_mask> key <key_value> port <port_id>]
1557  *  [balance offset <key_offset> mask <key_mask> port <port_id0> ... <port_id15>]
1558  */
1559 static void
1560 cmd_port_in_action_profile(struct pmd_internals *softnic,
1561 	char **tokens,
1562 	uint32_t n_tokens,
1563 	char *out,
1564 	size_t out_size)
1565 {
1566 	struct softnic_port_in_action_profile_params p;
1567 	struct softnic_port_in_action_profile *ap;
1568 	char *name;
1569 	uint32_t t0;
1570 
1571 	memset(&p, 0, sizeof(p));
1572 
1573 	if (n_tokens < 5) {
1574 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1575 		return;
1576 	}
1577 
1578 	if (strcmp(tokens[1], "in") != 0) {
1579 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
1580 		return;
1581 	}
1582 
1583 	if (strcmp(tokens[2], "action") != 0) {
1584 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "action");
1585 		return;
1586 	}
1587 
1588 	if (strcmp(tokens[3], "profile") != 0) {
1589 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
1590 		return;
1591 	}
1592 
1593 	name = tokens[4];
1594 
1595 	t0 = 5;
1596 
1597 	if (t0 < n_tokens &&
1598 		(strcmp(tokens[t0], "filter") == 0)) {
1599 		uint32_t size;
1600 
1601 		if (n_tokens < t0 + 10) {
1602 			snprintf(out, out_size, MSG_ARG_MISMATCH, "port in action profile filter");
1603 			return;
1604 		}
1605 
1606 		if (strcmp(tokens[t0 + 1], "match") == 0) {
1607 			p.fltr.filter_on_match = 1;
1608 		} else if (strcmp(tokens[t0 + 1], "mismatch") == 0) {
1609 			p.fltr.filter_on_match = 0;
1610 		} else {
1611 			snprintf(out, out_size, MSG_ARG_INVALID, "match or mismatch");
1612 			return;
1613 		}
1614 
1615 		if (strcmp(tokens[t0 + 2], "offset") != 0) {
1616 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
1617 			return;
1618 		}
1619 
1620 		if (softnic_parser_read_uint32(&p.fltr.key_offset,
1621 			tokens[t0 + 3]) != 0) {
1622 			snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
1623 			return;
1624 		}
1625 
1626 		if (strcmp(tokens[t0 + 4], "mask") != 0) {
1627 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mask");
1628 			return;
1629 		}
1630 
1631 		size = RTE_PORT_IN_ACTION_FLTR_KEY_SIZE;
1632 		if ((softnic_parse_hex_string(tokens[t0 + 5],
1633 			p.fltr.key_mask, &size) != 0) ||
1634 			size != RTE_PORT_IN_ACTION_FLTR_KEY_SIZE) {
1635 			snprintf(out, out_size, MSG_ARG_INVALID, "key_mask");
1636 			return;
1637 		}
1638 
1639 		if (strcmp(tokens[t0 + 6], "key") != 0) {
1640 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "key");
1641 			return;
1642 		}
1643 
1644 		size = RTE_PORT_IN_ACTION_FLTR_KEY_SIZE;
1645 		if ((softnic_parse_hex_string(tokens[t0 + 7],
1646 			p.fltr.key, &size) != 0) ||
1647 			size != RTE_PORT_IN_ACTION_FLTR_KEY_SIZE) {
1648 			snprintf(out, out_size, MSG_ARG_INVALID, "key_value");
1649 			return;
1650 		}
1651 
1652 		if (strcmp(tokens[t0 + 8], "port") != 0) {
1653 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
1654 			return;
1655 		}
1656 
1657 		if (softnic_parser_read_uint32(&p.fltr.port_id,
1658 			tokens[t0 + 9]) != 0) {
1659 			snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
1660 			return;
1661 		}
1662 
1663 		p.action_mask |= 1LLU << RTE_PORT_IN_ACTION_FLTR;
1664 		t0 += 10;
1665 	} /* filter */
1666 
1667 	if (t0 < n_tokens &&
1668 		(strcmp(tokens[t0], "balance") == 0)) {
1669 		uint32_t i;
1670 
1671 		if (n_tokens < t0 + 22) {
1672 			snprintf(out, out_size, MSG_ARG_MISMATCH,
1673 				"port in action profile balance");
1674 			return;
1675 		}
1676 
1677 		if (strcmp(tokens[t0 + 1], "offset") != 0) {
1678 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
1679 			return;
1680 		}
1681 
1682 		if (softnic_parser_read_uint32(&p.lb.key_offset,
1683 			tokens[t0 + 2]) != 0) {
1684 			snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
1685 			return;
1686 		}
1687 
1688 		if (strcmp(tokens[t0 + 3], "mask") != 0) {
1689 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mask");
1690 			return;
1691 		}
1692 
1693 		p.lb.key_size = RTE_PORT_IN_ACTION_LB_KEY_SIZE_MAX;
1694 		if (softnic_parse_hex_string(tokens[t0 + 4],
1695 			p.lb.key_mask, &p.lb.key_size) != 0) {
1696 			snprintf(out, out_size, MSG_ARG_INVALID, "key_mask");
1697 			return;
1698 		}
1699 
1700 		if (strcmp(tokens[t0 + 5], "port") != 0) {
1701 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
1702 			return;
1703 		}
1704 
1705 		for (i = 0; i < 16; i++)
1706 			if (softnic_parser_read_uint32(&p.lb.port_id[i],
1707 			tokens[t0 + 6 + i]) != 0) {
1708 				snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
1709 				return;
1710 			}
1711 
1712 		p.action_mask |= 1LLU << RTE_PORT_IN_ACTION_LB;
1713 		t0 += 22;
1714 	} /* balance */
1715 
1716 	if (t0 < n_tokens) {
1717 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1718 		return;
1719 	}
1720 
1721 	ap = softnic_port_in_action_profile_create(softnic, name, &p);
1722 	if (ap == NULL) {
1723 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
1724 		return;
1725 	}
1726 }
1727 
1728 /**
1729  * table action profile <profile_name>
1730  *  ipv4 | ipv6
1731  *  offset <ip_offset>
1732  *  fwd
1733  *  [balance offset <key_offset> mask <key_mask> outoffset <out_offset>]
1734  *  [meter srtcm | trtcm
1735  *      tc <n_tc>
1736  *      stats none | pkts | bytes | both]
1737  *  [tm spp <n_subports_per_port> pps <n_pipes_per_subport>]
1738  *  [encap ether | vlan | qinq | mpls | pppoe | qinq_pppoe |
1739  *      vxlan offset <ether_offset> ipv4 | ipv6 vlan on | off]
1740  *  [nat src | dst
1741  *      proto udp | tcp]
1742  *  [ttl drop | fwd
1743  *      stats none | pkts]
1744  *  [stats pkts | bytes | both]
1745  *  [time]
1746  *  [tag]
1747  *  [decap]
1748  *
1749  */
1750 static void
1751 cmd_table_action_profile(struct pmd_internals *softnic,
1752 	char **tokens,
1753 	uint32_t n_tokens,
1754 	char *out,
1755 	size_t out_size)
1756 {
1757 	struct softnic_table_action_profile_params p;
1758 	struct softnic_table_action_profile *ap;
1759 	char *name;
1760 	uint32_t t0;
1761 
1762 	memset(&p, 0, sizeof(p));
1763 
1764 	if (n_tokens < 8) {
1765 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1766 		return;
1767 	}
1768 
1769 	if (strcmp(tokens[1], "action") != 0) {
1770 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "action");
1771 		return;
1772 	}
1773 
1774 	if (strcmp(tokens[2], "profile") != 0) {
1775 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
1776 		return;
1777 	}
1778 
1779 	name = tokens[3];
1780 
1781 	if (strcmp(tokens[4], "ipv4") == 0) {
1782 		p.common.ip_version = 1;
1783 	} else if (strcmp(tokens[4], "ipv6") == 0) {
1784 		p.common.ip_version = 0;
1785 	} else {
1786 		snprintf(out, out_size, MSG_ARG_INVALID, "ipv4 or ipv6");
1787 		return;
1788 	}
1789 
1790 	if (strcmp(tokens[5], "offset") != 0) {
1791 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
1792 		return;
1793 	}
1794 
1795 	if (softnic_parser_read_uint32(&p.common.ip_offset,
1796 		tokens[6]) != 0) {
1797 		snprintf(out, out_size, MSG_ARG_INVALID, "ip_offset");
1798 		return;
1799 	}
1800 
1801 	if (strcmp(tokens[7], "fwd") != 0) {
1802 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "fwd");
1803 		return;
1804 	}
1805 
1806 	p.action_mask |= 1LLU << RTE_TABLE_ACTION_FWD;
1807 
1808 	t0 = 8;
1809 	if (t0 < n_tokens &&
1810 		(strcmp(tokens[t0], "balance") == 0)) {
1811 		if (n_tokens < t0 + 7) {
1812 			snprintf(out, out_size, MSG_ARG_MISMATCH, "table action profile balance");
1813 			return;
1814 		}
1815 
1816 		if (strcmp(tokens[t0 + 1], "offset") != 0) {
1817 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
1818 			return;
1819 		}
1820 
1821 		if (softnic_parser_read_uint32(&p.lb.key_offset,
1822 			tokens[t0 + 2]) != 0) {
1823 			snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
1824 			return;
1825 		}
1826 
1827 		if (strcmp(tokens[t0 + 3], "mask") != 0) {
1828 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mask");
1829 			return;
1830 		}
1831 
1832 		p.lb.key_size = RTE_PORT_IN_ACTION_LB_KEY_SIZE_MAX;
1833 		if (softnic_parse_hex_string(tokens[t0 + 4],
1834 			p.lb.key_mask, &p.lb.key_size) != 0) {
1835 			snprintf(out, out_size, MSG_ARG_INVALID, "key_mask");
1836 			return;
1837 		}
1838 
1839 		if (strcmp(tokens[t0 + 5], "outoffset") != 0) {
1840 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "outoffset");
1841 			return;
1842 		}
1843 
1844 		if (softnic_parser_read_uint32(&p.lb.out_offset,
1845 			tokens[t0 + 6]) != 0) {
1846 			snprintf(out, out_size, MSG_ARG_INVALID, "out_offset");
1847 			return;
1848 		}
1849 
1850 		p.action_mask |= 1LLU << RTE_TABLE_ACTION_LB;
1851 		t0 += 7;
1852 	} /* balance */
1853 
1854 	if (t0 < n_tokens &&
1855 		(strcmp(tokens[t0], "meter") == 0)) {
1856 		if (n_tokens < t0 + 6) {
1857 			snprintf(out, out_size, MSG_ARG_MISMATCH,
1858 				"table action profile meter");
1859 			return;
1860 		}
1861 
1862 		if (strcmp(tokens[t0 + 1], "srtcm") == 0) {
1863 			p.mtr.alg = RTE_TABLE_ACTION_METER_SRTCM;
1864 		} else if (strcmp(tokens[t0 + 1], "trtcm") == 0) {
1865 			p.mtr.alg = RTE_TABLE_ACTION_METER_TRTCM;
1866 		} else {
1867 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1868 				"srtcm or trtcm");
1869 			return;
1870 		}
1871 
1872 		if (strcmp(tokens[t0 + 2], "tc") != 0) {
1873 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc");
1874 			return;
1875 		}
1876 
1877 		if (softnic_parser_read_uint32(&p.mtr.n_tc,
1878 			tokens[t0 + 3]) != 0) {
1879 			snprintf(out, out_size, MSG_ARG_INVALID, "n_tc");
1880 			return;
1881 		}
1882 
1883 		if (strcmp(tokens[t0 + 4], "stats") != 0) {
1884 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
1885 			return;
1886 		}
1887 
1888 		if (strcmp(tokens[t0 + 5], "none") == 0) {
1889 			p.mtr.n_packets_enabled = 0;
1890 			p.mtr.n_bytes_enabled = 0;
1891 		} else if (strcmp(tokens[t0 + 5], "pkts") == 0) {
1892 			p.mtr.n_packets_enabled = 1;
1893 			p.mtr.n_bytes_enabled = 0;
1894 		} else if (strcmp(tokens[t0 + 5], "bytes") == 0) {
1895 			p.mtr.n_packets_enabled = 0;
1896 			p.mtr.n_bytes_enabled = 1;
1897 		} else if (strcmp(tokens[t0 + 5], "both") == 0) {
1898 			p.mtr.n_packets_enabled = 1;
1899 			p.mtr.n_bytes_enabled = 1;
1900 		} else {
1901 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1902 				"none or pkts or bytes or both");
1903 			return;
1904 		}
1905 
1906 		p.action_mask |= 1LLU << RTE_TABLE_ACTION_MTR;
1907 		t0 += 6;
1908 	} /* meter */
1909 
1910 	if (t0 < n_tokens &&
1911 		(strcmp(tokens[t0], "tm") == 0)) {
1912 		if (n_tokens < t0 + 5) {
1913 			snprintf(out, out_size, MSG_ARG_MISMATCH,
1914 				"table action profile tm");
1915 			return;
1916 		}
1917 
1918 		if (strcmp(tokens[t0 + 1], "spp") != 0) {
1919 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "spp");
1920 			return;
1921 		}
1922 
1923 		if (softnic_parser_read_uint32(&p.tm.n_subports_per_port,
1924 			tokens[t0 + 2]) != 0) {
1925 			snprintf(out, out_size, MSG_ARG_INVALID,
1926 				"n_subports_per_port");
1927 			return;
1928 		}
1929 
1930 		if (strcmp(tokens[t0 + 3], "pps") != 0) {
1931 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pps");
1932 			return;
1933 		}
1934 
1935 		if (softnic_parser_read_uint32(&p.tm.n_pipes_per_subport,
1936 			tokens[t0 + 4]) != 0) {
1937 			snprintf(out, out_size, MSG_ARG_INVALID,
1938 				"n_pipes_per_subport");
1939 			return;
1940 		}
1941 
1942 		p.action_mask |= 1LLU << RTE_TABLE_ACTION_TM;
1943 		t0 += 5;
1944 	} /* tm */
1945 
1946 	if (t0 < n_tokens &&
1947 		(strcmp(tokens[t0], "encap") == 0)) {
1948 		uint32_t n_extra_tokens = 0;
1949 
1950 		if (n_tokens < t0 + 2) {
1951 			snprintf(out, out_size, MSG_ARG_MISMATCH,
1952 				"action profile encap");
1953 			return;
1954 		}
1955 
1956 		if (strcmp(tokens[t0 + 1], "ether") == 0) {
1957 			p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_ETHER;
1958 		} else if (strcmp(tokens[t0 + 1], "vlan") == 0) {
1959 			p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_VLAN;
1960 		} else if (strcmp(tokens[t0 + 1], "qinq") == 0) {
1961 			p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_QINQ;
1962 		} else if (strcmp(tokens[t0 + 1], "mpls") == 0) {
1963 			p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_MPLS;
1964 		} else if (strcmp(tokens[t0 + 1], "pppoe") == 0) {
1965 			p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_PPPOE;
1966 		} else if (strcmp(tokens[t0 + 1], "vxlan") == 0) {
1967 			if (n_tokens < t0 + 2 + 5) {
1968 				snprintf(out, out_size, MSG_ARG_MISMATCH,
1969 					"action profile encap vxlan");
1970 				return;
1971 			}
1972 
1973 			if (strcmp(tokens[t0 + 2], "offset") != 0) {
1974 				snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1975 					"vxlan: offset");
1976 				return;
1977 			}
1978 
1979 			if (softnic_parser_read_uint32(&p.encap.vxlan.data_offset,
1980 				tokens[t0 + 2 + 1]) != 0) {
1981 				snprintf(out, out_size, MSG_ARG_INVALID,
1982 					"vxlan: ether_offset");
1983 				return;
1984 			}
1985 
1986 			if (strcmp(tokens[t0 + 2 + 2], "ipv4") == 0)
1987 				p.encap.vxlan.ip_version = 1;
1988 			else if (strcmp(tokens[t0 + 2 + 2], "ipv6") == 0)
1989 				p.encap.vxlan.ip_version = 0;
1990 			else {
1991 				snprintf(out, out_size, MSG_ARG_INVALID,
1992 					"vxlan: ipv4 or ipv6");
1993 				return;
1994 			}
1995 
1996 			if (strcmp(tokens[t0 + 2 + 3], "vlan") != 0) {
1997 				snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1998 					"vxlan: vlan");
1999 				return;
2000 			}
2001 
2002 			if (strcmp(tokens[t0 + 2 + 4], "on") == 0)
2003 				p.encap.vxlan.vlan = 1;
2004 			else if (strcmp(tokens[t0 + 2 + 4], "off") == 0)
2005 				p.encap.vxlan.vlan = 0;
2006 			else {
2007 				snprintf(out, out_size, MSG_ARG_INVALID,
2008 					"vxlan: on or off");
2009 				return;
2010 			}
2011 
2012 			p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_VXLAN;
2013 			n_extra_tokens = 5;
2014 
2015 		} else if (strcmp(tokens[t0 + 1], "qinq_pppoe") == 0) {
2016 			p.encap.encap_mask =
2017 				1LLU << RTE_TABLE_ACTION_ENCAP_QINQ_PPPOE;
2018 		} else {
2019 			snprintf(out, out_size, MSG_ARG_MISMATCH, "encap");
2020 			return;
2021 		}
2022 
2023 		p.action_mask |= 1LLU << RTE_TABLE_ACTION_ENCAP;
2024 		t0 += 2 + n_extra_tokens;
2025 	} /* encap */
2026 
2027 	if (t0 < n_tokens &&
2028 		(strcmp(tokens[t0], "nat") == 0)) {
2029 		if (n_tokens < t0 + 4) {
2030 			snprintf(out, out_size, MSG_ARG_MISMATCH,
2031 				"table action profile nat");
2032 			return;
2033 		}
2034 
2035 		if (strcmp(tokens[t0 + 1], "src") == 0) {
2036 			p.nat.source_nat = 1;
2037 		} else if (strcmp(tokens[t0 + 1], "dst") == 0) {
2038 			p.nat.source_nat = 0;
2039 		} else {
2040 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
2041 				"src or dst");
2042 			return;
2043 		}
2044 
2045 		if (strcmp(tokens[t0 + 2], "proto") != 0) {
2046 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "proto");
2047 			return;
2048 		}
2049 
2050 		if (strcmp(tokens[t0 + 3], "tcp") == 0) {
2051 			p.nat.proto = 0x06;
2052 		} else if (strcmp(tokens[t0 + 3], "udp") == 0) {
2053 			p.nat.proto = 0x11;
2054 		} else {
2055 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
2056 				"tcp or udp");
2057 			return;
2058 		}
2059 
2060 		p.action_mask |= 1LLU << RTE_TABLE_ACTION_NAT;
2061 		t0 += 4;
2062 	} /* nat */
2063 
2064 	if (t0 < n_tokens &&
2065 		(strcmp(tokens[t0], "ttl") == 0)) {
2066 		if (n_tokens < t0 + 4) {
2067 			snprintf(out, out_size, MSG_ARG_MISMATCH,
2068 				"table action profile ttl");
2069 			return;
2070 		}
2071 
2072 		if (strcmp(tokens[t0 + 1], "drop") == 0) {
2073 			p.ttl.drop = 1;
2074 		} else if (strcmp(tokens[t0 + 1], "fwd") == 0) {
2075 			p.ttl.drop = 0;
2076 		} else {
2077 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
2078 				"drop or fwd");
2079 			return;
2080 		}
2081 
2082 		if (strcmp(tokens[t0 + 2], "stats") != 0) {
2083 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
2084 			return;
2085 		}
2086 
2087 		if (strcmp(tokens[t0 + 3], "none") == 0) {
2088 			p.ttl.n_packets_enabled = 0;
2089 		} else if (strcmp(tokens[t0 + 3], "pkts") == 0) {
2090 			p.ttl.n_packets_enabled = 1;
2091 		} else {
2092 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
2093 				"none or pkts");
2094 			return;
2095 		}
2096 
2097 		p.action_mask |= 1LLU << RTE_TABLE_ACTION_TTL;
2098 		t0 += 4;
2099 	} /* ttl */
2100 
2101 	if (t0 < n_tokens &&
2102 		(strcmp(tokens[t0], "stats") == 0)) {
2103 		if (n_tokens < t0 + 2) {
2104 			snprintf(out, out_size, MSG_ARG_MISMATCH,
2105 				"table action profile stats");
2106 			return;
2107 		}
2108 
2109 		if (strcmp(tokens[t0 + 1], "pkts") == 0) {
2110 			p.stats.n_packets_enabled = 1;
2111 			p.stats.n_bytes_enabled = 0;
2112 		} else if (strcmp(tokens[t0 + 1], "bytes") == 0) {
2113 			p.stats.n_packets_enabled = 0;
2114 			p.stats.n_bytes_enabled = 1;
2115 		} else if (strcmp(tokens[t0 + 1], "both") == 0) {
2116 			p.stats.n_packets_enabled = 1;
2117 			p.stats.n_bytes_enabled = 1;
2118 		} else {
2119 			snprintf(out, out_size,	MSG_ARG_NOT_FOUND,
2120 				"pkts or bytes or both");
2121 			return;
2122 		}
2123 
2124 		p.action_mask |= 1LLU << RTE_TABLE_ACTION_STATS;
2125 		t0 += 2;
2126 	} /* stats */
2127 
2128 	if (t0 < n_tokens &&
2129 		(strcmp(tokens[t0], "time") == 0)) {
2130 		p.action_mask |= 1LLU << RTE_TABLE_ACTION_TIME;
2131 		t0 += 1;
2132 	} /* time */
2133 
2134 	if (t0 < n_tokens &&
2135 		(strcmp(tokens[t0], "tag") == 0)) {
2136 		p.action_mask |= 1LLU << RTE_TABLE_ACTION_TAG;
2137 		t0 += 1;
2138 	} /* tag */
2139 
2140 	if (t0 < n_tokens &&
2141 		(strcmp(tokens[t0], "decap") == 0)) {
2142 		p.action_mask |= 1LLU << RTE_TABLE_ACTION_DECAP;
2143 		t0 += 1;
2144 	} /* decap */
2145 
2146 	if (t0 < n_tokens && (strcmp(tokens[t0], "sym_crypto") == 0)) {
2147 		struct softnic_cryptodev *cryptodev;
2148 
2149 		if (n_tokens < t0 + 5 ||
2150 				strcmp(tokens[t0 + 1], "dev") ||
2151 				strcmp(tokens[t0 + 3], "offset")) {
2152 			snprintf(out, out_size, MSG_ARG_MISMATCH,
2153 				"table action profile sym_crypto");
2154 			return;
2155 		}
2156 
2157 		cryptodev = softnic_cryptodev_find(softnic, tokens[t0 + 2]);
2158 		if (cryptodev == NULL) {
2159 			snprintf(out, out_size, MSG_ARG_INVALID,
2160 				"table action profile sym_crypto");
2161 			return;
2162 		}
2163 
2164 		p.sym_crypto.cryptodev_id = cryptodev->dev_id;
2165 
2166 		if (softnic_parser_read_uint32(&p.sym_crypto.op_offset,
2167 				tokens[t0 + 4]) != 0) {
2168 			snprintf(out, out_size, MSG_ARG_INVALID,
2169 					"table action profile sym_crypto");
2170 			return;
2171 		}
2172 
2173 		p.sym_crypto.mp_create = cryptodev->mp_create;
2174 		p.sym_crypto.mp_init = cryptodev->mp_init;
2175 
2176 		p.action_mask |= 1LLU << RTE_TABLE_ACTION_SYM_CRYPTO;
2177 
2178 		t0 += 5;
2179 	} /* sym_crypto */
2180 
2181 	if (t0 < n_tokens) {
2182 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2183 		return;
2184 	}
2185 
2186 	ap = softnic_table_action_profile_create(softnic, name, &p);
2187 	if (ap == NULL) {
2188 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
2189 		return;
2190 	}
2191 }
2192 
2193 /**
2194  * pipeline <pipeline_name>
2195  *  period <timer_period_ms>
2196  *  offset_port_id <offset_port_id>
2197  */
2198 static void
2199 cmd_pipeline(struct pmd_internals *softnic,
2200 	char **tokens,
2201 	uint32_t n_tokens,
2202 	char *out,
2203 	size_t out_size)
2204 {
2205 	struct pipeline_params p;
2206 	char *name;
2207 	struct pipeline *pipeline;
2208 
2209 	if (n_tokens != 6) {
2210 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2211 		return;
2212 	}
2213 
2214 	name = tokens[1];
2215 
2216 	if (strcmp(tokens[2], "period") != 0) {
2217 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "period");
2218 		return;
2219 	}
2220 
2221 	if (softnic_parser_read_uint32(&p.timer_period_ms,
2222 		tokens[3]) != 0) {
2223 		snprintf(out, out_size, MSG_ARG_INVALID, "timer_period_ms");
2224 		return;
2225 	}
2226 
2227 	if (strcmp(tokens[4], "offset_port_id") != 0) {
2228 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset_port_id");
2229 		return;
2230 	}
2231 
2232 	if (softnic_parser_read_uint32(&p.offset_port_id,
2233 		tokens[5]) != 0) {
2234 		snprintf(out, out_size, MSG_ARG_INVALID, "offset_port_id");
2235 		return;
2236 	}
2237 
2238 	pipeline = softnic_pipeline_create(softnic, name, &p);
2239 	if (pipeline == NULL) {
2240 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
2241 		return;
2242 	}
2243 }
2244 
2245 /**
2246  * pipeline <pipeline_name> port in
2247  *  bsz <burst_size>
2248  *  link <link_name> rxq <queue_id>
2249  *  | swq <swq_name>
2250  *  | tmgr <tmgr_name>
2251  *  | tap <tap_name> mempool <mempool_name> mtu <mtu>
2252  *  | source mempool <mempool_name> file <file_name> bpp <n_bytes_per_pkt>
2253  *  | cryptodev <cryptodev_name> rxq <queue_id>
2254  *  [action <port_in_action_profile_name>]
2255  *  [disabled]
2256  */
2257 static void
2258 cmd_pipeline_port_in(struct pmd_internals *softnic,
2259 	char **tokens,
2260 	uint32_t n_tokens,
2261 	char *out,
2262 	size_t out_size)
2263 {
2264 	struct softnic_port_in_params p;
2265 	char *pipeline_name;
2266 	uint32_t t0;
2267 	int enabled, status;
2268 
2269 	memset(&p, 0, sizeof(p));
2270 
2271 	if (n_tokens < 7) {
2272 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2273 		return;
2274 	}
2275 
2276 	pipeline_name = tokens[1];
2277 
2278 	if (strcmp(tokens[2], "port") != 0) {
2279 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
2280 		return;
2281 	}
2282 
2283 	if (strcmp(tokens[3], "in") != 0) {
2284 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
2285 		return;
2286 	}
2287 
2288 	if (strcmp(tokens[4], "bsz") != 0) {
2289 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
2290 		return;
2291 	}
2292 
2293 	if (softnic_parser_read_uint32(&p.burst_size, tokens[5]) != 0) {
2294 		snprintf(out, out_size, MSG_ARG_INVALID, "burst_size");
2295 		return;
2296 	}
2297 
2298 	t0 = 6;
2299 
2300 	if (strcmp(tokens[t0], "link") == 0) {
2301 		if (n_tokens < t0 + 4) {
2302 			snprintf(out, out_size, MSG_ARG_MISMATCH,
2303 				"pipeline port in link");
2304 			return;
2305 		}
2306 
2307 		p.type = PORT_IN_RXQ;
2308 
2309 		strlcpy(p.dev_name, tokens[t0 + 1], sizeof(p.dev_name));
2310 
2311 		if (strcmp(tokens[t0 + 2], "rxq") != 0) {
2312 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq");
2313 			return;
2314 		}
2315 
2316 		if (softnic_parser_read_uint16(&p.rxq.queue_id,
2317 			tokens[t0 + 3]) != 0) {
2318 			snprintf(out, out_size, MSG_ARG_INVALID,
2319 				"queue_id");
2320 			return;
2321 		}
2322 		t0 += 4;
2323 	} else if (strcmp(tokens[t0], "swq") == 0) {
2324 		if (n_tokens < t0 + 2) {
2325 			snprintf(out, out_size, MSG_ARG_MISMATCH,
2326 				"pipeline port in swq");
2327 			return;
2328 		}
2329 
2330 		p.type = PORT_IN_SWQ;
2331 
2332 		strlcpy(p.dev_name, tokens[t0 + 1], sizeof(p.dev_name));
2333 
2334 		t0 += 2;
2335 	} else if (strcmp(tokens[t0], "tmgr") == 0) {
2336 		if (n_tokens < t0 + 2) {
2337 			snprintf(out, out_size, MSG_ARG_MISMATCH,
2338 				"pipeline port in tmgr");
2339 			return;
2340 		}
2341 
2342 		p.type = PORT_IN_TMGR;
2343 
2344 		strlcpy(p.dev_name, tokens[t0 + 1], sizeof(p.dev_name));
2345 
2346 		t0 += 2;
2347 	} else if (strcmp(tokens[t0], "tap") == 0) {
2348 		if (n_tokens < t0 + 6) {
2349 			snprintf(out, out_size, MSG_ARG_MISMATCH,
2350 				"pipeline port in tap");
2351 			return;
2352 		}
2353 
2354 		p.type = PORT_IN_TAP;
2355 
2356 		strlcpy(p.dev_name, tokens[t0 + 1], sizeof(p.dev_name));
2357 
2358 		if (strcmp(tokens[t0 + 2], "mempool") != 0) {
2359 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
2360 				"mempool");
2361 			return;
2362 		}
2363 
2364 		p.tap.mempool_name = tokens[t0 + 3];
2365 
2366 		if (strcmp(tokens[t0 + 4], "mtu") != 0) {
2367 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
2368 				"mtu");
2369 			return;
2370 		}
2371 
2372 		if (softnic_parser_read_uint32(&p.tap.mtu,
2373 			tokens[t0 + 5]) != 0) {
2374 			snprintf(out, out_size, MSG_ARG_INVALID, "mtu");
2375 			return;
2376 		}
2377 
2378 		t0 += 6;
2379 	} else if (strcmp(tokens[t0], "source") == 0) {
2380 		if (n_tokens < t0 + 6) {
2381 			snprintf(out, out_size, MSG_ARG_MISMATCH,
2382 				"pipeline port in source");
2383 			return;
2384 		}
2385 
2386 		p.type = PORT_IN_SOURCE;
2387 
2388 		if (strcmp(tokens[t0 + 1], "mempool") != 0) {
2389 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
2390 				"mempool");
2391 			return;
2392 		}
2393 
2394 		p.source.mempool_name = tokens[t0 + 2];
2395 
2396 		if (strcmp(tokens[t0 + 3], "file") != 0) {
2397 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
2398 				"file");
2399 			return;
2400 		}
2401 
2402 		p.source.file_name = tokens[t0 + 4];
2403 
2404 		if (strcmp(tokens[t0 + 5], "bpp") != 0) {
2405 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
2406 				"bpp");
2407 			return;
2408 		}
2409 
2410 		if (softnic_parser_read_uint32(&p.source.n_bytes_per_pkt,
2411 			tokens[t0 + 6]) != 0) {
2412 			snprintf(out, out_size, MSG_ARG_INVALID,
2413 				"n_bytes_per_pkt");
2414 			return;
2415 		}
2416 
2417 		t0 += 7;
2418 	} else if (strcmp(tokens[t0], "cryptodev") == 0) {
2419 		if (n_tokens < t0 + 3) {
2420 			snprintf(out, out_size, MSG_ARG_MISMATCH,
2421 				"pipeline port in cryptodev");
2422 			return;
2423 		}
2424 
2425 		p.type = PORT_IN_CRYPTODEV;
2426 
2427 		strlcpy(p.dev_name, tokens[t0 + 1], sizeof(p.dev_name));
2428 		if (softnic_parser_read_uint16(&p.rxq.queue_id,
2429 				tokens[t0 + 3]) != 0) {
2430 			snprintf(out, out_size, MSG_ARG_INVALID,
2431 				"rxq");
2432 			return;
2433 		}
2434 
2435 		p.cryptodev.arg_callback = NULL;
2436 		p.cryptodev.f_callback = NULL;
2437 
2438 		t0 += 4;
2439 	} else {
2440 		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
2441 		return;
2442 	}
2443 
2444 	if (n_tokens > t0 &&
2445 		(strcmp(tokens[t0], "action") == 0)) {
2446 		if (n_tokens < t0 + 2) {
2447 			snprintf(out, out_size, MSG_ARG_MISMATCH, "action");
2448 			return;
2449 		}
2450 
2451 		strlcpy(p.action_profile_name, tokens[t0 + 1],
2452 			sizeof(p.action_profile_name));
2453 
2454 		t0 += 2;
2455 	}
2456 
2457 	enabled = 1;
2458 	if (n_tokens > t0 &&
2459 		(strcmp(tokens[t0], "disabled") == 0)) {
2460 		enabled = 0;
2461 
2462 		t0 += 1;
2463 	}
2464 
2465 	if (n_tokens != t0) {
2466 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2467 		return;
2468 	}
2469 
2470 	status = softnic_pipeline_port_in_create(softnic,
2471 		pipeline_name,
2472 		&p,
2473 		enabled);
2474 	if (status) {
2475 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
2476 		return;
2477 	}
2478 }
2479 
2480 /**
2481  * pipeline <pipeline_name> port out
2482  *  bsz <burst_size>
2483  *  link <link_name> txq <txq_id>
2484  *  | swq <swq_name>
2485  *  | tmgr <tmgr_name>
2486  *  | tap <tap_name>
2487  *  | sink [file <file_name> pkts <max_n_pkts>]
2488  *  | cryptodev <cryptodev_name> txq <txq_id> offset <crypto_op_offset>
2489  */
2490 static void
2491 cmd_pipeline_port_out(struct pmd_internals *softnic,
2492 	char **tokens,
2493 	uint32_t n_tokens,
2494 	char *out,
2495 	size_t out_size)
2496 {
2497 	struct softnic_port_out_params p;
2498 	char *pipeline_name;
2499 	int status;
2500 
2501 	memset(&p, 0, sizeof(p));
2502 
2503 	if (n_tokens < 7) {
2504 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2505 		return;
2506 	}
2507 
2508 	pipeline_name = tokens[1];
2509 
2510 	if (strcmp(tokens[2], "port") != 0) {
2511 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
2512 		return;
2513 	}
2514 
2515 	if (strcmp(tokens[3], "out") != 0) {
2516 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "out");
2517 		return;
2518 	}
2519 
2520 	if (strcmp(tokens[4], "bsz") != 0) {
2521 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
2522 		return;
2523 	}
2524 
2525 	if (softnic_parser_read_uint32(&p.burst_size, tokens[5]) != 0) {
2526 		snprintf(out, out_size, MSG_ARG_INVALID, "burst_size");
2527 		return;
2528 	}
2529 
2530 	if (strcmp(tokens[6], "link") == 0) {
2531 		if (n_tokens != 10) {
2532 			snprintf(out, out_size, MSG_ARG_MISMATCH,
2533 				"pipeline port out link");
2534 			return;
2535 		}
2536 
2537 		p.type = PORT_OUT_TXQ;
2538 
2539 		strlcpy(p.dev_name, tokens[7], sizeof(p.dev_name));
2540 
2541 		if (strcmp(tokens[8], "txq") != 0) {
2542 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "txq");
2543 			return;
2544 		}
2545 
2546 		if (softnic_parser_read_uint16(&p.txq.queue_id,
2547 			tokens[9]) != 0) {
2548 			snprintf(out, out_size, MSG_ARG_INVALID, "queue_id");
2549 			return;
2550 		}
2551 	} else if (strcmp(tokens[6], "swq") == 0) {
2552 		if (n_tokens != 8) {
2553 			snprintf(out, out_size, MSG_ARG_MISMATCH,
2554 				"pipeline port out swq");
2555 			return;
2556 		}
2557 
2558 		p.type = PORT_OUT_SWQ;
2559 
2560 		strlcpy(p.dev_name, tokens[7], sizeof(p.dev_name));
2561 	} else if (strcmp(tokens[6], "tmgr") == 0) {
2562 		if (n_tokens != 8) {
2563 			snprintf(out, out_size, MSG_ARG_MISMATCH,
2564 				"pipeline port out tmgr");
2565 			return;
2566 		}
2567 
2568 		p.type = PORT_OUT_TMGR;
2569 
2570 		strlcpy(p.dev_name, tokens[7], sizeof(p.dev_name));
2571 	} else if (strcmp(tokens[6], "tap") == 0) {
2572 		if (n_tokens != 8) {
2573 			snprintf(out, out_size, MSG_ARG_MISMATCH,
2574 				"pipeline port out tap");
2575 			return;
2576 		}
2577 
2578 		p.type = PORT_OUT_TAP;
2579 
2580 		strlcpy(p.dev_name, tokens[7], sizeof(p.dev_name));
2581 	} else if (strcmp(tokens[6], "sink") == 0) {
2582 		if ((n_tokens != 7) && (n_tokens != 11)) {
2583 			snprintf(out, out_size, MSG_ARG_MISMATCH,
2584 				"pipeline port out sink");
2585 			return;
2586 		}
2587 
2588 		p.type = PORT_OUT_SINK;
2589 
2590 		if (n_tokens == 7) {
2591 			p.sink.file_name = NULL;
2592 			p.sink.max_n_pkts = 0;
2593 		} else {
2594 			if (strcmp(tokens[7], "file") != 0) {
2595 				snprintf(out, out_size, MSG_ARG_NOT_FOUND,
2596 					"file");
2597 				return;
2598 			}
2599 
2600 			p.sink.file_name = tokens[8];
2601 
2602 			if (strcmp(tokens[9], "pkts") != 0) {
2603 				snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pkts");
2604 				return;
2605 			}
2606 
2607 			if (softnic_parser_read_uint32(&p.sink.max_n_pkts,
2608 				tokens[10]) != 0) {
2609 				snprintf(out, out_size, MSG_ARG_INVALID, "max_n_pkts");
2610 				return;
2611 			}
2612 		}
2613 	} else if (strcmp(tokens[6], "cryptodev") == 0) {
2614 		if (n_tokens != 12) {
2615 			snprintf(out, out_size, MSG_ARG_MISMATCH,
2616 				"pipeline port out cryptodev");
2617 			return;
2618 		}
2619 
2620 		p.type = PORT_OUT_CRYPTODEV;
2621 
2622 		strlcpy(p.dev_name, tokens[7], sizeof(p.dev_name));
2623 
2624 		if (strcmp(tokens[8], "txq")) {
2625 			snprintf(out, out_size, MSG_ARG_MISMATCH,
2626 				"pipeline port out cryptodev");
2627 			return;
2628 		}
2629 
2630 		if (softnic_parser_read_uint16(&p.cryptodev.queue_id, tokens[9])
2631 				!= 0) {
2632 			snprintf(out, out_size, MSG_ARG_INVALID, "queue_id");
2633 			return;
2634 		}
2635 
2636 		if (strcmp(tokens[10], "offset")) {
2637 			snprintf(out, out_size, MSG_ARG_MISMATCH,
2638 				"pipeline port out cryptodev");
2639 			return;
2640 		}
2641 
2642 		if (softnic_parser_read_uint32(&p.cryptodev.op_offset,
2643 				tokens[11]) != 0) {
2644 			snprintf(out, out_size, MSG_ARG_INVALID, "queue_id");
2645 			return;
2646 		}
2647 	} else {
2648 		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
2649 		return;
2650 	}
2651 
2652 	status = softnic_pipeline_port_out_create(softnic, pipeline_name, &p);
2653 	if (status) {
2654 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
2655 		return;
2656 	}
2657 }
2658 
2659 /**
2660  * pipeline <pipeline_name> table
2661  *      match
2662  *      acl
2663  *          ipv4 | ipv6
2664  *          offset <ip_header_offset>
2665  *          size <n_rules>
2666  *      | array
2667  *          offset <key_offset>
2668  *          size <n_keys>
2669  *      | hash
2670  *          ext | lru
2671  *          key <key_size>
2672  *          mask <key_mask>
2673  *          offset <key_offset>
2674  *          buckets <n_buckets>
2675  *          size <n_keys>
2676  *      | lpm
2677  *          ipv4 | ipv6
2678  *          offset <ip_header_offset>
2679  *          size <n_rules>
2680  *      | stub
2681  *  [action <table_action_profile_name>]
2682  */
2683 static void
2684 cmd_pipeline_table(struct pmd_internals *softnic,
2685 	char **tokens,
2686 	uint32_t n_tokens,
2687 	char *out,
2688 	size_t out_size)
2689 {
2690 	struct softnic_table_params p;
2691 	char *pipeline_name;
2692 	uint32_t t0;
2693 	int status;
2694 
2695 	memset(&p, 0, sizeof(p));
2696 
2697 	if (n_tokens < 5) {
2698 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2699 		return;
2700 	}
2701 
2702 	pipeline_name = tokens[1];
2703 
2704 	if (strcmp(tokens[2], "table") != 0) {
2705 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
2706 		return;
2707 	}
2708 
2709 	if (strcmp(tokens[3], "match") != 0) {
2710 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "match");
2711 		return;
2712 	}
2713 
2714 	t0 = 4;
2715 	if (strcmp(tokens[t0], "acl") == 0) {
2716 		if (n_tokens < t0 + 6) {
2717 			snprintf(out, out_size, MSG_ARG_MISMATCH,
2718 				"pipeline table acl");
2719 			return;
2720 		}
2721 
2722 		p.match_type = TABLE_ACL;
2723 
2724 		if (strcmp(tokens[t0 + 1], "ipv4") == 0) {
2725 			p.match.acl.ip_version = 1;
2726 		} else if (strcmp(tokens[t0 + 1], "ipv6") == 0) {
2727 			p.match.acl.ip_version = 0;
2728 		} else {
2729 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
2730 				"ipv4 or ipv6");
2731 			return;
2732 		}
2733 
2734 		if (strcmp(tokens[t0 + 2], "offset") != 0) {
2735 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
2736 			return;
2737 		}
2738 
2739 		if (softnic_parser_read_uint32(&p.match.acl.ip_header_offset,
2740 			tokens[t0 + 3]) != 0) {
2741 			snprintf(out, out_size, MSG_ARG_INVALID,
2742 				"ip_header_offset");
2743 			return;
2744 		}
2745 
2746 		if (strcmp(tokens[t0 + 4], "size") != 0) {
2747 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
2748 			return;
2749 		}
2750 
2751 		if (softnic_parser_read_uint32(&p.match.acl.n_rules,
2752 			tokens[t0 + 5]) != 0) {
2753 			snprintf(out, out_size, MSG_ARG_INVALID, "n_rules");
2754 			return;
2755 		}
2756 
2757 		t0 += 6;
2758 	} else if (strcmp(tokens[t0], "array") == 0) {
2759 		if (n_tokens < t0 + 5) {
2760 			snprintf(out, out_size, MSG_ARG_MISMATCH,
2761 				"pipeline table array");
2762 			return;
2763 		}
2764 
2765 		p.match_type = TABLE_ARRAY;
2766 
2767 		if (strcmp(tokens[t0 + 1], "offset") != 0) {
2768 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
2769 			return;
2770 		}
2771 
2772 		if (softnic_parser_read_uint32(&p.match.array.key_offset,
2773 			tokens[t0 + 2]) != 0) {
2774 			snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
2775 			return;
2776 		}
2777 
2778 		if (strcmp(tokens[t0 + 3], "size") != 0) {
2779 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
2780 			return;
2781 		}
2782 
2783 		if (softnic_parser_read_uint32(&p.match.array.n_keys,
2784 			tokens[t0 + 4]) != 0) {
2785 			snprintf(out, out_size, MSG_ARG_INVALID, "n_keys");
2786 			return;
2787 		}
2788 
2789 		t0 += 5;
2790 	} else if (strcmp(tokens[t0], "hash") == 0) {
2791 		uint32_t key_mask_size = TABLE_RULE_MATCH_SIZE_MAX;
2792 
2793 		if (n_tokens < t0 + 12) {
2794 			snprintf(out, out_size, MSG_ARG_MISMATCH,
2795 				"pipeline table hash");
2796 			return;
2797 		}
2798 
2799 		p.match_type = TABLE_HASH;
2800 
2801 		if (strcmp(tokens[t0 + 1], "ext") == 0) {
2802 			p.match.hash.extendable_bucket = 1;
2803 		} else if (strcmp(tokens[t0 + 1], "lru") == 0) {
2804 			p.match.hash.extendable_bucket = 0;
2805 		} else {
2806 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
2807 				"ext or lru");
2808 			return;
2809 		}
2810 
2811 		if (strcmp(tokens[t0 + 2], "key") != 0) {
2812 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "key");
2813 			return;
2814 		}
2815 
2816 		if ((softnic_parser_read_uint32(&p.match.hash.key_size,
2817 			tokens[t0 + 3]) != 0) ||
2818 			p.match.hash.key_size == 0 ||
2819 			p.match.hash.key_size > TABLE_RULE_MATCH_SIZE_MAX) {
2820 			snprintf(out, out_size, MSG_ARG_INVALID, "key_size");
2821 			return;
2822 		}
2823 
2824 		if (strcmp(tokens[t0 + 4], "mask") != 0) {
2825 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mask");
2826 			return;
2827 		}
2828 
2829 		if ((softnic_parse_hex_string(tokens[t0 + 5],
2830 			p.match.hash.key_mask, &key_mask_size) != 0) ||
2831 			key_mask_size != p.match.hash.key_size) {
2832 			snprintf(out, out_size, MSG_ARG_INVALID, "key_mask");
2833 			return;
2834 		}
2835 
2836 		if (strcmp(tokens[t0 + 6], "offset") != 0) {
2837 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
2838 			return;
2839 		}
2840 
2841 		if (softnic_parser_read_uint32(&p.match.hash.key_offset,
2842 			tokens[t0 + 7]) != 0) {
2843 			snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
2844 			return;
2845 		}
2846 
2847 		if (strcmp(tokens[t0 + 8], "buckets") != 0) {
2848 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "buckets");
2849 			return;
2850 		}
2851 
2852 		if (softnic_parser_read_uint32(&p.match.hash.n_buckets,
2853 			tokens[t0 + 9]) != 0) {
2854 			snprintf(out, out_size, MSG_ARG_INVALID, "n_buckets");
2855 			return;
2856 		}
2857 
2858 		if (strcmp(tokens[t0 + 10], "size") != 0) {
2859 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
2860 			return;
2861 		}
2862 
2863 		if (softnic_parser_read_uint32(&p.match.hash.n_keys,
2864 			tokens[t0 + 11]) != 0) {
2865 			snprintf(out, out_size, MSG_ARG_INVALID, "n_keys");
2866 			return;
2867 		}
2868 
2869 		t0 += 12;
2870 	} else if (strcmp(tokens[t0], "lpm") == 0) {
2871 		if (n_tokens < t0 + 6) {
2872 			snprintf(out, out_size, MSG_ARG_MISMATCH,
2873 				"pipeline table lpm");
2874 			return;
2875 		}
2876 
2877 		p.match_type = TABLE_LPM;
2878 
2879 		if (strcmp(tokens[t0 + 1], "ipv4") == 0) {
2880 			p.match.lpm.key_size = 4;
2881 		} else if (strcmp(tokens[t0 + 1], "ipv6") == 0) {
2882 			p.match.lpm.key_size = 16;
2883 		} else {
2884 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
2885 				"ipv4 or ipv6");
2886 			return;
2887 		}
2888 
2889 		if (strcmp(tokens[t0 + 2], "offset") != 0) {
2890 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
2891 			return;
2892 		}
2893 
2894 		if (softnic_parser_read_uint32(&p.match.lpm.key_offset,
2895 			tokens[t0 + 3]) != 0) {
2896 			snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
2897 			return;
2898 		}
2899 
2900 		if (strcmp(tokens[t0 + 4], "size") != 0) {
2901 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
2902 			return;
2903 		}
2904 
2905 		if (softnic_parser_read_uint32(&p.match.lpm.n_rules,
2906 			tokens[t0 + 5]) != 0) {
2907 			snprintf(out, out_size, MSG_ARG_INVALID, "n_rules");
2908 			return;
2909 		}
2910 
2911 		t0 += 6;
2912 	} else if (strcmp(tokens[t0], "stub") == 0) {
2913 		p.match_type = TABLE_STUB;
2914 
2915 		t0 += 1;
2916 	} else {
2917 		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
2918 		return;
2919 	}
2920 
2921 	if (n_tokens > t0 &&
2922 		(strcmp(tokens[t0], "action") == 0)) {
2923 		if (n_tokens < t0 + 2) {
2924 			snprintf(out, out_size, MSG_ARG_MISMATCH, "action");
2925 			return;
2926 		}
2927 
2928 		strlcpy(p.action_profile_name, tokens[t0 + 1],
2929 			sizeof(p.action_profile_name));
2930 
2931 		t0 += 2;
2932 	}
2933 
2934 	if (n_tokens > t0) {
2935 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2936 		return;
2937 	}
2938 
2939 	status = softnic_pipeline_table_create(softnic, pipeline_name, &p);
2940 	if (status) {
2941 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
2942 		return;
2943 	}
2944 }
2945 
2946 /**
2947  * pipeline <pipeline_name> port in <port_id> table <table_id>
2948  */
2949 static void
2950 cmd_pipeline_port_in_table(struct pmd_internals *softnic,
2951 	char **tokens,
2952 	uint32_t n_tokens,
2953 	char *out,
2954 	size_t out_size)
2955 {
2956 	char *pipeline_name;
2957 	uint32_t port_id, table_id;
2958 	int status;
2959 
2960 	if (n_tokens != 7) {
2961 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2962 		return;
2963 	}
2964 
2965 	pipeline_name = tokens[1];
2966 
2967 	if (strcmp(tokens[2], "port") != 0) {
2968 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
2969 		return;
2970 	}
2971 
2972 	if (strcmp(tokens[3], "in") != 0) {
2973 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
2974 		return;
2975 	}
2976 
2977 	if (softnic_parser_read_uint32(&port_id, tokens[4]) != 0) {
2978 		snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
2979 		return;
2980 	}
2981 
2982 	if (strcmp(tokens[5], "table") != 0) {
2983 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
2984 		return;
2985 	}
2986 
2987 	if (softnic_parser_read_uint32(&table_id, tokens[6]) != 0) {
2988 		snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
2989 		return;
2990 	}
2991 
2992 	status = softnic_pipeline_port_in_connect_to_table(softnic,
2993 		pipeline_name,
2994 		port_id,
2995 		table_id);
2996 	if (status) {
2997 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
2998 		return;
2999 	}
3000 }
3001 
3002 /**
3003  * pipeline <pipeline_name> port in <port_id> stats read [clear]
3004  */
3005 
3006 #define MSG_PIPELINE_PORT_IN_STATS                         \
3007 	"Pkts in: %" PRIu64 "\n"                           \
3008 	"Pkts dropped by AH: %" PRIu64 "\n"                \
3009 	"Pkts dropped by other: %" PRIu64 "\n"
3010 
3011 static void
3012 cmd_pipeline_port_in_stats(struct pmd_internals *softnic,
3013 	char **tokens,
3014 	uint32_t n_tokens,
3015 	char *out,
3016 	size_t out_size)
3017 {
3018 	struct rte_pipeline_port_in_stats stats;
3019 	char *pipeline_name;
3020 	uint32_t port_id;
3021 	int clear, status;
3022 
3023 	if (n_tokens != 7 &&
3024 		n_tokens != 8) {
3025 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3026 		return;
3027 	}
3028 
3029 	pipeline_name = tokens[1];
3030 
3031 	if (strcmp(tokens[2], "port") != 0) {
3032 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
3033 		return;
3034 	}
3035 
3036 	if (strcmp(tokens[3], "in") != 0) {
3037 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
3038 		return;
3039 	}
3040 
3041 	if (softnic_parser_read_uint32(&port_id, tokens[4]) != 0) {
3042 		snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
3043 		return;
3044 	}
3045 
3046 	if (strcmp(tokens[5], "stats") != 0) {
3047 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
3048 		return;
3049 	}
3050 
3051 	if (strcmp(tokens[6], "read") != 0) {
3052 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "read");
3053 		return;
3054 	}
3055 
3056 	clear = 0;
3057 	if (n_tokens == 8) {
3058 		if (strcmp(tokens[7], "clear") != 0) {
3059 			snprintf(out, out_size, MSG_ARG_INVALID, "clear");
3060 			return;
3061 		}
3062 
3063 		clear = 1;
3064 	}
3065 
3066 	status = softnic_pipeline_port_in_stats_read(softnic,
3067 		pipeline_name,
3068 		port_id,
3069 		&stats,
3070 		clear);
3071 	if (status) {
3072 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
3073 		return;
3074 	}
3075 
3076 	snprintf(out, out_size, MSG_PIPELINE_PORT_IN_STATS,
3077 		stats.stats.n_pkts_in,
3078 		stats.n_pkts_dropped_by_ah,
3079 		stats.stats.n_pkts_drop);
3080 }
3081 
3082 /**
3083  * pipeline <pipeline_name> port in <port_id> enable
3084  */
3085 static void
3086 cmd_softnic_pipeline_port_in_enable(struct pmd_internals *softnic,
3087 	char **tokens,
3088 	uint32_t n_tokens,
3089 	char *out,
3090 	size_t out_size)
3091 {
3092 	char *pipeline_name;
3093 	uint32_t port_id;
3094 	int status;
3095 
3096 	if (n_tokens != 6) {
3097 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3098 		return;
3099 	}
3100 
3101 	pipeline_name = tokens[1];
3102 
3103 	if (strcmp(tokens[2], "port") != 0) {
3104 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
3105 		return;
3106 	}
3107 
3108 	if (strcmp(tokens[3], "in") != 0) {
3109 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
3110 		return;
3111 	}
3112 
3113 	if (softnic_parser_read_uint32(&port_id, tokens[4]) != 0) {
3114 		snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
3115 		return;
3116 	}
3117 
3118 	if (strcmp(tokens[5], "enable") != 0) {
3119 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
3120 		return;
3121 	}
3122 
3123 	status = softnic_pipeline_port_in_enable(softnic, pipeline_name, port_id);
3124 	if (status) {
3125 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
3126 		return;
3127 	}
3128 }
3129 
3130 /**
3131  * pipeline <pipeline_name> port in <port_id> disable
3132  */
3133 static void
3134 cmd_softnic_pipeline_port_in_disable(struct pmd_internals *softnic,
3135 	char **tokens,
3136 	uint32_t n_tokens,
3137 	char *out,
3138 	size_t out_size)
3139 {
3140 	char *pipeline_name;
3141 	uint32_t port_id;
3142 	int status;
3143 
3144 	if (n_tokens != 6) {
3145 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3146 		return;
3147 	}
3148 
3149 	pipeline_name = tokens[1];
3150 
3151 	if (strcmp(tokens[2], "port") != 0) {
3152 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
3153 		return;
3154 	}
3155 
3156 	if (strcmp(tokens[3], "in") != 0) {
3157 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
3158 		return;
3159 	}
3160 
3161 	if (softnic_parser_read_uint32(&port_id, tokens[4]) != 0) {
3162 		snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
3163 		return;
3164 	}
3165 
3166 	if (strcmp(tokens[5], "disable") != 0) {
3167 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
3168 		return;
3169 	}
3170 
3171 	status = softnic_pipeline_port_in_disable(softnic, pipeline_name, port_id);
3172 	if (status) {
3173 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
3174 		return;
3175 	}
3176 }
3177 
3178 /**
3179  * pipeline <pipeline_name> port out <port_id> stats read [clear]
3180  */
3181 #define MSG_PIPELINE_PORT_OUT_STATS                        \
3182 	"Pkts in: %" PRIu64 "\n"                           \
3183 	"Pkts dropped by AH: %" PRIu64 "\n"                \
3184 	"Pkts dropped by other: %" PRIu64 "\n"
3185 
3186 static void
3187 cmd_pipeline_port_out_stats(struct pmd_internals *softnic,
3188 	char **tokens,
3189 	uint32_t n_tokens,
3190 	char *out,
3191 	size_t out_size)
3192 {
3193 	struct rte_pipeline_port_out_stats stats;
3194 	char *pipeline_name;
3195 	uint32_t port_id;
3196 	int clear, status;
3197 
3198 	if (n_tokens != 7 &&
3199 		n_tokens != 8) {
3200 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3201 		return;
3202 	}
3203 
3204 	pipeline_name = tokens[1];
3205 
3206 	if (strcmp(tokens[2], "port") != 0) {
3207 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
3208 		return;
3209 	}
3210 
3211 	if (strcmp(tokens[3], "out") != 0) {
3212 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "out");
3213 		return;
3214 	}
3215 
3216 	if (softnic_parser_read_uint32(&port_id, tokens[4]) != 0) {
3217 		snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
3218 		return;
3219 	}
3220 
3221 	if (strcmp(tokens[5], "stats") != 0) {
3222 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
3223 		return;
3224 	}
3225 
3226 	if (strcmp(tokens[6], "read") != 0) {
3227 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "read");
3228 		return;
3229 	}
3230 
3231 	clear = 0;
3232 	if (n_tokens == 8) {
3233 		if (strcmp(tokens[7], "clear") != 0) {
3234 			snprintf(out, out_size, MSG_ARG_INVALID, "clear");
3235 			return;
3236 		}
3237 
3238 		clear = 1;
3239 	}
3240 
3241 	status = softnic_pipeline_port_out_stats_read(softnic,
3242 		pipeline_name,
3243 		port_id,
3244 		&stats,
3245 		clear);
3246 	if (status) {
3247 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
3248 		return;
3249 	}
3250 
3251 	snprintf(out, out_size, MSG_PIPELINE_PORT_OUT_STATS,
3252 		stats.stats.n_pkts_in,
3253 		stats.n_pkts_dropped_by_ah,
3254 		stats.stats.n_pkts_drop);
3255 }
3256 
3257 /**
3258  * pipeline <pipeline_name> table <table_id> stats read [clear]
3259  */
3260 #define MSG_PIPELINE_TABLE_STATS                                     \
3261 	"Pkts in: %" PRIu64 "\n"                                     \
3262 	"Pkts in with lookup miss: %" PRIu64 "\n"                    \
3263 	"Pkts in with lookup hit dropped by AH: %" PRIu64 "\n"       \
3264 	"Pkts in with lookup hit dropped by others: %" PRIu64 "\n"   \
3265 	"Pkts in with lookup miss dropped by AH: %" PRIu64 "\n"      \
3266 	"Pkts in with lookup miss dropped by others: %" PRIu64 "\n"
3267 
3268 static void
3269 cmd_pipeline_table_stats(struct pmd_internals *softnic,
3270 	char **tokens,
3271 	uint32_t n_tokens,
3272 	char *out,
3273 	size_t out_size)
3274 {
3275 	struct rte_pipeline_table_stats stats;
3276 	char *pipeline_name;
3277 	uint32_t table_id;
3278 	int clear, status;
3279 
3280 	if (n_tokens != 6 &&
3281 		n_tokens != 7) {
3282 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3283 		return;
3284 	}
3285 
3286 	pipeline_name = tokens[1];
3287 
3288 	if (strcmp(tokens[2], "table") != 0) {
3289 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
3290 		return;
3291 	}
3292 
3293 	if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
3294 		snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
3295 		return;
3296 	}
3297 
3298 	if (strcmp(tokens[4], "stats") != 0) {
3299 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
3300 		return;
3301 	}
3302 
3303 	if (strcmp(tokens[5], "read") != 0) {
3304 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "read");
3305 		return;
3306 	}
3307 
3308 	clear = 0;
3309 	if (n_tokens == 7) {
3310 		if (strcmp(tokens[6], "clear") != 0) {
3311 			snprintf(out, out_size, MSG_ARG_INVALID, "clear");
3312 			return;
3313 		}
3314 
3315 		clear = 1;
3316 	}
3317 
3318 	status = softnic_pipeline_table_stats_read(softnic,
3319 		pipeline_name,
3320 		table_id,
3321 		&stats,
3322 		clear);
3323 	if (status) {
3324 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
3325 		return;
3326 	}
3327 
3328 	snprintf(out, out_size, MSG_PIPELINE_TABLE_STATS,
3329 		stats.stats.n_pkts_in,
3330 		stats.stats.n_pkts_lookup_miss,
3331 		stats.n_pkts_dropped_by_lkp_hit_ah,
3332 		stats.n_pkts_dropped_lkp_hit,
3333 		stats.n_pkts_dropped_by_lkp_miss_ah,
3334 		stats.n_pkts_dropped_lkp_miss);
3335 }
3336 
3337 /**
3338  * <match> ::=
3339  *
3340  * match
3341  *    acl
3342  *       priority <priority>
3343  *       ipv4 | ipv6 <sa> <sa_depth> <da> <da_depth>
3344  *       <sp0> <sp1> <dp0> <dp1> <proto>
3345  *    | array <pos>
3346  *    | hash
3347  *       raw <key>
3348  *       | ipv4_5tuple <sa> <da> <sp> <dp> <proto>
3349  *       | ipv6_5tuple <sa> <da> <sp> <dp> <proto>
3350  *       | ipv4_addr <addr>
3351  *       | ipv6_addr <addr>
3352  *       | qinq <svlan> <cvlan>
3353  *    | lpm
3354  *       ipv4 | ipv6 <addr> <depth>
3355  */
3356 struct pkt_key_qinq {
3357 	uint16_t ethertype_svlan;
3358 	uint16_t svlan;
3359 	uint16_t ethertype_cvlan;
3360 	uint16_t cvlan;
3361 } __attribute__((__packed__));
3362 
3363 struct pkt_key_ipv4_5tuple {
3364 	uint8_t time_to_live;
3365 	uint8_t proto;
3366 	uint16_t hdr_checksum;
3367 	uint32_t sa;
3368 	uint32_t da;
3369 	uint16_t sp;
3370 	uint16_t dp;
3371 } __attribute__((__packed__));
3372 
3373 struct pkt_key_ipv6_5tuple {
3374 	uint16_t payload_length;
3375 	uint8_t proto;
3376 	uint8_t hop_limit;
3377 	uint8_t sa[16];
3378 	uint8_t da[16];
3379 	uint16_t sp;
3380 	uint16_t dp;
3381 } __attribute__((__packed__));
3382 
3383 struct pkt_key_ipv4_addr {
3384 	uint32_t addr;
3385 } __attribute__((__packed__));
3386 
3387 struct pkt_key_ipv6_addr {
3388 	uint8_t addr[16];
3389 } __attribute__((__packed__));
3390 
3391 static uint32_t
3392 parse_match(char **tokens,
3393 	uint32_t n_tokens,
3394 	char *out,
3395 	size_t out_size,
3396 	struct softnic_table_rule_match *m)
3397 {
3398 	memset(m, 0, sizeof(*m));
3399 
3400 	if (n_tokens < 2)
3401 		return 0;
3402 
3403 	if (strcmp(tokens[0], "match") != 0) {
3404 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "match");
3405 		return 0;
3406 	}
3407 
3408 	if (strcmp(tokens[1], "acl") == 0) {
3409 		if (n_tokens < 14) {
3410 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3411 			return 0;
3412 		}
3413 
3414 		m->match_type = TABLE_ACL;
3415 
3416 		if (strcmp(tokens[2], "priority") != 0) {
3417 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "priority");
3418 			return 0;
3419 		}
3420 
3421 		if (softnic_parser_read_uint32(&m->match.acl.priority,
3422 			tokens[3]) != 0) {
3423 			snprintf(out, out_size, MSG_ARG_INVALID, "priority");
3424 			return 0;
3425 		}
3426 
3427 		if (strcmp(tokens[4], "ipv4") == 0) {
3428 			struct in_addr saddr, daddr;
3429 
3430 			m->match.acl.ip_version = 1;
3431 
3432 			if (softnic_parse_ipv4_addr(tokens[5], &saddr) != 0) {
3433 				snprintf(out, out_size, MSG_ARG_INVALID, "sa");
3434 				return 0;
3435 			}
3436 			m->match.acl.ipv4.sa = rte_be_to_cpu_32(saddr.s_addr);
3437 
3438 			if (softnic_parse_ipv4_addr(tokens[7], &daddr) != 0) {
3439 				snprintf(out, out_size, MSG_ARG_INVALID, "da");
3440 				return 0;
3441 			}
3442 			m->match.acl.ipv4.da = rte_be_to_cpu_32(daddr.s_addr);
3443 		} else if (strcmp(tokens[4], "ipv6") == 0) {
3444 			struct in6_addr saddr, daddr;
3445 
3446 			m->match.acl.ip_version = 0;
3447 
3448 			if (softnic_parse_ipv6_addr(tokens[5], &saddr) != 0) {
3449 				snprintf(out, out_size, MSG_ARG_INVALID, "sa");
3450 				return 0;
3451 			}
3452 			memcpy(m->match.acl.ipv6.sa, saddr.s6_addr, 16);
3453 
3454 			if (softnic_parse_ipv6_addr(tokens[7], &daddr) != 0) {
3455 				snprintf(out, out_size, MSG_ARG_INVALID, "da");
3456 				return 0;
3457 			}
3458 			memcpy(m->match.acl.ipv6.da, daddr.s6_addr, 16);
3459 		} else {
3460 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
3461 				"ipv4 or ipv6");
3462 			return 0;
3463 		}
3464 
3465 		if (softnic_parser_read_uint32(&m->match.acl.sa_depth,
3466 			tokens[6]) != 0) {
3467 			snprintf(out, out_size, MSG_ARG_INVALID, "sa_depth");
3468 			return 0;
3469 		}
3470 
3471 		if (softnic_parser_read_uint32(&m->match.acl.da_depth,
3472 			tokens[8]) != 0) {
3473 			snprintf(out, out_size, MSG_ARG_INVALID, "da_depth");
3474 			return 0;
3475 		}
3476 
3477 		if (softnic_parser_read_uint16(&m->match.acl.sp0, tokens[9]) != 0) {
3478 			snprintf(out, out_size, MSG_ARG_INVALID, "sp0");
3479 			return 0;
3480 		}
3481 
3482 		if (softnic_parser_read_uint16(&m->match.acl.sp1, tokens[10]) != 0) {
3483 			snprintf(out, out_size, MSG_ARG_INVALID, "sp1");
3484 			return 0;
3485 		}
3486 
3487 		if (softnic_parser_read_uint16(&m->match.acl.dp0, tokens[11]) != 0) {
3488 			snprintf(out, out_size, MSG_ARG_INVALID, "dp0");
3489 			return 0;
3490 		}
3491 
3492 		if (softnic_parser_read_uint16(&m->match.acl.dp1, tokens[12]) != 0) {
3493 			snprintf(out, out_size, MSG_ARG_INVALID, "dp1");
3494 			return 0;
3495 		}
3496 
3497 		if (softnic_parser_read_uint8(&m->match.acl.proto, tokens[13]) != 0) {
3498 			snprintf(out, out_size, MSG_ARG_INVALID, "proto");
3499 			return 0;
3500 		}
3501 
3502 		m->match.acl.proto_mask = 0xff;
3503 
3504 		return 14;
3505 	} /* acl */
3506 
3507 	if (strcmp(tokens[1], "array") == 0) {
3508 		if (n_tokens < 3) {
3509 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3510 			return 0;
3511 		}
3512 
3513 		m->match_type = TABLE_ARRAY;
3514 
3515 		if (softnic_parser_read_uint32(&m->match.array.pos, tokens[2]) != 0) {
3516 			snprintf(out, out_size, MSG_ARG_INVALID, "pos");
3517 			return 0;
3518 		}
3519 
3520 		return 3;
3521 	} /* array */
3522 
3523 	if (strcmp(tokens[1], "hash") == 0) {
3524 		if (n_tokens < 3) {
3525 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3526 			return 0;
3527 		}
3528 
3529 		m->match_type = TABLE_HASH;
3530 
3531 		if (strcmp(tokens[2], "raw") == 0) {
3532 			uint32_t key_size = TABLE_RULE_MATCH_SIZE_MAX;
3533 
3534 			if (n_tokens < 4) {
3535 				snprintf(out, out_size, MSG_ARG_MISMATCH,
3536 					tokens[0]);
3537 				return 0;
3538 			}
3539 
3540 			if (softnic_parse_hex_string(tokens[3],
3541 				m->match.hash.key, &key_size) != 0) {
3542 				snprintf(out, out_size, MSG_ARG_INVALID, "key");
3543 				return 0;
3544 			}
3545 
3546 			return 4;
3547 		} /* hash raw */
3548 
3549 		if (strcmp(tokens[2], "ipv4_5tuple") == 0) {
3550 			struct pkt_key_ipv4_5tuple *ipv4 =
3551 				(struct pkt_key_ipv4_5tuple *)m->match.hash.key;
3552 			struct in_addr saddr, daddr;
3553 			uint16_t sp, dp;
3554 			uint8_t proto;
3555 
3556 			if (n_tokens < 8) {
3557 				snprintf(out, out_size, MSG_ARG_MISMATCH,
3558 					tokens[0]);
3559 				return 0;
3560 			}
3561 
3562 			if (softnic_parse_ipv4_addr(tokens[3], &saddr) != 0) {
3563 				snprintf(out, out_size, MSG_ARG_INVALID, "sa");
3564 				return 0;
3565 			}
3566 
3567 			if (softnic_parse_ipv4_addr(tokens[4], &daddr) != 0) {
3568 				snprintf(out, out_size, MSG_ARG_INVALID, "da");
3569 				return 0;
3570 			}
3571 
3572 			if (softnic_parser_read_uint16(&sp, tokens[5]) != 0) {
3573 				snprintf(out, out_size, MSG_ARG_INVALID, "sp");
3574 				return 0;
3575 			}
3576 
3577 			if (softnic_parser_read_uint16(&dp, tokens[6]) != 0) {
3578 				snprintf(out, out_size, MSG_ARG_INVALID, "dp");
3579 				return 0;
3580 			}
3581 
3582 			if (softnic_parser_read_uint8(&proto, tokens[7]) != 0) {
3583 				snprintf(out, out_size, MSG_ARG_INVALID,
3584 					"proto");
3585 				return 0;
3586 			}
3587 
3588 			ipv4->sa = saddr.s_addr;
3589 			ipv4->da = daddr.s_addr;
3590 			ipv4->sp = rte_cpu_to_be_16(sp);
3591 			ipv4->dp = rte_cpu_to_be_16(dp);
3592 			ipv4->proto = proto;
3593 
3594 			return 8;
3595 		} /* hash ipv4_5tuple */
3596 
3597 		if (strcmp(tokens[2], "ipv6_5tuple") == 0) {
3598 			struct pkt_key_ipv6_5tuple *ipv6 =
3599 				(struct pkt_key_ipv6_5tuple *)m->match.hash.key;
3600 			struct in6_addr saddr, daddr;
3601 			uint16_t sp, dp;
3602 			uint8_t proto;
3603 
3604 			if (n_tokens < 8) {
3605 				snprintf(out, out_size, MSG_ARG_MISMATCH,
3606 					tokens[0]);
3607 				return 0;
3608 			}
3609 
3610 			if (softnic_parse_ipv6_addr(tokens[3], &saddr) != 0) {
3611 				snprintf(out, out_size, MSG_ARG_INVALID, "sa");
3612 				return 0;
3613 			}
3614 
3615 			if (softnic_parse_ipv6_addr(tokens[4], &daddr) != 0) {
3616 				snprintf(out, out_size, MSG_ARG_INVALID, "da");
3617 				return 0;
3618 			}
3619 
3620 			if (softnic_parser_read_uint16(&sp, tokens[5]) != 0) {
3621 				snprintf(out, out_size, MSG_ARG_INVALID, "sp");
3622 				return 0;
3623 			}
3624 
3625 			if (softnic_parser_read_uint16(&dp, tokens[6]) != 0) {
3626 				snprintf(out, out_size, MSG_ARG_INVALID, "dp");
3627 				return 0;
3628 			}
3629 
3630 			if (softnic_parser_read_uint8(&proto, tokens[7]) != 0) {
3631 				snprintf(out, out_size, MSG_ARG_INVALID,
3632 					"proto");
3633 				return 0;
3634 			}
3635 
3636 			memcpy(ipv6->sa, saddr.s6_addr, 16);
3637 			memcpy(ipv6->da, daddr.s6_addr, 16);
3638 			ipv6->sp = rte_cpu_to_be_16(sp);
3639 			ipv6->dp = rte_cpu_to_be_16(dp);
3640 			ipv6->proto = proto;
3641 
3642 			return 8;
3643 		} /* hash ipv6_5tuple */
3644 
3645 		if (strcmp(tokens[2], "ipv4_addr") == 0) {
3646 			struct pkt_key_ipv4_addr *ipv4_addr =
3647 				(struct pkt_key_ipv4_addr *)m->match.hash.key;
3648 			struct in_addr addr;
3649 
3650 			if (n_tokens < 4) {
3651 				snprintf(out, out_size, MSG_ARG_MISMATCH,
3652 					tokens[0]);
3653 				return 0;
3654 			}
3655 
3656 			if (softnic_parse_ipv4_addr(tokens[3], &addr) != 0) {
3657 				snprintf(out, out_size, MSG_ARG_INVALID,
3658 					"addr");
3659 				return 0;
3660 			}
3661 
3662 			ipv4_addr->addr = addr.s_addr;
3663 
3664 			return 4;
3665 		} /* hash ipv4_addr */
3666 
3667 		if (strcmp(tokens[2], "ipv6_addr") == 0) {
3668 			struct pkt_key_ipv6_addr *ipv6_addr =
3669 				(struct pkt_key_ipv6_addr *)m->match.hash.key;
3670 			struct in6_addr addr;
3671 
3672 			if (n_tokens < 4) {
3673 				snprintf(out, out_size, MSG_ARG_MISMATCH,
3674 					tokens[0]);
3675 				return 0;
3676 			}
3677 
3678 			if (softnic_parse_ipv6_addr(tokens[3], &addr) != 0) {
3679 				snprintf(out, out_size, MSG_ARG_INVALID,
3680 					"addr");
3681 				return 0;
3682 			}
3683 
3684 			memcpy(ipv6_addr->addr, addr.s6_addr, 16);
3685 
3686 			return 4;
3687 		} /* hash ipv6_5tuple */
3688 
3689 		if (strcmp(tokens[2], "qinq") == 0) {
3690 			struct pkt_key_qinq *qinq =
3691 				(struct pkt_key_qinq *)m->match.hash.key;
3692 			uint16_t svlan, cvlan;
3693 
3694 			if (n_tokens < 5) {
3695 				snprintf(out, out_size, MSG_ARG_MISMATCH,
3696 					tokens[0]);
3697 				return 0;
3698 			}
3699 
3700 			if ((softnic_parser_read_uint16(&svlan, tokens[3]) != 0) ||
3701 				svlan > 0xFFF) {
3702 				snprintf(out, out_size, MSG_ARG_INVALID,
3703 					"svlan");
3704 				return 0;
3705 			}
3706 
3707 			if ((softnic_parser_read_uint16(&cvlan, tokens[4]) != 0) ||
3708 				cvlan > 0xFFF) {
3709 				snprintf(out, out_size, MSG_ARG_INVALID,
3710 					"cvlan");
3711 				return 0;
3712 			}
3713 
3714 			qinq->svlan = rte_cpu_to_be_16(svlan);
3715 			qinq->cvlan = rte_cpu_to_be_16(cvlan);
3716 
3717 			return 5;
3718 		} /* hash qinq */
3719 
3720 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3721 		return 0;
3722 	} /* hash */
3723 
3724 	if (strcmp(tokens[1], "lpm") == 0) {
3725 		if (n_tokens < 5) {
3726 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3727 			return 0;
3728 		}
3729 
3730 		m->match_type = TABLE_LPM;
3731 
3732 		if (strcmp(tokens[2], "ipv4") == 0) {
3733 			struct in_addr addr;
3734 
3735 			m->match.lpm.ip_version = 1;
3736 
3737 			if (softnic_parse_ipv4_addr(tokens[3], &addr) != 0) {
3738 				snprintf(out, out_size, MSG_ARG_INVALID,
3739 					"addr");
3740 				return 0;
3741 			}
3742 
3743 			m->match.lpm.ipv4 = rte_be_to_cpu_32(addr.s_addr);
3744 		} else if (strcmp(tokens[2], "ipv6") == 0) {
3745 			struct in6_addr addr;
3746 
3747 			m->match.lpm.ip_version = 0;
3748 
3749 			if (softnic_parse_ipv6_addr(tokens[3], &addr) != 0) {
3750 				snprintf(out, out_size, MSG_ARG_INVALID,
3751 					"addr");
3752 				return 0;
3753 			}
3754 
3755 			memcpy(m->match.lpm.ipv6, addr.s6_addr, 16);
3756 		} else {
3757 			snprintf(out, out_size, MSG_ARG_MISMATCH,
3758 				"ipv4 or ipv6");
3759 			return 0;
3760 		}
3761 
3762 		if (softnic_parser_read_uint8(&m->match.lpm.depth, tokens[4]) != 0) {
3763 			snprintf(out, out_size, MSG_ARG_INVALID, "depth");
3764 			return 0;
3765 		}
3766 
3767 		return 5;
3768 	} /* lpm */
3769 
3770 	snprintf(out, out_size, MSG_ARG_MISMATCH,
3771 		"acl or array or hash or lpm");
3772 	return 0;
3773 }
3774 
3775 /**
3776  * table_action ::=
3777  *
3778  * action
3779  *    fwd
3780  *       drop
3781  *       | port <port_id>
3782  *       | meta
3783  *       | table <table_id>
3784  *    [balance <out0> ... <out7>]
3785  *    [meter
3786  *       tc0 meter <meter_profile_id> policer g <pa> y <pa> r <pa>
3787  *       [tc1 meter <meter_profile_id> policer g <pa> y <pa> r <pa>
3788  *       tc2 meter <meter_profile_id> policer g <pa> y <pa> r <pa>
3789  *       tc3 meter <meter_profile_id> policer g <pa> y <pa> r <pa>]]
3790  *    [tm subport <subport_id> pipe <pipe_id>]
3791  *    [encap
3792  *       ether <da> <sa>
3793  *       | vlan <da> <sa> <pcp> <dei> <vid>
3794  *       | qinq <da> <sa> <pcp> <dei> <vid> <pcp> <dei> <vid>
3795  *       | qinq_pppoe <da> <sa> <pcp> <dei> <vid> <pcp> <dei> <vid> <session_id>
3796  *       | mpls unicast | multicast
3797  *          <da> <sa>
3798  *          label0 <label> <tc> <ttl>
3799  *          [label1 <label> <tc> <ttl>
3800  *          [label2 <label> <tc> <ttl>
3801  *          [label3 <label> <tc> <ttl>]]]
3802  *       | pppoe <da> <sa> <session_id>]
3803  *       | vxlan ether <da> <sa>
3804  *          [vlan <pcp> <dei> <vid>]
3805  *          ipv4 <sa> <da> <dscp> <ttl>
3806  *          | ipv6 <sa> <da> <flow_label> <dscp> <hop_limit>
3807  *          udp <sp> <dp>
3808  *          vxlan <vni>]
3809  *    [nat ipv4 | ipv6 <addr> <port>]
3810  *    [ttl dec | keep]
3811  *    [stats]
3812  *    [time]
3813  *    [tag <tag>]
3814  *    [decap <n>]
3815  *    [sym_crypto
3816  *       encrypt | decrypt
3817  *       type
3818  *       | cipher
3819  *          cipher_algo <algo> cipher_key <key> cipher_iv <iv>
3820  *       | cipher_auth
3821  *          cipher_algo <algo> cipher_key <key> cipher_iv <iv>
3822  *          auth_algo <algo> auth_key <key> digest_size <size>
3823  *       | aead
3824  *          aead_algo <algo> aead_key <key> aead_iv <iv> aead_aad <aad>
3825  *          digest_size <size>
3826  *       data_offset <data_offset>]
3827  *
3828  * where:
3829  *    <pa> ::= g | y | r | drop
3830  */
3831 static uint32_t
3832 parse_table_action_fwd(char **tokens,
3833 	uint32_t n_tokens,
3834 	struct softnic_table_rule_action *a)
3835 {
3836 	if (n_tokens == 0 ||
3837 		(strcmp(tokens[0], "fwd") != 0))
3838 		return 0;
3839 
3840 	tokens++;
3841 	n_tokens--;
3842 
3843 	if (n_tokens && (strcmp(tokens[0], "drop") == 0)) {
3844 		a->fwd.action = RTE_PIPELINE_ACTION_DROP;
3845 		a->action_mask |= 1 << RTE_TABLE_ACTION_FWD;
3846 		return 1 + 1;
3847 	}
3848 
3849 	if (n_tokens && (strcmp(tokens[0], "port") == 0)) {
3850 		uint32_t id;
3851 
3852 		if (n_tokens < 2 ||
3853 			softnic_parser_read_uint32(&id, tokens[1]))
3854 			return 0;
3855 
3856 		a->fwd.action = RTE_PIPELINE_ACTION_PORT;
3857 		a->fwd.id = id;
3858 		a->action_mask |= 1 << RTE_TABLE_ACTION_FWD;
3859 		return 1 + 2;
3860 	}
3861 
3862 	if (n_tokens && (strcmp(tokens[0], "meta") == 0)) {
3863 		a->fwd.action = RTE_PIPELINE_ACTION_PORT_META;
3864 		a->action_mask |= 1 << RTE_TABLE_ACTION_FWD;
3865 		return 1 + 1;
3866 	}
3867 
3868 	if (n_tokens && (strcmp(tokens[0], "table") == 0)) {
3869 		uint32_t id;
3870 
3871 		if (n_tokens < 2 ||
3872 			softnic_parser_read_uint32(&id, tokens[1]))
3873 			return 0;
3874 
3875 		a->fwd.action = RTE_PIPELINE_ACTION_TABLE;
3876 		a->fwd.id = id;
3877 		a->action_mask |= 1 << RTE_TABLE_ACTION_FWD;
3878 		return 1 + 2;
3879 	}
3880 
3881 	return 0;
3882 }
3883 
3884 static uint32_t
3885 parse_table_action_balance(char **tokens,
3886 	uint32_t n_tokens,
3887 	struct softnic_table_rule_action *a)
3888 {
3889 	uint32_t i;
3890 
3891 	if (n_tokens == 0 ||
3892 		(strcmp(tokens[0], "balance") != 0))
3893 		return 0;
3894 
3895 	tokens++;
3896 	n_tokens--;
3897 
3898 	if (n_tokens < RTE_TABLE_ACTION_LB_TABLE_SIZE)
3899 		return 0;
3900 
3901 	for (i = 0; i < RTE_TABLE_ACTION_LB_TABLE_SIZE; i++)
3902 		if (softnic_parser_read_uint32(&a->lb.out[i], tokens[i]) != 0)
3903 			return 0;
3904 
3905 	a->action_mask |= 1 << RTE_TABLE_ACTION_LB;
3906 	return 1 + RTE_TABLE_ACTION_LB_TABLE_SIZE;
3907 }
3908 
3909 static int
3910 parse_policer_action(char *token, enum rte_table_action_policer *a)
3911 {
3912 	if (strcmp(token, "g") == 0) {
3913 		*a = RTE_TABLE_ACTION_POLICER_COLOR_GREEN;
3914 		return 0;
3915 	}
3916 
3917 	if (strcmp(token, "y") == 0) {
3918 		*a = RTE_TABLE_ACTION_POLICER_COLOR_YELLOW;
3919 		return 0;
3920 	}
3921 
3922 	if (strcmp(token, "r") == 0) {
3923 		*a = RTE_TABLE_ACTION_POLICER_COLOR_RED;
3924 		return 0;
3925 	}
3926 
3927 	if (strcmp(token, "drop") == 0) {
3928 		*a = RTE_TABLE_ACTION_POLICER_DROP;
3929 		return 0;
3930 	}
3931 
3932 	return -1;
3933 }
3934 
3935 static uint32_t
3936 parse_table_action_meter_tc(char **tokens,
3937 	uint32_t n_tokens,
3938 	struct rte_table_action_mtr_tc_params *mtr)
3939 {
3940 	if (n_tokens < 9 ||
3941 		strcmp(tokens[0], "meter") ||
3942 		softnic_parser_read_uint32(&mtr->meter_profile_id, tokens[1]) ||
3943 		strcmp(tokens[2], "policer") ||
3944 		strcmp(tokens[3], "g") ||
3945 		parse_policer_action(tokens[4], &mtr->policer[RTE_COLOR_GREEN]) ||
3946 		strcmp(tokens[5], "y") ||
3947 		parse_policer_action(tokens[6], &mtr->policer[RTE_COLOR_YELLOW]) ||
3948 		strcmp(tokens[7], "r") ||
3949 		parse_policer_action(tokens[8], &mtr->policer[RTE_COLOR_RED]))
3950 		return 0;
3951 
3952 	return 9;
3953 }
3954 
3955 static uint32_t
3956 parse_table_action_meter(char **tokens,
3957 	uint32_t n_tokens,
3958 	struct softnic_table_rule_action *a)
3959 {
3960 	if (n_tokens == 0 ||
3961 		strcmp(tokens[0], "meter"))
3962 		return 0;
3963 
3964 	tokens++;
3965 	n_tokens--;
3966 
3967 	if (n_tokens < 10 ||
3968 		strcmp(tokens[0], "tc0") ||
3969 		(parse_table_action_meter_tc(tokens + 1,
3970 			n_tokens - 1,
3971 			&a->mtr.mtr[0]) == 0))
3972 		return 0;
3973 
3974 	tokens += 10;
3975 	n_tokens -= 10;
3976 
3977 	if (n_tokens == 0 ||
3978 		strcmp(tokens[0], "tc1")) {
3979 		a->mtr.tc_mask = 1;
3980 		a->action_mask |= 1 << RTE_TABLE_ACTION_MTR;
3981 		return 1 + 10;
3982 	}
3983 
3984 	if (n_tokens < 30 ||
3985 		(parse_table_action_meter_tc(tokens + 1,
3986 			n_tokens - 1, &a->mtr.mtr[1]) == 0) ||
3987 		strcmp(tokens[10], "tc2") ||
3988 		(parse_table_action_meter_tc(tokens + 11,
3989 			n_tokens - 11, &a->mtr.mtr[2]) == 0) ||
3990 		strcmp(tokens[20], "tc3") ||
3991 		(parse_table_action_meter_tc(tokens + 21,
3992 			n_tokens - 21, &a->mtr.mtr[3]) == 0))
3993 		return 0;
3994 
3995 	a->mtr.tc_mask = 0xF;
3996 	a->action_mask |= 1 << RTE_TABLE_ACTION_MTR;
3997 	return 1 + 10 + 3 * 10;
3998 }
3999 
4000 static uint32_t
4001 parse_table_action_tm(char **tokens,
4002 	uint32_t n_tokens,
4003 	struct softnic_table_rule_action *a)
4004 {
4005 	uint32_t subport_id, pipe_id;
4006 
4007 	if (n_tokens < 5 ||
4008 		strcmp(tokens[0], "tm") ||
4009 		strcmp(tokens[1], "subport") ||
4010 		softnic_parser_read_uint32(&subport_id, tokens[2]) ||
4011 		strcmp(tokens[3], "pipe") ||
4012 		softnic_parser_read_uint32(&pipe_id, tokens[4]))
4013 		return 0;
4014 
4015 	a->tm.subport_id = subport_id;
4016 	a->tm.pipe_id = pipe_id;
4017 	a->action_mask |= 1 << RTE_TABLE_ACTION_TM;
4018 	return 5;
4019 }
4020 
4021 static uint32_t
4022 parse_table_action_encap(char **tokens,
4023 	uint32_t n_tokens,
4024 	struct softnic_table_rule_action *a)
4025 {
4026 	if (n_tokens == 0 ||
4027 		strcmp(tokens[0], "encap"))
4028 		return 0;
4029 
4030 	tokens++;
4031 	n_tokens--;
4032 
4033 	/* ether */
4034 	if (n_tokens && (strcmp(tokens[0], "ether") == 0)) {
4035 		if (n_tokens < 3 ||
4036 			softnic_parse_mac_addr(tokens[1], &a->encap.ether.ether.da) ||
4037 			softnic_parse_mac_addr(tokens[2], &a->encap.ether.ether.sa))
4038 			return 0;
4039 
4040 		a->encap.type = RTE_TABLE_ACTION_ENCAP_ETHER;
4041 		a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
4042 		return 1 + 3;
4043 	}
4044 
4045 	/* vlan */
4046 	if (n_tokens && (strcmp(tokens[0], "vlan") == 0)) {
4047 		uint32_t pcp, dei, vid;
4048 
4049 		if (n_tokens < 6 ||
4050 			softnic_parse_mac_addr(tokens[1], &a->encap.vlan.ether.da) ||
4051 			softnic_parse_mac_addr(tokens[2], &a->encap.vlan.ether.sa) ||
4052 			softnic_parser_read_uint32(&pcp, tokens[3]) ||
4053 			pcp > 0x7 ||
4054 			softnic_parser_read_uint32(&dei, tokens[4]) ||
4055 			dei > 0x1 ||
4056 			softnic_parser_read_uint32(&vid, tokens[5]) ||
4057 			vid > 0xFFF)
4058 			return 0;
4059 
4060 		a->encap.vlan.vlan.pcp = pcp & 0x7;
4061 		a->encap.vlan.vlan.dei = dei & 0x1;
4062 		a->encap.vlan.vlan.vid = vid & 0xFFF;
4063 		a->encap.type = RTE_TABLE_ACTION_ENCAP_VLAN;
4064 		a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
4065 		return 1 + 6;
4066 	}
4067 
4068 	/* qinq */
4069 	if (n_tokens && (strcmp(tokens[0], "qinq") == 0)) {
4070 		uint32_t svlan_pcp, svlan_dei, svlan_vid;
4071 		uint32_t cvlan_pcp, cvlan_dei, cvlan_vid;
4072 
4073 		if (n_tokens < 9 ||
4074 			softnic_parse_mac_addr(tokens[1], &a->encap.qinq.ether.da) ||
4075 			softnic_parse_mac_addr(tokens[2], &a->encap.qinq.ether.sa) ||
4076 			softnic_parser_read_uint32(&svlan_pcp, tokens[3]) ||
4077 			svlan_pcp > 0x7 ||
4078 			softnic_parser_read_uint32(&svlan_dei, tokens[4]) ||
4079 			svlan_dei > 0x1 ||
4080 			softnic_parser_read_uint32(&svlan_vid, tokens[5]) ||
4081 			svlan_vid > 0xFFF ||
4082 			softnic_parser_read_uint32(&cvlan_pcp, tokens[6]) ||
4083 			cvlan_pcp > 0x7 ||
4084 			softnic_parser_read_uint32(&cvlan_dei, tokens[7]) ||
4085 			cvlan_dei > 0x1 ||
4086 			softnic_parser_read_uint32(&cvlan_vid, tokens[8]) ||
4087 			cvlan_vid > 0xFFF)
4088 			return 0;
4089 
4090 		a->encap.qinq.svlan.pcp = svlan_pcp & 0x7;
4091 		a->encap.qinq.svlan.dei = svlan_dei & 0x1;
4092 		a->encap.qinq.svlan.vid = svlan_vid & 0xFFF;
4093 		a->encap.qinq.cvlan.pcp = cvlan_pcp & 0x7;
4094 		a->encap.qinq.cvlan.dei = cvlan_dei & 0x1;
4095 		a->encap.qinq.cvlan.vid = cvlan_vid & 0xFFF;
4096 		a->encap.type = RTE_TABLE_ACTION_ENCAP_QINQ;
4097 		a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
4098 		return 1 + 9;
4099 	}
4100 
4101 	/* qinq_pppoe */
4102 	if (n_tokens && (strcmp(tokens[0], "qinq_pppoe") == 0)) {
4103 		uint32_t svlan_pcp, svlan_dei, svlan_vid;
4104 		uint32_t cvlan_pcp, cvlan_dei, cvlan_vid;
4105 
4106 		if (n_tokens < 10 ||
4107 			softnic_parse_mac_addr(tokens[1],
4108 				&a->encap.qinq_pppoe.ether.da) ||
4109 			softnic_parse_mac_addr(tokens[2],
4110 				&a->encap.qinq_pppoe.ether.sa) ||
4111 			softnic_parser_read_uint32(&svlan_pcp, tokens[3]) ||
4112 			svlan_pcp > 0x7 ||
4113 			softnic_parser_read_uint32(&svlan_dei, tokens[4]) ||
4114 			svlan_dei > 0x1 ||
4115 			softnic_parser_read_uint32(&svlan_vid, tokens[5]) ||
4116 			svlan_vid > 0xFFF ||
4117 			softnic_parser_read_uint32(&cvlan_pcp, tokens[6]) ||
4118 			cvlan_pcp > 0x7 ||
4119 			softnic_parser_read_uint32(&cvlan_dei, tokens[7]) ||
4120 			cvlan_dei > 0x1 ||
4121 			softnic_parser_read_uint32(&cvlan_vid, tokens[8]) ||
4122 			cvlan_vid > 0xFFF ||
4123 			softnic_parser_read_uint16(&a->encap.qinq_pppoe.pppoe.session_id,
4124 				tokens[9]))
4125 			return 0;
4126 
4127 		a->encap.qinq_pppoe.svlan.pcp = svlan_pcp & 0x7;
4128 		a->encap.qinq_pppoe.svlan.dei = svlan_dei & 0x1;
4129 		a->encap.qinq_pppoe.svlan.vid = svlan_vid & 0xFFF;
4130 		a->encap.qinq_pppoe.cvlan.pcp = cvlan_pcp & 0x7;
4131 		a->encap.qinq_pppoe.cvlan.dei = cvlan_dei & 0x1;
4132 		a->encap.qinq_pppoe.cvlan.vid = cvlan_vid & 0xFFF;
4133 		a->encap.type = RTE_TABLE_ACTION_ENCAP_QINQ_PPPOE;
4134 		a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
4135 		return 1 + 10;
4136 	}
4137 
4138 	/* mpls */
4139 	if (n_tokens && (strcmp(tokens[0], "mpls") == 0)) {
4140 		uint32_t label, tc, ttl;
4141 
4142 		if (n_tokens < 8)
4143 			return 0;
4144 
4145 		if (strcmp(tokens[1], "unicast") == 0)
4146 			a->encap.mpls.unicast = 1;
4147 		else if (strcmp(tokens[1], "multicast") == 0)
4148 			a->encap.mpls.unicast = 0;
4149 		else
4150 			return 0;
4151 
4152 		if (softnic_parse_mac_addr(tokens[2], &a->encap.mpls.ether.da) ||
4153 			softnic_parse_mac_addr(tokens[3], &a->encap.mpls.ether.sa) ||
4154 			strcmp(tokens[4], "label0") ||
4155 			softnic_parser_read_uint32(&label, tokens[5]) ||
4156 			label > 0xFFFFF ||
4157 			softnic_parser_read_uint32(&tc, tokens[6]) ||
4158 			tc > 0x7 ||
4159 			softnic_parser_read_uint32(&ttl, tokens[7]) ||
4160 			ttl > 0x3F)
4161 			return 0;
4162 
4163 		a->encap.mpls.mpls[0].label = label;
4164 		a->encap.mpls.mpls[0].tc = tc;
4165 		a->encap.mpls.mpls[0].ttl = ttl;
4166 
4167 		tokens += 8;
4168 		n_tokens -= 8;
4169 
4170 		if (n_tokens == 0 ||
4171 			strcmp(tokens[0], "label1")) {
4172 			a->encap.mpls.mpls_count = 1;
4173 			a->encap.type = RTE_TABLE_ACTION_ENCAP_MPLS;
4174 			a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
4175 			return 1 + 8;
4176 		}
4177 
4178 		if (n_tokens < 4 ||
4179 			softnic_parser_read_uint32(&label, tokens[1]) ||
4180 			label > 0xFFFFF ||
4181 			softnic_parser_read_uint32(&tc, tokens[2]) ||
4182 			tc > 0x7 ||
4183 			softnic_parser_read_uint32(&ttl, tokens[3]) ||
4184 			ttl > 0x3F)
4185 			return 0;
4186 
4187 		a->encap.mpls.mpls[1].label = label;
4188 		a->encap.mpls.mpls[1].tc = tc;
4189 		a->encap.mpls.mpls[1].ttl = ttl;
4190 
4191 		tokens += 4;
4192 		n_tokens -= 4;
4193 
4194 		if (n_tokens == 0 ||
4195 			strcmp(tokens[0], "label2")) {
4196 			a->encap.mpls.mpls_count = 2;
4197 			a->encap.type = RTE_TABLE_ACTION_ENCAP_MPLS;
4198 			a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
4199 			return 1 + 8 + 4;
4200 		}
4201 
4202 		if (n_tokens < 4 ||
4203 			softnic_parser_read_uint32(&label, tokens[1]) ||
4204 			label > 0xFFFFF ||
4205 			softnic_parser_read_uint32(&tc, tokens[2]) ||
4206 			tc > 0x7 ||
4207 			softnic_parser_read_uint32(&ttl, tokens[3]) ||
4208 			ttl > 0x3F)
4209 			return 0;
4210 
4211 		a->encap.mpls.mpls[2].label = label;
4212 		a->encap.mpls.mpls[2].tc = tc;
4213 		a->encap.mpls.mpls[2].ttl = ttl;
4214 
4215 		tokens += 4;
4216 		n_tokens -= 4;
4217 
4218 		if (n_tokens == 0 ||
4219 			strcmp(tokens[0], "label3")) {
4220 			a->encap.mpls.mpls_count = 3;
4221 			a->encap.type = RTE_TABLE_ACTION_ENCAP_MPLS;
4222 			a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
4223 			return 1 + 8 + 4 + 4;
4224 		}
4225 
4226 		if (n_tokens < 4 ||
4227 			softnic_parser_read_uint32(&label, tokens[1]) ||
4228 			label > 0xFFFFF ||
4229 			softnic_parser_read_uint32(&tc, tokens[2]) ||
4230 			tc > 0x7 ||
4231 			softnic_parser_read_uint32(&ttl, tokens[3]) ||
4232 			ttl > 0x3F)
4233 			return 0;
4234 
4235 		a->encap.mpls.mpls[3].label = label;
4236 		a->encap.mpls.mpls[3].tc = tc;
4237 		a->encap.mpls.mpls[3].ttl = ttl;
4238 
4239 		a->encap.mpls.mpls_count = 4;
4240 		a->encap.type = RTE_TABLE_ACTION_ENCAP_MPLS;
4241 		a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
4242 		return 1 + 8 + 4 + 4 + 4;
4243 	}
4244 
4245 	/* pppoe */
4246 	if (n_tokens && (strcmp(tokens[0], "pppoe") == 0)) {
4247 		if (n_tokens < 4 ||
4248 			softnic_parse_mac_addr(tokens[1], &a->encap.pppoe.ether.da) ||
4249 			softnic_parse_mac_addr(tokens[2], &a->encap.pppoe.ether.sa) ||
4250 			softnic_parser_read_uint16(&a->encap.pppoe.pppoe.session_id,
4251 				tokens[3]))
4252 			return 0;
4253 
4254 		a->encap.type = RTE_TABLE_ACTION_ENCAP_PPPOE;
4255 		a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
4256 		return 1 + 4;
4257 	}
4258 
4259 	/* vxlan */
4260 	if (n_tokens && (strcmp(tokens[0], "vxlan") == 0)) {
4261 		uint32_t n = 0;
4262 
4263 		n_tokens--;
4264 		tokens++;
4265 		n++;
4266 
4267 		/* ether <da> <sa> */
4268 		if ((n_tokens < 3) ||
4269 			strcmp(tokens[0], "ether") ||
4270 			softnic_parse_mac_addr(tokens[1], &a->encap.vxlan.ether.da) ||
4271 			softnic_parse_mac_addr(tokens[2], &a->encap.vxlan.ether.sa))
4272 			return 0;
4273 
4274 		n_tokens -= 3;
4275 		tokens += 3;
4276 		n += 3;
4277 
4278 		/* [vlan <pcp> <dei> <vid>] */
4279 		if (strcmp(tokens[0], "vlan") == 0) {
4280 			uint32_t pcp, dei, vid;
4281 
4282 			if ((n_tokens < 4) ||
4283 				softnic_parser_read_uint32(&pcp, tokens[1]) ||
4284 				(pcp > 7) ||
4285 				softnic_parser_read_uint32(&dei, tokens[2]) ||
4286 				(dei > 1) ||
4287 				softnic_parser_read_uint32(&vid, tokens[3]) ||
4288 				(vid > 0xFFF))
4289 				return 0;
4290 
4291 			a->encap.vxlan.vlan.pcp = pcp;
4292 			a->encap.vxlan.vlan.dei = dei;
4293 			a->encap.vxlan.vlan.vid = vid;
4294 
4295 			n_tokens -= 4;
4296 			tokens += 4;
4297 			n += 4;
4298 		}
4299 
4300 		/* ipv4 <sa> <da> <dscp> <ttl>
4301 		   | ipv6 <sa> <da> <flow_label> <dscp> <hop_limit> */
4302 		if (strcmp(tokens[0], "ipv4") == 0) {
4303 			struct in_addr sa, da;
4304 			uint8_t dscp, ttl;
4305 
4306 			if ((n_tokens < 5) ||
4307 				softnic_parse_ipv4_addr(tokens[1], &sa) ||
4308 				softnic_parse_ipv4_addr(tokens[2], &da) ||
4309 				softnic_parser_read_uint8(&dscp, tokens[3]) ||
4310 				(dscp > 64) ||
4311 				softnic_parser_read_uint8(&ttl, tokens[4]))
4312 				return 0;
4313 
4314 			a->encap.vxlan.ipv4.sa = rte_be_to_cpu_32(sa.s_addr);
4315 			a->encap.vxlan.ipv4.da = rte_be_to_cpu_32(da.s_addr);
4316 			a->encap.vxlan.ipv4.dscp = dscp;
4317 			a->encap.vxlan.ipv4.ttl = ttl;
4318 
4319 			n_tokens -= 5;
4320 			tokens += 5;
4321 			n += 5;
4322 		} else if (strcmp(tokens[0], "ipv6") == 0) {
4323 			struct in6_addr sa, da;
4324 			uint32_t flow_label;
4325 			uint8_t dscp, hop_limit;
4326 
4327 			if ((n_tokens < 6) ||
4328 				softnic_parse_ipv6_addr(tokens[1], &sa) ||
4329 				softnic_parse_ipv6_addr(tokens[2], &da) ||
4330 				softnic_parser_read_uint32(&flow_label, tokens[3]) ||
4331 				softnic_parser_read_uint8(&dscp, tokens[4]) ||
4332 				(dscp > 64) ||
4333 				softnic_parser_read_uint8(&hop_limit, tokens[5]))
4334 				return 0;
4335 
4336 			memcpy(a->encap.vxlan.ipv6.sa, sa.s6_addr, 16);
4337 			memcpy(a->encap.vxlan.ipv6.da, da.s6_addr, 16);
4338 			a->encap.vxlan.ipv6.flow_label = flow_label;
4339 			a->encap.vxlan.ipv6.dscp = dscp;
4340 			a->encap.vxlan.ipv6.hop_limit = hop_limit;
4341 
4342 			n_tokens -= 6;
4343 			tokens += 6;
4344 			n += 6;
4345 		} else
4346 			return 0;
4347 
4348 		/* udp <sp> <dp> */
4349 		if ((n_tokens < 3) ||
4350 			strcmp(tokens[0], "udp") ||
4351 			softnic_parser_read_uint16(&a->encap.vxlan.udp.sp, tokens[1]) ||
4352 			softnic_parser_read_uint16(&a->encap.vxlan.udp.dp, tokens[2]))
4353 			return 0;
4354 
4355 		n_tokens -= 3;
4356 		tokens += 3;
4357 		n += 3;
4358 
4359 		/* vxlan <vni> */
4360 		if ((n_tokens < 2) ||
4361 			strcmp(tokens[0], "vxlan") ||
4362 			softnic_parser_read_uint32(&a->encap.vxlan.vxlan.vni, tokens[1]) ||
4363 			(a->encap.vxlan.vxlan.vni > 0xFFFFFF))
4364 			return 0;
4365 
4366 		n_tokens -= 2;
4367 		tokens += 2;
4368 		n += 2;
4369 
4370 		a->encap.type = RTE_TABLE_ACTION_ENCAP_VXLAN;
4371 		a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
4372 		return 1 + n;
4373 	}
4374 
4375 	return 0;
4376 }
4377 
4378 static uint32_t
4379 parse_table_action_nat(char **tokens,
4380 	uint32_t n_tokens,
4381 	struct softnic_table_rule_action *a)
4382 {
4383 	if (n_tokens < 4 ||
4384 		strcmp(tokens[0], "nat"))
4385 		return 0;
4386 
4387 	if (strcmp(tokens[1], "ipv4") == 0) {
4388 		struct in_addr addr;
4389 		uint16_t port;
4390 
4391 		if (softnic_parse_ipv4_addr(tokens[2], &addr) ||
4392 			softnic_parser_read_uint16(&port, tokens[3]))
4393 			return 0;
4394 
4395 		a->nat.ip_version = 1;
4396 		a->nat.addr.ipv4 = rte_be_to_cpu_32(addr.s_addr);
4397 		a->nat.port = port;
4398 		a->action_mask |= 1 << RTE_TABLE_ACTION_NAT;
4399 		return 4;
4400 	}
4401 
4402 	if (strcmp(tokens[1], "ipv6") == 0) {
4403 		struct in6_addr addr;
4404 		uint16_t port;
4405 
4406 		if (softnic_parse_ipv6_addr(tokens[2], &addr) ||
4407 			softnic_parser_read_uint16(&port, tokens[3]))
4408 			return 0;
4409 
4410 		a->nat.ip_version = 0;
4411 		memcpy(a->nat.addr.ipv6, addr.s6_addr, 16);
4412 		a->nat.port = port;
4413 		a->action_mask |= 1 << RTE_TABLE_ACTION_NAT;
4414 		return 4;
4415 	}
4416 
4417 	return 0;
4418 }
4419 
4420 static uint32_t
4421 parse_table_action_ttl(char **tokens,
4422 	uint32_t n_tokens,
4423 	struct softnic_table_rule_action *a)
4424 {
4425 	if (n_tokens < 2 ||
4426 		strcmp(tokens[0], "ttl"))
4427 		return 0;
4428 
4429 	if (strcmp(tokens[1], "dec") == 0)
4430 		a->ttl.decrement = 1;
4431 	else if (strcmp(tokens[1], "keep") == 0)
4432 		a->ttl.decrement = 0;
4433 	else
4434 		return 0;
4435 
4436 	a->action_mask |= 1 << RTE_TABLE_ACTION_TTL;
4437 	return 2;
4438 }
4439 
4440 static uint32_t
4441 parse_table_action_stats(char **tokens,
4442 	uint32_t n_tokens,
4443 	struct softnic_table_rule_action *a)
4444 {
4445 	if (n_tokens < 1 ||
4446 		strcmp(tokens[0], "stats"))
4447 		return 0;
4448 
4449 	a->stats.n_packets = 0;
4450 	a->stats.n_bytes = 0;
4451 	a->action_mask |= 1 << RTE_TABLE_ACTION_STATS;
4452 	return 1;
4453 }
4454 
4455 static uint32_t
4456 parse_table_action_time(char **tokens,
4457 	uint32_t n_tokens,
4458 	struct softnic_table_rule_action *a)
4459 {
4460 	if (n_tokens < 1 ||
4461 		strcmp(tokens[0], "time"))
4462 		return 0;
4463 
4464 	a->time.time = rte_rdtsc();
4465 	a->action_mask |= 1 << RTE_TABLE_ACTION_TIME;
4466 	return 1;
4467 }
4468 
4469 static void
4470 parse_free_sym_crypto_param_data(struct rte_table_action_sym_crypto_params *p)
4471 {
4472 	struct rte_crypto_sym_xform *xform[2] = {NULL};
4473 	uint32_t i;
4474 
4475 	xform[0] = p->xform;
4476 	if (xform[0])
4477 		xform[1] = xform[0]->next;
4478 
4479 	for (i = 0; i < 2; i++) {
4480 		if (xform[i] == NULL)
4481 			continue;
4482 
4483 		switch (xform[i]->type) {
4484 		case RTE_CRYPTO_SYM_XFORM_CIPHER:
4485 			if (p->cipher_auth.cipher_iv.val)
4486 				free(p->cipher_auth.cipher_iv.val);
4487 			if (p->cipher_auth.cipher_iv_update.val)
4488 				free(p->cipher_auth.cipher_iv_update.val);
4489 			break;
4490 		case RTE_CRYPTO_SYM_XFORM_AUTH:
4491 			if (p->cipher_auth.auth_iv.val)
4492 				free(p->cipher_auth.cipher_iv.val);
4493 			if (p->cipher_auth.auth_iv_update.val)
4494 				free(p->cipher_auth.cipher_iv_update.val);
4495 			break;
4496 		case RTE_CRYPTO_SYM_XFORM_AEAD:
4497 			if (p->aead.iv.val)
4498 				free(p->aead.iv.val);
4499 			if (p->aead.aad.val)
4500 				free(p->aead.aad.val);
4501 			break;
4502 		default:
4503 			continue;
4504 		}
4505 	}
4506 
4507 }
4508 
4509 static struct rte_crypto_sym_xform *
4510 parse_table_action_cipher(struct rte_table_action_sym_crypto_params *p,
4511 		uint8_t *key, uint32_t max_key_len, char **tokens,
4512 		uint32_t n_tokens, uint32_t encrypt, uint32_t *used_n_tokens)
4513 {
4514 	struct rte_crypto_sym_xform *xform_cipher;
4515 	int status;
4516 	size_t len;
4517 
4518 	if (n_tokens < 7 || strcmp(tokens[1], "cipher_algo") ||
4519 			strcmp(tokens[3], "cipher_key") ||
4520 			strcmp(tokens[5], "cipher_iv"))
4521 		return NULL;
4522 
4523 	xform_cipher = calloc(1, sizeof(*xform_cipher));
4524 	if (xform_cipher == NULL)
4525 		return NULL;
4526 
4527 	xform_cipher->type = RTE_CRYPTO_SYM_XFORM_CIPHER;
4528 	xform_cipher->cipher.op = encrypt ? RTE_CRYPTO_CIPHER_OP_ENCRYPT :
4529 			RTE_CRYPTO_CIPHER_OP_DECRYPT;
4530 
4531 	/* cipher_algo */
4532 	status = rte_cryptodev_get_cipher_algo_enum(
4533 			&xform_cipher->cipher.algo, tokens[2]);
4534 	if (status < 0)
4535 		goto error_exit;
4536 
4537 	/* cipher_key */
4538 	len = strlen(tokens[4]);
4539 	if (len / 2 > max_key_len) {
4540 		status = -ENOMEM;
4541 		goto error_exit;
4542 	}
4543 
4544 	status = softnic_parse_hex_string(tokens[4], key, (uint32_t *)&len);
4545 	if (status < 0)
4546 		goto error_exit;
4547 
4548 	xform_cipher->cipher.key.data = key;
4549 	xform_cipher->cipher.key.length = (uint16_t)len;
4550 
4551 	/* cipher_iv */
4552 	len = strlen(tokens[6]);
4553 
4554 	p->cipher_auth.cipher_iv.val = calloc(1, len / 2 + 1);
4555 	if (p->cipher_auth.cipher_iv.val == NULL)
4556 		goto error_exit;
4557 
4558 	status = softnic_parse_hex_string(tokens[6],
4559 			p->cipher_auth.cipher_iv.val,
4560 			(uint32_t *)&len);
4561 	if (status < 0)
4562 		goto error_exit;
4563 
4564 	xform_cipher->cipher.iv.length = (uint16_t)len;
4565 	xform_cipher->cipher.iv.offset = RTE_TABLE_ACTION_SYM_CRYPTO_IV_OFFSET;
4566 	p->cipher_auth.cipher_iv.length = (uint32_t)len;
4567 	*used_n_tokens = 7;
4568 
4569 	return xform_cipher;
4570 
4571 error_exit:
4572 	if (p->cipher_auth.cipher_iv.val) {
4573 		free(p->cipher_auth.cipher_iv.val);
4574 		p->cipher_auth.cipher_iv.val = NULL;
4575 	}
4576 
4577 	free(xform_cipher);
4578 
4579 	return NULL;
4580 }
4581 
4582 static struct rte_crypto_sym_xform *
4583 parse_table_action_cipher_auth(struct rte_table_action_sym_crypto_params *p,
4584 		uint8_t *key, uint32_t max_key_len, char **tokens,
4585 		uint32_t n_tokens, uint32_t encrypt, uint32_t *used_n_tokens)
4586 {
4587 	struct rte_crypto_sym_xform *xform_cipher;
4588 	struct rte_crypto_sym_xform *xform_auth;
4589 	int status;
4590 	size_t len;
4591 
4592 	if (n_tokens < 13 ||
4593 			strcmp(tokens[7], "auth_algo") ||
4594 			strcmp(tokens[9], "auth_key") ||
4595 			strcmp(tokens[11], "digest_size"))
4596 		return NULL;
4597 
4598 	xform_auth = calloc(1, sizeof(*xform_auth));
4599 	if (xform_auth == NULL)
4600 		return NULL;
4601 
4602 	xform_auth->type = RTE_CRYPTO_SYM_XFORM_AUTH;
4603 	xform_auth->auth.op = encrypt ? RTE_CRYPTO_AUTH_OP_GENERATE :
4604 			RTE_CRYPTO_AUTH_OP_VERIFY;
4605 
4606 	/* auth_algo */
4607 	status = rte_cryptodev_get_auth_algo_enum(&xform_auth->auth.algo,
4608 			tokens[8]);
4609 	if (status < 0)
4610 		goto error_exit;
4611 
4612 	/* auth_key */
4613 	len = strlen(tokens[10]);
4614 	if (len / 2 > max_key_len) {
4615 		status = -ENOMEM;
4616 		goto error_exit;
4617 	}
4618 
4619 	status = softnic_parse_hex_string(tokens[10], key, (uint32_t *)&len);
4620 	if (status < 0)
4621 		goto error_exit;
4622 
4623 	xform_auth->auth.key.data = key;
4624 	xform_auth->auth.key.length = (uint16_t)len;
4625 
4626 	key += xform_auth->auth.key.length;
4627 	max_key_len -= xform_auth->auth.key.length;
4628 
4629 	if (strcmp(tokens[11], "digest_size"))
4630 		goto error_exit;
4631 
4632 	status = softnic_parser_read_uint16(&xform_auth->auth.digest_length,
4633 			tokens[12]);
4634 	if (status < 0)
4635 		goto error_exit;
4636 
4637 	xform_cipher = parse_table_action_cipher(p, key, max_key_len, tokens, 7,
4638 			encrypt, used_n_tokens);
4639 	if (xform_cipher == NULL)
4640 		goto error_exit;
4641 
4642 	*used_n_tokens += 6;
4643 
4644 	if (encrypt) {
4645 		xform_cipher->next = xform_auth;
4646 		return xform_cipher;
4647 	} else {
4648 		xform_auth->next = xform_cipher;
4649 		return xform_auth;
4650 	}
4651 
4652 error_exit:
4653 	if (p->cipher_auth.auth_iv.val) {
4654 		free(p->cipher_auth.auth_iv.val);
4655 		p->cipher_auth.auth_iv.val = 0;
4656 	}
4657 
4658 	free(xform_auth);
4659 
4660 	return NULL;
4661 }
4662 
4663 static struct rte_crypto_sym_xform *
4664 parse_table_action_aead(struct rte_table_action_sym_crypto_params *p,
4665 		uint8_t *key, uint32_t max_key_len, char **tokens,
4666 		uint32_t n_tokens, uint32_t encrypt, uint32_t *used_n_tokens)
4667 {
4668 	struct rte_crypto_sym_xform *xform_aead;
4669 	int status;
4670 	size_t len;
4671 
4672 	if (n_tokens < 11 || strcmp(tokens[1], "aead_algo") ||
4673 			strcmp(tokens[3], "aead_key") ||
4674 			strcmp(tokens[5], "aead_iv") ||
4675 			strcmp(tokens[7], "aead_aad") ||
4676 			strcmp(tokens[9], "digest_size"))
4677 		return NULL;
4678 
4679 	xform_aead = calloc(1, sizeof(*xform_aead));
4680 	if (xform_aead == NULL)
4681 		return NULL;
4682 
4683 	xform_aead->type = RTE_CRYPTO_SYM_XFORM_AEAD;
4684 	xform_aead->aead.op = encrypt ? RTE_CRYPTO_AEAD_OP_ENCRYPT :
4685 			RTE_CRYPTO_AEAD_OP_DECRYPT;
4686 
4687 	/* aead_algo */
4688 	status = rte_cryptodev_get_aead_algo_enum(&xform_aead->aead.algo,
4689 			tokens[2]);
4690 	if (status < 0)
4691 		goto error_exit;
4692 
4693 	/* aead_key */
4694 	len = strlen(tokens[4]);
4695 	if (len / 2 > max_key_len) {
4696 		status = -ENOMEM;
4697 		goto error_exit;
4698 	}
4699 
4700 	status = softnic_parse_hex_string(tokens[4], key, (uint32_t *)&len);
4701 	if (status < 0)
4702 		goto error_exit;
4703 
4704 	xform_aead->aead.key.data = key;
4705 	xform_aead->aead.key.length = (uint16_t)len;
4706 
4707 	/* aead_iv */
4708 	len = strlen(tokens[6]);
4709 	p->aead.iv.val = calloc(1, len / 2 + 1);
4710 	if (p->aead.iv.val == NULL)
4711 		goto error_exit;
4712 
4713 	status = softnic_parse_hex_string(tokens[6], p->aead.iv.val,
4714 			(uint32_t *)&len);
4715 	if (status < 0)
4716 		goto error_exit;
4717 
4718 	xform_aead->aead.iv.length = (uint16_t)len;
4719 	xform_aead->aead.iv.offset = RTE_TABLE_ACTION_SYM_CRYPTO_IV_OFFSET;
4720 	p->aead.iv.length = (uint32_t)len;
4721 
4722 	/* aead_aad */
4723 	len = strlen(tokens[8]);
4724 	p->aead.aad.val = calloc(1, len / 2 + 1);
4725 	if (p->aead.aad.val == NULL)
4726 		goto error_exit;
4727 
4728 	status = softnic_parse_hex_string(tokens[8], p->aead.aad.val, (uint32_t *)&len);
4729 	if (status < 0)
4730 		goto error_exit;
4731 
4732 	xform_aead->aead.aad_length = (uint16_t)len;
4733 	p->aead.aad.length = (uint32_t)len;
4734 
4735 	/* digest_size */
4736 	status = softnic_parser_read_uint16(&xform_aead->aead.digest_length,
4737 			tokens[10]);
4738 	if (status < 0)
4739 		goto error_exit;
4740 
4741 	*used_n_tokens = 11;
4742 
4743 	return xform_aead;
4744 
4745 error_exit:
4746 	if (p->aead.iv.val) {
4747 		free(p->aead.iv.val);
4748 		p->aead.iv.val = NULL;
4749 	}
4750 	if (p->aead.aad.val) {
4751 		free(p->aead.aad.val);
4752 		p->aead.aad.val = NULL;
4753 	}
4754 
4755 	free(xform_aead);
4756 
4757 	return NULL;
4758 }
4759 
4760 
4761 static uint32_t
4762 parse_table_action_sym_crypto(char **tokens,
4763 	uint32_t n_tokens,
4764 	struct softnic_table_rule_action *a)
4765 {
4766 	struct rte_table_action_sym_crypto_params *p = &a->sym_crypto;
4767 	struct rte_crypto_sym_xform *xform = NULL;
4768 	uint8_t *key = a->sym_crypto_key;
4769 	uint32_t max_key_len = SYM_CRYPTO_MAX_KEY_SIZE;
4770 	uint32_t used_n_tokens;
4771 	uint32_t encrypt;
4772 	int status;
4773 
4774 	if ((n_tokens < 12) ||
4775 		strcmp(tokens[0], "sym_crypto") ||
4776 		strcmp(tokens[2], "type"))
4777 		return 0;
4778 
4779 	memset(p, 0, sizeof(*p));
4780 
4781 	if (strcmp(tokens[1], "encrypt") == 0)
4782 		encrypt = 1;
4783 	else
4784 		encrypt = 0;
4785 
4786 	status = softnic_parser_read_uint32(&p->data_offset, tokens[n_tokens - 1]);
4787 	if (status < 0)
4788 		return 0;
4789 
4790 	if (strcmp(tokens[3], "cipher") == 0) {
4791 		tokens += 3;
4792 		n_tokens -= 3;
4793 
4794 		xform = parse_table_action_cipher(p, key, max_key_len, tokens,
4795 				n_tokens, encrypt, &used_n_tokens);
4796 	} else if (strcmp(tokens[3], "cipher_auth") == 0) {
4797 		tokens += 3;
4798 		n_tokens -= 3;
4799 
4800 		xform = parse_table_action_cipher_auth(p, key, max_key_len,
4801 				tokens, n_tokens, encrypt, &used_n_tokens);
4802 	} else if (strcmp(tokens[3], "aead") == 0) {
4803 		tokens += 3;
4804 		n_tokens -= 3;
4805 
4806 		xform = parse_table_action_aead(p, key, max_key_len, tokens,
4807 				n_tokens, encrypt, &used_n_tokens);
4808 	}
4809 
4810 	if (xform == NULL)
4811 		return 0;
4812 
4813 	p->xform = xform;
4814 
4815 	if (strcmp(tokens[used_n_tokens], "data_offset")) {
4816 		parse_free_sym_crypto_param_data(p);
4817 		return 0;
4818 	}
4819 
4820 	a->action_mask |= 1 << RTE_TABLE_ACTION_SYM_CRYPTO;
4821 
4822 	return used_n_tokens + 5;
4823 }
4824 
4825 static uint32_t
4826 parse_table_action_tag(char **tokens,
4827 	uint32_t n_tokens,
4828 	struct softnic_table_rule_action *a)
4829 {
4830 	if (n_tokens < 2 ||
4831 		strcmp(tokens[0], "tag"))
4832 		return 0;
4833 
4834 	if (softnic_parser_read_uint32(&a->tag.tag, tokens[1]))
4835 		return 0;
4836 
4837 	a->action_mask |= 1 << RTE_TABLE_ACTION_TAG;
4838 	return 2;
4839 }
4840 
4841 static uint32_t
4842 parse_table_action_decap(char **tokens,
4843 	uint32_t n_tokens,
4844 	struct softnic_table_rule_action *a)
4845 {
4846 	if (n_tokens < 2 ||
4847 		strcmp(tokens[0], "decap"))
4848 		return 0;
4849 
4850 	if (softnic_parser_read_uint16(&a->decap.n, tokens[1]))
4851 		return 0;
4852 
4853 	a->action_mask |= 1 << RTE_TABLE_ACTION_DECAP;
4854 	return 2;
4855 }
4856 
4857 static uint32_t
4858 parse_table_action(char **tokens,
4859 	uint32_t n_tokens,
4860 	char *out,
4861 	size_t out_size,
4862 	struct softnic_table_rule_action *a)
4863 {
4864 	uint32_t n_tokens0 = n_tokens;
4865 
4866 	memset(a, 0, sizeof(*a));
4867 
4868 	if (n_tokens < 2 ||
4869 		strcmp(tokens[0], "action"))
4870 		return 0;
4871 
4872 	tokens++;
4873 	n_tokens--;
4874 
4875 	if (n_tokens && (strcmp(tokens[0], "fwd") == 0)) {
4876 		uint32_t n;
4877 
4878 		n = parse_table_action_fwd(tokens, n_tokens, a);
4879 		if (n == 0) {
4880 			snprintf(out, out_size, MSG_ARG_INVALID,
4881 				"action fwd");
4882 			return 0;
4883 		}
4884 
4885 		tokens += n;
4886 		n_tokens -= n;
4887 	}
4888 
4889 	if (n_tokens && (strcmp(tokens[0], "balance") == 0)) {
4890 		uint32_t n;
4891 
4892 		n = parse_table_action_balance(tokens, n_tokens, a);
4893 		if (n == 0) {
4894 			snprintf(out, out_size, MSG_ARG_INVALID,
4895 				"action balance");
4896 			return 0;
4897 		}
4898 
4899 		tokens += n;
4900 		n_tokens -= n;
4901 	}
4902 
4903 	if (n_tokens && (strcmp(tokens[0], "meter") == 0)) {
4904 		uint32_t n;
4905 
4906 		n = parse_table_action_meter(tokens, n_tokens, a);
4907 		if (n == 0) {
4908 			snprintf(out, out_size, MSG_ARG_INVALID,
4909 				"action meter");
4910 			return 0;
4911 		}
4912 
4913 		tokens += n;
4914 		n_tokens -= n;
4915 	}
4916 
4917 	if (n_tokens && (strcmp(tokens[0], "tm") == 0)) {
4918 		uint32_t n;
4919 
4920 		n = parse_table_action_tm(tokens, n_tokens, a);
4921 		if (n == 0) {
4922 			snprintf(out, out_size, MSG_ARG_INVALID,
4923 				"action tm");
4924 			return 0;
4925 		}
4926 
4927 		tokens += n;
4928 		n_tokens -= n;
4929 	}
4930 
4931 	if (n_tokens && (strcmp(tokens[0], "encap") == 0)) {
4932 		uint32_t n;
4933 
4934 		n = parse_table_action_encap(tokens, n_tokens, a);
4935 		if (n == 0) {
4936 			snprintf(out, out_size, MSG_ARG_INVALID,
4937 				"action encap");
4938 			return 0;
4939 		}
4940 
4941 		tokens += n;
4942 		n_tokens -= n;
4943 	}
4944 
4945 	if (n_tokens && (strcmp(tokens[0], "nat") == 0)) {
4946 		uint32_t n;
4947 
4948 		n = parse_table_action_nat(tokens, n_tokens, a);
4949 		if (n == 0) {
4950 			snprintf(out, out_size, MSG_ARG_INVALID,
4951 				"action nat");
4952 			return 0;
4953 		}
4954 
4955 		tokens += n;
4956 		n_tokens -= n;
4957 	}
4958 
4959 	if (n_tokens && (strcmp(tokens[0], "ttl") == 0)) {
4960 		uint32_t n;
4961 
4962 		n = parse_table_action_ttl(tokens, n_tokens, a);
4963 		if (n == 0) {
4964 			snprintf(out, out_size, MSG_ARG_INVALID,
4965 				"action ttl");
4966 			return 0;
4967 		}
4968 
4969 		tokens += n;
4970 		n_tokens -= n;
4971 	}
4972 
4973 	if (n_tokens && (strcmp(tokens[0], "stats") == 0)) {
4974 		uint32_t n;
4975 
4976 		n = parse_table_action_stats(tokens, n_tokens, a);
4977 		if (n == 0) {
4978 			snprintf(out, out_size, MSG_ARG_INVALID,
4979 				"action stats");
4980 			return 0;
4981 		}
4982 
4983 		tokens += n;
4984 		n_tokens -= n;
4985 	}
4986 
4987 	if (n_tokens && (strcmp(tokens[0], "time") == 0)) {
4988 		uint32_t n;
4989 
4990 		n = parse_table_action_time(tokens, n_tokens, a);
4991 		if (n == 0) {
4992 			snprintf(out, out_size, MSG_ARG_INVALID,
4993 				"action time");
4994 			return 0;
4995 		}
4996 
4997 		tokens += n;
4998 		n_tokens -= n;
4999 	}
5000 
5001 	if (n_tokens && (strcmp(tokens[0], "tag") == 0)) {
5002 		uint32_t n;
5003 
5004 		n = parse_table_action_tag(tokens, n_tokens, a);
5005 		if (n == 0) {
5006 			snprintf(out, out_size, MSG_ARG_INVALID,
5007 				"action tag");
5008 			return 0;
5009 		}
5010 
5011 		tokens += n;
5012 		n_tokens -= n;
5013 	}
5014 
5015 	if (n_tokens && (strcmp(tokens[0], "decap") == 0)) {
5016 		uint32_t n;
5017 
5018 		n = parse_table_action_decap(tokens, n_tokens, a);
5019 		if (n == 0) {
5020 			snprintf(out, out_size, MSG_ARG_INVALID,
5021 				"action decap");
5022 			return 0;
5023 		}
5024 
5025 		tokens += n;
5026 		n_tokens -= n;
5027 	}
5028 
5029 	if (n_tokens && (strcmp(tokens[0], "sym_crypto") == 0)) {
5030 		uint32_t n;
5031 
5032 		n = parse_table_action_sym_crypto(tokens, n_tokens, a);
5033 		if (n == 0) {
5034 			snprintf(out, out_size, MSG_ARG_INVALID,
5035 				"action sym_crypto");
5036 		}
5037 
5038 		tokens += n;
5039 		n_tokens -= n;
5040 	}
5041 
5042 	if (n_tokens0 - n_tokens == 1) {
5043 		snprintf(out, out_size, MSG_ARG_INVALID, "action");
5044 		return 0;
5045 	}
5046 
5047 	return n_tokens0 - n_tokens;
5048 }
5049 
5050 /**
5051  * pipeline <pipeline_name> table <table_id> rule add
5052  *    match <match>
5053  *    action <table_action>
5054  */
5055 static void
5056 cmd_softnic_pipeline_table_rule_add(struct pmd_internals *softnic,
5057 	char **tokens,
5058 	uint32_t n_tokens,
5059 	char *out,
5060 	size_t out_size)
5061 {
5062 	struct softnic_table_rule_match m;
5063 	struct softnic_table_rule_action a;
5064 	char *pipeline_name;
5065 	void *data;
5066 	uint32_t table_id, t0, n_tokens_parsed;
5067 	int status;
5068 
5069 	if (n_tokens < 8) {
5070 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5071 		return;
5072 	}
5073 
5074 	pipeline_name = tokens[1];
5075 
5076 	if (strcmp(tokens[2], "table") != 0) {
5077 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
5078 		return;
5079 	}
5080 
5081 	if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
5082 		snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
5083 		return;
5084 	}
5085 
5086 	if (strcmp(tokens[4], "rule") != 0) {
5087 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule");
5088 		return;
5089 	}
5090 
5091 	if (strcmp(tokens[5], "add") != 0) {
5092 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
5093 		return;
5094 	}
5095 
5096 	t0 = 6;
5097 
5098 	/* match */
5099 	n_tokens_parsed = parse_match(tokens + t0,
5100 		n_tokens - t0,
5101 		out,
5102 		out_size,
5103 		&m);
5104 	if (n_tokens_parsed == 0)
5105 		return;
5106 	t0 += n_tokens_parsed;
5107 
5108 	/* action */
5109 	n_tokens_parsed = parse_table_action(tokens + t0,
5110 		n_tokens - t0,
5111 		out,
5112 		out_size,
5113 		&a);
5114 	if (n_tokens_parsed == 0)
5115 		return;
5116 	t0 += n_tokens_parsed;
5117 
5118 	if (t0 != n_tokens) {
5119 		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
5120 		return;
5121 	}
5122 
5123 	status = softnic_pipeline_table_rule_add(softnic,
5124 		pipeline_name,
5125 		table_id,
5126 		&m,
5127 		&a,
5128 		&data);
5129 	if (status) {
5130 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
5131 		return;
5132 	}
5133 }
5134 
5135 /**
5136  * pipeline <pipeline_name> table <table_id> rule add
5137  *    match
5138  *       default
5139  *    action
5140  *       fwd
5141  *          drop
5142  *          | port <port_id>
5143  *          | meta
5144  *          | table <table_id>
5145  */
5146 static void
5147 cmd_softnic_pipeline_table_rule_add_default(struct pmd_internals *softnic,
5148 	char **tokens,
5149 	uint32_t n_tokens,
5150 	char *out,
5151 	size_t out_size)
5152 {
5153 	struct softnic_table_rule_action action;
5154 	void *data;
5155 	char *pipeline_name;
5156 	uint32_t table_id;
5157 	int status;
5158 
5159 	if (n_tokens != 11 &&
5160 		n_tokens != 12) {
5161 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5162 		return;
5163 	}
5164 
5165 	pipeline_name = tokens[1];
5166 
5167 	if (strcmp(tokens[2], "table") != 0) {
5168 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
5169 		return;
5170 	}
5171 
5172 	if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
5173 		snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
5174 		return;
5175 	}
5176 
5177 	if (strcmp(tokens[4], "rule") != 0) {
5178 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule");
5179 		return;
5180 	}
5181 
5182 	if (strcmp(tokens[5], "add") != 0) {
5183 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
5184 		return;
5185 	}
5186 
5187 	if (strcmp(tokens[6], "match") != 0) {
5188 		snprintf(out, out_size, MSG_ARG_INVALID, "match");
5189 		return;
5190 	}
5191 
5192 	if (strcmp(tokens[7], "default") != 0) {
5193 		snprintf(out, out_size, MSG_ARG_INVALID, "default");
5194 		return;
5195 	}
5196 
5197 	if (strcmp(tokens[8], "action") != 0) {
5198 		snprintf(out, out_size, MSG_ARG_INVALID, "action");
5199 		return;
5200 	}
5201 
5202 	if (strcmp(tokens[9], "fwd") != 0) {
5203 		snprintf(out, out_size, MSG_ARG_INVALID, "fwd");
5204 		return;
5205 	}
5206 
5207 	action.action_mask = 1 << RTE_TABLE_ACTION_FWD;
5208 
5209 	if (strcmp(tokens[10], "drop") == 0) {
5210 		if (n_tokens != 11) {
5211 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5212 			return;
5213 		}
5214 
5215 		action.fwd.action = RTE_PIPELINE_ACTION_DROP;
5216 	} else if (strcmp(tokens[10], "port") == 0) {
5217 		uint32_t id;
5218 
5219 		if (n_tokens != 12) {
5220 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5221 			return;
5222 		}
5223 
5224 		if (softnic_parser_read_uint32(&id, tokens[11]) != 0) {
5225 			snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
5226 			return;
5227 		}
5228 
5229 		action.fwd.action = RTE_PIPELINE_ACTION_PORT;
5230 		action.fwd.id = id;
5231 	} else if (strcmp(tokens[10], "meta") == 0) {
5232 		if (n_tokens != 11) {
5233 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5234 			return;
5235 		}
5236 
5237 		action.fwd.action = RTE_PIPELINE_ACTION_PORT_META;
5238 	} else if (strcmp(tokens[10], "table") == 0) {
5239 		uint32_t id;
5240 
5241 		if (n_tokens != 12) {
5242 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5243 			return;
5244 		}
5245 
5246 		if (softnic_parser_read_uint32(&id, tokens[11]) != 0) {
5247 			snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
5248 			return;
5249 		}
5250 
5251 		action.fwd.action = RTE_PIPELINE_ACTION_TABLE;
5252 		action.fwd.id = id;
5253 	} else {
5254 		snprintf(out, out_size, MSG_ARG_INVALID,
5255 			"drop or port or meta or table");
5256 		return;
5257 	}
5258 
5259 	status = softnic_pipeline_table_rule_add_default(softnic,
5260 		pipeline_name,
5261 		table_id,
5262 		&action,
5263 		&data);
5264 	if (status) {
5265 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
5266 		return;
5267 	}
5268 }
5269 
5270 /**
5271  * pipeline <pipeline_name> table <table_id> rule add bulk <file_name> <n_rules>
5272  *
5273  * File <file_name>:
5274  * - line format: match <match> action <action>
5275  */
5276 static int
5277 cli_rule_file_process(const char *file_name,
5278 	size_t line_len_max,
5279 	struct softnic_table_rule_match *m,
5280 	struct softnic_table_rule_action *a,
5281 	uint32_t *n_rules,
5282 	uint32_t *line_number,
5283 	char *out,
5284 	size_t out_size);
5285 
5286 static void
5287 cmd_softnic_pipeline_table_rule_add_bulk(struct pmd_internals *softnic,
5288 	char **tokens,
5289 	uint32_t n_tokens,
5290 	char *out,
5291 	size_t out_size)
5292 {
5293 	struct softnic_table_rule_match *match;
5294 	struct softnic_table_rule_action *action;
5295 	void **data;
5296 	char *pipeline_name, *file_name;
5297 	uint32_t table_id, n_rules, n_rules_parsed, line_number;
5298 	int status;
5299 
5300 	if (n_tokens != 9) {
5301 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5302 		return;
5303 	}
5304 
5305 	pipeline_name = tokens[1];
5306 
5307 	if (strcmp(tokens[2], "table") != 0) {
5308 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
5309 		return;
5310 	}
5311 
5312 	if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
5313 		snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
5314 		return;
5315 	}
5316 
5317 	if (strcmp(tokens[4], "rule") != 0) {
5318 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule");
5319 		return;
5320 	}
5321 
5322 	if (strcmp(tokens[5], "add") != 0) {
5323 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
5324 		return;
5325 	}
5326 
5327 	if (strcmp(tokens[6], "bulk") != 0) {
5328 		snprintf(out, out_size, MSG_ARG_INVALID, "bulk");
5329 		return;
5330 	}
5331 
5332 	file_name = tokens[7];
5333 
5334 	if ((softnic_parser_read_uint32(&n_rules, tokens[8]) != 0) ||
5335 		n_rules == 0) {
5336 		snprintf(out, out_size, MSG_ARG_INVALID, "n_rules");
5337 		return;
5338 	}
5339 
5340 	/* Memory allocation. */
5341 	match = calloc(n_rules, sizeof(struct softnic_table_rule_match));
5342 	action = calloc(n_rules, sizeof(struct softnic_table_rule_action));
5343 	data = calloc(n_rules, sizeof(void *));
5344 	if (match == NULL ||
5345 		action == NULL ||
5346 		data == NULL) {
5347 		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
5348 		free(data);
5349 		free(action);
5350 		free(match);
5351 		return;
5352 	}
5353 
5354 	/* Load rule file */
5355 	n_rules_parsed = n_rules;
5356 	status = cli_rule_file_process(file_name,
5357 		1024,
5358 		match,
5359 		action,
5360 		&n_rules_parsed,
5361 		&line_number,
5362 		out,
5363 		out_size);
5364 	if (status) {
5365 		snprintf(out, out_size, MSG_FILE_ERR, file_name, line_number);
5366 		free(data);
5367 		free(action);
5368 		free(match);
5369 		return;
5370 	}
5371 	if (n_rules_parsed != n_rules) {
5372 		snprintf(out, out_size, MSG_FILE_NOT_ENOUGH, file_name);
5373 		free(data);
5374 		free(action);
5375 		free(match);
5376 		return;
5377 	}
5378 
5379 	/* Rule bulk add */
5380 	status = softnic_pipeline_table_rule_add_bulk(softnic,
5381 		pipeline_name,
5382 		table_id,
5383 		match,
5384 		action,
5385 		data,
5386 		&n_rules);
5387 	if (status) {
5388 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
5389 		free(data);
5390 		free(action);
5391 		free(match);
5392 		return;
5393 	}
5394 
5395 	/* Memory free */
5396 	free(data);
5397 	free(action);
5398 	free(match);
5399 }
5400 
5401 /**
5402  * pipeline <pipeline_name> table <table_id> rule delete
5403  *    match <match>
5404  */
5405 static void
5406 cmd_softnic_pipeline_table_rule_delete(struct pmd_internals *softnic,
5407 	char **tokens,
5408 	uint32_t n_tokens,
5409 	char *out,
5410 	size_t out_size)
5411 {
5412 	struct softnic_table_rule_match m;
5413 	char *pipeline_name;
5414 	uint32_t table_id, n_tokens_parsed, t0;
5415 	int status;
5416 
5417 	if (n_tokens < 8) {
5418 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5419 		return;
5420 	}
5421 
5422 	pipeline_name = tokens[1];
5423 
5424 	if (strcmp(tokens[2], "table") != 0) {
5425 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
5426 		return;
5427 	}
5428 
5429 	if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
5430 		snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
5431 		return;
5432 	}
5433 
5434 	if (strcmp(tokens[4], "rule") != 0) {
5435 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule");
5436 		return;
5437 	}
5438 
5439 	if (strcmp(tokens[5], "delete") != 0) {
5440 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
5441 		return;
5442 	}
5443 
5444 	t0 = 6;
5445 
5446 	/* match */
5447 	n_tokens_parsed = parse_match(tokens + t0,
5448 		n_tokens - t0,
5449 		out,
5450 		out_size,
5451 		&m);
5452 	if (n_tokens_parsed == 0)
5453 		return;
5454 	t0 += n_tokens_parsed;
5455 
5456 	if (n_tokens != t0) {
5457 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5458 		return;
5459 	}
5460 
5461 	status = softnic_pipeline_table_rule_delete(softnic,
5462 		pipeline_name,
5463 		table_id,
5464 		&m);
5465 	if (status) {
5466 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
5467 		return;
5468 	}
5469 }
5470 
5471 /**
5472  * pipeline <pipeline_name> table <table_id> rule delete
5473  *    match
5474  *       default
5475  */
5476 static void
5477 cmd_softnic_pipeline_table_rule_delete_default(struct pmd_internals *softnic,
5478 	char **tokens,
5479 	uint32_t n_tokens,
5480 	char *out,
5481 	size_t out_size)
5482 {
5483 	char *pipeline_name;
5484 	uint32_t table_id;
5485 	int status;
5486 
5487 	if (n_tokens != 8) {
5488 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5489 		return;
5490 	}
5491 
5492 	pipeline_name = tokens[1];
5493 
5494 	if (strcmp(tokens[2], "table") != 0) {
5495 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
5496 		return;
5497 	}
5498 
5499 	if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
5500 		snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
5501 		return;
5502 	}
5503 
5504 	if (strcmp(tokens[4], "rule") != 0) {
5505 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule");
5506 		return;
5507 	}
5508 
5509 	if (strcmp(tokens[5], "delete") != 0) {
5510 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
5511 		return;
5512 	}
5513 
5514 	if (strcmp(tokens[6], "match") != 0) {
5515 		snprintf(out, out_size, MSG_ARG_INVALID, "match");
5516 		return;
5517 	}
5518 
5519 	if (strcmp(tokens[7], "default") != 0) {
5520 		snprintf(out, out_size, MSG_ARG_INVALID, "default");
5521 		return;
5522 	}
5523 
5524 	status = softnic_pipeline_table_rule_delete_default(softnic,
5525 		pipeline_name,
5526 		table_id);
5527 	if (status) {
5528 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
5529 		return;
5530 	}
5531 }
5532 
5533 /**
5534  * pipeline <pipeline_name> table <table_id> rule read stats [clear]
5535  */
5536 static void
5537 cmd_softnic_pipeline_table_rule_stats_read(struct pmd_internals *softnic __rte_unused,
5538 	char **tokens,
5539 	uint32_t n_tokens __rte_unused,
5540 	char *out,
5541 	size_t out_size)
5542 {
5543 	snprintf(out, out_size, MSG_CMD_UNIMPLEM, tokens[0]);
5544 }
5545 
5546 /**
5547  * pipeline <pipeline_name> table <table_id> meter profile <meter_profile_id>
5548  *  add srtcm cir <cir> cbs <cbs> ebs <ebs>
5549  *  | trtcm cir <cir> pir <pir> cbs <cbs> pbs <pbs>
5550  */
5551 static void
5552 cmd_pipeline_table_meter_profile_add(struct pmd_internals *softnic,
5553 	char **tokens,
5554 	uint32_t n_tokens,
5555 	char *out,
5556 	size_t out_size)
5557 {
5558 	struct rte_table_action_meter_profile p;
5559 	char *pipeline_name;
5560 	uint32_t table_id, meter_profile_id;
5561 	int status;
5562 
5563 	if (n_tokens < 9) {
5564 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5565 		return;
5566 	}
5567 
5568 	pipeline_name = tokens[1];
5569 
5570 	if (strcmp(tokens[2], "table") != 0) {
5571 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
5572 		return;
5573 	}
5574 
5575 	if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
5576 		snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
5577 		return;
5578 	}
5579 
5580 	if (strcmp(tokens[4], "meter") != 0) {
5581 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
5582 		return;
5583 	}
5584 
5585 	if (strcmp(tokens[5], "profile") != 0) {
5586 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
5587 		return;
5588 	}
5589 
5590 	if (softnic_parser_read_uint32(&meter_profile_id, tokens[6]) != 0) {
5591 		snprintf(out, out_size, MSG_ARG_INVALID, "meter_profile_id");
5592 		return;
5593 	}
5594 
5595 	if (strcmp(tokens[7], "add") != 0) {
5596 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
5597 		return;
5598 	}
5599 
5600 	if (strcmp(tokens[8], "srtcm") == 0) {
5601 		if (n_tokens != 15) {
5602 			snprintf(out, out_size, MSG_ARG_MISMATCH,
5603 				tokens[0]);
5604 			return;
5605 		}
5606 
5607 		p.alg = RTE_TABLE_ACTION_METER_SRTCM;
5608 
5609 		if (strcmp(tokens[9], "cir") != 0) {
5610 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cir");
5611 			return;
5612 		}
5613 
5614 		if (softnic_parser_read_uint64(&p.srtcm.cir, tokens[10]) != 0) {
5615 			snprintf(out, out_size, MSG_ARG_INVALID, "cir");
5616 			return;
5617 		}
5618 
5619 		if (strcmp(tokens[11], "cbs") != 0) {
5620 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cbs");
5621 			return;
5622 		}
5623 
5624 		if (softnic_parser_read_uint64(&p.srtcm.cbs, tokens[12]) != 0) {
5625 			snprintf(out, out_size, MSG_ARG_INVALID, "cbs");
5626 			return;
5627 		}
5628 
5629 		if (strcmp(tokens[13], "ebs") != 0) {
5630 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "ebs");
5631 			return;
5632 		}
5633 
5634 		if (softnic_parser_read_uint64(&p.srtcm.ebs, tokens[14]) != 0) {
5635 			snprintf(out, out_size, MSG_ARG_INVALID, "ebs");
5636 			return;
5637 		}
5638 	} else if (strcmp(tokens[8], "trtcm") == 0) {
5639 		if (n_tokens != 17) {
5640 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5641 			return;
5642 		}
5643 
5644 		p.alg = RTE_TABLE_ACTION_METER_TRTCM;
5645 
5646 		if (strcmp(tokens[9], "cir") != 0) {
5647 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cir");
5648 			return;
5649 		}
5650 
5651 		if (softnic_parser_read_uint64(&p.trtcm.cir, tokens[10]) != 0) {
5652 			snprintf(out, out_size, MSG_ARG_INVALID, "cir");
5653 			return;
5654 		}
5655 
5656 		if (strcmp(tokens[11], "pir") != 0) {
5657 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pir");
5658 			return;
5659 		}
5660 
5661 		if (softnic_parser_read_uint64(&p.trtcm.pir, tokens[12]) != 0) {
5662 			snprintf(out, out_size, MSG_ARG_INVALID, "pir");
5663 			return;
5664 		}
5665 		if (strcmp(tokens[13], "cbs") != 0) {
5666 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cbs");
5667 			return;
5668 		}
5669 
5670 		if (softnic_parser_read_uint64(&p.trtcm.cbs, tokens[14]) != 0) {
5671 			snprintf(out, out_size, MSG_ARG_INVALID, "cbs");
5672 			return;
5673 		}
5674 
5675 		if (strcmp(tokens[15], "pbs") != 0) {
5676 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pbs");
5677 			return;
5678 		}
5679 
5680 		if (softnic_parser_read_uint64(&p.trtcm.pbs, tokens[16]) != 0) {
5681 			snprintf(out, out_size, MSG_ARG_INVALID, "pbs");
5682 			return;
5683 		}
5684 	} else {
5685 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5686 		return;
5687 	}
5688 
5689 	status = softnic_pipeline_table_mtr_profile_add(softnic,
5690 		pipeline_name,
5691 		table_id,
5692 		meter_profile_id,
5693 		&p);
5694 	if (status) {
5695 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
5696 		return;
5697 	}
5698 }
5699 
5700 /**
5701  * pipeline <pipeline_name> table <table_id>
5702  *  meter profile <meter_profile_id> delete
5703  */
5704 static void
5705 cmd_pipeline_table_meter_profile_delete(struct pmd_internals *softnic,
5706 	char **tokens,
5707 	uint32_t n_tokens,
5708 	char *out,
5709 	size_t out_size)
5710 {
5711 	char *pipeline_name;
5712 	uint32_t table_id, meter_profile_id;
5713 	int status;
5714 
5715 	if (n_tokens != 8) {
5716 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5717 		return;
5718 	}
5719 
5720 	pipeline_name = tokens[1];
5721 
5722 	if (strcmp(tokens[2], "table") != 0) {
5723 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
5724 		return;
5725 	}
5726 
5727 	if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
5728 		snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
5729 		return;
5730 	}
5731 
5732 	if (strcmp(tokens[4], "meter") != 0) {
5733 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
5734 		return;
5735 	}
5736 
5737 	if (strcmp(tokens[5], "profile") != 0) {
5738 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
5739 		return;
5740 	}
5741 
5742 	if (softnic_parser_read_uint32(&meter_profile_id, tokens[6]) != 0) {
5743 		snprintf(out, out_size, MSG_ARG_INVALID, "meter_profile_id");
5744 		return;
5745 	}
5746 
5747 	if (strcmp(tokens[7], "delete") != 0) {
5748 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
5749 		return;
5750 	}
5751 
5752 	status = softnic_pipeline_table_mtr_profile_delete(softnic,
5753 		pipeline_name,
5754 		table_id,
5755 		meter_profile_id);
5756 	if (status) {
5757 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
5758 		return;
5759 	}
5760 }
5761 
5762 /**
5763  * pipeline <pipeline_name> table <table_id> rule read meter [clear]
5764  */
5765 static void
5766 cmd_pipeline_table_rule_meter_read(struct pmd_internals *softnic __rte_unused,
5767 	char **tokens,
5768 	uint32_t n_tokens __rte_unused,
5769 	char *out,
5770 	size_t out_size)
5771 {
5772 	snprintf(out, out_size, MSG_CMD_UNIMPLEM, tokens[0]);
5773 }
5774 
5775 /**
5776  * pipeline <pipeline_name> table <table_id> dscp <file_name>
5777  *
5778  * File <file_name>:
5779  *  - exactly 64 lines
5780  *  - line format: <tc_id> <tc_queue_id> <color>, with <color> as: g | y | r
5781  */
5782 static int
5783 load_dscp_table(struct rte_table_action_dscp_table *dscp_table,
5784 	const char *file_name,
5785 	uint32_t *line_number)
5786 {
5787 	FILE *f = NULL;
5788 	uint32_t dscp, l;
5789 
5790 	/* Check input arguments */
5791 	if (dscp_table == NULL ||
5792 		file_name == NULL ||
5793 		line_number == NULL) {
5794 		if (line_number)
5795 			*line_number = 0;
5796 		return -EINVAL;
5797 	}
5798 
5799 	/* Open input file */
5800 	f = fopen(file_name, "r");
5801 	if (f == NULL) {
5802 		*line_number = 0;
5803 		return -EINVAL;
5804 	}
5805 
5806 	/* Read file */
5807 	for (dscp = 0, l = 1; ; l++) {
5808 		char line[64];
5809 		char *tokens[3];
5810 		enum rte_color color;
5811 		uint32_t tc_id, tc_queue_id, n_tokens = RTE_DIM(tokens);
5812 
5813 		if (fgets(line, sizeof(line), f) == NULL)
5814 			break;
5815 
5816 		if (is_comment(line))
5817 			continue;
5818 
5819 		if (softnic_parse_tokenize_string(line, tokens, &n_tokens)) {
5820 			*line_number = l;
5821 			fclose(f);
5822 			return -EINVAL;
5823 		}
5824 
5825 		if (n_tokens == 0)
5826 			continue;
5827 
5828 		if (dscp >= RTE_DIM(dscp_table->entry) ||
5829 			n_tokens != RTE_DIM(tokens) ||
5830 			softnic_parser_read_uint32(&tc_id, tokens[0]) ||
5831 			tc_id >= RTE_TABLE_ACTION_TC_MAX ||
5832 			softnic_parser_read_uint32(&tc_queue_id, tokens[1]) ||
5833 			tc_queue_id >= RTE_TABLE_ACTION_TC_QUEUE_MAX ||
5834 			(strlen(tokens[2]) != 1)) {
5835 			*line_number = l;
5836 			fclose(f);
5837 			return -EINVAL;
5838 		}
5839 
5840 		switch (tokens[2][0]) {
5841 		case 'g':
5842 		case 'G':
5843 			color = RTE_COLOR_GREEN;
5844 			break;
5845 
5846 		case 'y':
5847 		case 'Y':
5848 			color = RTE_COLOR_YELLOW;
5849 			break;
5850 
5851 		case 'r':
5852 		case 'R':
5853 			color = RTE_COLOR_RED;
5854 			break;
5855 
5856 		default:
5857 			*line_number = l;
5858 			fclose(f);
5859 			return -EINVAL;
5860 		}
5861 
5862 		dscp_table->entry[dscp].tc_id = tc_id;
5863 		dscp_table->entry[dscp].tc_queue_id = tc_queue_id;
5864 		dscp_table->entry[dscp].color = color;
5865 		dscp++;
5866 	}
5867 
5868 	/* Close file */
5869 	fclose(f);
5870 	return 0;
5871 }
5872 
5873 static void
5874 cmd_pipeline_table_dscp(struct pmd_internals *softnic,
5875 	char **tokens,
5876 	uint32_t n_tokens,
5877 	char *out,
5878 	size_t out_size)
5879 {
5880 	struct rte_table_action_dscp_table dscp_table;
5881 	char *pipeline_name, *file_name;
5882 	uint32_t table_id, line_number;
5883 	int status;
5884 
5885 	if (n_tokens != 6) {
5886 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5887 		return;
5888 	}
5889 
5890 	pipeline_name = tokens[1];
5891 
5892 	if (strcmp(tokens[2], "table") != 0) {
5893 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
5894 		return;
5895 	}
5896 
5897 	if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) {
5898 		snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
5899 		return;
5900 	}
5901 
5902 	if (strcmp(tokens[4], "dscp") != 0) {
5903 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "dscp");
5904 		return;
5905 	}
5906 
5907 	file_name = tokens[5];
5908 
5909 	status = load_dscp_table(&dscp_table, file_name, &line_number);
5910 	if (status) {
5911 		snprintf(out, out_size, MSG_FILE_ERR, file_name, line_number);
5912 		return;
5913 	}
5914 
5915 	status = softnic_pipeline_table_dscp_table_update(softnic,
5916 		pipeline_name,
5917 		table_id,
5918 		UINT64_MAX,
5919 		&dscp_table);
5920 	if (status) {
5921 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
5922 		return;
5923 	}
5924 }
5925 
5926 /**
5927  * pipeline <pipeline_name> table <table_id> rule read ttl [clear]
5928  */
5929 static void
5930 cmd_softnic_pipeline_table_rule_ttl_read(struct pmd_internals *softnic __rte_unused,
5931 	char **tokens,
5932 	uint32_t n_tokens __rte_unused,
5933 	char *out,
5934 	size_t out_size)
5935 {
5936 	snprintf(out, out_size, MSG_CMD_UNIMPLEM, tokens[0]);
5937 }
5938 
5939 /**
5940  * thread <thread_id> pipeline <pipeline_name> enable
5941  */
5942 static void
5943 cmd_softnic_thread_pipeline_enable(struct pmd_internals *softnic,
5944 	char **tokens,
5945 	uint32_t n_tokens,
5946 	char *out,
5947 	size_t out_size)
5948 {
5949 	char *pipeline_name;
5950 	uint32_t thread_id;
5951 	int status;
5952 
5953 	if (n_tokens != 5) {
5954 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5955 		return;
5956 	}
5957 
5958 	if (softnic_parser_read_uint32(&thread_id, tokens[1]) != 0) {
5959 		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
5960 		return;
5961 	}
5962 
5963 	if (strcmp(tokens[2], "pipeline") != 0) {
5964 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
5965 		return;
5966 	}
5967 
5968 	pipeline_name = tokens[3];
5969 
5970 	if (strcmp(tokens[4], "enable") != 0) {
5971 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
5972 		return;
5973 	}
5974 
5975 	status = softnic_thread_pipeline_enable(softnic, thread_id, pipeline_name);
5976 	if (status) {
5977 		snprintf(out, out_size, MSG_CMD_FAIL, "thread pipeline enable");
5978 		return;
5979 	}
5980 }
5981 
5982 /**
5983  * thread <thread_id> pipeline <pipeline_name> disable
5984  */
5985 static void
5986 cmd_softnic_thread_pipeline_disable(struct pmd_internals *softnic,
5987 	char **tokens,
5988 	uint32_t n_tokens,
5989 	char *out,
5990 	size_t out_size)
5991 {
5992 	char *pipeline_name;
5993 	uint32_t thread_id;
5994 	int status;
5995 
5996 	if (n_tokens != 5) {
5997 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
5998 		return;
5999 	}
6000 
6001 	if (softnic_parser_read_uint32(&thread_id, tokens[1]) != 0) {
6002 		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
6003 		return;
6004 	}
6005 
6006 	if (strcmp(tokens[2], "pipeline") != 0) {
6007 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
6008 		return;
6009 	}
6010 
6011 	pipeline_name = tokens[3];
6012 
6013 	if (strcmp(tokens[4], "disable") != 0) {
6014 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
6015 		return;
6016 	}
6017 
6018 	status = softnic_thread_pipeline_disable(softnic, thread_id, pipeline_name);
6019 	if (status) {
6020 		snprintf(out, out_size, MSG_CMD_FAIL,
6021 			"thread pipeline disable");
6022 		return;
6023 	}
6024 }
6025 
6026 /**
6027  * flowapi map
6028  *  group <group_id>
6029  *  ingress | egress
6030  *  pipeline <pipeline_name>
6031  *  table <table_id>
6032  */
6033 static void
6034 cmd_softnic_flowapi_map(struct pmd_internals *softnic,
6035 		char **tokens,
6036 		uint32_t n_tokens,
6037 		char *out,
6038 		size_t out_size)
6039 {
6040 	char *pipeline_name;
6041 	uint32_t group_id, table_id;
6042 	int ingress, status;
6043 
6044 	if (n_tokens != 9) {
6045 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
6046 		return;
6047 	}
6048 
6049 	if (strcmp(tokens[1], "map") != 0) {
6050 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "map");
6051 		return;
6052 	}
6053 
6054 	if (strcmp(tokens[2], "group") != 0) {
6055 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group");
6056 		return;
6057 	}
6058 
6059 	if (softnic_parser_read_uint32(&group_id, tokens[3]) != 0) {
6060 		snprintf(out, out_size, MSG_ARG_INVALID, "group_id");
6061 		return;
6062 	}
6063 
6064 	if (strcmp(tokens[4], "ingress") == 0) {
6065 		ingress = 1;
6066 	} else if (strcmp(tokens[4], "egress") == 0) {
6067 		ingress = 0;
6068 	} else {
6069 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "ingress | egress");
6070 		return;
6071 	}
6072 
6073 	if (strcmp(tokens[5], "pipeline") != 0) {
6074 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
6075 		return;
6076 	}
6077 
6078 	pipeline_name = tokens[6];
6079 
6080 	if (strcmp(tokens[7], "table") != 0) {
6081 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
6082 		return;
6083 	}
6084 
6085 	if (softnic_parser_read_uint32(&table_id, tokens[8]) != 0) {
6086 		snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
6087 		return;
6088 	}
6089 
6090 	status = flow_attr_map_set(softnic,
6091 			group_id,
6092 			ingress,
6093 			pipeline_name,
6094 			table_id);
6095 	if (status) {
6096 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
6097 		return;
6098 	}
6099 }
6100 
6101 void
6102 softnic_cli_process(char *in, char *out, size_t out_size, void *arg)
6103 {
6104 	char *tokens[CMD_MAX_TOKENS];
6105 	uint32_t n_tokens = RTE_DIM(tokens);
6106 	struct pmd_internals *softnic = arg;
6107 	int status;
6108 
6109 	if (is_comment(in))
6110 		return;
6111 
6112 	status = softnic_parse_tokenize_string(in, tokens, &n_tokens);
6113 	if (status) {
6114 		snprintf(out, out_size, MSG_ARG_TOO_MANY, "");
6115 		return;
6116 	}
6117 
6118 	if (n_tokens == 0)
6119 		return;
6120 
6121 	if (strcmp(tokens[0], "mempool") == 0) {
6122 		cmd_mempool(softnic, tokens, n_tokens, out, out_size);
6123 		return;
6124 	}
6125 
6126 	if (strcmp(tokens[0], "link") == 0) {
6127 		cmd_link(softnic, tokens, n_tokens, out, out_size);
6128 		return;
6129 	}
6130 
6131 	if (strcmp(tokens[0], "swq") == 0) {
6132 		cmd_swq(softnic, tokens, n_tokens, out, out_size);
6133 		return;
6134 	}
6135 
6136 	if (strcmp(tokens[0], "tmgr") == 0) {
6137 		if (n_tokens == 2) {
6138 			cmd_tmgr(softnic, tokens, n_tokens, out, out_size);
6139 			return;
6140 		}
6141 
6142 		if (n_tokens >= 3 &&
6143 			(strcmp(tokens[1], "shaper") == 0) &&
6144 			(strcmp(tokens[2], "profile") == 0)) {
6145 			cmd_tmgr_shaper_profile(softnic, tokens, n_tokens, out, out_size);
6146 			return;
6147 		}
6148 
6149 		if (n_tokens >= 3 &&
6150 			(strcmp(tokens[1], "shared") == 0) &&
6151 			(strcmp(tokens[2], "shaper") == 0)) {
6152 			cmd_tmgr_shared_shaper(softnic, tokens, n_tokens, out, out_size);
6153 			return;
6154 		}
6155 
6156 		if (n_tokens >= 2 &&
6157 			(strcmp(tokens[1], "node") == 0)) {
6158 			cmd_tmgr_node(softnic, tokens, n_tokens, out, out_size);
6159 			return;
6160 		}
6161 
6162 		if (n_tokens >= 2 &&
6163 			(strcmp(tokens[1], "hierarchy-default") == 0)) {
6164 			cmd_tmgr_hierarchy_default(softnic, tokens, n_tokens, out, out_size);
6165 			return;
6166 		}
6167 
6168 		if (n_tokens >= 3 &&
6169 			(strcmp(tokens[1], "hierarchy") == 0) &&
6170 			(strcmp(tokens[2], "commit") == 0)) {
6171 			cmd_tmgr_hierarchy_commit(softnic, tokens, n_tokens, out, out_size);
6172 			return;
6173 		}
6174 	}
6175 
6176 	if (strcmp(tokens[0], "tap") == 0) {
6177 		cmd_tap(softnic, tokens, n_tokens, out, out_size);
6178 		return;
6179 	}
6180 
6181 	if (strcmp(tokens[0], "cryptodev") == 0) {
6182 		cmd_cryptodev(softnic, tokens, n_tokens, out, out_size);
6183 		return;
6184 	}
6185 
6186 	if (strcmp(tokens[0], "port") == 0) {
6187 		cmd_port_in_action_profile(softnic, tokens, n_tokens, out, out_size);
6188 		return;
6189 	}
6190 
6191 	if (strcmp(tokens[0], "table") == 0) {
6192 		cmd_table_action_profile(softnic, tokens, n_tokens, out, out_size);
6193 		return;
6194 	}
6195 
6196 	if (strcmp(tokens[0], "pipeline") == 0) {
6197 		if (n_tokens >= 3 &&
6198 			(strcmp(tokens[2], "period") == 0)) {
6199 			cmd_pipeline(softnic, tokens, n_tokens, out, out_size);
6200 			return;
6201 		}
6202 
6203 		if (n_tokens >= 5 &&
6204 			(strcmp(tokens[2], "port") == 0) &&
6205 			(strcmp(tokens[3], "in") == 0) &&
6206 			(strcmp(tokens[4], "bsz") == 0)) {
6207 			cmd_pipeline_port_in(softnic, tokens, n_tokens, out, out_size);
6208 			return;
6209 		}
6210 
6211 		if (n_tokens >= 5 &&
6212 			(strcmp(tokens[2], "port") == 0) &&
6213 			(strcmp(tokens[3], "out") == 0) &&
6214 			(strcmp(tokens[4], "bsz") == 0)) {
6215 			cmd_pipeline_port_out(softnic, tokens, n_tokens, out, out_size);
6216 			return;
6217 		}
6218 
6219 		if (n_tokens >= 4 &&
6220 			(strcmp(tokens[2], "table") == 0) &&
6221 			(strcmp(tokens[3], "match") == 0)) {
6222 			cmd_pipeline_table(softnic, tokens, n_tokens, out, out_size);
6223 			return;
6224 		}
6225 
6226 		if (n_tokens >= 6 &&
6227 			(strcmp(tokens[2], "port") == 0) &&
6228 			(strcmp(tokens[3], "in") == 0) &&
6229 			(strcmp(tokens[5], "table") == 0)) {
6230 			cmd_pipeline_port_in_table(softnic, tokens, n_tokens,
6231 				out, out_size);
6232 			return;
6233 		}
6234 
6235 		if (n_tokens >= 6 &&
6236 			(strcmp(tokens[2], "port") == 0) &&
6237 			(strcmp(tokens[3], "in") == 0) &&
6238 			(strcmp(tokens[5], "stats") == 0)) {
6239 			cmd_pipeline_port_in_stats(softnic, tokens, n_tokens,
6240 				out, out_size);
6241 			return;
6242 		}
6243 
6244 		if (n_tokens >= 6 &&
6245 			(strcmp(tokens[2], "port") == 0) &&
6246 			(strcmp(tokens[3], "in") == 0) &&
6247 			(strcmp(tokens[5], "enable") == 0)) {
6248 			cmd_softnic_pipeline_port_in_enable(softnic, tokens, n_tokens,
6249 				out, out_size);
6250 			return;
6251 		}
6252 
6253 		if (n_tokens >= 6 &&
6254 			(strcmp(tokens[2], "port") == 0) &&
6255 			(strcmp(tokens[3], "in") == 0) &&
6256 			(strcmp(tokens[5], "disable") == 0)) {
6257 			cmd_softnic_pipeline_port_in_disable(softnic, tokens, n_tokens,
6258 				out, out_size);
6259 			return;
6260 		}
6261 
6262 		if (n_tokens >= 6 &&
6263 			(strcmp(tokens[2], "port") == 0) &&
6264 			(strcmp(tokens[3], "out") == 0) &&
6265 			(strcmp(tokens[5], "stats") == 0)) {
6266 			cmd_pipeline_port_out_stats(softnic, tokens, n_tokens,
6267 				out, out_size);
6268 			return;
6269 		}
6270 
6271 		if (n_tokens >= 5 &&
6272 			(strcmp(tokens[2], "table") == 0) &&
6273 			(strcmp(tokens[4], "stats") == 0)) {
6274 			cmd_pipeline_table_stats(softnic, tokens, n_tokens,
6275 				out, out_size);
6276 			return;
6277 		}
6278 
6279 		if (n_tokens >= 7 &&
6280 			(strcmp(tokens[2], "table") == 0) &&
6281 			(strcmp(tokens[4], "rule") == 0) &&
6282 			(strcmp(tokens[5], "add") == 0) &&
6283 			(strcmp(tokens[6], "match") == 0)) {
6284 			if (n_tokens >= 8 &&
6285 				(strcmp(tokens[7], "default") == 0)) {
6286 				cmd_softnic_pipeline_table_rule_add_default(softnic, tokens,
6287 					n_tokens, out, out_size);
6288 				return;
6289 			}
6290 
6291 			cmd_softnic_pipeline_table_rule_add(softnic, tokens, n_tokens,
6292 				out, out_size);
6293 			return;
6294 		}
6295 
6296 		if (n_tokens >= 7 &&
6297 			(strcmp(tokens[2], "table") == 0) &&
6298 			(strcmp(tokens[4], "rule") == 0) &&
6299 			(strcmp(tokens[5], "add") == 0) &&
6300 			(strcmp(tokens[6], "bulk") == 0)) {
6301 			cmd_softnic_pipeline_table_rule_add_bulk(softnic, tokens,
6302 				n_tokens, out, out_size);
6303 			return;
6304 		}
6305 
6306 		if (n_tokens >= 7 &&
6307 			(strcmp(tokens[2], "table") == 0) &&
6308 			(strcmp(tokens[4], "rule") == 0) &&
6309 			(strcmp(tokens[5], "delete") == 0) &&
6310 			(strcmp(tokens[6], "match") == 0)) {
6311 			if (n_tokens >= 8 &&
6312 				(strcmp(tokens[7], "default") == 0)) {
6313 				cmd_softnic_pipeline_table_rule_delete_default(softnic, tokens,
6314 					n_tokens, out, out_size);
6315 				return;
6316 				}
6317 
6318 			cmd_softnic_pipeline_table_rule_delete(softnic, tokens, n_tokens,
6319 				out, out_size);
6320 			return;
6321 		}
6322 
6323 		if (n_tokens >= 7 &&
6324 			(strcmp(tokens[2], "table") == 0) &&
6325 			(strcmp(tokens[4], "rule") == 0) &&
6326 			(strcmp(tokens[5], "read") == 0) &&
6327 			(strcmp(tokens[6], "stats") == 0)) {
6328 			cmd_softnic_pipeline_table_rule_stats_read(softnic, tokens, n_tokens,
6329 				out, out_size);
6330 			return;
6331 		}
6332 
6333 		if (n_tokens >= 8 &&
6334 			(strcmp(tokens[2], "table") == 0) &&
6335 			(strcmp(tokens[4], "meter") == 0) &&
6336 			(strcmp(tokens[5], "profile") == 0) &&
6337 			(strcmp(tokens[7], "add") == 0)) {
6338 			cmd_pipeline_table_meter_profile_add(softnic, tokens, n_tokens,
6339 				out, out_size);
6340 			return;
6341 		}
6342 
6343 		if (n_tokens >= 8 &&
6344 			(strcmp(tokens[2], "table") == 0) &&
6345 			(strcmp(tokens[4], "meter") == 0) &&
6346 			(strcmp(tokens[5], "profile") == 0) &&
6347 			(strcmp(tokens[7], "delete") == 0)) {
6348 			cmd_pipeline_table_meter_profile_delete(softnic, tokens,
6349 				n_tokens, out, out_size);
6350 			return;
6351 		}
6352 
6353 		if (n_tokens >= 7 &&
6354 			(strcmp(tokens[2], "table") == 0) &&
6355 			(strcmp(tokens[4], "rule") == 0) &&
6356 			(strcmp(tokens[5], "read") == 0) &&
6357 			(strcmp(tokens[6], "meter") == 0)) {
6358 			cmd_pipeline_table_rule_meter_read(softnic, tokens, n_tokens,
6359 				out, out_size);
6360 			return;
6361 		}
6362 
6363 		if (n_tokens >= 5 &&
6364 			(strcmp(tokens[2], "table") == 0) &&
6365 			(strcmp(tokens[4], "dscp") == 0)) {
6366 			cmd_pipeline_table_dscp(softnic, tokens, n_tokens,
6367 				out, out_size);
6368 			return;
6369 		}
6370 
6371 		if (n_tokens >= 7 &&
6372 			(strcmp(tokens[2], "table") == 0) &&
6373 			(strcmp(tokens[4], "rule") == 0) &&
6374 			(strcmp(tokens[5], "read") == 0) &&
6375 			(strcmp(tokens[6], "ttl") == 0)) {
6376 			cmd_softnic_pipeline_table_rule_ttl_read(softnic, tokens, n_tokens,
6377 				out, out_size);
6378 			return;
6379 		}
6380 	}
6381 
6382 	if (strcmp(tokens[0], "thread") == 0) {
6383 		if (n_tokens >= 5 &&
6384 			(strcmp(tokens[4], "enable") == 0)) {
6385 			cmd_softnic_thread_pipeline_enable(softnic, tokens, n_tokens,
6386 				out, out_size);
6387 			return;
6388 		}
6389 
6390 		if (n_tokens >= 5 &&
6391 			(strcmp(tokens[4], "disable") == 0)) {
6392 			cmd_softnic_thread_pipeline_disable(softnic, tokens, n_tokens,
6393 				out, out_size);
6394 			return;
6395 		}
6396 	}
6397 
6398 	if (strcmp(tokens[0], "flowapi") == 0) {
6399 		cmd_softnic_flowapi_map(softnic, tokens, n_tokens, out,
6400 					out_size);
6401 		return;
6402 	}
6403 
6404 	snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
6405 }
6406 
6407 int
6408 softnic_cli_script_process(struct pmd_internals *softnic,
6409 	const char *file_name,
6410 	size_t msg_in_len_max,
6411 	size_t msg_out_len_max)
6412 {
6413 	char *msg_in = NULL, *msg_out = NULL;
6414 	FILE *f = NULL;
6415 
6416 	/* Check input arguments */
6417 	if (file_name == NULL ||
6418 		(strlen(file_name) == 0) ||
6419 		msg_in_len_max == 0 ||
6420 		msg_out_len_max == 0)
6421 		return -EINVAL;
6422 
6423 	msg_in = malloc(msg_in_len_max + 1);
6424 	msg_out = malloc(msg_out_len_max + 1);
6425 	if (msg_in == NULL ||
6426 		msg_out == NULL) {
6427 		free(msg_out);
6428 		free(msg_in);
6429 		return -ENOMEM;
6430 	}
6431 
6432 	/* Open input file */
6433 	f = fopen(file_name, "r");
6434 	if (f == NULL) {
6435 		free(msg_out);
6436 		free(msg_in);
6437 		return -EIO;
6438 	}
6439 
6440 	/* Read file */
6441 	for ( ; ; ) {
6442 		if (fgets(msg_in, msg_in_len_max + 1, f) == NULL)
6443 			break;
6444 
6445 		printf("%s", msg_in);
6446 		msg_out[0] = 0;
6447 
6448 		softnic_cli_process(msg_in,
6449 			msg_out,
6450 			msg_out_len_max,
6451 			softnic);
6452 
6453 		if (strlen(msg_out))
6454 			printf("%s", msg_out);
6455 	}
6456 
6457 	/* Close file */
6458 	fclose(f);
6459 	free(msg_out);
6460 	free(msg_in);
6461 	return 0;
6462 }
6463 
6464 static int
6465 cli_rule_file_process(const char *file_name,
6466 	size_t line_len_max,
6467 	struct softnic_table_rule_match *m,
6468 	struct softnic_table_rule_action *a,
6469 	uint32_t *n_rules,
6470 	uint32_t *line_number,
6471 	char *out,
6472 	size_t out_size)
6473 {
6474 	FILE *f = NULL;
6475 	char *line = NULL;
6476 	uint32_t rule_id, line_id;
6477 	int status = 0;
6478 
6479 	/* Check input arguments */
6480 	if (file_name == NULL ||
6481 		(strlen(file_name) == 0) ||
6482 		line_len_max == 0) {
6483 		*line_number = 0;
6484 		return -EINVAL;
6485 	}
6486 
6487 	/* Memory allocation */
6488 	line = malloc(line_len_max + 1);
6489 	if (line == NULL) {
6490 		*line_number = 0;
6491 		return -ENOMEM;
6492 	}
6493 
6494 	/* Open file */
6495 	f = fopen(file_name, "r");
6496 	if (f == NULL) {
6497 		*line_number = 0;
6498 		free(line);
6499 		return -EIO;
6500 	}
6501 
6502 	/* Read file */
6503 	for (line_id = 1, rule_id = 0; rule_id < *n_rules; line_id++) {
6504 		char *tokens[CMD_MAX_TOKENS];
6505 		uint32_t n_tokens, n_tokens_parsed, t0;
6506 
6507 		/* Read next line from file. */
6508 		if (fgets(line, line_len_max + 1, f) == NULL)
6509 			break;
6510 
6511 		/* Comment. */
6512 		if (is_comment(line))
6513 			continue;
6514 
6515 		/* Parse line. */
6516 		n_tokens = RTE_DIM(tokens);
6517 		status = softnic_parse_tokenize_string(line, tokens, &n_tokens);
6518 		if (status) {
6519 			status = -EINVAL;
6520 			break;
6521 		}
6522 
6523 		/* Empty line. */
6524 		if (n_tokens == 0)
6525 			continue;
6526 		t0 = 0;
6527 
6528 		/* Rule match. */
6529 		n_tokens_parsed = parse_match(tokens + t0,
6530 			n_tokens - t0,
6531 			out,
6532 			out_size,
6533 			&m[rule_id]);
6534 		if (n_tokens_parsed == 0) {
6535 			status = -EINVAL;
6536 			break;
6537 		}
6538 		t0 += n_tokens_parsed;
6539 
6540 		/* Rule action. */
6541 		n_tokens_parsed = parse_table_action(tokens + t0,
6542 			n_tokens - t0,
6543 			out,
6544 			out_size,
6545 			&a[rule_id]);
6546 		if (n_tokens_parsed == 0) {
6547 			status = -EINVAL;
6548 			break;
6549 		}
6550 		t0 += n_tokens_parsed;
6551 
6552 		/* Line completed. */
6553 		if (t0 < n_tokens) {
6554 			status = -EINVAL;
6555 			break;
6556 		}
6557 
6558 		/* Increment rule count */
6559 		rule_id++;
6560 	}
6561 
6562 	/* Close file */
6563 	fclose(f);
6564 
6565 	/* Memory free */
6566 	free(line);
6567 
6568 	*n_rules = rule_id;
6569 	*line_number = line_id;
6570 	return status;
6571 }
6572