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