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