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