xref: /dpdk/examples/ip_pipeline/cli.c (revision 06c761d6fb50c8ba5990fa48838c478b5dbd89c0)
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 #include "kni.h"
16 #include "link.h"
17 #include "mempool.h"
18 #include "parser.h"
19 #include "pipeline.h"
20 #include "swq.h"
21 #include "tap.h"
22 #include "thread.h"
23 #include "tmgr.h"
24 
25 #ifndef CMD_MAX_TOKENS
26 #define CMD_MAX_TOKENS     256
27 #endif
28 
29 #define MSG_OUT_OF_MEMORY   "Not enough memory.\n"
30 #define MSG_CMD_UNKNOWN     "Unknown command \"%s\".\n"
31 #define MSG_CMD_UNIMPLEM    "Command \"%s\" not implemented.\n"
32 #define MSG_ARG_NOT_ENOUGH  "Not enough arguments for command \"%s\".\n"
33 #define MSG_ARG_TOO_MANY    "Too many arguments for command \"%s\".\n"
34 #define MSG_ARG_MISMATCH    "Wrong number of arguments for command \"%s\".\n"
35 #define MSG_ARG_NOT_FOUND   "Argument \"%s\" not found.\n"
36 #define MSG_ARG_INVALID     "Invalid value for argument \"%s\".\n"
37 #define MSG_FILE_ERR        "Error in file \"%s\" at line %u.\n"
38 #define MSG_FILE_NOT_ENOUGH "Not enough rules in file \"%s\".\n"
39 #define MSG_CMD_FAIL        "Command \"%s\" failed.\n"
40 
41 static int
42 is_comment(char *in)
43 {
44 	if ((strlen(in) && index("!#%;", in[0])) ||
45 		(strncmp(in, "//", 2) == 0) ||
46 		(strncmp(in, "--", 2) == 0))
47 		return 1;
48 
49 	return 0;
50 }
51 
52 /**
53  * mempool <mempool_name>
54  *  buffer <buffer_size>
55  *  pool <pool_size>
56  *  cache <cache_size>
57  *  cpu <cpu_id>
58  */
59 static void
60 cmd_mempool(char **tokens,
61 	uint32_t n_tokens,
62 	char *out,
63 	size_t out_size)
64 {
65 	struct mempool_params p;
66 	char *name;
67 	struct mempool *mempool;
68 
69 	if (n_tokens != 10) {
70 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
71 		return;
72 	}
73 
74 	name = tokens[1];
75 
76 	if (strcmp(tokens[2], "buffer") != 0) {
77 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "buffer");
78 		return;
79 	}
80 
81 	if (parser_read_uint32(&p.buffer_size, tokens[3]) != 0) {
82 		snprintf(out, out_size, MSG_ARG_INVALID, "buffer_size");
83 		return;
84 	}
85 
86 	if (strcmp(tokens[4], "pool") != 0) {
87 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pool");
88 		return;
89 	}
90 
91 	if (parser_read_uint32(&p.pool_size, tokens[5]) != 0) {
92 		snprintf(out, out_size, MSG_ARG_INVALID, "pool_size");
93 		return;
94 	}
95 
96 	if (strcmp(tokens[6], "cache") != 0) {
97 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cache");
98 		return;
99 	}
100 
101 	if (parser_read_uint32(&p.cache_size, tokens[7]) != 0) {
102 		snprintf(out, out_size, MSG_ARG_INVALID, "cache_size");
103 		return;
104 	}
105 
106 	if (strcmp(tokens[8], "cpu") != 0) {
107 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cpu");
108 		return;
109 	}
110 
111 	if (parser_read_uint32(&p.cpu_id, tokens[9]) != 0) {
112 		snprintf(out, out_size, MSG_ARG_INVALID, "cpu_id");
113 		return;
114 	}
115 
116 	mempool = mempool_create(name, &p);
117 	if (mempool == NULL) {
118 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
119 		return;
120 	}
121 }
122 
123 /**
124  * link <link_name>
125  *  dev <device_name> | port <port_id>
126  *  rxq <n_queues> <queue_size> <mempool_name>
127  *  txq <n_queues> <queue_size>
128  *  promiscuous on | off
129  *  [rss <qid_0> ... <qid_n>]
130  */
131 static void
132 cmd_link(char **tokens,
133 	uint32_t n_tokens,
134 	char *out,
135 	size_t out_size)
136 {
137 	struct link_params p;
138 	struct link_params_rss rss;
139 	struct link *link;
140 	char *name;
141 
142 	memset(&p, 0, sizeof(p));
143 
144 	if ((n_tokens < 13) || (n_tokens > 14 + LINK_RXQ_RSS_MAX)) {
145 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
146 		return;
147 	}
148 	name = tokens[1];
149 
150 	if (strcmp(tokens[2], "dev") == 0)
151 		p.dev_name = tokens[3];
152 	else if (strcmp(tokens[2], "port") == 0) {
153 		p.dev_name = NULL;
154 
155 		if (parser_read_uint16(&p.port_id, tokens[3]) != 0) {
156 			snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
157 			return;
158 		}
159 	} else {
160 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "dev or port");
161 		return;
162 	}
163 
164 	if (strcmp(tokens[4], "rxq") != 0) {
165 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq");
166 		return;
167 	}
168 
169 	if (parser_read_uint32(&p.rx.n_queues, tokens[5]) != 0) {
170 		snprintf(out, out_size, MSG_ARG_INVALID, "n_queues");
171 		return;
172 	}
173 	if (parser_read_uint32(&p.rx.queue_size, tokens[6]) != 0) {
174 		snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
175 		return;
176 	}
177 
178 	p.rx.mempool_name = tokens[7];
179 
180 	if (strcmp(tokens[8], "txq") != 0) {
181 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "txq");
182 		return;
183 	}
184 
185 	if (parser_read_uint32(&p.tx.n_queues, tokens[9]) != 0) {
186 		snprintf(out, out_size, MSG_ARG_INVALID, "n_queues");
187 		return;
188 	}
189 
190 	if (parser_read_uint32(&p.tx.queue_size, tokens[10]) != 0) {
191 		snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
192 		return;
193 	}
194 
195 	if (strcmp(tokens[11], "promiscuous") != 0) {
196 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "promiscuous");
197 		return;
198 	}
199 
200 	if (strcmp(tokens[12], "on") == 0)
201 		p.promiscuous = 1;
202 	else if (strcmp(tokens[12], "off") == 0)
203 		p.promiscuous = 0;
204 	else {
205 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "on or off");
206 		return;
207 	}
208 
209 	/* RSS */
210 	p.rx.rss = NULL;
211 	if (n_tokens > 13) {
212 		uint32_t queue_id, i;
213 
214 		if (strcmp(tokens[13], "rss") != 0) {
215 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rss");
216 			return;
217 		}
218 
219 		p.rx.rss = &rss;
220 
221 		rss.n_queues = 0;
222 		for (i = 14; i < n_tokens; i++) {
223 			if (parser_read_uint32(&queue_id, tokens[i]) != 0) {
224 				snprintf(out, out_size, MSG_ARG_INVALID,
225 					"queue_id");
226 				return;
227 			}
228 
229 			rss.queue_id[rss.n_queues] = queue_id;
230 			rss.n_queues++;
231 		}
232 	}
233 
234 	link = link_create(name, &p);
235 	if (link == NULL) {
236 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
237 		return;
238 	}
239 }
240 
241 /* Print the link stats and info */
242 static void
243 print_link_info(struct link *link, char *out, size_t out_size)
244 {
245 	struct rte_eth_stats stats;
246 	struct ether_addr mac_addr;
247 	struct rte_eth_link eth_link;
248 	uint16_t mtu;
249 
250 	memset(&stats, 0, sizeof(stats));
251 	rte_eth_stats_get(link->port_id, &stats);
252 
253 	rte_eth_macaddr_get(link->port_id, &mac_addr);
254 	rte_eth_link_get(link->port_id, &eth_link);
255 	rte_eth_dev_get_mtu(link->port_id, &mtu);
256 
257 	snprintf(out, out_size,
258 		"\n"
259 		"%s: flags=<%s> mtu %u\n"
260 		"\tether %02X:%02X:%02X:%02X:%02X:%02X rxqueues %u txqueues %u\n"
261 		"\tport# %u  speed %u Mbps\n"
262 		"\tRX packets %" PRIu64"  bytes %" PRIu64"\n"
263 		"\tRX errors %" PRIu64"  missed %" PRIu64"  no-mbuf %" PRIu64"\n"
264 		"\tTX packets %" PRIu64"  bytes %" PRIu64"\n"
265 		"\tTX errors %" PRIu64"\n",
266 		link->name,
267 		eth_link.link_status == 0 ? "DOWN" : "UP",
268 		mtu,
269 		mac_addr.addr_bytes[0], mac_addr.addr_bytes[1],
270 		mac_addr.addr_bytes[2], mac_addr.addr_bytes[3],
271 		mac_addr.addr_bytes[4], mac_addr.addr_bytes[5],
272 		link->n_rxq,
273 		link->n_txq,
274 		link->port_id,
275 		eth_link.link_speed,
276 		stats.ipackets,
277 		stats.ibytes,
278 		stats.ierrors,
279 		stats.imissed,
280 		stats.rx_nombuf,
281 		stats.opackets,
282 		stats.obytes,
283 		stats.oerrors);
284 }
285 
286 /*
287  * link show [<link_name>]
288  */
289 static void
290 cmd_link_show(char **tokens, uint32_t n_tokens, char *out, size_t out_size)
291 {
292 	struct link *link;
293 	char *link_name;
294 
295 	if (n_tokens != 2 && n_tokens != 3) {
296 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
297 		return;
298 	}
299 
300 	if (n_tokens == 2) {
301 		link = link_next(NULL);
302 
303 		while (link != NULL) {
304 			out_size = out_size - strlen(out);
305 			out = &out[strlen(out)];
306 
307 			print_link_info(link, out, out_size);
308 			link = link_next(link);
309 		}
310 	} else {
311 		out_size = out_size - strlen(out);
312 		out = &out[strlen(out)];
313 
314 		link_name = tokens[2];
315 		link = link_find(link_name);
316 
317 		if (link == NULL) {
318 			snprintf(out, out_size, MSG_ARG_INVALID,
319 					"Link does not exist");
320 			return;
321 		}
322 		print_link_info(link, out, out_size);
323 	}
324 }
325 
326 /**
327  * swq <swq_name>
328  *  size <size>
329  *  cpu <cpu_id>
330  */
331 static void
332 cmd_swq(char **tokens,
333 	uint32_t n_tokens,
334 	char *out,
335 	size_t out_size)
336 {
337 	struct swq_params p;
338 	char *name;
339 	struct swq *swq;
340 
341 	if (n_tokens != 6) {
342 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
343 		return;
344 	}
345 
346 	name = tokens[1];
347 
348 	if (strcmp(tokens[2], "size") != 0) {
349 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
350 		return;
351 	}
352 
353 	if (parser_read_uint32(&p.size, tokens[3]) != 0) {
354 		snprintf(out, out_size, MSG_ARG_INVALID, "size");
355 		return;
356 	}
357 
358 	if (strcmp(tokens[4], "cpu") != 0) {
359 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cpu");
360 		return;
361 	}
362 
363 	if (parser_read_uint32(&p.cpu_id, tokens[5]) != 0) {
364 		snprintf(out, out_size, MSG_ARG_INVALID, "cpu_id");
365 		return;
366 	}
367 
368 	swq = swq_create(name, &p);
369 	if (swq == NULL) {
370 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
371 		return;
372 	}
373 }
374 
375 /**
376  * tmgr subport profile
377  *  <tb_rate> <tb_size>
378  *  <tc0_rate> <tc1_rate> <tc2_rate> <tc3_rate>
379  *  <tc_period>
380  */
381 static void
382 cmd_tmgr_subport_profile(char **tokens,
383 	uint32_t n_tokens,
384 	char *out,
385 	size_t out_size)
386 {
387 	struct rte_sched_subport_params p;
388 	int status, i;
389 
390 	if (n_tokens != 10) {
391 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
392 		return;
393 	}
394 
395 	if (parser_read_uint32(&p.tb_rate, tokens[3]) != 0) {
396 		snprintf(out, out_size, MSG_ARG_INVALID, "tb_rate");
397 		return;
398 	}
399 
400 	if (parser_read_uint32(&p.tb_size, tokens[4]) != 0) {
401 		snprintf(out, out_size, MSG_ARG_INVALID, "tb_size");
402 		return;
403 	}
404 
405 	for (i = 0; i < RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE; i++)
406 		if (parser_read_uint32(&p.tc_rate[i], tokens[5 + i]) != 0) {
407 			snprintf(out, out_size, MSG_ARG_INVALID, "tc_rate");
408 			return;
409 		}
410 
411 	if (parser_read_uint32(&p.tc_period, tokens[9]) != 0) {
412 		snprintf(out, out_size, MSG_ARG_INVALID, "tc_period");
413 		return;
414 	}
415 
416 	status = tmgr_subport_profile_add(&p);
417 	if (status != 0) {
418 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
419 		return;
420 	}
421 }
422 
423 /**
424  * tmgr pipe profile
425  *  <tb_rate> <tb_size>
426  *  <tc0_rate> <tc1_rate> <tc2_rate> <tc3_rate>
427  *  <tc_period>
428  *  <tc_ov_weight>
429  *  <wrr_weight0..15>
430  */
431 static void
432 cmd_tmgr_pipe_profile(char **tokens,
433 	uint32_t n_tokens,
434 	char *out,
435 	size_t out_size)
436 {
437 	struct rte_sched_pipe_params p;
438 	int status, i;
439 
440 	if (n_tokens != 27) {
441 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
442 		return;
443 	}
444 
445 	if (parser_read_uint32(&p.tb_rate, tokens[3]) != 0) {
446 		snprintf(out, out_size, MSG_ARG_INVALID, "tb_rate");
447 		return;
448 	}
449 
450 	if (parser_read_uint32(&p.tb_size, tokens[4]) != 0) {
451 		snprintf(out, out_size, MSG_ARG_INVALID, "tb_size");
452 		return;
453 	}
454 
455 	for (i = 0; i < RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE; i++)
456 		if (parser_read_uint32(&p.tc_rate[i], tokens[5 + i]) != 0) {
457 			snprintf(out, out_size, MSG_ARG_INVALID, "tc_rate");
458 			return;
459 		}
460 
461 	if (parser_read_uint32(&p.tc_period, tokens[9]) != 0) {
462 		snprintf(out, out_size, MSG_ARG_INVALID, "tc_period");
463 		return;
464 	}
465 
466 #ifdef RTE_SCHED_SUBPORT_TC_OV
467 	if (parser_read_uint8(&p.tc_ov_weight, tokens[10]) != 0) {
468 		snprintf(out, out_size, MSG_ARG_INVALID, "tc_ov_weight");
469 		return;
470 	}
471 #endif
472 
473 	for (i = 0; i < RTE_SCHED_QUEUES_PER_PIPE; i++)
474 		if (parser_read_uint8(&p.wrr_weights[i], tokens[11 + i]) != 0) {
475 			snprintf(out, out_size, MSG_ARG_INVALID, "wrr_weights");
476 			return;
477 		}
478 
479 	status = tmgr_pipe_profile_add(&p);
480 	if (status != 0) {
481 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
482 		return;
483 	}
484 }
485 
486 /**
487  * tmgr <tmgr_name>
488  *  rate <rate>
489  *  spp <n_subports_per_port>
490  *  pps <n_pipes_per_subport>
491  *  qsize <qsize_tc0> <qsize_tc1> <qsize_tc2> <qsize_tc3>
492  *  fo <frame_overhead>
493  *  mtu <mtu>
494  *  cpu <cpu_id>
495  */
496 static void
497 cmd_tmgr(char **tokens,
498 	uint32_t n_tokens,
499 	char *out,
500 	size_t out_size)
501 {
502 	struct tmgr_port_params p;
503 	char *name;
504 	struct tmgr_port *tmgr_port;
505 	int i;
506 
507 	if (n_tokens != 19) {
508 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
509 		return;
510 	}
511 
512 	name = tokens[1];
513 
514 	if (strcmp(tokens[2], "rate") != 0) {
515 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rate");
516 		return;
517 	}
518 
519 	if (parser_read_uint32(&p.rate, tokens[3]) != 0) {
520 		snprintf(out, out_size, MSG_ARG_INVALID, "rate");
521 		return;
522 	}
523 
524 	if (strcmp(tokens[4], "spp") != 0) {
525 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "spp");
526 		return;
527 	}
528 
529 	if (parser_read_uint32(&p.n_subports_per_port, tokens[5]) != 0) {
530 		snprintf(out, out_size, MSG_ARG_INVALID, "n_subports_per_port");
531 		return;
532 	}
533 
534 	if (strcmp(tokens[6], "pps") != 0) {
535 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pps");
536 		return;
537 	}
538 
539 	if (parser_read_uint32(&p.n_pipes_per_subport, tokens[7]) != 0) {
540 		snprintf(out, out_size, MSG_ARG_INVALID, "n_pipes_per_subport");
541 		return;
542 	}
543 
544 	if (strcmp(tokens[8], "qsize") != 0) {
545 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "qsize");
546 		return;
547 	}
548 
549 	for (i = 0; i < RTE_SCHED_TRAFFIC_CLASSES_PER_PIPE; i++)
550 		if (parser_read_uint16(&p.qsize[i], tokens[9 + i]) != 0) {
551 			snprintf(out, out_size, MSG_ARG_INVALID, "qsize");
552 			return;
553 		}
554 
555 	if (strcmp(tokens[13], "fo") != 0) {
556 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "fo");
557 		return;
558 	}
559 
560 	if (parser_read_uint32(&p.frame_overhead, tokens[14]) != 0) {
561 		snprintf(out, out_size, MSG_ARG_INVALID, "frame_overhead");
562 		return;
563 	}
564 
565 	if (strcmp(tokens[15], "mtu") != 0) {
566 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mtu");
567 		return;
568 	}
569 
570 	if (parser_read_uint32(&p.mtu, tokens[16]) != 0) {
571 		snprintf(out, out_size, MSG_ARG_INVALID, "mtu");
572 		return;
573 	}
574 
575 	if (strcmp(tokens[17], "cpu") != 0) {
576 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cpu");
577 		return;
578 	}
579 
580 	if (parser_read_uint32(&p.cpu_id, tokens[18]) != 0) {
581 		snprintf(out, out_size, MSG_ARG_INVALID, "cpu_id");
582 		return;
583 	}
584 
585 	tmgr_port = tmgr_port_create(name, &p);
586 	if (tmgr_port == NULL) {
587 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
588 		return;
589 	}
590 }
591 
592 /**
593  * tmgr <tmgr_name> subport <subport_id>
594  *  profile <subport_profile_id>
595  */
596 static void
597 cmd_tmgr_subport(char **tokens,
598 	uint32_t n_tokens,
599 	char *out,
600 	size_t out_size)
601 {
602 	uint32_t subport_id, subport_profile_id;
603 	int status;
604 	char *name;
605 
606 	if (n_tokens != 6) {
607 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
608 		return;
609 	}
610 
611 	name = tokens[1];
612 
613 	if (parser_read_uint32(&subport_id, tokens[3]) != 0) {
614 		snprintf(out, out_size, MSG_ARG_INVALID, "subport_id");
615 		return;
616 	}
617 
618 	if (parser_read_uint32(&subport_profile_id, tokens[5]) != 0) {
619 		snprintf(out, out_size, MSG_ARG_INVALID, "subport_profile_id");
620 		return;
621 	}
622 
623 	status = tmgr_subport_config(name, subport_id, subport_profile_id);
624 	if (status) {
625 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
626 		return;
627 	}
628 }
629 
630 /**
631  * tmgr <tmgr_name> subport <subport_id> pipe
632  *  from <pipe_id_first> to <pipe_id_last>
633  *  profile <pipe_profile_id>
634  */
635 static void
636 cmd_tmgr_subport_pipe(char **tokens,
637 	uint32_t n_tokens,
638 	char *out,
639 	size_t out_size)
640 {
641 	uint32_t subport_id, pipe_id_first, pipe_id_last, pipe_profile_id;
642 	int status;
643 	char *name;
644 
645 	if (n_tokens != 11) {
646 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
647 		return;
648 	}
649 
650 	name = tokens[1];
651 
652 	if (parser_read_uint32(&subport_id, tokens[3]) != 0) {
653 		snprintf(out, out_size, MSG_ARG_INVALID, "subport_id");
654 		return;
655 	}
656 
657 	if (strcmp(tokens[4], "pipe") != 0) {
658 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipe");
659 		return;
660 	}
661 
662 	if (strcmp(tokens[5], "from") != 0) {
663 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "from");
664 		return;
665 	}
666 
667 	if (parser_read_uint32(&pipe_id_first, tokens[6]) != 0) {
668 		snprintf(out, out_size, MSG_ARG_INVALID, "pipe_id_first");
669 		return;
670 	}
671 
672 	if (strcmp(tokens[7], "to") != 0) {
673 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "to");
674 		return;
675 	}
676 
677 	if (parser_read_uint32(&pipe_id_last, tokens[8]) != 0) {
678 		snprintf(out, out_size, MSG_ARG_INVALID, "pipe_id_last");
679 		return;
680 	}
681 
682 	if (strcmp(tokens[9], "profile") != 0) {
683 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
684 		return;
685 	}
686 
687 	if (parser_read_uint32(&pipe_profile_id, tokens[10]) != 0) {
688 		snprintf(out, out_size, MSG_ARG_INVALID, "pipe_profile_id");
689 		return;
690 	}
691 
692 	status = tmgr_pipe_config(name, subport_id, pipe_id_first,
693 			pipe_id_last, pipe_profile_id);
694 	if (status) {
695 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
696 		return;
697 	}
698 }
699 
700 /**
701  * tap <tap_name>
702  */
703 static void
704 cmd_tap(char **tokens,
705 	uint32_t n_tokens,
706 	char *out,
707 	size_t out_size)
708 {
709 	char *name;
710 	struct tap *tap;
711 
712 	if (n_tokens != 2) {
713 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
714 		return;
715 	}
716 
717 	name = tokens[1];
718 
719 	tap = tap_create(name);
720 	if (tap == NULL) {
721 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
722 		return;
723 	}
724 }
725 
726 /**
727  * kni <kni_name>
728  *  link <link_name>
729  *  mempool <mempool_name>
730  *  [thread <thread_id>]
731  */
732 static void
733 cmd_kni(char **tokens,
734 	uint32_t n_tokens,
735 	char *out,
736 	size_t out_size)
737 {
738 	struct kni_params p;
739 	char *name;
740 	struct kni *kni;
741 
742 	memset(&p, 0, sizeof(p));
743 	if ((n_tokens != 6) && (n_tokens != 8)) {
744 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
745 		return;
746 	}
747 
748 	name = tokens[1];
749 
750 	if (strcmp(tokens[2], "link") != 0) {
751 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "link");
752 		return;
753 	}
754 
755 	p.link_name = tokens[3];
756 
757 	if (strcmp(tokens[4], "mempool") != 0) {
758 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mempool");
759 		return;
760 	}
761 
762 	p.mempool_name = tokens[5];
763 
764 	if (n_tokens == 8) {
765 		if (strcmp(tokens[6], "thread") != 0) {
766 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "thread");
767 			return;
768 		}
769 
770 		if (parser_read_uint32(&p.thread_id, tokens[7]) != 0) {
771 			snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
772 			return;
773 		}
774 
775 		p.force_bind = 1;
776 	} else
777 		p.force_bind = 0;
778 
779 	kni = kni_create(name, &p);
780 	if (kni == NULL) {
781 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
782 		return;
783 	}
784 }
785 
786 /**
787  * port in action profile <profile_name>
788  *  [filter match | mismatch offset <key_offset> mask <key_mask> key <key_value> port <port_id>]
789  *  [balance offset <key_offset> mask <key_mask> port <port_id0> ... <port_id15>]
790  */
791 static void
792 cmd_port_in_action_profile(char **tokens,
793 	uint32_t n_tokens,
794 	char *out,
795 	size_t out_size)
796 {
797 	struct port_in_action_profile_params p;
798 	struct port_in_action_profile *ap;
799 	char *name;
800 	uint32_t t0;
801 
802 	memset(&p, 0, sizeof(p));
803 
804 	if (n_tokens < 5) {
805 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
806 		return;
807 	}
808 
809 	if (strcmp(tokens[1], "in") != 0) {
810 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
811 		return;
812 	}
813 
814 	if (strcmp(tokens[2], "action") != 0) {
815 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "action");
816 		return;
817 	}
818 
819 	if (strcmp(tokens[3], "profile") != 0) {
820 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
821 		return;
822 	}
823 
824 	name = tokens[4];
825 
826 	t0 = 5;
827 
828 	if ((t0 < n_tokens) && (strcmp(tokens[t0], "filter") == 0)) {
829 		uint32_t size;
830 
831 		if (n_tokens < t0 + 10) {
832 			snprintf(out, out_size, MSG_ARG_MISMATCH, "port in action profile filter");
833 			return;
834 		}
835 
836 		if (strcmp(tokens[t0 + 1], "match") == 0)
837 			p.fltr.filter_on_match = 1;
838 		else if (strcmp(tokens[t0 + 1], "mismatch") == 0)
839 			p.fltr.filter_on_match = 0;
840 		else {
841 			snprintf(out, out_size, MSG_ARG_INVALID, "match or mismatch");
842 			return;
843 		}
844 
845 		if (strcmp(tokens[t0 + 2], "offset") != 0) {
846 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
847 			return;
848 		}
849 
850 		if (parser_read_uint32(&p.fltr.key_offset, tokens[t0 + 3]) != 0) {
851 			snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
852 			return;
853 		}
854 
855 		if (strcmp(tokens[t0 + 4], "mask") != 0) {
856 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mask");
857 			return;
858 		}
859 
860 		size = RTE_PORT_IN_ACTION_FLTR_KEY_SIZE;
861 		if ((parse_hex_string(tokens[t0 + 5], p.fltr.key_mask, &size) != 0) ||
862 			(size != RTE_PORT_IN_ACTION_FLTR_KEY_SIZE)) {
863 			snprintf(out, out_size, MSG_ARG_INVALID, "key_mask");
864 			return;
865 		}
866 
867 		if (strcmp(tokens[t0 + 6], "key") != 0) {
868 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "key");
869 			return;
870 		}
871 
872 		size = RTE_PORT_IN_ACTION_FLTR_KEY_SIZE;
873 		if ((parse_hex_string(tokens[t0 + 7], p.fltr.key, &size) != 0) ||
874 			(size != RTE_PORT_IN_ACTION_FLTR_KEY_SIZE)) {
875 			snprintf(out, out_size, MSG_ARG_INVALID, "key_value");
876 			return;
877 		}
878 
879 		if (strcmp(tokens[t0 + 8], "port") != 0) {
880 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
881 			return;
882 		}
883 
884 		if (parser_read_uint32(&p.fltr.port_id, tokens[t0 + 9]) != 0) {
885 			snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
886 			return;
887 		}
888 
889 		p.action_mask |= 1LLU << RTE_PORT_IN_ACTION_FLTR;
890 		t0 += 10;
891 	} /* filter */
892 
893 	if ((t0 < n_tokens) && (strcmp(tokens[t0], "balance") == 0)) {
894 		uint32_t i;
895 
896 		if (n_tokens < t0 + 22) {
897 			snprintf(out, out_size, MSG_ARG_MISMATCH,
898 				"port in action profile balance");
899 			return;
900 		}
901 
902 		if (strcmp(tokens[t0 + 1], "offset") != 0) {
903 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
904 			return;
905 		}
906 
907 		if (parser_read_uint32(&p.lb.key_offset, tokens[t0 + 2]) != 0) {
908 			snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
909 			return;
910 		}
911 
912 		if (strcmp(tokens[t0 + 3], "mask") != 0) {
913 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mask");
914 			return;
915 		}
916 
917 		p.lb.key_size = RTE_PORT_IN_ACTION_LB_KEY_SIZE_MAX;
918 		if (parse_hex_string(tokens[t0 + 4], p.lb.key_mask, &p.lb.key_size) != 0) {
919 			snprintf(out, out_size, MSG_ARG_INVALID, "key_mask");
920 			return;
921 		}
922 
923 		if (strcmp(tokens[t0 + 5], "port") != 0) {
924 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
925 			return;
926 		}
927 
928 		for (i = 0; i < 16; i++)
929 			if (parser_read_uint32(&p.lb.port_id[i], tokens[t0 + 6 + i]) != 0) {
930 				snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
931 				return;
932 			}
933 
934 		p.action_mask |= 1LLU << RTE_PORT_IN_ACTION_LB;
935 		t0 += 22;
936 	} /* balance */
937 
938 	if (t0 < n_tokens) {
939 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
940 		return;
941 	}
942 
943 	ap = port_in_action_profile_create(name, &p);
944 	if (ap == NULL) {
945 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
946 		return;
947 	}
948 }
949 
950 /**
951  * table action profile <profile_name>
952  *  ipv4 | ipv6
953  *  offset <ip_offset>
954  *  fwd
955  *  [balance offset <key_offset> mask <key_mask> outoffset <out_offset>]
956  *  [meter srtcm | trtcm
957  *      tc <n_tc>
958  *      stats none | pkts | bytes | both]
959  *  [tm spp <n_subports_per_port> pps <n_pipes_per_subport>]
960  *  [encap ether | vlan | qinq | mpls | pppoe]
961  *  [nat src | dst
962  *      proto udp | tcp]
963  *  [ttl drop | fwd
964  *      stats none | pkts]
965  *  [stats pkts | bytes | both]
966  *  [time]
967  */
968 static void
969 cmd_table_action_profile(char **tokens,
970 	uint32_t n_tokens,
971 	char *out,
972 	size_t out_size)
973 {
974 	struct table_action_profile_params p;
975 	struct table_action_profile *ap;
976 	char *name;
977 	uint32_t t0;
978 
979 	memset(&p, 0, sizeof(p));
980 
981 	if (n_tokens < 8) {
982 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
983 		return;
984 	}
985 
986 	if (strcmp(tokens[1], "action") != 0) {
987 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "action");
988 		return;
989 	}
990 
991 	if (strcmp(tokens[2], "profile") != 0) {
992 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
993 		return;
994 	}
995 
996 	name = tokens[3];
997 
998 	if (strcmp(tokens[4], "ipv4") == 0)
999 		p.common.ip_version = 1;
1000 	else if (strcmp(tokens[4], "ipv6") == 0)
1001 		p.common.ip_version = 0;
1002 	else {
1003 		snprintf(out, out_size, MSG_ARG_INVALID, "ipv4 or ipv6");
1004 		return;
1005 	}
1006 
1007 	if (strcmp(tokens[5], "offset") != 0) {
1008 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
1009 		return;
1010 	}
1011 
1012 	if (parser_read_uint32(&p.common.ip_offset, tokens[6]) != 0) {
1013 		snprintf(out, out_size, MSG_ARG_INVALID, "ip_offset");
1014 		return;
1015 	}
1016 
1017 	if (strcmp(tokens[7], "fwd") != 0) {
1018 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "fwd");
1019 		return;
1020 	}
1021 
1022 	p.action_mask |= 1LLU << RTE_TABLE_ACTION_FWD;
1023 
1024 	t0 = 8;
1025 	if ((t0 < n_tokens) && (strcmp(tokens[t0], "balance") == 0)) {
1026 		if (n_tokens < t0 + 7) {
1027 			snprintf(out, out_size, MSG_ARG_MISMATCH, "table action profile balance");
1028 			return;
1029 		}
1030 
1031 		if (strcmp(tokens[t0 + 1], "offset") != 0) {
1032 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
1033 			return;
1034 		}
1035 
1036 		if (parser_read_uint32(&p.lb.key_offset, tokens[t0 + 2]) != 0) {
1037 			snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
1038 			return;
1039 		}
1040 
1041 		if (strcmp(tokens[t0 + 3], "mask") != 0) {
1042 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mask");
1043 			return;
1044 		}
1045 
1046 		p.lb.key_size = RTE_PORT_IN_ACTION_LB_KEY_SIZE_MAX;
1047 		if (parse_hex_string(tokens[t0 + 4], p.lb.key_mask, &p.lb.key_size) != 0) {
1048 			snprintf(out, out_size, MSG_ARG_INVALID, "key_mask");
1049 			return;
1050 		}
1051 
1052 		if (strcmp(tokens[t0 + 5], "outoffset") != 0) {
1053 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "outoffset");
1054 			return;
1055 		}
1056 
1057 		if (parser_read_uint32(&p.lb.out_offset, tokens[t0 + 6]) != 0) {
1058 			snprintf(out, out_size, MSG_ARG_INVALID, "out_offset");
1059 			return;
1060 		}
1061 
1062 		p.action_mask |= 1LLU << RTE_TABLE_ACTION_LB;
1063 		t0 += 7;
1064 	} /* balance */
1065 
1066 	if ((t0 < n_tokens) && (strcmp(tokens[t0], "meter") == 0)) {
1067 		if (n_tokens < t0 + 6) {
1068 			snprintf(out, out_size, MSG_ARG_MISMATCH,
1069 				"table action profile meter");
1070 			return;
1071 		}
1072 
1073 		if (strcmp(tokens[t0 + 1], "srtcm") == 0)
1074 			p.mtr.alg = RTE_TABLE_ACTION_METER_SRTCM;
1075 		else if (strcmp(tokens[t0 + 1], "trtcm") == 0)
1076 			p.mtr.alg = RTE_TABLE_ACTION_METER_TRTCM;
1077 		else {
1078 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1079 				"srtcm or trtcm");
1080 			return;
1081 		}
1082 
1083 		if (strcmp(tokens[t0 + 2], "tc") != 0) {
1084 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "tc");
1085 			return;
1086 		}
1087 
1088 		if (parser_read_uint32(&p.mtr.n_tc, tokens[t0 + 3]) != 0) {
1089 			snprintf(out, out_size, MSG_ARG_INVALID, "n_tc");
1090 			return;
1091 		}
1092 
1093 		if (strcmp(tokens[t0 + 4], "stats") != 0) {
1094 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
1095 			return;
1096 		}
1097 
1098 		if (strcmp(tokens[t0 + 5], "none") == 0) {
1099 			p.mtr.n_packets_enabled = 0;
1100 			p.mtr.n_bytes_enabled = 0;
1101 		} else if (strcmp(tokens[t0 + 5], "pkts") == 0) {
1102 			p.mtr.n_packets_enabled = 1;
1103 			p.mtr.n_bytes_enabled = 0;
1104 		} else if (strcmp(tokens[t0 + 5], "bytes") == 0) {
1105 			p.mtr.n_packets_enabled = 0;
1106 			p.mtr.n_bytes_enabled = 1;
1107 		} else if (strcmp(tokens[t0 + 5], "both") == 0) {
1108 			p.mtr.n_packets_enabled = 1;
1109 			p.mtr.n_bytes_enabled = 1;
1110 		} else {
1111 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1112 				"none or pkts or bytes or both");
1113 			return;
1114 		}
1115 
1116 		p.action_mask |= 1LLU << RTE_TABLE_ACTION_MTR;
1117 		t0 += 6;
1118 	} /* meter */
1119 
1120 	if ((t0 < n_tokens) && (strcmp(tokens[t0], "tm") == 0)) {
1121 		if (n_tokens < t0 + 5) {
1122 			snprintf(out, out_size, MSG_ARG_MISMATCH,
1123 				"table action profile tm");
1124 			return;
1125 		}
1126 
1127 		if (strcmp(tokens[t0 + 1], "spp") != 0) {
1128 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "spp");
1129 			return;
1130 		}
1131 
1132 		if (parser_read_uint32(&p.tm.n_subports_per_port,
1133 			tokens[t0 + 2]) != 0) {
1134 			snprintf(out, out_size, MSG_ARG_INVALID,
1135 				"n_subports_per_port");
1136 			return;
1137 		}
1138 
1139 		if (strcmp(tokens[t0 + 3], "pps") != 0) {
1140 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pps");
1141 			return;
1142 		}
1143 
1144 		if (parser_read_uint32(&p.tm.n_pipes_per_subport,
1145 			tokens[t0 + 4]) != 0) {
1146 			snprintf(out, out_size, MSG_ARG_INVALID,
1147 				"n_pipes_per_subport");
1148 			return;
1149 		}
1150 
1151 		p.action_mask |= 1LLU << RTE_TABLE_ACTION_TM;
1152 		t0 += 5;
1153 	} /* tm */
1154 
1155 	if ((t0 < n_tokens) && (strcmp(tokens[t0], "encap") == 0)) {
1156 		if (n_tokens < t0 + 2) {
1157 			snprintf(out, out_size, MSG_ARG_MISMATCH,
1158 				"action profile encap");
1159 			return;
1160 		}
1161 
1162 		if (strcmp(tokens[t0 + 1], "ether") == 0)
1163 			p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_ETHER;
1164 		else if (strcmp(tokens[t0 + 1], "vlan") == 0)
1165 			p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_VLAN;
1166 		else if (strcmp(tokens[t0 + 1], "qinq") == 0)
1167 			p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_QINQ;
1168 		else if (strcmp(tokens[t0 + 1], "mpls") == 0)
1169 			p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_MPLS;
1170 		else if (strcmp(tokens[t0 + 1], "pppoe") == 0)
1171 			p.encap.encap_mask = 1LLU << RTE_TABLE_ACTION_ENCAP_PPPOE;
1172 		else {
1173 			snprintf(out, out_size, MSG_ARG_MISMATCH, "encap");
1174 			return;
1175 		}
1176 
1177 		p.action_mask |= 1LLU << RTE_TABLE_ACTION_ENCAP;
1178 		t0 += 2;
1179 	} /* encap */
1180 
1181 	if ((t0 < n_tokens) && (strcmp(tokens[t0], "nat") == 0)) {
1182 		if (n_tokens < t0 + 4) {
1183 			snprintf(out, out_size, MSG_ARG_MISMATCH,
1184 				"table action profile nat");
1185 			return;
1186 		}
1187 
1188 		if (strcmp(tokens[t0 + 1], "src") == 0)
1189 			p.nat.source_nat = 1;
1190 		else if (strcmp(tokens[t0 + 1], "dst") == 0)
1191 			p.nat.source_nat = 0;
1192 		else {
1193 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1194 				"src or dst");
1195 			return;
1196 		}
1197 
1198 		if (strcmp(tokens[t0 + 2], "proto") != 0) {
1199 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "proto");
1200 			return;
1201 		}
1202 
1203 		if (strcmp(tokens[t0 + 3], "tcp") == 0)
1204 			p.nat.proto = 0x06;
1205 		else if (strcmp(tokens[t0 + 3], "udp") == 0)
1206 			p.nat.proto = 0x11;
1207 		else {
1208 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1209 				"tcp or udp");
1210 			return;
1211 		}
1212 
1213 		p.action_mask |= 1LLU << RTE_TABLE_ACTION_NAT;
1214 		t0 += 4;
1215 	} /* nat */
1216 
1217 	if ((t0 < n_tokens) && (strcmp(tokens[t0], "ttl") == 0)) {
1218 		if (n_tokens < t0 + 4) {
1219 			snprintf(out, out_size, MSG_ARG_MISMATCH,
1220 				"table action profile ttl");
1221 			return;
1222 		}
1223 
1224 		if (strcmp(tokens[t0 + 1], "drop") == 0)
1225 			p.ttl.drop = 1;
1226 		else if (strcmp(tokens[t0 + 1], "fwd") == 0)
1227 			p.ttl.drop = 0;
1228 		else {
1229 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1230 				"drop or fwd");
1231 			return;
1232 		}
1233 
1234 		if (strcmp(tokens[t0 + 2], "stats") != 0) {
1235 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
1236 			return;
1237 		}
1238 
1239 		if (strcmp(tokens[t0 + 3], "none") == 0)
1240 			p.ttl.n_packets_enabled = 0;
1241 		else if (strcmp(tokens[t0 + 3], "pkts") == 0)
1242 			p.ttl.n_packets_enabled = 1;
1243 		else {
1244 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1245 				"none or pkts");
1246 			return;
1247 		}
1248 
1249 		p.action_mask |= 1LLU << RTE_TABLE_ACTION_TTL;
1250 		t0 += 4;
1251 	} /* ttl */
1252 
1253 	if ((t0 < n_tokens) && (strcmp(tokens[t0], "stats") == 0)) {
1254 		if (n_tokens < t0 + 2) {
1255 			snprintf(out, out_size, MSG_ARG_MISMATCH,
1256 				"table action profile stats");
1257 			return;
1258 		}
1259 
1260 		if (strcmp(tokens[t0 + 1], "pkts") == 0) {
1261 			p.stats.n_packets_enabled = 1;
1262 			p.stats.n_bytes_enabled = 0;
1263 		} else if (strcmp(tokens[t0 + 1], "bytes") == 0) {
1264 			p.stats.n_packets_enabled = 0;
1265 			p.stats.n_bytes_enabled = 1;
1266 		} else if (strcmp(tokens[t0 + 1], "both") == 0) {
1267 			p.stats.n_packets_enabled = 1;
1268 			p.stats.n_bytes_enabled = 1;
1269 		} else {
1270 			snprintf(out, out_size,	MSG_ARG_NOT_FOUND,
1271 				"pkts or bytes or both");
1272 			return;
1273 		}
1274 
1275 		p.action_mask |= 1LLU << RTE_TABLE_ACTION_STATS;
1276 		t0 += 2;
1277 	} /* stats */
1278 
1279 	if ((t0 < n_tokens) && (strcmp(tokens[t0], "time") == 0)) {
1280 		p.action_mask |= 1LLU << RTE_TABLE_ACTION_TIME;
1281 		t0 += 1;
1282 	} /* time */
1283 
1284 	if (t0 < n_tokens) {
1285 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1286 		return;
1287 	}
1288 
1289 	ap = table_action_profile_create(name, &p);
1290 	if (ap == NULL) {
1291 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
1292 		return;
1293 	}
1294 }
1295 
1296 /**
1297  * pipeline <pipeline_name>
1298  *  period <timer_period_ms>
1299  *  offset_port_id <offset_port_id>
1300  *  cpu <cpu_id>
1301  */
1302 static void
1303 cmd_pipeline(char **tokens,
1304 	uint32_t n_tokens,
1305 	char *out,
1306 	size_t out_size)
1307 {
1308 	struct pipeline_params p;
1309 	char *name;
1310 	struct pipeline *pipeline;
1311 
1312 	if (n_tokens != 8) {
1313 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1314 		return;
1315 	}
1316 
1317 	name = tokens[1];
1318 
1319 	if (strcmp(tokens[2], "period") != 0) {
1320 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "period");
1321 		return;
1322 	}
1323 
1324 	if (parser_read_uint32(&p.timer_period_ms, tokens[3]) != 0) {
1325 		snprintf(out, out_size, MSG_ARG_INVALID, "timer_period_ms");
1326 		return;
1327 	}
1328 
1329 	if (strcmp(tokens[4], "offset_port_id") != 0) {
1330 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset_port_id");
1331 		return;
1332 	}
1333 
1334 	if (parser_read_uint32(&p.offset_port_id, tokens[5]) != 0) {
1335 		snprintf(out, out_size, MSG_ARG_INVALID, "offset_port_id");
1336 		return;
1337 	}
1338 
1339 	if (strcmp(tokens[6], "cpu") != 0) {
1340 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cpu");
1341 		return;
1342 	}
1343 
1344 	if (parser_read_uint32(&p.cpu_id, tokens[7]) != 0) {
1345 		snprintf(out, out_size, MSG_ARG_INVALID, "cpu_id");
1346 		return;
1347 	}
1348 
1349 	pipeline = pipeline_create(name, &p);
1350 	if (pipeline == NULL) {
1351 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
1352 		return;
1353 	}
1354 }
1355 
1356 /**
1357  * pipeline <pipeline_name> port in
1358  *  bsz <burst_size>
1359  *  link <link_name> rxq <queue_id>
1360  *  | swq <swq_name>
1361  *  | tmgr <tmgr_name>
1362  *  | tap <tap_name> mempool <mempool_name> mtu <mtu>
1363  *  | kni <kni_name>
1364  *  | source mempool <mempool_name> file <file_name> bpp <n_bytes_per_pkt>
1365  *  [action <port_in_action_profile_name>]
1366  *  [disabled]
1367  */
1368 static void
1369 cmd_pipeline_port_in(char **tokens,
1370 	uint32_t n_tokens,
1371 	char *out,
1372 	size_t out_size)
1373 {
1374 	struct port_in_params p;
1375 	char *pipeline_name;
1376 	uint32_t t0;
1377 	int enabled, status;
1378 
1379 	if (n_tokens < 7) {
1380 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1381 		return;
1382 	}
1383 
1384 	pipeline_name = tokens[1];
1385 
1386 	if (strcmp(tokens[2], "port") != 0) {
1387 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
1388 		return;
1389 	}
1390 
1391 	if (strcmp(tokens[3], "in") != 0) {
1392 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
1393 		return;
1394 	}
1395 
1396 	if (strcmp(tokens[4], "bsz") != 0) {
1397 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
1398 		return;
1399 	}
1400 
1401 	if (parser_read_uint32(&p.burst_size, tokens[5]) != 0) {
1402 		snprintf(out, out_size, MSG_ARG_INVALID, "burst_size");
1403 		return;
1404 	}
1405 
1406 	t0 = 6;
1407 
1408 	if (strcmp(tokens[t0], "link") == 0) {
1409 		if (n_tokens < t0 + 4) {
1410 			snprintf(out, out_size, MSG_ARG_MISMATCH,
1411 				"pipeline port in link");
1412 			return;
1413 		}
1414 
1415 		p.type = PORT_IN_RXQ;
1416 
1417 		p.dev_name = tokens[t0 + 1];
1418 
1419 		if (strcmp(tokens[t0 + 2], "rxq") != 0) {
1420 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq");
1421 			return;
1422 		}
1423 
1424 		if (parser_read_uint16(&p.rxq.queue_id, tokens[t0 + 3]) != 0) {
1425 			snprintf(out, out_size, MSG_ARG_INVALID,
1426 				"queue_id");
1427 			return;
1428 		}
1429 		t0 += 4;
1430 	} else if (strcmp(tokens[t0], "swq") == 0) {
1431 		if (n_tokens < t0 + 2) {
1432 			snprintf(out, out_size, MSG_ARG_MISMATCH,
1433 				"pipeline port in swq");
1434 			return;
1435 		}
1436 
1437 		p.type = PORT_IN_SWQ;
1438 
1439 		p.dev_name = tokens[t0 + 1];
1440 
1441 		t0 += 2;
1442 	} else if (strcmp(tokens[t0], "tmgr") == 0) {
1443 		if (n_tokens < t0 + 2) {
1444 			snprintf(out, out_size, MSG_ARG_MISMATCH,
1445 				"pipeline port in tmgr");
1446 			return;
1447 		}
1448 
1449 		p.type = PORT_IN_TMGR;
1450 
1451 		p.dev_name = tokens[t0 + 1];
1452 
1453 		t0 += 2;
1454 	} else if (strcmp(tokens[t0], "tap") == 0) {
1455 		if (n_tokens < t0 + 6) {
1456 			snprintf(out, out_size, MSG_ARG_MISMATCH,
1457 				"pipeline port in tap");
1458 			return;
1459 		}
1460 
1461 		p.type = PORT_IN_TAP;
1462 
1463 		p.dev_name = tokens[t0 + 1];
1464 
1465 		if (strcmp(tokens[t0 + 2], "mempool") != 0) {
1466 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1467 				"mempool");
1468 			return;
1469 		}
1470 
1471 		p.tap.mempool_name = tokens[t0 + 3];
1472 
1473 		if (strcmp(tokens[t0 + 4], "mtu") != 0) {
1474 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1475 				"mtu");
1476 			return;
1477 		}
1478 
1479 		if (parser_read_uint32(&p.tap.mtu, tokens[t0 + 5]) != 0) {
1480 			snprintf(out, out_size, MSG_ARG_INVALID, "mtu");
1481 			return;
1482 		}
1483 
1484 		t0 += 6;
1485 	} else if (strcmp(tokens[t0], "kni") == 0) {
1486 		if (n_tokens < t0 + 2) {
1487 			snprintf(out, out_size, MSG_ARG_MISMATCH,
1488 				"pipeline port in kni");
1489 			return;
1490 		}
1491 
1492 		p.type = PORT_IN_KNI;
1493 
1494 		p.dev_name = tokens[t0 + 1];
1495 
1496 		t0 += 2;
1497 	} else if (strcmp(tokens[t0], "source") == 0) {
1498 		if (n_tokens < t0 + 6) {
1499 			snprintf(out, out_size, MSG_ARG_MISMATCH,
1500 				"pipeline port in source");
1501 			return;
1502 		}
1503 
1504 		p.type = PORT_IN_SOURCE;
1505 
1506 		p.dev_name = NULL;
1507 
1508 		if (strcmp(tokens[t0 + 1], "mempool") != 0) {
1509 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1510 				"mempool");
1511 			return;
1512 		}
1513 
1514 		p.source.mempool_name = tokens[t0 + 2];
1515 
1516 		if (strcmp(tokens[t0 + 3], "file") != 0) {
1517 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1518 				"file");
1519 			return;
1520 		}
1521 
1522 		p.source.file_name = tokens[t0 + 4];
1523 
1524 		if (strcmp(tokens[t0 + 5], "bpp") != 0) {
1525 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1526 				"bpp");
1527 			return;
1528 		}
1529 
1530 		if (parser_read_uint32(&p.source.n_bytes_per_pkt, tokens[t0 + 6]) != 0) {
1531 			snprintf(out, out_size, MSG_ARG_INVALID,
1532 				"n_bytes_per_pkt");
1533 			return;
1534 		}
1535 
1536 		t0 += 7;
1537 	} else {
1538 		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
1539 		return;
1540 	}
1541 
1542 	p.action_profile_name = NULL;
1543 	if ((n_tokens > t0) && (strcmp(tokens[t0], "action") == 0)) {
1544 		if (n_tokens < t0 + 2) {
1545 			snprintf(out, out_size, MSG_ARG_MISMATCH, "action");
1546 			return;
1547 		}
1548 
1549 		p.action_profile_name = tokens[t0 + 1];
1550 
1551 		t0 += 2;
1552 	}
1553 
1554 	enabled = 1;
1555 	if ((n_tokens > t0) &&
1556 		(strcmp(tokens[t0], "disabled") == 0)) {
1557 		enabled = 0;
1558 
1559 		t0 += 1;
1560 	}
1561 
1562 	if (n_tokens != t0) {
1563 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1564 		return;
1565 	}
1566 
1567 	status = pipeline_port_in_create(pipeline_name,
1568 		&p, enabled);
1569 	if (status) {
1570 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
1571 		return;
1572 	}
1573 }
1574 
1575 /**
1576  * pipeline <pipeline_name> port out
1577  *  bsz <burst_size>
1578  *  link <link_name> txq <txq_id>
1579  *  | swq <swq_name>
1580  *  | tmgr <tmgr_name>
1581  *  | tap <tap_name>
1582  *  | kni <kni_name>
1583  *  | sink [file <file_name> pkts <max_n_pkts>]
1584  */
1585 static void
1586 cmd_pipeline_port_out(char **tokens,
1587 	uint32_t n_tokens,
1588 	char *out,
1589 	size_t out_size)
1590 {
1591 	struct port_out_params p;
1592 	char *pipeline_name;
1593 	int status;
1594 
1595 	memset(&p, 0, sizeof(p));
1596 
1597 	if (n_tokens < 7) {
1598 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1599 		return;
1600 	}
1601 
1602 	pipeline_name = tokens[1];
1603 
1604 	if (strcmp(tokens[2], "port") != 0) {
1605 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
1606 		return;
1607 	}
1608 
1609 	if (strcmp(tokens[3], "out") != 0) {
1610 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "out");
1611 		return;
1612 	}
1613 
1614 	if (strcmp(tokens[4], "bsz") != 0) {
1615 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
1616 		return;
1617 	}
1618 
1619 	if (parser_read_uint32(&p.burst_size, tokens[5]) != 0) {
1620 		snprintf(out, out_size, MSG_ARG_INVALID, "burst_size");
1621 		return;
1622 	}
1623 
1624 	if (strcmp(tokens[6], "link") == 0) {
1625 		if (n_tokens != 10) {
1626 			snprintf(out, out_size, MSG_ARG_MISMATCH,
1627 				"pipeline port out link");
1628 			return;
1629 		}
1630 
1631 		p.type = PORT_OUT_TXQ;
1632 
1633 		p.dev_name = tokens[7];
1634 
1635 		if (strcmp(tokens[8], "txq") != 0) {
1636 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "txq");
1637 			return;
1638 		}
1639 
1640 		if (parser_read_uint16(&p.txq.queue_id, tokens[9]) != 0) {
1641 			snprintf(out, out_size, MSG_ARG_INVALID, "queue_id");
1642 			return;
1643 		}
1644 	} else if (strcmp(tokens[6], "swq") == 0) {
1645 		if (n_tokens != 8) {
1646 			snprintf(out, out_size, MSG_ARG_MISMATCH,
1647 				"pipeline port out swq");
1648 			return;
1649 		}
1650 
1651 		p.type = PORT_OUT_SWQ;
1652 
1653 		p.dev_name = tokens[7];
1654 	} else if (strcmp(tokens[6], "tmgr") == 0) {
1655 		if (n_tokens != 8) {
1656 			snprintf(out, out_size, MSG_ARG_MISMATCH,
1657 				"pipeline port out tmgr");
1658 			return;
1659 		}
1660 
1661 		p.type = PORT_OUT_TMGR;
1662 
1663 		p.dev_name = tokens[7];
1664 	} else if (strcmp(tokens[6], "tap") == 0) {
1665 		if (n_tokens != 8) {
1666 			snprintf(out, out_size, MSG_ARG_MISMATCH,
1667 				"pipeline port out tap");
1668 			return;
1669 		}
1670 
1671 		p.type = PORT_OUT_TAP;
1672 
1673 		p.dev_name = tokens[7];
1674 	} else if (strcmp(tokens[6], "kni") == 0) {
1675 		if (n_tokens != 8) {
1676 			snprintf(out, out_size, MSG_ARG_MISMATCH,
1677 				"pipeline port out kni");
1678 			return;
1679 		}
1680 
1681 		p.type = PORT_OUT_KNI;
1682 
1683 		p.dev_name = tokens[7];
1684 	} else if (strcmp(tokens[6], "sink") == 0) {
1685 		if ((n_tokens != 7) && (n_tokens != 11)) {
1686 			snprintf(out, out_size, MSG_ARG_MISMATCH,
1687 				"pipeline port out sink");
1688 			return;
1689 		}
1690 
1691 		p.type = PORT_OUT_SINK;
1692 
1693 		p.dev_name = NULL;
1694 
1695 		if (n_tokens == 7) {
1696 			p.sink.file_name = NULL;
1697 			p.sink.max_n_pkts = 0;
1698 		} else {
1699 			if (strcmp(tokens[7], "file") != 0) {
1700 				snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1701 					"file");
1702 				return;
1703 			}
1704 
1705 			p.sink.file_name = tokens[8];
1706 
1707 			if (strcmp(tokens[9], "pkts") != 0) {
1708 				snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pkts");
1709 				return;
1710 			}
1711 
1712 			if (parser_read_uint32(&p.sink.max_n_pkts, tokens[10]) != 0) {
1713 				snprintf(out, out_size, MSG_ARG_INVALID, "max_n_pkts");
1714 				return;
1715 			}
1716 		}
1717 	} else {
1718 		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
1719 		return;
1720 	}
1721 
1722 	status = pipeline_port_out_create(pipeline_name, &p);
1723 	if (status) {
1724 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
1725 		return;
1726 	}
1727 }
1728 
1729 /**
1730  * pipeline <pipeline_name> table
1731  *      match
1732  *      acl
1733  *          ipv4 | ipv6
1734  *          offset <ip_header_offset>
1735  *          size <n_rules>
1736  *      | array
1737  *          offset <key_offset>
1738  *          size <n_keys>
1739  *      | hash
1740  *          ext | lru
1741  *          key <key_size>
1742  *          mask <key_mask>
1743  *          offset <key_offset>
1744  *          buckets <n_buckets>
1745  *          size <n_keys>
1746  *      | lpm
1747  *          ipv4 | ipv6
1748  *          offset <ip_header_offset>
1749  *          size <n_rules>
1750  *      | stub
1751  *  [action <table_action_profile_name>]
1752  */
1753 static void
1754 cmd_pipeline_table(char **tokens,
1755 	uint32_t n_tokens,
1756 	char *out,
1757 	size_t out_size)
1758 {
1759 	uint8_t key_mask[TABLE_RULE_MATCH_SIZE_MAX];
1760 	struct table_params p;
1761 	char *pipeline_name;
1762 	uint32_t t0;
1763 	int status;
1764 
1765 	if (n_tokens < 5) {
1766 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1767 		return;
1768 	}
1769 
1770 	pipeline_name = tokens[1];
1771 
1772 	if (strcmp(tokens[2], "table") != 0) {
1773 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
1774 		return;
1775 	}
1776 
1777 	if (strcmp(tokens[3], "match") != 0) {
1778 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "match");
1779 		return;
1780 	}
1781 
1782 	t0 = 4;
1783 	if (strcmp(tokens[t0], "acl") == 0) {
1784 		if (n_tokens < t0 + 6) {
1785 			snprintf(out, out_size, MSG_ARG_MISMATCH,
1786 				"pipeline table acl");
1787 			return;
1788 		}
1789 
1790 		p.match_type = TABLE_ACL;
1791 
1792 		if (strcmp(tokens[t0 + 1], "ipv4") == 0)
1793 			p.match.acl.ip_version = 1;
1794 		else if (strcmp(tokens[t0 + 1], "ipv6") == 0)
1795 			p.match.acl.ip_version = 0;
1796 		else {
1797 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1798 				"ipv4 or ipv6");
1799 			return;
1800 		}
1801 
1802 		if (strcmp(tokens[t0 + 2], "offset") != 0) {
1803 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
1804 			return;
1805 		}
1806 
1807 		if (parser_read_uint32(&p.match.acl.ip_header_offset,
1808 			tokens[t0 + 3]) != 0) {
1809 			snprintf(out, out_size, MSG_ARG_INVALID,
1810 				"ip_header_offset");
1811 			return;
1812 		}
1813 
1814 		if (strcmp(tokens[t0 + 4], "size") != 0) {
1815 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
1816 			return;
1817 		}
1818 
1819 		if (parser_read_uint32(&p.match.acl.n_rules,
1820 			tokens[t0 + 5]) != 0) {
1821 			snprintf(out, out_size, MSG_ARG_INVALID, "n_rules");
1822 			return;
1823 		}
1824 
1825 		t0 += 6;
1826 	} else if (strcmp(tokens[t0], "array") == 0) {
1827 		if (n_tokens < t0 + 5) {
1828 			snprintf(out, out_size, MSG_ARG_MISMATCH,
1829 				"pipeline table array");
1830 			return;
1831 		}
1832 
1833 		p.match_type = TABLE_ARRAY;
1834 
1835 		if (strcmp(tokens[t0 + 1], "offset") != 0) {
1836 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
1837 			return;
1838 		}
1839 
1840 		if (parser_read_uint32(&p.match.array.key_offset,
1841 			tokens[t0 + 2]) != 0) {
1842 			snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
1843 			return;
1844 		}
1845 
1846 		if (strcmp(tokens[t0 + 3], "size") != 0) {
1847 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
1848 			return;
1849 		}
1850 
1851 		if (parser_read_uint32(&p.match.array.n_keys,
1852 			tokens[t0 + 4]) != 0) {
1853 			snprintf(out, out_size, MSG_ARG_INVALID, "n_keys");
1854 			return;
1855 		}
1856 
1857 		t0 += 5;
1858 	} else if (strcmp(tokens[t0], "hash") == 0) {
1859 		uint32_t key_mask_size = TABLE_RULE_MATCH_SIZE_MAX;
1860 
1861 		if (n_tokens < t0 + 12) {
1862 			snprintf(out, out_size, MSG_ARG_MISMATCH,
1863 				"pipeline table hash");
1864 			return;
1865 		}
1866 
1867 		p.match_type = TABLE_HASH;
1868 
1869 		if (strcmp(tokens[t0 + 1], "ext") == 0)
1870 			p.match.hash.extendable_bucket = 1;
1871 		else if (strcmp(tokens[t0 + 1], "lru") == 0)
1872 			p.match.hash.extendable_bucket = 0;
1873 		else {
1874 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1875 				"ext or lru");
1876 			return;
1877 		}
1878 
1879 		if (strcmp(tokens[t0 + 2], "key") != 0) {
1880 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "key");
1881 			return;
1882 		}
1883 
1884 		if ((parser_read_uint32(&p.match.hash.key_size,
1885 			tokens[t0 + 3]) != 0) ||
1886 			(p.match.hash.key_size == 0) ||
1887 			(p.match.hash.key_size > TABLE_RULE_MATCH_SIZE_MAX)) {
1888 			snprintf(out, out_size, MSG_ARG_INVALID, "key_size");
1889 			return;
1890 		}
1891 
1892 		if (strcmp(tokens[t0 + 4], "mask") != 0) {
1893 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mask");
1894 			return;
1895 		}
1896 
1897 		if ((parse_hex_string(tokens[t0 + 5],
1898 			key_mask, &key_mask_size) != 0) ||
1899 			(key_mask_size != p.match.hash.key_size)) {
1900 			snprintf(out, out_size, MSG_ARG_INVALID, "key_mask");
1901 			return;
1902 		}
1903 		p.match.hash.key_mask = key_mask;
1904 
1905 		if (strcmp(tokens[t0 + 6], "offset") != 0) {
1906 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
1907 			return;
1908 		}
1909 
1910 		if (parser_read_uint32(&p.match.hash.key_offset,
1911 			tokens[t0 + 7]) != 0) {
1912 			snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
1913 			return;
1914 		}
1915 
1916 		if (strcmp(tokens[t0 + 8], "buckets") != 0) {
1917 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "buckets");
1918 			return;
1919 		}
1920 
1921 		if (parser_read_uint32(&p.match.hash.n_buckets,
1922 			tokens[t0 + 9]) != 0) {
1923 			snprintf(out, out_size, MSG_ARG_INVALID, "n_buckets");
1924 			return;
1925 		}
1926 
1927 		if (strcmp(tokens[t0 + 10], "size") != 0) {
1928 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
1929 			return;
1930 		}
1931 
1932 		if (parser_read_uint32(&p.match.hash.n_keys,
1933 			tokens[t0 + 11]) != 0) {
1934 			snprintf(out, out_size, MSG_ARG_INVALID, "n_keys");
1935 			return;
1936 		}
1937 
1938 		t0 += 12;
1939 	} else if (strcmp(tokens[t0], "lpm") == 0) {
1940 		if (n_tokens < t0 + 6) {
1941 			snprintf(out, out_size, MSG_ARG_MISMATCH,
1942 				"pipeline table lpm");
1943 			return;
1944 		}
1945 
1946 		p.match_type = TABLE_LPM;
1947 
1948 		if (strcmp(tokens[t0 + 1], "ipv4") == 0)
1949 			p.match.lpm.key_size = 4;
1950 		else if (strcmp(tokens[t0 + 1], "ipv6") == 0)
1951 			p.match.lpm.key_size = 16;
1952 		else {
1953 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
1954 				"ipv4 or ipv6");
1955 			return;
1956 		}
1957 
1958 		if (strcmp(tokens[t0 + 2], "offset") != 0) {
1959 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "offset");
1960 			return;
1961 		}
1962 
1963 		if (parser_read_uint32(&p.match.lpm.key_offset,
1964 			tokens[t0 + 3]) != 0) {
1965 			snprintf(out, out_size, MSG_ARG_INVALID, "key_offset");
1966 			return;
1967 		}
1968 
1969 		if (strcmp(tokens[t0 + 4], "size") != 0) {
1970 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
1971 			return;
1972 		}
1973 
1974 		if (parser_read_uint32(&p.match.lpm.n_rules,
1975 			tokens[t0 + 5]) != 0) {
1976 			snprintf(out, out_size, MSG_ARG_INVALID, "n_rules");
1977 			return;
1978 		}
1979 
1980 		t0 += 6;
1981 	} else if (strcmp(tokens[t0], "stub") == 0) {
1982 		p.match_type = TABLE_STUB;
1983 
1984 		t0 += 1;
1985 	} else {
1986 		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
1987 		return;
1988 	}
1989 
1990 	p.action_profile_name = NULL;
1991 	if ((n_tokens > t0) && (strcmp(tokens[t0], "action") == 0)) {
1992 		if (n_tokens < t0 + 2) {
1993 			snprintf(out, out_size, MSG_ARG_MISMATCH, "action");
1994 			return;
1995 		}
1996 
1997 		p.action_profile_name = tokens[t0 + 1];
1998 
1999 		t0 += 2;
2000 	}
2001 
2002 	if (n_tokens > t0) {
2003 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2004 		return;
2005 	}
2006 
2007 	status = pipeline_table_create(pipeline_name, &p);
2008 	if (status) {
2009 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
2010 		return;
2011 	}
2012 }
2013 
2014 /**
2015  * pipeline <pipeline_name> port in <port_id> table <table_id>
2016  */
2017 static void
2018 cmd_pipeline_port_in_table(char **tokens,
2019 	uint32_t n_tokens,
2020 	char *out,
2021 	size_t out_size)
2022 {
2023 	char *pipeline_name;
2024 	uint32_t port_id, table_id;
2025 	int status;
2026 
2027 	if (n_tokens != 7) {
2028 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2029 		return;
2030 	}
2031 
2032 	pipeline_name = tokens[1];
2033 
2034 	if (strcmp(tokens[2], "port") != 0) {
2035 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
2036 		return;
2037 	}
2038 
2039 	if (strcmp(tokens[3], "in") != 0) {
2040 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
2041 		return;
2042 	}
2043 
2044 	if (parser_read_uint32(&port_id, tokens[4]) != 0) {
2045 		snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
2046 		return;
2047 	}
2048 
2049 	if (strcmp(tokens[5], "table") != 0) {
2050 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
2051 		return;
2052 	}
2053 
2054 	if (parser_read_uint32(&table_id, tokens[6]) != 0) {
2055 		snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
2056 		return;
2057 	}
2058 
2059 	status = pipeline_port_in_connect_to_table(pipeline_name,
2060 		port_id,
2061 		table_id);
2062 	if (status) {
2063 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
2064 		return;
2065 	}
2066 }
2067 
2068 /**
2069  * pipeline <pipeline_name> port in <port_id> stats read [clear]
2070  */
2071 
2072 #define MSG_PIPELINE_PORT_IN_STATS                         \
2073 	"Pkts in: %" PRIu64 "\n"                           \
2074 	"Pkts dropped by AH: %" PRIu64 "\n"                \
2075 	"Pkts dropped by other: %" PRIu64 "\n"
2076 
2077 static void
2078 cmd_pipeline_port_in_stats(char **tokens,
2079 	uint32_t n_tokens,
2080 	char *out,
2081 	size_t out_size)
2082 {
2083 	struct rte_pipeline_port_in_stats stats;
2084 	char *pipeline_name;
2085 	uint32_t port_id;
2086 	int clear, status;
2087 
2088 	if ((n_tokens != 7) && (n_tokens != 8)) {
2089 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2090 		return;
2091 	}
2092 
2093 	pipeline_name = tokens[1];
2094 
2095 	if (strcmp(tokens[2], "port") != 0) {
2096 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
2097 		return;
2098 	}
2099 
2100 	if (strcmp(tokens[3], "in") != 0) {
2101 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
2102 		return;
2103 	}
2104 
2105 	if (parser_read_uint32(&port_id, tokens[4]) != 0) {
2106 		snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
2107 		return;
2108 	}
2109 
2110 	if (strcmp(tokens[5], "stats") != 0) {
2111 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
2112 		return;
2113 	}
2114 
2115 	if (strcmp(tokens[6], "read") != 0) {
2116 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "read");
2117 		return;
2118 	}
2119 
2120 	clear = 0;
2121 	if (n_tokens == 8) {
2122 		if (strcmp(tokens[7], "clear") != 0) {
2123 			snprintf(out, out_size, MSG_ARG_INVALID, "clear");
2124 			return;
2125 		}
2126 
2127 		clear = 1;
2128 	}
2129 
2130 	status = pipeline_port_in_stats_read(pipeline_name,
2131 		port_id,
2132 		&stats,
2133 		clear);
2134 	if (status) {
2135 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
2136 		return;
2137 	}
2138 
2139 	snprintf(out, out_size, MSG_PIPELINE_PORT_IN_STATS,
2140 		stats.stats.n_pkts_in,
2141 		stats.n_pkts_dropped_by_ah,
2142 		stats.stats.n_pkts_drop);
2143 }
2144 
2145 /**
2146  * pipeline <pipeline_name> port in <port_id> enable
2147  */
2148 static void
2149 cmd_pipeline_port_in_enable(char **tokens,
2150 	uint32_t n_tokens,
2151 	char *out,
2152 	size_t out_size)
2153 {
2154 	char *pipeline_name;
2155 	uint32_t port_id;
2156 	int status;
2157 
2158 	if (n_tokens != 6) {
2159 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2160 		return;
2161 	}
2162 
2163 	pipeline_name = tokens[1];
2164 
2165 	if (strcmp(tokens[2], "port") != 0) {
2166 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
2167 		return;
2168 	}
2169 
2170 	if (strcmp(tokens[3], "in") != 0) {
2171 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
2172 		return;
2173 	}
2174 
2175 	if (parser_read_uint32(&port_id, tokens[4]) != 0) {
2176 		snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
2177 		return;
2178 	}
2179 
2180 	if (strcmp(tokens[5], "enable") != 0) {
2181 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
2182 		return;
2183 	}
2184 
2185 	status = pipeline_port_in_enable(pipeline_name, port_id);
2186 	if (status) {
2187 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
2188 		return;
2189 	}
2190 }
2191 
2192 /**
2193  * pipeline <pipeline_name> port in <port_id> disable
2194  */
2195 static void
2196 cmd_pipeline_port_in_disable(char **tokens,
2197 	uint32_t n_tokens,
2198 	char *out,
2199 	size_t out_size)
2200 {
2201 	char *pipeline_name;
2202 	uint32_t port_id;
2203 	int status;
2204 
2205 	if (n_tokens != 6) {
2206 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2207 		return;
2208 	}
2209 
2210 	pipeline_name = tokens[1];
2211 
2212 	if (strcmp(tokens[2], "port") != 0) {
2213 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
2214 		return;
2215 	}
2216 
2217 	if (strcmp(tokens[3], "in") != 0) {
2218 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
2219 		return;
2220 	}
2221 
2222 	if (parser_read_uint32(&port_id, tokens[4]) != 0) {
2223 		snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
2224 		return;
2225 	}
2226 
2227 	if (strcmp(tokens[5], "disable") != 0) {
2228 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
2229 		return;
2230 	}
2231 
2232 	status = pipeline_port_in_disable(pipeline_name, port_id);
2233 	if (status) {
2234 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
2235 		return;
2236 	}
2237 }
2238 
2239 /**
2240  * pipeline <pipeline_name> port out <port_id> stats read [clear]
2241  */
2242 #define MSG_PIPELINE_PORT_OUT_STATS                        \
2243 	"Pkts in: %" PRIu64 "\n"                           \
2244 	"Pkts dropped by AH: %" PRIu64 "\n"                \
2245 	"Pkts dropped by other: %" PRIu64 "\n"
2246 
2247 static void
2248 cmd_pipeline_port_out_stats(char **tokens,
2249 	uint32_t n_tokens,
2250 	char *out,
2251 	size_t out_size)
2252 {
2253 	struct rte_pipeline_port_out_stats stats;
2254 	char *pipeline_name;
2255 	uint32_t port_id;
2256 	int clear, status;
2257 
2258 	if ((n_tokens != 7) && (n_tokens != 8)) {
2259 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2260 		return;
2261 	}
2262 
2263 	pipeline_name = tokens[1];
2264 
2265 	if (strcmp(tokens[2], "port") != 0) {
2266 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
2267 		return;
2268 	}
2269 
2270 	if (strcmp(tokens[3], "out") != 0) {
2271 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "out");
2272 		return;
2273 	}
2274 
2275 	if (parser_read_uint32(&port_id, tokens[4]) != 0) {
2276 		snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
2277 		return;
2278 	}
2279 
2280 	if (strcmp(tokens[5], "stats") != 0) {
2281 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
2282 		return;
2283 	}
2284 
2285 	if (strcmp(tokens[6], "read") != 0) {
2286 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "read");
2287 		return;
2288 	}
2289 
2290 	clear = 0;
2291 	if (n_tokens == 8) {
2292 		if (strcmp(tokens[7], "clear") != 0) {
2293 			snprintf(out, out_size, MSG_ARG_INVALID, "clear");
2294 			return;
2295 		}
2296 
2297 		clear = 1;
2298 	}
2299 
2300 	status = pipeline_port_out_stats_read(pipeline_name,
2301 		port_id,
2302 		&stats,
2303 		clear);
2304 	if (status) {
2305 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
2306 		return;
2307 	}
2308 
2309 	snprintf(out, out_size, MSG_PIPELINE_PORT_OUT_STATS,
2310 		stats.stats.n_pkts_in,
2311 		stats.n_pkts_dropped_by_ah,
2312 		stats.stats.n_pkts_drop);
2313 }
2314 
2315 /**
2316  * pipeline <pipeline_name> table <table_id> stats read [clear]
2317  */
2318 #define MSG_PIPELINE_TABLE_STATS                                     \
2319 	"Pkts in: %" PRIu64 "\n"                                     \
2320 	"Pkts in with lookup miss: %" PRIu64 "\n"                    \
2321 	"Pkts in with lookup hit dropped by AH: %" PRIu64 "\n"       \
2322 	"Pkts in with lookup hit dropped by others: %" PRIu64 "\n"   \
2323 	"Pkts in with lookup miss dropped by AH: %" PRIu64 "\n"      \
2324 	"Pkts in with lookup miss dropped by others: %" PRIu64 "\n"
2325 
2326 static void
2327 cmd_pipeline_table_stats(char **tokens,
2328 	uint32_t n_tokens,
2329 	char *out,
2330 	size_t out_size)
2331 {
2332 	struct rte_pipeline_table_stats stats;
2333 	char *pipeline_name;
2334 	uint32_t table_id;
2335 	int clear, status;
2336 
2337 	if ((n_tokens != 6) && (n_tokens != 7)) {
2338 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2339 		return;
2340 	}
2341 
2342 	pipeline_name = tokens[1];
2343 
2344 	if (strcmp(tokens[2], "table") != 0) {
2345 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
2346 		return;
2347 	}
2348 
2349 	if (parser_read_uint32(&table_id, tokens[3]) != 0) {
2350 		snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
2351 		return;
2352 	}
2353 
2354 	if (strcmp(tokens[4], "stats") != 0) {
2355 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
2356 		return;
2357 	}
2358 
2359 	if (strcmp(tokens[5], "read") != 0) {
2360 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "read");
2361 		return;
2362 	}
2363 
2364 	clear = 0;
2365 	if (n_tokens == 7) {
2366 		if (strcmp(tokens[6], "clear") != 0) {
2367 			snprintf(out, out_size, MSG_ARG_INVALID, "clear");
2368 			return;
2369 		}
2370 
2371 		clear = 1;
2372 	}
2373 
2374 	status = pipeline_table_stats_read(pipeline_name,
2375 		table_id,
2376 		&stats,
2377 		clear);
2378 	if (status) {
2379 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
2380 		return;
2381 	}
2382 
2383 	snprintf(out, out_size, MSG_PIPELINE_TABLE_STATS,
2384 		stats.stats.n_pkts_in,
2385 		stats.stats.n_pkts_lookup_miss,
2386 		stats.n_pkts_dropped_by_lkp_hit_ah,
2387 		stats.n_pkts_dropped_lkp_hit,
2388 		stats.n_pkts_dropped_by_lkp_miss_ah,
2389 		stats.n_pkts_dropped_lkp_miss);
2390 }
2391 
2392 /**
2393  * <match> ::=
2394  *
2395  * match
2396  *    acl
2397  *       priority <priority>
2398  *       ipv4 | ipv6 <sa> <sa_depth> <da> <da_depth>
2399  *       <sp0> <sp1> <dp0> <dp1> <proto>
2400  *    | array <pos>
2401  *    | hash
2402  *       raw <key>
2403  *       | ipv4_5tuple <sa> <da> <sp> <dp> <proto>
2404  *       | ipv6_5tuple <sa> <da> <sp> <dp> <proto>
2405  *       | ipv4_addr <addr>
2406  *       | ipv6_addr <addr>
2407  *       | qinq <svlan> <cvlan>
2408  *    | lpm
2409  *       ipv4 | ipv6 <addr> <depth>
2410  */
2411 struct pkt_key_qinq {
2412 	uint16_t ethertype_svlan;
2413 	uint16_t svlan;
2414 	uint16_t ethertype_cvlan;
2415 	uint16_t cvlan;
2416 } __attribute__((__packed__));
2417 
2418 struct pkt_key_ipv4_5tuple {
2419 	uint8_t time_to_live;
2420 	uint8_t proto;
2421 	uint16_t hdr_checksum;
2422 	uint32_t sa;
2423 	uint32_t da;
2424 	uint16_t sp;
2425 	uint16_t dp;
2426 } __attribute__((__packed__));
2427 
2428 struct pkt_key_ipv6_5tuple {
2429 	uint16_t payload_length;
2430 	uint8_t proto;
2431 	uint8_t hop_limit;
2432 	uint8_t sa[16];
2433 	uint8_t da[16];
2434 	uint16_t sp;
2435 	uint16_t dp;
2436 } __attribute__((__packed__));
2437 
2438 struct pkt_key_ipv4_addr {
2439 	uint32_t addr;
2440 } __attribute__((__packed__));
2441 
2442 struct pkt_key_ipv6_addr {
2443 	uint8_t addr[16];
2444 } __attribute__((__packed__));
2445 
2446 static uint32_t
2447 parse_match(char **tokens,
2448 	uint32_t n_tokens,
2449 	char *out,
2450 	size_t out_size,
2451 	struct table_rule_match *m)
2452 {
2453 	memset(m, 0, sizeof(*m));
2454 
2455 	if (n_tokens < 2)
2456 		return 0;
2457 
2458 	if (strcmp(tokens[0], "match") != 0) {
2459 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "match");
2460 		return 0;
2461 	}
2462 
2463 	if (strcmp(tokens[1], "acl") == 0) {
2464 		if (n_tokens < 14) {
2465 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2466 			return 0;
2467 		}
2468 
2469 		m->match_type = TABLE_ACL;
2470 
2471 		if (strcmp(tokens[2], "priority") != 0) {
2472 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "priority");
2473 			return 0;
2474 		}
2475 
2476 		if (parser_read_uint32(&m->match.acl.priority,
2477 			tokens[3]) != 0) {
2478 			snprintf(out, out_size, MSG_ARG_INVALID, "priority");
2479 			return 0;
2480 		}
2481 
2482 		if (strcmp(tokens[4], "ipv4") == 0) {
2483 			struct in_addr saddr, daddr;
2484 
2485 			m->match.acl.ip_version = 1;
2486 
2487 			if (parse_ipv4_addr(tokens[5], &saddr) != 0) {
2488 				snprintf(out, out_size, MSG_ARG_INVALID, "sa");
2489 				return 0;
2490 			}
2491 			m->match.acl.ipv4.sa = rte_be_to_cpu_32(saddr.s_addr);
2492 
2493 			if (parse_ipv4_addr(tokens[7], &daddr) != 0) {
2494 				snprintf(out, out_size, MSG_ARG_INVALID, "da");
2495 				return 0;
2496 			}
2497 			m->match.acl.ipv4.da = rte_be_to_cpu_32(daddr.s_addr);
2498 		} else if (strcmp(tokens[4], "ipv6") == 0) {
2499 			struct in6_addr saddr, daddr;
2500 
2501 			m->match.acl.ip_version = 0;
2502 
2503 			if (parse_ipv6_addr(tokens[5], &saddr) != 0) {
2504 				snprintf(out, out_size, MSG_ARG_INVALID, "sa");
2505 				return 0;
2506 			}
2507 			memcpy(m->match.acl.ipv6.sa, saddr.s6_addr, 16);
2508 
2509 			if (parse_ipv6_addr(tokens[7], &daddr) != 0) {
2510 				snprintf(out, out_size, MSG_ARG_INVALID, "da");
2511 				return 0;
2512 			}
2513 			memcpy(m->match.acl.ipv6.da, daddr.s6_addr, 16);
2514 		} else {
2515 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
2516 				"ipv4 or ipv6");
2517 			return 0;
2518 		}
2519 
2520 		if (parser_read_uint32(&m->match.acl.sa_depth,
2521 			tokens[6]) != 0) {
2522 			snprintf(out, out_size, MSG_ARG_INVALID, "sa_depth");
2523 			return 0;
2524 		}
2525 
2526 		if (parser_read_uint32(&m->match.acl.da_depth,
2527 			tokens[8]) != 0) {
2528 			snprintf(out, out_size, MSG_ARG_INVALID, "da_depth");
2529 			return 0;
2530 		}
2531 
2532 		if (parser_read_uint16(&m->match.acl.sp0, tokens[9]) != 0) {
2533 			snprintf(out, out_size, MSG_ARG_INVALID, "sp0");
2534 			return 0;
2535 		}
2536 
2537 		if (parser_read_uint16(&m->match.acl.sp1, tokens[10]) != 0) {
2538 			snprintf(out, out_size, MSG_ARG_INVALID, "sp1");
2539 			return 0;
2540 		}
2541 
2542 		if (parser_read_uint16(&m->match.acl.dp0, tokens[11]) != 0) {
2543 			snprintf(out, out_size, MSG_ARG_INVALID, "dp0");
2544 			return 0;
2545 		}
2546 
2547 		if (parser_read_uint16(&m->match.acl.dp1, tokens[12]) != 0) {
2548 			snprintf(out, out_size, MSG_ARG_INVALID, "dp1");
2549 			return 0;
2550 		}
2551 
2552 		if (parser_read_uint8(&m->match.acl.proto, tokens[13]) != 0) {
2553 			snprintf(out, out_size, MSG_ARG_INVALID, "proto");
2554 			return 0;
2555 		}
2556 
2557 		m->match.acl.proto_mask = 0xff;
2558 
2559 		return 14;
2560 	} /* acl */
2561 
2562 	if (strcmp(tokens[1], "array") == 0) {
2563 		if (n_tokens < 3) {
2564 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2565 			return 0;
2566 		}
2567 
2568 		m->match_type = TABLE_ARRAY;
2569 
2570 		if (parser_read_uint32(&m->match.array.pos, tokens[2]) != 0) {
2571 			snprintf(out, out_size, MSG_ARG_INVALID, "pos");
2572 			return 0;
2573 		}
2574 
2575 		return 3;
2576 	} /* array */
2577 
2578 	if (strcmp(tokens[1], "hash") == 0) {
2579 		if (n_tokens < 3) {
2580 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2581 			return 0;
2582 		}
2583 
2584 		m->match_type = TABLE_HASH;
2585 
2586 		if (strcmp(tokens[2], "raw") == 0) {
2587 			uint32_t key_size = TABLE_RULE_MATCH_SIZE_MAX;
2588 
2589 			if (n_tokens < 4) {
2590 				snprintf(out, out_size, MSG_ARG_MISMATCH,
2591 					tokens[0]);
2592 				return 0;
2593 			}
2594 
2595 			if (parse_hex_string(tokens[3],
2596 				m->match.hash.key, &key_size) != 0) {
2597 				snprintf(out, out_size, MSG_ARG_INVALID, "key");
2598 				return 0;
2599 			}
2600 
2601 			return 4;
2602 		} /* hash raw */
2603 
2604 		if (strcmp(tokens[2], "ipv4_5tuple") == 0) {
2605 			struct pkt_key_ipv4_5tuple *ipv4 =
2606 				(struct pkt_key_ipv4_5tuple *) m->match.hash.key;
2607 			struct in_addr saddr, daddr;
2608 			uint16_t sp, dp;
2609 			uint8_t proto;
2610 
2611 			if (n_tokens < 8) {
2612 				snprintf(out, out_size, MSG_ARG_MISMATCH,
2613 					tokens[0]);
2614 				return 0;
2615 			}
2616 
2617 			if (parse_ipv4_addr(tokens[3], &saddr) != 0) {
2618 				snprintf(out, out_size, MSG_ARG_INVALID, "sa");
2619 				return 0;
2620 			}
2621 
2622 			if (parse_ipv4_addr(tokens[4], &daddr) != 0) {
2623 				snprintf(out, out_size, MSG_ARG_INVALID, "da");
2624 				return 0;
2625 			}
2626 
2627 			if (parser_read_uint16(&sp, tokens[5]) != 0) {
2628 				snprintf(out, out_size, MSG_ARG_INVALID, "sp");
2629 				return 0;
2630 			}
2631 
2632 			if (parser_read_uint16(&dp, tokens[6]) != 0) {
2633 				snprintf(out, out_size, MSG_ARG_INVALID, "dp");
2634 				return 0;
2635 			}
2636 
2637 			if (parser_read_uint8(&proto, tokens[7]) != 0) {
2638 				snprintf(out, out_size, MSG_ARG_INVALID,
2639 					"proto");
2640 				return 0;
2641 			}
2642 
2643 			ipv4->sa = saddr.s_addr;
2644 			ipv4->da = daddr.s_addr;
2645 			ipv4->sp = rte_cpu_to_be_16(sp);
2646 			ipv4->dp = rte_cpu_to_be_16(dp);
2647 			ipv4->proto = proto;
2648 
2649 			return 8;
2650 		} /* hash ipv4_5tuple */
2651 
2652 		if (strcmp(tokens[2], "ipv6_5tuple") == 0) {
2653 			struct pkt_key_ipv6_5tuple *ipv6 =
2654 				(struct pkt_key_ipv6_5tuple *) m->match.hash.key;
2655 			struct in6_addr saddr, daddr;
2656 			uint16_t sp, dp;
2657 			uint8_t proto;
2658 
2659 			if (n_tokens < 8) {
2660 				snprintf(out, out_size, MSG_ARG_MISMATCH,
2661 					tokens[0]);
2662 				return 0;
2663 			}
2664 
2665 			if (parse_ipv6_addr(tokens[3], &saddr) != 0) {
2666 				snprintf(out, out_size, MSG_ARG_INVALID, "sa");
2667 				return 0;
2668 			}
2669 
2670 			if (parse_ipv6_addr(tokens[4], &daddr) != 0) {
2671 				snprintf(out, out_size, MSG_ARG_INVALID, "da");
2672 				return 0;
2673 			}
2674 
2675 			if (parser_read_uint16(&sp, tokens[5]) != 0) {
2676 				snprintf(out, out_size, MSG_ARG_INVALID, "sp");
2677 				return 0;
2678 			}
2679 
2680 			if (parser_read_uint16(&dp, tokens[6]) != 0) {
2681 				snprintf(out, out_size, MSG_ARG_INVALID, "dp");
2682 				return 0;
2683 			}
2684 
2685 			if (parser_read_uint8(&proto, tokens[7]) != 0) {
2686 				snprintf(out, out_size, MSG_ARG_INVALID,
2687 					"proto");
2688 				return 0;
2689 			}
2690 
2691 			memcpy(ipv6->sa, saddr.s6_addr, 16);
2692 			memcpy(ipv6->da, daddr.s6_addr, 16);
2693 			ipv6->sp = rte_cpu_to_be_16(sp);
2694 			ipv6->dp = rte_cpu_to_be_16(dp);
2695 			ipv6->proto = proto;
2696 
2697 			return 8;
2698 		} /* hash ipv6_5tuple */
2699 
2700 		if (strcmp(tokens[2], "ipv4_addr") == 0) {
2701 			struct pkt_key_ipv4_addr *ipv4_addr =
2702 				(struct pkt_key_ipv4_addr *) m->match.hash.key;
2703 			struct in_addr addr;
2704 
2705 			if (n_tokens < 4) {
2706 				snprintf(out, out_size, MSG_ARG_MISMATCH,
2707 					tokens[0]);
2708 				return 0;
2709 			}
2710 
2711 			if (parse_ipv4_addr(tokens[3], &addr) != 0) {
2712 				snprintf(out, out_size, MSG_ARG_INVALID,
2713 					"addr");
2714 				return 0;
2715 			}
2716 
2717 			ipv4_addr->addr = addr.s_addr;
2718 
2719 			return 4;
2720 		} /* hash ipv4_addr */
2721 
2722 		if (strcmp(tokens[2], "ipv6_addr") == 0) {
2723 			struct pkt_key_ipv6_addr *ipv6_addr =
2724 				(struct pkt_key_ipv6_addr *) m->match.hash.key;
2725 			struct in6_addr addr;
2726 
2727 			if (n_tokens < 4) {
2728 				snprintf(out, out_size, MSG_ARG_MISMATCH,
2729 					tokens[0]);
2730 				return 0;
2731 			}
2732 
2733 			if (parse_ipv6_addr(tokens[3], &addr) != 0) {
2734 				snprintf(out, out_size, MSG_ARG_INVALID,
2735 					"addr");
2736 				return 0;
2737 			}
2738 
2739 			memcpy(ipv6_addr->addr, addr.s6_addr, 16);
2740 
2741 			return 4;
2742 		} /* hash ipv6_5tuple */
2743 
2744 		if (strcmp(tokens[2], "qinq") == 0) {
2745 			struct pkt_key_qinq *qinq =
2746 				(struct pkt_key_qinq *) m->match.hash.key;
2747 			uint16_t svlan, cvlan;
2748 
2749 			if (n_tokens < 5) {
2750 				snprintf(out, out_size, MSG_ARG_MISMATCH,
2751 					tokens[0]);
2752 				return 0;
2753 			}
2754 
2755 			if ((parser_read_uint16(&svlan, tokens[3]) != 0) ||
2756 				(svlan > 0xFFF)) {
2757 				snprintf(out, out_size, MSG_ARG_INVALID,
2758 					"svlan");
2759 				return 0;
2760 			}
2761 
2762 			if ((parser_read_uint16(&cvlan, tokens[4]) != 0) ||
2763 				(cvlan > 0xFFF)) {
2764 				snprintf(out, out_size, MSG_ARG_INVALID,
2765 					"cvlan");
2766 				return 0;
2767 			}
2768 
2769 			qinq->svlan = rte_cpu_to_be_16(svlan);
2770 			qinq->cvlan = rte_cpu_to_be_16(cvlan);
2771 
2772 			return 5;
2773 		} /* hash qinq */
2774 
2775 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2776 		return 0;
2777 	} /* hash */
2778 
2779 	if (strcmp(tokens[1], "lpm") == 0) {
2780 		if (n_tokens < 5) {
2781 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2782 			return 0;
2783 		}
2784 
2785 		m->match_type = TABLE_LPM;
2786 
2787 		if (strcmp(tokens[2], "ipv4") == 0) {
2788 			struct in_addr addr;
2789 
2790 			m->match.lpm.ip_version = 1;
2791 
2792 			if (parse_ipv4_addr(tokens[3], &addr) != 0) {
2793 				snprintf(out, out_size, MSG_ARG_INVALID,
2794 					"addr");
2795 				return 0;
2796 			}
2797 
2798 			m->match.lpm.ipv4 = rte_be_to_cpu_32(addr.s_addr);
2799 		} else if (strcmp(tokens[2], "ipv6") == 0) {
2800 			struct in6_addr addr;
2801 
2802 			m->match.lpm.ip_version = 0;
2803 
2804 			if (parse_ipv6_addr(tokens[3], &addr) != 0) {
2805 				snprintf(out, out_size, MSG_ARG_INVALID,
2806 					"addr");
2807 				return 0;
2808 			}
2809 
2810 			memcpy(m->match.lpm.ipv6, addr.s6_addr, 16);
2811 		} else {
2812 			snprintf(out, out_size, MSG_ARG_MISMATCH,
2813 				"ipv4 or ipv6");
2814 			return 0;
2815 		}
2816 
2817 		if (parser_read_uint8(&m->match.lpm.depth, tokens[4]) != 0) {
2818 			snprintf(out, out_size, MSG_ARG_INVALID, "depth");
2819 			return 0;
2820 		}
2821 
2822 		return 5;
2823 	} /* lpm */
2824 
2825 	snprintf(out, out_size, MSG_ARG_MISMATCH,
2826 		"acl or array or hash or lpm");
2827 	return 0;
2828 }
2829 
2830 /**
2831  * table_action ::=
2832  *
2833  * action
2834  *    fwd
2835  *       drop
2836  *       | port <port_id>
2837  *       | meta
2838  *       | table <table_id>
2839  *    [balance <out0> ... <out7>]
2840  *    [meter
2841  *       tc0 meter <meter_profile_id> policer g <pa> y <pa> r <pa>
2842  *       [tc1 meter <meter_profile_id> policer g <pa> y <pa> r <pa>
2843  *       tc2 meter <meter_profile_id> policer g <pa> y <pa> r <pa>
2844  *       tc3 meter <meter_profile_id> policer g <pa> y <pa> r <pa>]]
2845  *    [tm subport <subport_id> pipe <pipe_id>]
2846  *    [encap
2847  *       ether <da> <sa>
2848  *       | vlan <da> <sa> <pcp> <dei> <vid>
2849  *       | qinq <da> <sa> <pcp> <dei> <vid> <pcp> <dei> <vid>
2850  *       | mpls unicast | multicast
2851  *          <da> <sa>
2852  *          label0 <label> <tc> <ttl>
2853  *          [label1 <label> <tc> <ttl>
2854  *          [label2 <label> <tc> <ttl>
2855  *          [label3 <label> <tc> <ttl>]]]
2856  *       | pppoe <da> <sa> <session_id>]
2857  *    [nat ipv4 | ipv6 <addr> <port>]
2858  *    [ttl dec | keep]
2859  *    [stats]
2860  *    [time]
2861  *
2862  * where:
2863  *    <pa> ::= g | y | r | drop
2864  */
2865 static uint32_t
2866 parse_table_action_fwd(char **tokens,
2867 	uint32_t n_tokens,
2868 	struct table_rule_action *a)
2869 {
2870 	if ((n_tokens == 0) || (strcmp(tokens[0], "fwd") != 0))
2871 		return 0;
2872 
2873 	tokens++;
2874 	n_tokens--;
2875 
2876 	if (n_tokens && (strcmp(tokens[0], "drop") == 0)) {
2877 		a->fwd.action = RTE_PIPELINE_ACTION_DROP;
2878 		a->action_mask |= 1 << RTE_TABLE_ACTION_FWD;
2879 		return 1 + 1;
2880 	}
2881 
2882 	if (n_tokens && (strcmp(tokens[0], "port") == 0)) {
2883 		uint32_t id;
2884 
2885 		if ((n_tokens < 2) ||
2886 			parser_read_uint32(&id, tokens[1]))
2887 			return 0;
2888 
2889 		a->fwd.action = RTE_PIPELINE_ACTION_PORT;
2890 		a->fwd.id = id;
2891 		a->action_mask |= 1 << RTE_TABLE_ACTION_FWD;
2892 		return 1 + 2;
2893 	}
2894 
2895 	if (n_tokens && (strcmp(tokens[0], "meta") == 0)) {
2896 		a->fwd.action = RTE_PIPELINE_ACTION_PORT_META;
2897 		a->action_mask |= 1 << RTE_TABLE_ACTION_FWD;
2898 		return 1 + 1;
2899 	}
2900 
2901 	if (n_tokens && (strcmp(tokens[0], "table") == 0)) {
2902 		uint32_t id;
2903 
2904 		if ((n_tokens < 2) ||
2905 			parser_read_uint32(&id, tokens[1]))
2906 			return 0;
2907 
2908 		a->fwd.action = RTE_PIPELINE_ACTION_TABLE;
2909 		a->fwd.id = id;
2910 		a->action_mask |= 1 << RTE_TABLE_ACTION_FWD;
2911 		return 1 + 2;
2912 	}
2913 
2914 	return 0;
2915 }
2916 
2917 static uint32_t
2918 parse_table_action_balance(char **tokens,
2919 	uint32_t n_tokens,
2920 	struct table_rule_action *a)
2921 {
2922 	uint32_t i;
2923 
2924 	if ((n_tokens == 0) || (strcmp(tokens[0], "balance") != 0))
2925 		return 0;
2926 
2927 	tokens++;
2928 	n_tokens--;
2929 
2930 	if (n_tokens < RTE_TABLE_ACTION_LB_TABLE_SIZE)
2931 		return 0;
2932 
2933 	for (i = 0; i < RTE_TABLE_ACTION_LB_TABLE_SIZE; i++)
2934 		if (parser_read_uint32(&a->lb.out[i], tokens[i]) != 0)
2935 			return 0;
2936 
2937 	a->action_mask |= 1 << RTE_TABLE_ACTION_LB;
2938 	return 1 + RTE_TABLE_ACTION_LB_TABLE_SIZE;
2939 
2940 }
2941 
2942 static int
2943 parse_policer_action(char *token, enum rte_table_action_policer *a)
2944 {
2945 	if (strcmp(token, "g") == 0) {
2946 		*a = RTE_TABLE_ACTION_POLICER_COLOR_GREEN;
2947 		return 0;
2948 	}
2949 
2950 	if (strcmp(token, "y") == 0) {
2951 		*a = RTE_TABLE_ACTION_POLICER_COLOR_YELLOW;
2952 		return 0;
2953 	}
2954 
2955 	if (strcmp(token, "r") == 0) {
2956 		*a = RTE_TABLE_ACTION_POLICER_COLOR_RED;
2957 		return 0;
2958 	}
2959 
2960 	if (strcmp(token, "drop") == 0) {
2961 		*a = RTE_TABLE_ACTION_POLICER_DROP;
2962 		return 0;
2963 	}
2964 
2965 	return -1;
2966 }
2967 
2968 static uint32_t
2969 parse_table_action_meter_tc(char **tokens,
2970 	uint32_t n_tokens,
2971 	struct rte_table_action_mtr_tc_params *mtr)
2972 {
2973 	if ((n_tokens < 9) ||
2974 		strcmp(tokens[0], "meter") ||
2975 		parser_read_uint32(&mtr->meter_profile_id, tokens[1]) ||
2976 		strcmp(tokens[2], "policer") ||
2977 		strcmp(tokens[3], "g") ||
2978 		parse_policer_action(tokens[4], &mtr->policer[e_RTE_METER_GREEN]) ||
2979 		strcmp(tokens[5], "y") ||
2980 		parse_policer_action(tokens[6], &mtr->policer[e_RTE_METER_YELLOW]) ||
2981 		strcmp(tokens[7], "r") ||
2982 		parse_policer_action(tokens[8], &mtr->policer[e_RTE_METER_RED]))
2983 		return 0;
2984 
2985 	return 9;
2986 }
2987 
2988 static uint32_t
2989 parse_table_action_meter(char **tokens,
2990 	uint32_t n_tokens,
2991 	struct table_rule_action *a)
2992 {
2993 	if ((n_tokens == 0) || strcmp(tokens[0], "meter"))
2994 		return 0;
2995 
2996 	tokens++;
2997 	n_tokens--;
2998 
2999 	if ((n_tokens < 10) ||
3000 		strcmp(tokens[0], "tc0") ||
3001 		(parse_table_action_meter_tc(tokens + 1,
3002 			n_tokens - 1,
3003 			&a->mtr.mtr[0]) == 0))
3004 		return 0;
3005 
3006 	tokens += 10;
3007 	n_tokens -= 10;
3008 
3009 	if ((n_tokens == 0) || strcmp(tokens[0], "tc1")) {
3010 		a->mtr.tc_mask = 1;
3011 		a->action_mask |= 1 << RTE_TABLE_ACTION_MTR;
3012 		return 1 + 10;
3013 	}
3014 
3015 	if ((n_tokens < 30) ||
3016 		(parse_table_action_meter_tc(tokens + 1,
3017 			n_tokens - 1, &a->mtr.mtr[1]) == 0) ||
3018 		strcmp(tokens[10], "tc2") ||
3019 		(parse_table_action_meter_tc(tokens + 11,
3020 			n_tokens - 11, &a->mtr.mtr[2]) == 0) ||
3021 		strcmp(tokens[20], "tc3") ||
3022 		(parse_table_action_meter_tc(tokens + 21,
3023 			n_tokens - 21, &a->mtr.mtr[3]) == 0))
3024 		return 0;
3025 
3026 	a->mtr.tc_mask = 0xF;
3027 	a->action_mask |= 1 << RTE_TABLE_ACTION_MTR;
3028 	return 1 + 10 + 3 * 10;
3029 }
3030 
3031 static uint32_t
3032 parse_table_action_tm(char **tokens,
3033 	uint32_t n_tokens,
3034 	struct table_rule_action *a)
3035 {
3036 	uint32_t subport_id, pipe_id;
3037 
3038 	if ((n_tokens < 5) ||
3039 		strcmp(tokens[0], "tm") ||
3040 		strcmp(tokens[1], "subport") ||
3041 		parser_read_uint32(&subport_id, tokens[2]) ||
3042 		strcmp(tokens[3], "pipe") ||
3043 		parser_read_uint32(&pipe_id, tokens[4]))
3044 		return 0;
3045 
3046 	a->tm.subport_id = subport_id;
3047 	a->tm.pipe_id = pipe_id;
3048 	a->action_mask |= 1 << RTE_TABLE_ACTION_TM;
3049 	return 5;
3050 }
3051 
3052 static uint32_t
3053 parse_table_action_encap(char **tokens,
3054 	uint32_t n_tokens,
3055 	struct table_rule_action *a)
3056 {
3057 	if ((n_tokens == 0) || strcmp(tokens[0], "encap"))
3058 		return 0;
3059 
3060 	tokens++;
3061 	n_tokens--;
3062 
3063 	/* ether */
3064 	if (n_tokens && (strcmp(tokens[0], "ether") == 0)) {
3065 		if ((n_tokens < 3) ||
3066 			parse_mac_addr(tokens[1], &a->encap.ether.ether.da) ||
3067 			parse_mac_addr(tokens[2], &a->encap.ether.ether.sa))
3068 			return 0;
3069 
3070 		a->encap.type = RTE_TABLE_ACTION_ENCAP_ETHER;
3071 		a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
3072 		return 1 + 3;
3073 	}
3074 
3075 	/* vlan */
3076 	if (n_tokens && (strcmp(tokens[0], "vlan") == 0)) {
3077 		uint32_t pcp, dei, vid;
3078 
3079 		if ((n_tokens < 6) ||
3080 			parse_mac_addr(tokens[1], &a->encap.vlan.ether.da) ||
3081 			parse_mac_addr(tokens[2], &a->encap.vlan.ether.sa) ||
3082 			parser_read_uint32(&pcp, tokens[3]) ||
3083 			(pcp > 0x7) ||
3084 			parser_read_uint32(&dei, tokens[4]) ||
3085 			(dei > 0x1) ||
3086 			parser_read_uint32(&vid, tokens[5]) ||
3087 			(vid > 0xFFF))
3088 			return 0;
3089 
3090 		a->encap.vlan.vlan.pcp = pcp & 0x7;
3091 		a->encap.vlan.vlan.dei = dei & 0x1;
3092 		a->encap.vlan.vlan.vid = vid & 0xFFF;
3093 		a->encap.type = RTE_TABLE_ACTION_ENCAP_VLAN;
3094 		a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
3095 		return 1 + 6;
3096 	}
3097 
3098 	/* qinq */
3099 	if (n_tokens && (strcmp(tokens[0], "qinq") == 0)) {
3100 		uint32_t svlan_pcp, svlan_dei, svlan_vid;
3101 		uint32_t cvlan_pcp, cvlan_dei, cvlan_vid;
3102 
3103 		if ((n_tokens < 9) ||
3104 			parse_mac_addr(tokens[1], &a->encap.qinq.ether.da) ||
3105 			parse_mac_addr(tokens[2], &a->encap.qinq.ether.sa) ||
3106 			parser_read_uint32(&svlan_pcp, tokens[3]) ||
3107 			(svlan_pcp > 0x7) ||
3108 			parser_read_uint32(&svlan_dei, tokens[4]) ||
3109 			(svlan_dei > 0x1) ||
3110 			parser_read_uint32(&svlan_vid, tokens[5]) ||
3111 			(svlan_vid > 0xFFF) ||
3112 			parser_read_uint32(&cvlan_pcp, tokens[6]) ||
3113 			(cvlan_pcp > 0x7) ||
3114 			parser_read_uint32(&cvlan_dei, tokens[7]) ||
3115 			(cvlan_dei > 0x1) ||
3116 			parser_read_uint32(&cvlan_vid, tokens[8]) ||
3117 			(cvlan_vid > 0xFFF))
3118 			return 0;
3119 
3120 		a->encap.qinq.svlan.pcp = svlan_pcp & 0x7;
3121 		a->encap.qinq.svlan.dei = svlan_dei & 0x1;
3122 		a->encap.qinq.svlan.vid = svlan_vid & 0xFFF;
3123 		a->encap.qinq.cvlan.pcp = cvlan_pcp & 0x7;
3124 		a->encap.qinq.cvlan.dei = cvlan_dei & 0x1;
3125 		a->encap.qinq.cvlan.vid = cvlan_vid & 0xFFF;
3126 		a->encap.type = RTE_TABLE_ACTION_ENCAP_QINQ;
3127 		a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
3128 		return 1 + 9;
3129 	}
3130 
3131 	/* mpls */
3132 	if (n_tokens && (strcmp(tokens[0], "mpls") == 0)) {
3133 		uint32_t label, tc, ttl;
3134 
3135 		if (n_tokens < 8)
3136 			return 0;
3137 
3138 		if (strcmp(tokens[1], "unicast") == 0)
3139 			a->encap.mpls.unicast = 1;
3140 		else if (strcmp(tokens[1], "multicast") == 0)
3141 			a->encap.mpls.unicast = 0;
3142 		else
3143 			return 0;
3144 
3145 		if (parse_mac_addr(tokens[2], &a->encap.mpls.ether.da) ||
3146 			parse_mac_addr(tokens[3], &a->encap.mpls.ether.sa) ||
3147 			strcmp(tokens[4], "label0") ||
3148 			parser_read_uint32(&label, tokens[5]) ||
3149 			(label > 0xFFFFF) ||
3150 			parser_read_uint32(&tc, tokens[6]) ||
3151 			(tc > 0x7) ||
3152 			parser_read_uint32(&ttl, tokens[7]) ||
3153 			(ttl > 0x3F))
3154 			return 0;
3155 
3156 		a->encap.mpls.mpls[0].label = label;
3157 		a->encap.mpls.mpls[0].tc = tc;
3158 		a->encap.mpls.mpls[0].ttl = ttl;
3159 
3160 		tokens += 8;
3161 		n_tokens -= 8;
3162 
3163 		if ((n_tokens == 0) || strcmp(tokens[0], "label1")) {
3164 			a->encap.mpls.mpls_count = 1;
3165 			a->encap.type = RTE_TABLE_ACTION_ENCAP_MPLS;
3166 			a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
3167 			return 1 + 8;
3168 		}
3169 
3170 		if ((n_tokens < 4) ||
3171 			parser_read_uint32(&label, tokens[1]) ||
3172 			(label > 0xFFFFF) ||
3173 			parser_read_uint32(&tc, tokens[2]) ||
3174 			(tc > 0x7) ||
3175 			parser_read_uint32(&ttl, tokens[3]) ||
3176 			(ttl > 0x3F))
3177 			return 0;
3178 
3179 		a->encap.mpls.mpls[1].label = label;
3180 		a->encap.mpls.mpls[1].tc = tc;
3181 		a->encap.mpls.mpls[1].ttl = ttl;
3182 
3183 		tokens += 4;
3184 		n_tokens -= 4;
3185 
3186 		if ((n_tokens == 0) || strcmp(tokens[0], "label2")) {
3187 			a->encap.mpls.mpls_count = 2;
3188 			a->encap.type = RTE_TABLE_ACTION_ENCAP_MPLS;
3189 			a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
3190 			return 1 + 8 + 4;
3191 		}
3192 
3193 		if ((n_tokens < 4) ||
3194 			parser_read_uint32(&label, tokens[1]) ||
3195 			(label > 0xFFFFF) ||
3196 			parser_read_uint32(&tc, tokens[2]) ||
3197 			(tc > 0x7) ||
3198 			parser_read_uint32(&ttl, tokens[3]) ||
3199 			(ttl > 0x3F))
3200 			return 0;
3201 
3202 		a->encap.mpls.mpls[2].label = label;
3203 		a->encap.mpls.mpls[2].tc = tc;
3204 		a->encap.mpls.mpls[2].ttl = ttl;
3205 
3206 		tokens += 4;
3207 		n_tokens -= 4;
3208 
3209 		if ((n_tokens == 0) || strcmp(tokens[0], "label3")) {
3210 			a->encap.mpls.mpls_count = 3;
3211 			a->encap.type = RTE_TABLE_ACTION_ENCAP_MPLS;
3212 			a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
3213 			return 1 + 8 + 4 + 4;
3214 		}
3215 
3216 		if ((n_tokens < 4) ||
3217 			parser_read_uint32(&label, tokens[1]) ||
3218 			(label > 0xFFFFF) ||
3219 			parser_read_uint32(&tc, tokens[2]) ||
3220 			(tc > 0x7) ||
3221 			parser_read_uint32(&ttl, tokens[3]) ||
3222 			(ttl > 0x3F))
3223 			return 0;
3224 
3225 		a->encap.mpls.mpls[3].label = label;
3226 		a->encap.mpls.mpls[3].tc = tc;
3227 		a->encap.mpls.mpls[3].ttl = ttl;
3228 
3229 		a->encap.mpls.mpls_count = 4;
3230 		a->encap.type = RTE_TABLE_ACTION_ENCAP_MPLS;
3231 		a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
3232 		return 1 + 8 + 4 + 4 + 4;
3233 	}
3234 
3235 	/* pppoe */
3236 	if (n_tokens && (strcmp(tokens[0], "pppoe") == 0)) {
3237 		if ((n_tokens < 4) ||
3238 			parse_mac_addr(tokens[1], &a->encap.pppoe.ether.da) ||
3239 			parse_mac_addr(tokens[2], &a->encap.pppoe.ether.sa) ||
3240 			parser_read_uint16(&a->encap.pppoe.pppoe.session_id,
3241 				tokens[3]))
3242 			return 0;
3243 
3244 		a->encap.type = RTE_TABLE_ACTION_ENCAP_PPPOE;
3245 		a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP;
3246 		return 1 + 4;
3247 	}
3248 
3249 	return 0;
3250 }
3251 
3252 static uint32_t
3253 parse_table_action_nat(char **tokens,
3254 	uint32_t n_tokens,
3255 	struct table_rule_action *a)
3256 {
3257 	if ((n_tokens < 4) ||
3258 		strcmp(tokens[0], "nat"))
3259 		return 0;
3260 
3261 	if (strcmp(tokens[1], "ipv4") == 0) {
3262 		struct in_addr addr;
3263 		uint16_t port;
3264 
3265 		if (parse_ipv4_addr(tokens[2], &addr) ||
3266 			parser_read_uint16(&port, tokens[3]))
3267 			return 0;
3268 
3269 		a->nat.ip_version = 1;
3270 		a->nat.addr.ipv4 = rte_be_to_cpu_32(addr.s_addr);
3271 		a->nat.port = port;
3272 		a->action_mask |= 1 << RTE_TABLE_ACTION_NAT;
3273 		return 4;
3274 	}
3275 
3276 	if (strcmp(tokens[1], "ipv6") == 0) {
3277 		struct in6_addr addr;
3278 		uint16_t port;
3279 
3280 		if (parse_ipv6_addr(tokens[2], &addr) ||
3281 			parser_read_uint16(&port, tokens[3]))
3282 			return 0;
3283 
3284 		a->nat.ip_version = 0;
3285 		memcpy(a->nat.addr.ipv6, addr.s6_addr, 16);
3286 		a->nat.port = port;
3287 		a->action_mask |= 1 << RTE_TABLE_ACTION_NAT;
3288 		return 4;
3289 	}
3290 
3291 	return 0;
3292 }
3293 
3294 static uint32_t
3295 parse_table_action_ttl(char **tokens,
3296 	uint32_t n_tokens,
3297 	struct table_rule_action *a)
3298 {
3299 	if ((n_tokens < 2) ||
3300 		strcmp(tokens[0], "ttl"))
3301 		return 0;
3302 
3303 	if (strcmp(tokens[1], "dec") == 0)
3304 		a->ttl.decrement = 1;
3305 	else if (strcmp(tokens[1], "keep") == 0)
3306 		a->ttl.decrement = 0;
3307 	else
3308 		return 0;
3309 
3310 	a->action_mask |= 1 << RTE_TABLE_ACTION_TTL;
3311 	return 2;
3312 }
3313 
3314 static uint32_t
3315 parse_table_action_stats(char **tokens,
3316 	uint32_t n_tokens,
3317 	struct table_rule_action *a)
3318 {
3319 	if ((n_tokens < 1) ||
3320 		strcmp(tokens[0], "stats"))
3321 		return 0;
3322 
3323 	a->stats.n_packets = 0;
3324 	a->stats.n_bytes = 0;
3325 	a->action_mask |= 1 << RTE_TABLE_ACTION_STATS;
3326 	return 1;
3327 }
3328 
3329 static uint32_t
3330 parse_table_action_time(char **tokens,
3331 	uint32_t n_tokens,
3332 	struct table_rule_action *a)
3333 {
3334 	if ((n_tokens < 1) ||
3335 		strcmp(tokens[0], "time"))
3336 		return 0;
3337 
3338 	a->time.time = rte_rdtsc();
3339 	a->action_mask |= 1 << RTE_TABLE_ACTION_TIME;
3340 	return 1;
3341 }
3342 
3343 static uint32_t
3344 parse_table_action(char **tokens,
3345 	uint32_t n_tokens,
3346 	char *out,
3347 	size_t out_size,
3348 	struct table_rule_action *a)
3349 {
3350 	uint32_t n_tokens0 = n_tokens;
3351 
3352 	memset(a, 0, sizeof(*a));
3353 
3354 	if ((n_tokens < 2) ||
3355 		strcmp(tokens[0], "action"))
3356 		return 0;
3357 
3358 	tokens++;
3359 	n_tokens--;
3360 
3361 	if (n_tokens && (strcmp(tokens[0], "fwd") == 0)) {
3362 		uint32_t n;
3363 
3364 		n = parse_table_action_fwd(tokens, n_tokens, a);
3365 		if (n == 0) {
3366 			snprintf(out, out_size, MSG_ARG_INVALID,
3367 				"action fwd");
3368 			return 0;
3369 		}
3370 
3371 		tokens += n;
3372 		n_tokens -= n;
3373 	}
3374 
3375 	if (n_tokens && (strcmp(tokens[0], "balance") == 0)) {
3376 		uint32_t n;
3377 
3378 		n = parse_table_action_balance(tokens, n_tokens, a);
3379 		if (n == 0) {
3380 			snprintf(out, out_size, MSG_ARG_INVALID,
3381 				"action balance");
3382 			return 0;
3383 		}
3384 
3385 		tokens += n;
3386 		n_tokens -= n;
3387 	}
3388 
3389 	if (n_tokens && (strcmp(tokens[0], "meter") == 0)) {
3390 		uint32_t n;
3391 
3392 		n = parse_table_action_meter(tokens, n_tokens, a);
3393 		if (n == 0) {
3394 			snprintf(out, out_size, MSG_ARG_INVALID,
3395 				"action meter");
3396 			return 0;
3397 		}
3398 
3399 		tokens += n;
3400 		n_tokens -= n;
3401 	}
3402 
3403 	if (n_tokens && (strcmp(tokens[0], "tm") == 0)) {
3404 		uint32_t n;
3405 
3406 		n = parse_table_action_tm(tokens, n_tokens, a);
3407 		if (n == 0) {
3408 			snprintf(out, out_size, MSG_ARG_INVALID,
3409 				"action tm");
3410 			return 0;
3411 		}
3412 
3413 		tokens += n;
3414 		n_tokens -= n;
3415 	}
3416 
3417 	if (n_tokens && (strcmp(tokens[0], "encap") == 0)) {
3418 		uint32_t n;
3419 
3420 		n = parse_table_action_encap(tokens, n_tokens, a);
3421 		if (n == 0) {
3422 			snprintf(out, out_size, MSG_ARG_INVALID,
3423 				"action encap");
3424 			return 0;
3425 		}
3426 
3427 		tokens += n;
3428 		n_tokens -= n;
3429 	}
3430 
3431 	if (n_tokens && (strcmp(tokens[0], "nat") == 0)) {
3432 		uint32_t n;
3433 
3434 		n = parse_table_action_nat(tokens, n_tokens, a);
3435 		if (n == 0) {
3436 			snprintf(out, out_size, MSG_ARG_INVALID,
3437 				"action nat");
3438 			return 0;
3439 		}
3440 
3441 		tokens += n;
3442 		n_tokens -= n;
3443 	}
3444 
3445 	if (n_tokens && (strcmp(tokens[0], "ttl") == 0)) {
3446 		uint32_t n;
3447 
3448 		n = parse_table_action_ttl(tokens, n_tokens, a);
3449 		if (n == 0) {
3450 			snprintf(out, out_size, MSG_ARG_INVALID,
3451 				"action ttl");
3452 			return 0;
3453 		}
3454 
3455 		tokens += n;
3456 		n_tokens -= n;
3457 	}
3458 
3459 	if (n_tokens && (strcmp(tokens[0], "stats") == 0)) {
3460 		uint32_t n;
3461 
3462 		n = parse_table_action_stats(tokens, n_tokens, a);
3463 		if (n == 0) {
3464 			snprintf(out, out_size, MSG_ARG_INVALID,
3465 				"action stats");
3466 			return 0;
3467 		}
3468 
3469 		tokens += n;
3470 		n_tokens -= n;
3471 	}
3472 
3473 	if (n_tokens && (strcmp(tokens[0], "time") == 0)) {
3474 		uint32_t n;
3475 
3476 		n = parse_table_action_time(tokens, n_tokens, a);
3477 		if (n == 0) {
3478 			snprintf(out, out_size, MSG_ARG_INVALID,
3479 				"action time");
3480 			return 0;
3481 		}
3482 
3483 		tokens += n;
3484 		n_tokens -= n;
3485 	}
3486 
3487 	if (n_tokens0 - n_tokens == 1) {
3488 		snprintf(out, out_size, MSG_ARG_INVALID, "action");
3489 		return 0;
3490 	}
3491 
3492 	return n_tokens0 - n_tokens;
3493 }
3494 
3495 /**
3496  * pipeline <pipeline_name> table <table_id> rule add
3497  *    match <match>
3498  *    action <table_action>
3499  */
3500 static void
3501 cmd_pipeline_table_rule_add(char **tokens,
3502 	uint32_t n_tokens,
3503 	char *out,
3504 	size_t out_size)
3505 {
3506 	struct table_rule_match m;
3507 	struct table_rule_action a;
3508 	char *pipeline_name;
3509 	void *data;
3510 	uint32_t table_id, t0, n_tokens_parsed;
3511 	int status;
3512 
3513 	if (n_tokens < 8) {
3514 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3515 		return;
3516 	}
3517 
3518 	pipeline_name = tokens[1];
3519 
3520 	if (strcmp(tokens[2], "table") != 0) {
3521 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
3522 		return;
3523 	}
3524 
3525 	if (parser_read_uint32(&table_id, tokens[3]) != 0) {
3526 		snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
3527 		return;
3528 	}
3529 
3530 	if (strcmp(tokens[4], "rule") != 0) {
3531 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule");
3532 		return;
3533 	}
3534 
3535 	if (strcmp(tokens[5], "add") != 0) {
3536 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
3537 		return;
3538 	}
3539 
3540 	t0 = 6;
3541 
3542 	/* match */
3543 	n_tokens_parsed = parse_match(tokens + t0,
3544 		n_tokens - t0,
3545 		out,
3546 		out_size,
3547 		&m);
3548 	if (n_tokens_parsed == 0)
3549 		return;
3550 	t0 += n_tokens_parsed;
3551 
3552 	/* action */
3553 	n_tokens_parsed = parse_table_action(tokens + t0,
3554 		n_tokens - t0,
3555 		out,
3556 		out_size,
3557 		&a);
3558 	if (n_tokens_parsed == 0)
3559 		return;
3560 	t0 += n_tokens_parsed;
3561 
3562 	if (t0 != n_tokens) {
3563 		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
3564 		return;
3565 	}
3566 
3567 	status = pipeline_table_rule_add(pipeline_name, table_id,
3568 		&m, &a, &data);
3569 	if (status) {
3570 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
3571 		return;
3572 	}
3573 }
3574 
3575 /**
3576  * pipeline <pipeline_name> table <table_id> rule add
3577  *    match
3578  *       default
3579  *    action
3580  *       fwd
3581  *          drop
3582  *          | port <port_id>
3583  *          | meta
3584  *          | table <table_id>
3585  */
3586 static void
3587 cmd_pipeline_table_rule_add_default(char **tokens,
3588 	uint32_t n_tokens,
3589 	char *out,
3590 	size_t out_size)
3591 {
3592 	struct table_rule_action action;
3593 	void *data;
3594 	char *pipeline_name;
3595 	uint32_t table_id;
3596 	int status;
3597 
3598 	if ((n_tokens != 11) && (n_tokens != 12)) {
3599 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3600 		return;
3601 	}
3602 
3603 	pipeline_name = tokens[1];
3604 
3605 	if (strcmp(tokens[2], "table") != 0) {
3606 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
3607 		return;
3608 	}
3609 
3610 	if (parser_read_uint32(&table_id, tokens[3]) != 0) {
3611 		snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
3612 		return;
3613 	}
3614 
3615 	if (strcmp(tokens[4], "rule") != 0) {
3616 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule");
3617 		return;
3618 	}
3619 
3620 	if (strcmp(tokens[5], "add") != 0) {
3621 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
3622 		return;
3623 	}
3624 
3625 	if (strcmp(tokens[6], "match") != 0) {
3626 		snprintf(out, out_size, MSG_ARG_INVALID, "match");
3627 		return;
3628 	}
3629 
3630 	if (strcmp(tokens[7], "default") != 0) {
3631 		snprintf(out, out_size, MSG_ARG_INVALID, "default");
3632 		return;
3633 	}
3634 
3635 	if (strcmp(tokens[8], "action") != 0) {
3636 		snprintf(out, out_size, MSG_ARG_INVALID, "action");
3637 		return;
3638 	}
3639 
3640 	if (strcmp(tokens[9], "fwd") != 0) {
3641 		snprintf(out, out_size, MSG_ARG_INVALID, "fwd");
3642 		return;
3643 	}
3644 
3645 	action.action_mask = 1 << RTE_TABLE_ACTION_FWD;
3646 
3647 	if (strcmp(tokens[10], "drop") == 0) {
3648 		if (n_tokens != 11) {
3649 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3650 			return;
3651 		}
3652 
3653 		action.fwd.action = RTE_PIPELINE_ACTION_DROP;
3654 	} else if (strcmp(tokens[10], "port") == 0) {
3655 		uint32_t id;
3656 
3657 		if (n_tokens != 12) {
3658 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3659 			return;
3660 		}
3661 
3662 		if (parser_read_uint32(&id, tokens[11]) != 0) {
3663 			snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
3664 			return;
3665 		}
3666 
3667 		action.fwd.action = RTE_PIPELINE_ACTION_PORT;
3668 		action.fwd.id = id;
3669 	} else if (strcmp(tokens[10], "meta") == 0) {
3670 		if (n_tokens != 11) {
3671 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3672 			return;
3673 		}
3674 
3675 		action.fwd.action = RTE_PIPELINE_ACTION_PORT_META;
3676 	} else if (strcmp(tokens[10], "table") == 0) {
3677 		uint32_t id;
3678 
3679 		if (n_tokens != 12) {
3680 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3681 			return;
3682 		}
3683 
3684 		if (parser_read_uint32(&id, tokens[11]) != 0) {
3685 			snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
3686 			return;
3687 		}
3688 
3689 		action.fwd.action = RTE_PIPELINE_ACTION_TABLE;
3690 		action.fwd.id = id;
3691 	} else {
3692 		snprintf(out, out_size, MSG_ARG_INVALID,
3693 			"drop or port or meta or table");
3694 		return;
3695 	}
3696 
3697 	status = pipeline_table_rule_add_default(pipeline_name,
3698 		table_id,
3699 		&action,
3700 		&data);
3701 	if (status) {
3702 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
3703 		return;
3704 	}
3705 }
3706 
3707 /**
3708  * pipeline <pipeline_name> table <table_id> rule add bulk <file_name> <n_rules>
3709  *
3710  * File <file_name>:
3711  * - line format: match <match> action <action>
3712  */
3713 static int
3714 cli_rule_file_process(const char *file_name,
3715 	size_t line_len_max,
3716 	struct table_rule_match *m,
3717 	struct table_rule_action *a,
3718 	uint32_t *n_rules,
3719 	uint32_t *line_number,
3720 	char *out,
3721 	size_t out_size);
3722 
3723 static void
3724 cmd_pipeline_table_rule_add_bulk(char **tokens,
3725 	uint32_t n_tokens,
3726 	char *out,
3727 	size_t out_size)
3728 {
3729 	struct table_rule_match *match;
3730 	struct table_rule_action *action;
3731 	void **data;
3732 	char *pipeline_name, *file_name;
3733 	uint32_t table_id, n_rules, n_rules_parsed, line_number;
3734 	int status;
3735 
3736 	if (n_tokens != 9) {
3737 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3738 		return;
3739 	}
3740 
3741 	pipeline_name = tokens[1];
3742 
3743 	if (strcmp(tokens[2], "table") != 0) {
3744 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
3745 		return;
3746 	}
3747 
3748 	if (parser_read_uint32(&table_id, tokens[3]) != 0) {
3749 		snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
3750 		return;
3751 	}
3752 
3753 	if (strcmp(tokens[4], "rule") != 0) {
3754 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule");
3755 		return;
3756 	}
3757 
3758 	if (strcmp(tokens[5], "add") != 0) {
3759 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
3760 		return;
3761 	}
3762 
3763 	if (strcmp(tokens[6], "bulk") != 0) {
3764 		snprintf(out, out_size, MSG_ARG_INVALID, "bulk");
3765 		return;
3766 	}
3767 
3768 	file_name = tokens[7];
3769 
3770 	if ((parser_read_uint32(&n_rules, tokens[8]) != 0) ||
3771 		(n_rules == 0)) {
3772 		snprintf(out, out_size, MSG_ARG_INVALID, "n_rules");
3773 		return;
3774 	}
3775 
3776 	/* Memory allocation. */
3777 	match = calloc(n_rules, sizeof(struct table_rule_match));
3778 	action = calloc(n_rules, sizeof(struct table_rule_action));
3779 	data = calloc(n_rules, sizeof(void *));
3780 	if ((match == NULL) || (action == NULL) || (data == NULL)) {
3781 		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
3782 		free(data);
3783 		free(action);
3784 		free(match);
3785 		return;
3786 	}
3787 
3788 	/* Load rule file */
3789 	n_rules_parsed = n_rules;
3790 	status = cli_rule_file_process(file_name,
3791 		1024,
3792 		match,
3793 		action,
3794 		&n_rules_parsed,
3795 		&line_number,
3796 		out,
3797 		out_size);
3798 	if (status) {
3799 		snprintf(out, out_size, MSG_FILE_ERR, file_name, line_number);
3800 		free(data);
3801 		free(action);
3802 		free(match);
3803 		return;
3804 	}
3805 	if (n_rules_parsed != n_rules) {
3806 		snprintf(out, out_size, MSG_FILE_NOT_ENOUGH, file_name);
3807 		free(data);
3808 		free(action);
3809 		free(match);
3810 		return;
3811 	}
3812 
3813 	/* Rule bulk add */
3814 	status = pipeline_table_rule_add_bulk(pipeline_name,
3815 		table_id,
3816 		match,
3817 		action,
3818 		data,
3819 		&n_rules);
3820 	if (status) {
3821 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
3822 		free(data);
3823 		free(action);
3824 		free(match);
3825 		return;
3826 	}
3827 
3828 	/* Memory free */
3829 	free(data);
3830 	free(action);
3831 	free(match);
3832 }
3833 
3834 /**
3835  * pipeline <pipeline_name> table <table_id> rule delete
3836  *    match <match>
3837  */
3838 static void
3839 cmd_pipeline_table_rule_delete(char **tokens,
3840 	uint32_t n_tokens,
3841 	char *out,
3842 	size_t out_size)
3843 {
3844 	struct table_rule_match m;
3845 	char *pipeline_name;
3846 	uint32_t table_id, n_tokens_parsed, t0;
3847 	int status;
3848 
3849 	if (n_tokens < 8) {
3850 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3851 		return;
3852 	}
3853 
3854 	pipeline_name = tokens[1];
3855 
3856 	if (strcmp(tokens[2], "table") != 0) {
3857 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
3858 		return;
3859 	}
3860 
3861 	if (parser_read_uint32(&table_id, tokens[3]) != 0) {
3862 		snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
3863 		return;
3864 	}
3865 
3866 	if (strcmp(tokens[4], "rule") != 0) {
3867 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule");
3868 		return;
3869 	}
3870 
3871 	if (strcmp(tokens[5], "delete") != 0) {
3872 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
3873 		return;
3874 	}
3875 
3876 	t0 = 6;
3877 
3878 	/* match */
3879 	n_tokens_parsed = parse_match(tokens + t0,
3880 		n_tokens - t0,
3881 		out,
3882 		out_size,
3883 		&m);
3884 	if (n_tokens_parsed == 0)
3885 		return;
3886 	t0 += n_tokens_parsed;
3887 
3888 	if (n_tokens != t0) {
3889 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3890 		return;
3891 	}
3892 
3893 	status = pipeline_table_rule_delete(pipeline_name,
3894 		table_id,
3895 		&m);
3896 	if (status) {
3897 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
3898 		return;
3899 	}
3900 }
3901 
3902 /**
3903  * pipeline <pipeline_name> table <table_id> rule delete
3904  *    match
3905  *       default
3906  */
3907 static void
3908 cmd_pipeline_table_rule_delete_default(char **tokens,
3909 	uint32_t n_tokens,
3910 	char *out,
3911 	size_t out_size)
3912 {
3913 	char *pipeline_name;
3914 	uint32_t table_id;
3915 	int status;
3916 
3917 	if (n_tokens != 8) {
3918 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3919 		return;
3920 	}
3921 
3922 	pipeline_name = tokens[1];
3923 
3924 	if (strcmp(tokens[2], "table") != 0) {
3925 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
3926 		return;
3927 	}
3928 
3929 	if (parser_read_uint32(&table_id, tokens[3]) != 0) {
3930 		snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
3931 		return;
3932 	}
3933 
3934 	if (strcmp(tokens[4], "rule") != 0) {
3935 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule");
3936 		return;
3937 	}
3938 
3939 	if (strcmp(tokens[5], "delete") != 0) {
3940 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
3941 		return;
3942 	}
3943 
3944 	if (strcmp(tokens[6], "match") != 0) {
3945 		snprintf(out, out_size, MSG_ARG_INVALID, "match");
3946 		return;
3947 	}
3948 
3949 	if (strcmp(tokens[7], "default") != 0) {
3950 		snprintf(out, out_size, MSG_ARG_INVALID, "default");
3951 		return;
3952 	}
3953 
3954 	status = pipeline_table_rule_delete_default(pipeline_name,
3955 		table_id);
3956 	if (status) {
3957 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
3958 		return;
3959 	}
3960 }
3961 
3962 /**
3963  * pipeline <pipeline_name> table <table_id> rule read stats [clear]
3964  */
3965 static void
3966 cmd_pipeline_table_rule_stats_read(char **tokens,
3967 	uint32_t n_tokens __rte_unused,
3968 	char *out,
3969 	size_t out_size)
3970 {
3971 	snprintf(out, out_size, MSG_CMD_UNIMPLEM, tokens[0]);
3972 }
3973 
3974 /**
3975  * pipeline <pipeline_name> table <table_id> meter profile <meter_profile_id>
3976  *  add srtcm cir <cir> cbs <cbs> ebs <ebs>
3977  *  | trtcm cir <cir> pir <pir> cbs <cbs> pbs <pbs>
3978  */
3979 static void
3980 cmd_pipeline_table_meter_profile_add(char **tokens,
3981 	uint32_t n_tokens,
3982 	char *out,
3983 	size_t out_size)
3984 {
3985 	struct rte_table_action_meter_profile p;
3986 	char *pipeline_name;
3987 	uint32_t table_id, meter_profile_id;
3988 	int status;
3989 
3990 	if (n_tokens < 9) {
3991 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3992 		return;
3993 	}
3994 
3995 	pipeline_name = tokens[1];
3996 
3997 	if (strcmp(tokens[2], "table") != 0) {
3998 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
3999 		return;
4000 	}
4001 
4002 	if (parser_read_uint32(&table_id, tokens[3]) != 0) {
4003 		snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
4004 		return;
4005 	}
4006 
4007 	if (strcmp(tokens[4], "meter") != 0) {
4008 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
4009 		return;
4010 	}
4011 
4012 	if (strcmp(tokens[5], "profile") != 0) {
4013 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
4014 		return;
4015 	}
4016 
4017 	if (parser_read_uint32(&meter_profile_id, tokens[6]) != 0) {
4018 		snprintf(out, out_size, MSG_ARG_INVALID, "meter_profile_id");
4019 		return;
4020 	}
4021 
4022 	if (strcmp(tokens[7], "add") != 0) {
4023 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
4024 		return;
4025 	}
4026 
4027 	if (strcmp(tokens[8], "srtcm") == 0) {
4028 		if (n_tokens != 15) {
4029 			snprintf(out, out_size, MSG_ARG_MISMATCH,
4030 				tokens[0]);
4031 			return;
4032 		}
4033 
4034 		p.alg = RTE_TABLE_ACTION_METER_SRTCM;
4035 
4036 		if (strcmp(tokens[9], "cir") != 0) {
4037 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cir");
4038 			return;
4039 		}
4040 
4041 		if (parser_read_uint64(&p.srtcm.cir, tokens[10]) != 0) {
4042 			snprintf(out, out_size, MSG_ARG_INVALID, "cir");
4043 			return;
4044 		}
4045 
4046 		if (strcmp(tokens[11], "cbs") != 0) {
4047 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cbs");
4048 			return;
4049 		}
4050 
4051 		if (parser_read_uint64(&p.srtcm.cbs, tokens[12]) != 0) {
4052 			snprintf(out, out_size, MSG_ARG_INVALID, "cbs");
4053 			return;
4054 		}
4055 
4056 		if (strcmp(tokens[13], "ebs") != 0) {
4057 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "ebs");
4058 			return;
4059 		}
4060 
4061 		if (parser_read_uint64(&p.srtcm.ebs, tokens[14]) != 0) {
4062 			snprintf(out, out_size, MSG_ARG_INVALID, "ebs");
4063 			return;
4064 		}
4065 	} else if (strcmp(tokens[8], "trtcm") == 0) {
4066 		if (n_tokens != 17) {
4067 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4068 			return;
4069 		}
4070 
4071 		p.alg = RTE_TABLE_ACTION_METER_TRTCM;
4072 
4073 		if (strcmp(tokens[9], "cir") != 0) {
4074 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cir");
4075 			return;
4076 		}
4077 
4078 		if (parser_read_uint64(&p.trtcm.cir, tokens[10]) != 0) {
4079 			snprintf(out, out_size, MSG_ARG_INVALID, "cir");
4080 			return;
4081 		}
4082 
4083 		if (strcmp(tokens[11], "pir") != 0) {
4084 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pir");
4085 			return;
4086 		}
4087 
4088 		if (parser_read_uint64(&p.trtcm.pir, tokens[12]) != 0) {
4089 			snprintf(out, out_size, MSG_ARG_INVALID, "pir");
4090 			return;
4091 		}
4092 		if (strcmp(tokens[13], "cbs") != 0) {
4093 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cbs");
4094 			return;
4095 		}
4096 
4097 		if (parser_read_uint64(&p.trtcm.cbs, tokens[14]) != 0) {
4098 			snprintf(out, out_size, MSG_ARG_INVALID, "cbs");
4099 			return;
4100 		}
4101 
4102 		if (strcmp(tokens[15], "pbs") != 0) {
4103 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pbs");
4104 			return;
4105 		}
4106 
4107 		if (parser_read_uint64(&p.trtcm.pbs, tokens[16]) != 0) {
4108 			snprintf(out, out_size, MSG_ARG_INVALID, "pbs");
4109 			return;
4110 		}
4111 	} else {
4112 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4113 		return;
4114 	}
4115 
4116 	status = pipeline_table_mtr_profile_add(pipeline_name,
4117 		table_id,
4118 		meter_profile_id,
4119 		&p);
4120 	if (status) {
4121 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
4122 		return;
4123 	}
4124 }
4125 
4126 /**
4127  * pipeline <pipeline_name> table <table_id>
4128  *  meter profile <meter_profile_id> delete
4129  */
4130 static void
4131 cmd_pipeline_table_meter_profile_delete(char **tokens,
4132 	uint32_t n_tokens,
4133 	char *out,
4134 	size_t out_size)
4135 {
4136 	char *pipeline_name;
4137 	uint32_t table_id, meter_profile_id;
4138 	int status;
4139 
4140 	if (n_tokens != 8) {
4141 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4142 		return;
4143 	}
4144 
4145 	pipeline_name = tokens[1];
4146 
4147 	if (strcmp(tokens[2], "table") != 0) {
4148 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
4149 		return;
4150 	}
4151 
4152 	if (parser_read_uint32(&table_id, tokens[3]) != 0) {
4153 		snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
4154 		return;
4155 	}
4156 
4157 	if (strcmp(tokens[4], "meter") != 0) {
4158 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
4159 		return;
4160 	}
4161 
4162 	if (strcmp(tokens[5], "profile") != 0) {
4163 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
4164 		return;
4165 	}
4166 
4167 	if (parser_read_uint32(&meter_profile_id, tokens[6]) != 0) {
4168 		snprintf(out, out_size, MSG_ARG_INVALID, "meter_profile_id");
4169 		return;
4170 	}
4171 
4172 	if (strcmp(tokens[7], "delete") != 0) {
4173 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
4174 		return;
4175 	}
4176 
4177 	status = pipeline_table_mtr_profile_delete(pipeline_name,
4178 		table_id,
4179 		meter_profile_id);
4180 	if (status) {
4181 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
4182 		return;
4183 	}
4184 }
4185 
4186 /**
4187  * pipeline <pipeline_name> table <table_id> rule read meter [clear]
4188  */
4189 static void
4190 cmd_pipeline_table_rule_meter_read(char **tokens,
4191 	uint32_t n_tokens __rte_unused,
4192 	char *out,
4193 	size_t out_size)
4194 {
4195 	snprintf(out, out_size, MSG_CMD_UNIMPLEM, tokens[0]);
4196 }
4197 
4198 /**
4199  * pipeline <pipeline_name> table <table_id> dscp <file_name>
4200  *
4201  * File <file_name>:
4202  *  - exactly 64 lines
4203  *  - line format: <tc_id> <tc_queue_id> <color>, with <color> as: g | y | r
4204  */
4205 static int
4206 load_dscp_table(struct rte_table_action_dscp_table *dscp_table,
4207 	const char *file_name,
4208 	uint32_t *line_number)
4209 {
4210 	FILE *f = NULL;
4211 	uint32_t dscp, l;
4212 
4213 	/* Check input arguments */
4214 	if ((dscp_table == NULL) ||
4215 		(file_name == NULL) ||
4216 		(line_number == NULL)) {
4217 		if (line_number)
4218 			*line_number = 0;
4219 		return -EINVAL;
4220 	}
4221 
4222 	/* Open input file */
4223 	f = fopen(file_name, "r");
4224 	if (f == NULL) {
4225 		*line_number = 0;
4226 		return -EINVAL;
4227 	}
4228 
4229 	/* Read file */
4230 	for (dscp = 0, l = 1; ; l++) {
4231 		char line[64];
4232 		char *tokens[3];
4233 		enum rte_meter_color color;
4234 		uint32_t tc_id, tc_queue_id, n_tokens = RTE_DIM(tokens);
4235 
4236 		if (fgets(line, sizeof(line), f) == NULL)
4237 			break;
4238 
4239 		if (is_comment(line))
4240 			continue;
4241 
4242 		if (parse_tokenize_string(line, tokens, &n_tokens)) {
4243 			*line_number = l;
4244 			fclose(f);
4245 			return -EINVAL;
4246 		}
4247 
4248 		if (n_tokens == 0)
4249 			continue;
4250 
4251 		if ((dscp >= RTE_DIM(dscp_table->entry)) ||
4252 			(n_tokens != RTE_DIM(tokens)) ||
4253 			parser_read_uint32(&tc_id, tokens[0]) ||
4254 			(tc_id >= RTE_TABLE_ACTION_TC_MAX) ||
4255 			parser_read_uint32(&tc_queue_id, tokens[1]) ||
4256 			(tc_queue_id >= RTE_TABLE_ACTION_TC_QUEUE_MAX) ||
4257 			(strlen(tokens[2]) != 1)) {
4258 			*line_number = l;
4259 			fclose(f);
4260 			return -EINVAL;
4261 		}
4262 
4263 		switch (tokens[2][0]) {
4264 		case 'g':
4265 		case 'G':
4266 			color = e_RTE_METER_GREEN;
4267 			break;
4268 
4269 		case 'y':
4270 		case 'Y':
4271 			color = e_RTE_METER_YELLOW;
4272 			break;
4273 
4274 		case 'r':
4275 		case 'R':
4276 			color = e_RTE_METER_RED;
4277 			break;
4278 
4279 		default:
4280 			*line_number = l;
4281 			fclose(f);
4282 			return -EINVAL;
4283 		}
4284 
4285 		dscp_table->entry[dscp].tc_id = tc_id;
4286 		dscp_table->entry[dscp].tc_queue_id = tc_queue_id;
4287 		dscp_table->entry[dscp].color = color;
4288 		dscp++;
4289 	}
4290 
4291 	/* Close file */
4292 	fclose(f);
4293 	return 0;
4294 }
4295 
4296 static void
4297 cmd_pipeline_table_dscp(char **tokens,
4298 	uint32_t n_tokens,
4299 	char *out,
4300 	size_t out_size)
4301 {
4302 	struct rte_table_action_dscp_table dscp_table;
4303 	char *pipeline_name, *file_name;
4304 	uint32_t table_id, line_number;
4305 	int status;
4306 
4307 	if (n_tokens != 6) {
4308 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4309 		return;
4310 	}
4311 
4312 	pipeline_name = tokens[1];
4313 
4314 	if (strcmp(tokens[2], "table") != 0) {
4315 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
4316 		return;
4317 	}
4318 
4319 	if (parser_read_uint32(&table_id, tokens[3]) != 0) {
4320 		snprintf(out, out_size, MSG_ARG_INVALID, "table_id");
4321 		return;
4322 	}
4323 
4324 	if (strcmp(tokens[4], "dscp") != 0) {
4325 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "dscp");
4326 		return;
4327 	}
4328 
4329 	file_name = tokens[5];
4330 
4331 	status = load_dscp_table(&dscp_table, file_name, &line_number);
4332 	if (status) {
4333 		snprintf(out, out_size, MSG_FILE_ERR, file_name, line_number);
4334 		return;
4335 	}
4336 
4337 	status = pipeline_table_dscp_table_update(pipeline_name,
4338 		table_id,
4339 		UINT64_MAX,
4340 		&dscp_table);
4341 	if (status) {
4342 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
4343 		return;
4344 	}
4345 }
4346 
4347 /**
4348  * pipeline <pipeline_name> table <table_id> rule read ttl [clear]
4349  */
4350 static void
4351 cmd_pipeline_table_rule_ttl_read(char **tokens,
4352 	uint32_t n_tokens __rte_unused,
4353 	char *out,
4354 	size_t out_size)
4355 {
4356 	snprintf(out, out_size, MSG_CMD_UNIMPLEM, tokens[0]);
4357 }
4358 
4359 /**
4360  * thread <thread_id> pipeline <pipeline_name> enable
4361  */
4362 static void
4363 cmd_thread_pipeline_enable(char **tokens,
4364 	uint32_t n_tokens,
4365 	char *out,
4366 	size_t out_size)
4367 {
4368 	char *pipeline_name;
4369 	uint32_t thread_id;
4370 	int status;
4371 
4372 	if (n_tokens != 5) {
4373 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4374 		return;
4375 	}
4376 
4377 	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
4378 		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
4379 		return;
4380 	}
4381 
4382 	if (strcmp(tokens[2], "pipeline") != 0) {
4383 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
4384 		return;
4385 	}
4386 
4387 	pipeline_name = tokens[3];
4388 
4389 	if (strcmp(tokens[4], "enable") != 0) {
4390 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
4391 		return;
4392 	}
4393 
4394 	status = thread_pipeline_enable(thread_id, pipeline_name);
4395 	if (status) {
4396 		snprintf(out, out_size, MSG_CMD_FAIL, "thread pipeline enable");
4397 		return;
4398 	}
4399 }
4400 
4401 /**
4402  * thread <thread_id> pipeline <pipeline_name> disable
4403  */
4404 static void
4405 cmd_thread_pipeline_disable(char **tokens,
4406 	uint32_t n_tokens,
4407 	char *out,
4408 	size_t out_size)
4409 {
4410 	char *pipeline_name;
4411 	uint32_t thread_id;
4412 	int status;
4413 
4414 	if (n_tokens != 5) {
4415 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
4416 		return;
4417 	}
4418 
4419 	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
4420 		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
4421 		return;
4422 	}
4423 
4424 	if (strcmp(tokens[2], "pipeline") != 0) {
4425 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
4426 		return;
4427 	}
4428 
4429 	pipeline_name = tokens[3];
4430 
4431 	if (strcmp(tokens[4], "disable") != 0) {
4432 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
4433 		return;
4434 	}
4435 
4436 	status = thread_pipeline_disable(thread_id, pipeline_name);
4437 	if (status) {
4438 		snprintf(out, out_size, MSG_CMD_FAIL,
4439 			"thread pipeline disable");
4440 		return;
4441 	}
4442 }
4443 
4444 void
4445 cli_process(char *in, char *out, size_t out_size)
4446 {
4447 	char *tokens[CMD_MAX_TOKENS];
4448 	uint32_t n_tokens = RTE_DIM(tokens);
4449 	int status;
4450 
4451 	if (is_comment(in))
4452 		return;
4453 
4454 	status = parse_tokenize_string(in, tokens, &n_tokens);
4455 	if (status) {
4456 		snprintf(out, out_size, MSG_ARG_TOO_MANY, "");
4457 		return;
4458 	}
4459 
4460 	if (n_tokens == 0)
4461 		return;
4462 
4463 	if (strcmp(tokens[0], "mempool") == 0) {
4464 		cmd_mempool(tokens, n_tokens, out, out_size);
4465 		return;
4466 	}
4467 
4468 	if (strcmp(tokens[0], "link") == 0) {
4469 		if (strcmp(tokens[1], "show") == 0) {
4470 			cmd_link_show(tokens, n_tokens, out, out_size);
4471 			return;
4472 		}
4473 
4474 		cmd_link(tokens, n_tokens, out, out_size);
4475 		return;
4476 	}
4477 
4478 	if (strcmp(tokens[0], "swq") == 0) {
4479 		cmd_swq(tokens, n_tokens, out, out_size);
4480 		return;
4481 	}
4482 
4483 	if (strcmp(tokens[0], "tmgr") == 0) {
4484 		if ((n_tokens >= 3) &&
4485 			(strcmp(tokens[1], "subport") == 0) &&
4486 			(strcmp(tokens[2], "profile") == 0)) {
4487 			cmd_tmgr_subport_profile(tokens, n_tokens,
4488 				out, out_size);
4489 			return;
4490 		}
4491 
4492 		if ((n_tokens >= 3) &&
4493 			(strcmp(tokens[1], "pipe") == 0) &&
4494 			(strcmp(tokens[2], "profile") == 0)) {
4495 			cmd_tmgr_pipe_profile(tokens, n_tokens, out, out_size);
4496 			return;
4497 		}
4498 
4499 		if ((n_tokens >= 5) &&
4500 			(strcmp(tokens[2], "subport") == 0) &&
4501 			(strcmp(tokens[4], "profile") == 0)) {
4502 			cmd_tmgr_subport(tokens, n_tokens, out, out_size);
4503 			return;
4504 		}
4505 
4506 		if ((n_tokens >= 5) &&
4507 			(strcmp(tokens[2], "subport") == 0) &&
4508 			(strcmp(tokens[4], "pipe") == 0)) {
4509 			cmd_tmgr_subport_pipe(tokens, n_tokens, out, out_size);
4510 			return;
4511 		}
4512 
4513 		cmd_tmgr(tokens, n_tokens, out, out_size);
4514 		return;
4515 	}
4516 
4517 	if (strcmp(tokens[0], "tap") == 0) {
4518 		cmd_tap(tokens, n_tokens, out, out_size);
4519 		return;
4520 	}
4521 
4522 	if (strcmp(tokens[0], "kni") == 0) {
4523 		cmd_kni(tokens, n_tokens, out, out_size);
4524 		return;
4525 	}
4526 
4527 	if (strcmp(tokens[0], "port") == 0) {
4528 		cmd_port_in_action_profile(tokens, n_tokens, out, out_size);
4529 		return;
4530 	}
4531 
4532 	if (strcmp(tokens[0], "table") == 0) {
4533 		cmd_table_action_profile(tokens, n_tokens, out, out_size);
4534 		return;
4535 	}
4536 
4537 	if (strcmp(tokens[0], "pipeline") == 0) {
4538 		if ((n_tokens >= 3) &&
4539 			(strcmp(tokens[2], "period") == 0)) {
4540 			cmd_pipeline(tokens, n_tokens, out, out_size);
4541 			return;
4542 		}
4543 
4544 		if ((n_tokens >= 5) &&
4545 			(strcmp(tokens[2], "port") == 0) &&
4546 			(strcmp(tokens[3], "in") == 0) &&
4547 			(strcmp(tokens[4], "bsz") == 0)) {
4548 			cmd_pipeline_port_in(tokens, n_tokens, out, out_size);
4549 			return;
4550 		}
4551 
4552 		if ((n_tokens >= 5) &&
4553 			(strcmp(tokens[2], "port") == 0) &&
4554 			(strcmp(tokens[3], "out") == 0) &&
4555 			(strcmp(tokens[4], "bsz") == 0)) {
4556 			cmd_pipeline_port_out(tokens, n_tokens, out, out_size);
4557 			return;
4558 		}
4559 
4560 		if ((n_tokens >= 4) &&
4561 			(strcmp(tokens[2], "table") == 0) &&
4562 			(strcmp(tokens[3], "match") == 0)) {
4563 			cmd_pipeline_table(tokens, n_tokens, out, out_size);
4564 			return;
4565 		}
4566 
4567 		if ((n_tokens >= 6) &&
4568 			(strcmp(tokens[2], "port") == 0) &&
4569 			(strcmp(tokens[3], "in") == 0) &&
4570 			(strcmp(tokens[5], "table") == 0)) {
4571 			cmd_pipeline_port_in_table(tokens, n_tokens,
4572 				out, out_size);
4573 			return;
4574 		}
4575 
4576 		if ((n_tokens >= 6) &&
4577 			(strcmp(tokens[2], "port") == 0) &&
4578 			(strcmp(tokens[3], "in") == 0) &&
4579 			(strcmp(tokens[5], "stats") == 0)) {
4580 			cmd_pipeline_port_in_stats(tokens, n_tokens,
4581 				out, out_size);
4582 			return;
4583 		}
4584 
4585 		if ((n_tokens >= 6) &&
4586 			(strcmp(tokens[2], "port") == 0) &&
4587 			(strcmp(tokens[3], "in") == 0) &&
4588 			(strcmp(tokens[5], "enable") == 0)) {
4589 			cmd_pipeline_port_in_enable(tokens, n_tokens,
4590 				out, out_size);
4591 			return;
4592 		}
4593 
4594 		if ((n_tokens >= 6) &&
4595 			(strcmp(tokens[2], "port") == 0) &&
4596 			(strcmp(tokens[3], "in") == 0) &&
4597 			(strcmp(tokens[5], "disable") == 0)) {
4598 			cmd_pipeline_port_in_disable(tokens, n_tokens,
4599 				out, out_size);
4600 			return;
4601 		}
4602 
4603 		if ((n_tokens >= 6) &&
4604 			(strcmp(tokens[2], "port") == 0) &&
4605 			(strcmp(tokens[3], "out") == 0) &&
4606 			(strcmp(tokens[5], "stats") == 0)) {
4607 			cmd_pipeline_port_out_stats(tokens, n_tokens,
4608 				out, out_size);
4609 			return;
4610 		}
4611 
4612 		if ((n_tokens >= 5) &&
4613 			(strcmp(tokens[2], "table") == 0) &&
4614 			(strcmp(tokens[4], "stats") == 0)) {
4615 			cmd_pipeline_table_stats(tokens, n_tokens,
4616 				out, out_size);
4617 			return;
4618 		}
4619 
4620 		if ((n_tokens >= 7) &&
4621 			(strcmp(tokens[2], "table") == 0) &&
4622 			(strcmp(tokens[4], "rule") == 0) &&
4623 			(strcmp(tokens[5], "add") == 0) &&
4624 			(strcmp(tokens[6], "match") == 0)) {
4625 			if ((n_tokens >= 8) &&
4626 				(strcmp(tokens[7], "default") == 0)) {
4627 				cmd_pipeline_table_rule_add_default(tokens,
4628 					n_tokens, out, out_size);
4629 				return;
4630 			}
4631 
4632 			cmd_pipeline_table_rule_add(tokens, n_tokens,
4633 				out, out_size);
4634 			return;
4635 		}
4636 
4637 		if ((n_tokens >= 7) &&
4638 			(strcmp(tokens[2], "table") == 0) &&
4639 			(strcmp(tokens[4], "rule") == 0) &&
4640 			(strcmp(tokens[5], "add") == 0) &&
4641 			(strcmp(tokens[6], "bulk") == 0)) {
4642 			cmd_pipeline_table_rule_add_bulk(tokens,
4643 				n_tokens, out, out_size);
4644 			return;
4645 		}
4646 
4647 		if ((n_tokens >= 7) &&
4648 			(strcmp(tokens[2], "table") == 0) &&
4649 			(strcmp(tokens[4], "rule") == 0) &&
4650 			(strcmp(tokens[5], "delete") == 0) &&
4651 			(strcmp(tokens[6], "match") == 0)) {
4652 			if ((n_tokens >= 8) &&
4653 				(strcmp(tokens[7], "default") == 0)) {
4654 				cmd_pipeline_table_rule_delete_default(tokens,
4655 					n_tokens, out, out_size);
4656 				return;
4657 				}
4658 
4659 			cmd_pipeline_table_rule_delete(tokens, n_tokens,
4660 				out, out_size);
4661 			return;
4662 		}
4663 
4664 		if ((n_tokens >= 7) &&
4665 			(strcmp(tokens[2], "table") == 0) &&
4666 			(strcmp(tokens[4], "rule") == 0) &&
4667 			(strcmp(tokens[5], "read") == 0) &&
4668 			(strcmp(tokens[6], "stats") == 0)) {
4669 			cmd_pipeline_table_rule_stats_read(tokens, n_tokens,
4670 				out, out_size);
4671 			return;
4672 		}
4673 
4674 		if ((n_tokens >= 8) &&
4675 			(strcmp(tokens[2], "table") == 0) &&
4676 			(strcmp(tokens[4], "meter") == 0) &&
4677 			(strcmp(tokens[5], "profile") == 0) &&
4678 			(strcmp(tokens[7], "add") == 0)) {
4679 			cmd_pipeline_table_meter_profile_add(tokens, n_tokens,
4680 				out, out_size);
4681 			return;
4682 		}
4683 
4684 		if ((n_tokens >= 8) &&
4685 			(strcmp(tokens[2], "table") == 0) &&
4686 			(strcmp(tokens[4], "meter") == 0) &&
4687 			(strcmp(tokens[5], "profile") == 0) &&
4688 			(strcmp(tokens[7], "delete") == 0)) {
4689 			cmd_pipeline_table_meter_profile_delete(tokens,
4690 				n_tokens, out, out_size);
4691 			return;
4692 		}
4693 
4694 		if ((n_tokens >= 7) &&
4695 			(strcmp(tokens[2], "table") == 0) &&
4696 			(strcmp(tokens[4], "rule") == 0) &&
4697 			(strcmp(tokens[5], "read") == 0) &&
4698 			(strcmp(tokens[6], "meter") == 0)) {
4699 			cmd_pipeline_table_rule_meter_read(tokens, n_tokens,
4700 				out, out_size);
4701 			return;
4702 		}
4703 
4704 		if ((n_tokens >= 5) &&
4705 			(strcmp(tokens[2], "table") == 0) &&
4706 			(strcmp(tokens[4], "dscp") == 0)) {
4707 			cmd_pipeline_table_dscp(tokens, n_tokens,
4708 				out, out_size);
4709 			return;
4710 		}
4711 
4712 		if ((n_tokens >= 7) &&
4713 			(strcmp(tokens[2], "table") == 0) &&
4714 			(strcmp(tokens[4], "rule") == 0) &&
4715 			(strcmp(tokens[5], "read") == 0) &&
4716 			(strcmp(tokens[6], "ttl") == 0)) {
4717 			cmd_pipeline_table_rule_ttl_read(tokens, n_tokens,
4718 				out, out_size);
4719 			return;
4720 		}
4721 	}
4722 
4723 	if (strcmp(tokens[0], "thread") == 0) {
4724 		if ((n_tokens >= 5) &&
4725 			(strcmp(tokens[4], "enable") == 0)) {
4726 			cmd_thread_pipeline_enable(tokens, n_tokens,
4727 				out, out_size);
4728 			return;
4729 		}
4730 
4731 		if ((n_tokens >= 5) &&
4732 			(strcmp(tokens[4], "disable") == 0)) {
4733 			cmd_thread_pipeline_disable(tokens, n_tokens,
4734 				out, out_size);
4735 			return;
4736 		}
4737 	}
4738 
4739 	snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
4740 }
4741 
4742 int
4743 cli_script_process(const char *file_name,
4744 	size_t msg_in_len_max,
4745 	size_t msg_out_len_max)
4746 {
4747 	char *msg_in = NULL, *msg_out = NULL;
4748 	FILE *f = NULL;
4749 
4750 	/* Check input arguments */
4751 	if ((file_name == NULL) ||
4752 		(strlen(file_name) == 0) ||
4753 		(msg_in_len_max == 0) ||
4754 		(msg_out_len_max == 0))
4755 		return -EINVAL;
4756 
4757 	msg_in = malloc(msg_in_len_max + 1);
4758 	msg_out = malloc(msg_out_len_max + 1);
4759 	if ((msg_in == NULL) ||
4760 		(msg_out == NULL)) {
4761 		free(msg_out);
4762 		free(msg_in);
4763 		return -ENOMEM;
4764 	}
4765 
4766 	/* Open input file */
4767 	f = fopen(file_name, "r");
4768 	if (f == NULL) {
4769 		free(msg_out);
4770 		free(msg_in);
4771 		return -EIO;
4772 	}
4773 
4774 	/* Read file */
4775 	for ( ; ; ) {
4776 		if (fgets(msg_in, msg_in_len_max + 1, f) == NULL)
4777 			break;
4778 
4779 		printf("%s", msg_in);
4780 		msg_out[0] = 0;
4781 
4782 		cli_process(msg_in,
4783 			msg_out,
4784 			msg_out_len_max);
4785 
4786 		if (strlen(msg_out))
4787 			printf("%s", msg_out);
4788 	}
4789 
4790 	/* Close file */
4791 	fclose(f);
4792 	free(msg_out);
4793 	free(msg_in);
4794 	return 0;
4795 }
4796 
4797 static int
4798 cli_rule_file_process(const char *file_name,
4799 	size_t line_len_max,
4800 	struct table_rule_match *m,
4801 	struct table_rule_action *a,
4802 	uint32_t *n_rules,
4803 	uint32_t *line_number,
4804 	char *out,
4805 	size_t out_size)
4806 {
4807 	FILE *f = NULL;
4808 	char *line = NULL;
4809 	uint32_t rule_id, line_id;
4810 	int status = 0;
4811 
4812 	/* Check input arguments */
4813 	if ((file_name == NULL) ||
4814 		(strlen(file_name) == 0) ||
4815 		(line_len_max == 0)) {
4816 		*line_number = 0;
4817 		return -EINVAL;
4818 	}
4819 
4820 	/* Memory allocation */
4821 	line = malloc(line_len_max + 1);
4822 	if (line == NULL) {
4823 		*line_number = 0;
4824 		return -ENOMEM;
4825 	}
4826 
4827 	/* Open file */
4828 	f = fopen(file_name, "r");
4829 	if (f == NULL) {
4830 		*line_number = 0;
4831 		free(line);
4832 		return -EIO;
4833 	}
4834 
4835 	/* Read file */
4836 	for (line_id = 1, rule_id = 0; rule_id < *n_rules; line_id++) {
4837 		char *tokens[CMD_MAX_TOKENS];
4838 		uint32_t n_tokens, n_tokens_parsed, t0;
4839 
4840 		/* Read next line from file. */
4841 		if (fgets(line, line_len_max + 1, f) == NULL)
4842 			break;
4843 
4844 		/* Comment. */
4845 		if (is_comment(line))
4846 			continue;
4847 
4848 		/* Parse line. */
4849 		n_tokens = RTE_DIM(tokens);
4850 		status = parse_tokenize_string(line, tokens, &n_tokens);
4851 		if (status) {
4852 			status = -EINVAL;
4853 			break;
4854 		}
4855 
4856 		/* Empty line. */
4857 		if (n_tokens == 0)
4858 			continue;
4859 		t0 = 0;
4860 
4861 		/* Rule match. */
4862 		n_tokens_parsed = parse_match(tokens + t0,
4863 			n_tokens - t0,
4864 			out,
4865 			out_size,
4866 			&m[rule_id]);
4867 		if (n_tokens_parsed == 0) {
4868 			status = -EINVAL;
4869 			break;
4870 		}
4871 		t0 += n_tokens_parsed;
4872 
4873 		/* Rule action. */
4874 		n_tokens_parsed = parse_table_action(tokens + t0,
4875 			n_tokens - t0,
4876 			out,
4877 			out_size,
4878 			&a[rule_id]);
4879 		if (n_tokens_parsed == 0) {
4880 			status = -EINVAL;
4881 			break;
4882 		}
4883 		t0 += n_tokens_parsed;
4884 
4885 		/* Line completed. */
4886 		if (t0 < n_tokens) {
4887 			status = -EINVAL;
4888 			break;
4889 		}
4890 
4891 		/* Increment rule count */
4892 		rule_id++;
4893 	}
4894 
4895 	/* Close file */
4896 	fclose(f);
4897 
4898 	/* Memory free */
4899 	free(line);
4900 
4901 	*n_rules = rule_id;
4902 	*line_number = line_id;
4903 	return status;
4904 }
4905