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