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