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