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