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