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