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