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