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