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