xref: /dpdk/examples/pipeline/cli.c (revision 64051bb1f144c418f3fc76e6d0973337b05d5886)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2020 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_ethdev.h>
12 #include <rte_swx_port_ethdev.h>
13 #include <rte_swx_port_ring.h>
14 #include <rte_swx_port_source_sink.h>
15 #include <rte_swx_port_fd.h>
16 #include <rte_swx_pipeline.h>
17 #include <rte_swx_ctl.h>
18 
19 #include "cli.h"
20 
21 #include "obj.h"
22 #include "thread.h"
23 
24 #ifndef CMD_MAX_TOKENS
25 #define CMD_MAX_TOKENS     256
26 #endif
27 
28 #define MSG_OUT_OF_MEMORY   "Not enough memory.\n"
29 #define MSG_CMD_UNKNOWN     "Unknown command \"%s\".\n"
30 #define MSG_CMD_UNIMPLEM    "Command \"%s\" not implemented.\n"
31 #define MSG_ARG_NOT_ENOUGH  "Not enough arguments for command \"%s\".\n"
32 #define MSG_ARG_TOO_MANY    "Too many arguments for command \"%s\".\n"
33 #define MSG_ARG_MISMATCH    "Wrong number of arguments for command \"%s\".\n"
34 #define MSG_ARG_NOT_FOUND   "Argument \"%s\" not found.\n"
35 #define MSG_ARG_INVALID     "Invalid value for argument \"%s\".\n"
36 #define MSG_FILE_ERR        "Error in file \"%s\" at line %u.\n"
37 #define MSG_FILE_NOT_ENOUGH "Not enough rules in file \"%s\".\n"
38 #define MSG_CMD_FAIL        "Command \"%s\" failed.\n"
39 
40 #define skip_white_spaces(pos)			\
41 ({						\
42 	__typeof__(pos) _p = (pos);		\
43 	for ( ; isspace(*_p); _p++)		\
44 		;				\
45 	_p;					\
46 })
47 
48 static int
49 parser_read_uint64(uint64_t *value, const char *p)
50 {
51 	char *next;
52 	uint64_t val;
53 
54 	p = skip_white_spaces(p);
55 	if (!isdigit(*p))
56 		return -EINVAL;
57 
58 	val = strtoul(p, &next, 0);
59 	if (p == next)
60 		return -EINVAL;
61 
62 	p = next;
63 	switch (*p) {
64 	case 'T':
65 		val *= 1024ULL;
66 		/* fall through */
67 	case 'G':
68 		val *= 1024ULL;
69 		/* fall through */
70 	case 'M':
71 		val *= 1024ULL;
72 		/* fall through */
73 	case 'k':
74 	case 'K':
75 		val *= 1024ULL;
76 		p++;
77 		break;
78 	}
79 
80 	p = skip_white_spaces(p);
81 	if (*p != '\0')
82 		return -EINVAL;
83 
84 	*value = val;
85 	return 0;
86 }
87 
88 static int
89 parser_read_uint32(uint32_t *value, const char *p)
90 {
91 	uint64_t val = 0;
92 	int ret = parser_read_uint64(&val, p);
93 
94 	if (ret < 0)
95 		return ret;
96 
97 	if (val > UINT32_MAX)
98 		return -ERANGE;
99 
100 	*value = val;
101 	return 0;
102 }
103 
104 static int
105 parser_read_uint16(uint16_t *value, const char *p)
106 {
107 	uint64_t val = 0;
108 	int ret = parser_read_uint64(&val, p);
109 
110 	if (ret < 0)
111 		return ret;
112 
113 	if (val > UINT16_MAX)
114 		return -ERANGE;
115 
116 	*value = val;
117 	return 0;
118 }
119 
120 #define PARSE_DELIMITER " \f\n\r\t\v"
121 
122 static int
123 parse_tokenize_string(char *string, char *tokens[], uint32_t *n_tokens)
124 {
125 	uint32_t i;
126 
127 	if ((string == NULL) ||
128 		(tokens == NULL) ||
129 		(*n_tokens < 1))
130 		return -EINVAL;
131 
132 	for (i = 0; i < *n_tokens; i++) {
133 		tokens[i] = strtok_r(string, PARSE_DELIMITER, &string);
134 		if (tokens[i] == NULL)
135 			break;
136 	}
137 
138 	if ((i == *n_tokens) && strtok_r(string, PARSE_DELIMITER, &string))
139 		return -E2BIG;
140 
141 	*n_tokens = i;
142 	return 0;
143 }
144 
145 static int
146 is_comment(char *in)
147 {
148 	if ((strlen(in) && index("!#%;", in[0])) ||
149 		(strncmp(in, "//", 2) == 0) ||
150 		(strncmp(in, "--", 2) == 0))
151 		return 1;
152 
153 	return 0;
154 }
155 
156 static const char cmd_mempool_help[] =
157 "mempool <mempool_name>\n"
158 "   buffer <buffer_size>\n"
159 "   pool <pool_size>\n"
160 "   cache <cache_size>\n"
161 "   cpu <cpu_id>\n";
162 
163 static void
164 cmd_mempool(char **tokens,
165 	uint32_t n_tokens,
166 	char *out,
167 	size_t out_size,
168 	void *obj)
169 {
170 	struct mempool_params p;
171 	char *name;
172 	struct mempool *mempool;
173 
174 	if (n_tokens != 10) {
175 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
176 		return;
177 	}
178 
179 	name = tokens[1];
180 
181 	if (strcmp(tokens[2], "buffer") != 0) {
182 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "buffer");
183 		return;
184 	}
185 
186 	if (parser_read_uint32(&p.buffer_size, tokens[3]) != 0) {
187 		snprintf(out, out_size, MSG_ARG_INVALID, "buffer_size");
188 		return;
189 	}
190 
191 	if (strcmp(tokens[4], "pool") != 0) {
192 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pool");
193 		return;
194 	}
195 
196 	if (parser_read_uint32(&p.pool_size, tokens[5]) != 0) {
197 		snprintf(out, out_size, MSG_ARG_INVALID, "pool_size");
198 		return;
199 	}
200 
201 	if (strcmp(tokens[6], "cache") != 0) {
202 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cache");
203 		return;
204 	}
205 
206 	if (parser_read_uint32(&p.cache_size, tokens[7]) != 0) {
207 		snprintf(out, out_size, MSG_ARG_INVALID, "cache_size");
208 		return;
209 	}
210 
211 	if (strcmp(tokens[8], "cpu") != 0) {
212 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cpu");
213 		return;
214 	}
215 
216 	if (parser_read_uint32(&p.cpu_id, tokens[9]) != 0) {
217 		snprintf(out, out_size, MSG_ARG_INVALID, "cpu_id");
218 		return;
219 	}
220 
221 	mempool = mempool_create(obj, name, &p);
222 	if (mempool == NULL) {
223 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
224 		return;
225 	}
226 }
227 
228 static const char cmd_link_help[] =
229 "link <link_name>\n"
230 "   dev <device_name> | port <port_id>\n"
231 "   rxq <n_queues> <queue_size> <mempool_name>\n"
232 "   txq <n_queues> <queue_size>\n"
233 "   promiscuous on | off\n"
234 "   [rss <qid_0> ... <qid_n>]\n";
235 
236 static void
237 cmd_link(char **tokens,
238 	uint32_t n_tokens,
239 	char *out,
240 	size_t out_size,
241 	void *obj)
242 {
243 	struct link_params p;
244 	struct link_params_rss rss;
245 	struct link *link;
246 	char *name;
247 
248 	memset(&p, 0, sizeof(p));
249 
250 	if ((n_tokens < 13) || (n_tokens > 14 + LINK_RXQ_RSS_MAX)) {
251 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
252 		return;
253 	}
254 	name = tokens[1];
255 
256 	if (strcmp(tokens[2], "dev") == 0)
257 		p.dev_name = tokens[3];
258 	else if (strcmp(tokens[2], "port") == 0) {
259 		p.dev_name = NULL;
260 
261 		if (parser_read_uint16(&p.port_id, tokens[3]) != 0) {
262 			snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
263 			return;
264 		}
265 	} else {
266 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "dev or port");
267 		return;
268 	}
269 
270 	if (strcmp(tokens[4], "rxq") != 0) {
271 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq");
272 		return;
273 	}
274 
275 	if (parser_read_uint32(&p.rx.n_queues, tokens[5]) != 0) {
276 		snprintf(out, out_size, MSG_ARG_INVALID, "n_queues");
277 		return;
278 	}
279 	if (parser_read_uint32(&p.rx.queue_size, tokens[6]) != 0) {
280 		snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
281 		return;
282 	}
283 
284 	p.rx.mempool_name = tokens[7];
285 
286 	if (strcmp(tokens[8], "txq") != 0) {
287 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "txq");
288 		return;
289 	}
290 
291 	if (parser_read_uint32(&p.tx.n_queues, tokens[9]) != 0) {
292 		snprintf(out, out_size, MSG_ARG_INVALID, "n_queues");
293 		return;
294 	}
295 
296 	if (parser_read_uint32(&p.tx.queue_size, tokens[10]) != 0) {
297 		snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
298 		return;
299 	}
300 
301 	if (strcmp(tokens[11], "promiscuous") != 0) {
302 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "promiscuous");
303 		return;
304 	}
305 
306 	if (strcmp(tokens[12], "on") == 0)
307 		p.promiscuous = 1;
308 	else if (strcmp(tokens[12], "off") == 0)
309 		p.promiscuous = 0;
310 	else {
311 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "on or off");
312 		return;
313 	}
314 
315 	/* RSS */
316 	p.rx.rss = NULL;
317 	if (n_tokens > 13) {
318 		uint32_t queue_id, i;
319 
320 		if (strcmp(tokens[13], "rss") != 0) {
321 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rss");
322 			return;
323 		}
324 
325 		p.rx.rss = &rss;
326 
327 		rss.n_queues = 0;
328 		for (i = 14; i < n_tokens; i++) {
329 			if (parser_read_uint32(&queue_id, tokens[i]) != 0) {
330 				snprintf(out, out_size, MSG_ARG_INVALID,
331 					"queue_id");
332 				return;
333 			}
334 
335 			rss.queue_id[rss.n_queues] = queue_id;
336 			rss.n_queues++;
337 		}
338 	}
339 
340 	link = link_create(obj, name, &p);
341 	if (link == NULL) {
342 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
343 		return;
344 	}
345 }
346 
347 /* Print the link stats and info */
348 static void
349 print_link_info(struct link *link, char *out, size_t out_size)
350 {
351 	struct rte_eth_stats stats;
352 	struct rte_ether_addr mac_addr;
353 	struct rte_eth_link eth_link;
354 	uint16_t mtu;
355 	int ret;
356 
357 	memset(&stats, 0, sizeof(stats));
358 	rte_eth_stats_get(link->port_id, &stats);
359 
360 	ret = rte_eth_macaddr_get(link->port_id, &mac_addr);
361 	if (ret != 0) {
362 		snprintf(out, out_size, "\n%s: MAC address get failed: %s",
363 			 link->name, rte_strerror(-ret));
364 		return;
365 	}
366 
367 	ret = rte_eth_link_get(link->port_id, &eth_link);
368 	if (ret < 0) {
369 		snprintf(out, out_size, "\n%s: link get failed: %s",
370 			 link->name, rte_strerror(-ret));
371 		return;
372 	}
373 
374 	rte_eth_dev_get_mtu(link->port_id, &mtu);
375 
376 	snprintf(out, out_size,
377 		"\n"
378 		"%s: flags=<%s> mtu %u\n"
379 		"\tether %02X:%02X:%02X:%02X:%02X:%02X rxqueues %u txqueues %u\n"
380 		"\tport# %u  speed %s\n"
381 		"\tRX packets %" PRIu64"  bytes %" PRIu64"\n"
382 		"\tRX errors %" PRIu64"  missed %" PRIu64"  no-mbuf %" PRIu64"\n"
383 		"\tTX packets %" PRIu64"  bytes %" PRIu64"\n"
384 		"\tTX errors %" PRIu64"\n",
385 		link->name,
386 		eth_link.link_status == 0 ? "DOWN" : "UP",
387 		mtu,
388 		mac_addr.addr_bytes[0], mac_addr.addr_bytes[1],
389 		mac_addr.addr_bytes[2], mac_addr.addr_bytes[3],
390 		mac_addr.addr_bytes[4], mac_addr.addr_bytes[5],
391 		link->n_rxq,
392 		link->n_txq,
393 		link->port_id,
394 		rte_eth_link_speed_to_str(eth_link.link_speed),
395 		stats.ipackets,
396 		stats.ibytes,
397 		stats.ierrors,
398 		stats.imissed,
399 		stats.rx_nombuf,
400 		stats.opackets,
401 		stats.obytes,
402 		stats.oerrors);
403 }
404 
405 /*
406  * link show [<link_name>]
407  */
408 static void
409 cmd_link_show(char **tokens,
410 	      uint32_t n_tokens,
411 	      char *out,
412 	      size_t out_size,
413 	      void *obj)
414 {
415 	struct link *link;
416 	char *link_name;
417 
418 	if (n_tokens != 2 && n_tokens != 3) {
419 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
420 		return;
421 	}
422 
423 	if (n_tokens == 2) {
424 		link = link_next(obj, NULL);
425 
426 		while (link != NULL) {
427 			out_size = out_size - strlen(out);
428 			out = &out[strlen(out)];
429 
430 			print_link_info(link, out, out_size);
431 			link = link_next(obj, link);
432 		}
433 	} else {
434 		out_size = out_size - strlen(out);
435 		out = &out[strlen(out)];
436 
437 		link_name = tokens[2];
438 		link = link_find(obj, link_name);
439 
440 		if (link == NULL) {
441 			snprintf(out, out_size, MSG_ARG_INVALID,
442 					"Link does not exist");
443 			return;
444 		}
445 		print_link_info(link, out, out_size);
446 	}
447 }
448 
449 static const char cmd_ring_help[] =
450 "ring <ring_name> size <size> numa <numa_node>\n";
451 
452 static void
453 cmd_ring(char **tokens,
454 	uint32_t n_tokens,
455 	char *out,
456 	size_t out_size,
457 	void *obj)
458 {
459 	struct ring_params p;
460 	char *name;
461 	struct ring *ring;
462 
463 	if (n_tokens != 6) {
464 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
465 		return;
466 	}
467 
468 	name = tokens[1];
469 
470 	if (strcmp(tokens[2], "size") != 0) {
471 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
472 		return;
473 	}
474 
475 	if (parser_read_uint32(&p.size, tokens[3]) != 0) {
476 		snprintf(out, out_size, MSG_ARG_INVALID, "size");
477 		return;
478 	}
479 
480 	if (strcmp(tokens[4], "numa") != 0) {
481 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
482 		return;
483 	}
484 
485 	if (parser_read_uint32(&p.numa_node, tokens[5]) != 0) {
486 		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
487 		return;
488 	}
489 
490 	ring = ring_create(obj, name, &p);
491 	if (!ring) {
492 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
493 		return;
494 	}
495 }
496 
497 static const char cmd_tap_help[] =
498 "tap <tap_name>\n";
499 
500 static void
501 cmd_tap(char **tokens,
502 	uint32_t n_tokens,
503 	char *out,
504 	size_t out_size,
505 	void *obj)
506 {
507 	struct tap *tap;
508 	char *name;
509 
510 	if (n_tokens < 2) {
511 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
512 		return;
513 	}
514 	name = tokens[1];
515 
516 	tap = tap_create(obj, name);
517 	if (tap == NULL) {
518 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
519 		return;
520 	}
521 }
522 
523 static const char cmd_pipeline_create_help[] =
524 "pipeline <pipeline_name> create <numa_node>\n";
525 
526 static void
527 cmd_pipeline_create(char **tokens,
528 	uint32_t n_tokens,
529 	char *out,
530 	size_t out_size,
531 	void *obj)
532 {
533 	struct pipeline *p;
534 	char *name;
535 	uint32_t numa_node;
536 
537 	if (n_tokens != 4) {
538 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
539 		return;
540 	}
541 
542 	name = tokens[1];
543 
544 	if (parser_read_uint32(&numa_node, tokens[3]) != 0) {
545 		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
546 		return;
547 	}
548 
549 	p = pipeline_create(obj, name, (int)numa_node);
550 	if (!p) {
551 		snprintf(out, out_size, "pipeline create error.");
552 		return;
553 	}
554 }
555 
556 static const char cmd_pipeline_port_in_help[] =
557 "pipeline <pipeline_name> port in <port_id>\n"
558 "   link <link_name> rxq <queue_id> bsz <burst_size>\n"
559 "   ring <ring_name> bsz <burst_size>\n"
560 "   | source <mempool_name> <file_name>\n"
561 "   | tap <tap_name> mempool <mempool_name> mtu <mtu> bsz <burst_size>\n";
562 
563 static void
564 cmd_pipeline_port_in(char **tokens,
565 	uint32_t n_tokens,
566 	char *out,
567 	size_t out_size,
568 	void *obj)
569 {
570 	struct pipeline *p;
571 	int status;
572 	uint32_t port_id = 0, t0;
573 
574 	if (n_tokens < 6) {
575 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
576 		return;
577 	}
578 
579 	p = pipeline_find(obj, tokens[1]);
580 	if (!p || p->ctl) {
581 		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
582 		return;
583 	}
584 
585 	if (strcmp(tokens[2], "port") != 0) {
586 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
587 		return;
588 	}
589 
590 	if (strcmp(tokens[3], "in") != 0) {
591 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
592 		return;
593 	}
594 
595 	if (parser_read_uint32(&port_id, tokens[4]) != 0) {
596 		snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
597 		return;
598 	}
599 
600 	t0 = 5;
601 
602 	if (strcmp(tokens[t0], "link") == 0) {
603 		struct rte_swx_port_ethdev_reader_params params;
604 		struct link *link;
605 
606 		if (n_tokens < t0 + 6) {
607 			snprintf(out, out_size, MSG_ARG_MISMATCH,
608 				"pipeline port in link");
609 			return;
610 		}
611 
612 		link = link_find(obj, tokens[t0 + 1]);
613 		if (!link) {
614 			snprintf(out, out_size, MSG_ARG_INVALID,
615 				"link_name");
616 			return;
617 		}
618 		params.dev_name = link->dev_name;
619 
620 		if (strcmp(tokens[t0 + 2], "rxq") != 0) {
621 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq");
622 			return;
623 		}
624 
625 		if (parser_read_uint16(&params.queue_id, tokens[t0 + 3]) != 0) {
626 			snprintf(out, out_size, MSG_ARG_INVALID,
627 				"queue_id");
628 			return;
629 		}
630 
631 		if (strcmp(tokens[t0 + 4], "bsz") != 0) {
632 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
633 			return;
634 		}
635 
636 		if (parser_read_uint32(&params.burst_size, tokens[t0 + 5])) {
637 			snprintf(out, out_size, MSG_ARG_INVALID,
638 				"burst_size");
639 			return;
640 		}
641 
642 		t0 += 6;
643 
644 		status = rte_swx_pipeline_port_in_config(p->p,
645 			port_id,
646 			"ethdev",
647 			&params);
648 	} else if (strcmp(tokens[t0], "ring") == 0) {
649 		struct rte_swx_port_ring_reader_params params;
650 		struct ring *ring;
651 
652 		if (n_tokens < t0 + 4) {
653 			snprintf(out, out_size, MSG_ARG_MISMATCH,
654 				"pipeline port in ring");
655 			return;
656 		}
657 
658 		ring = ring_find(obj, tokens[t0 + 1]);
659 		if (!ring) {
660 			snprintf(out, out_size, MSG_ARG_INVALID,
661 				"ring_name");
662 			return;
663 		}
664 		params.name = ring->name;
665 
666 		if (strcmp(tokens[t0 + 2], "bsz") != 0) {
667 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
668 			return;
669 		}
670 
671 		if (parser_read_uint32(&params.burst_size, tokens[t0 + 3])) {
672 			snprintf(out, out_size, MSG_ARG_INVALID,
673 				"burst_size");
674 			return;
675 		}
676 
677 		t0 += 4;
678 
679 		status = rte_swx_pipeline_port_in_config(p->p,
680 			port_id,
681 			"ring",
682 			&params);
683 	} else if (strcmp(tokens[t0], "source") == 0) {
684 		struct rte_swx_port_source_params params;
685 		struct mempool *mp;
686 
687 		if (n_tokens < t0 + 3) {
688 			snprintf(out, out_size, MSG_ARG_MISMATCH,
689 				"pipeline port in source");
690 			return;
691 		}
692 
693 		mp = mempool_find(obj, tokens[t0 + 1]);
694 		if (!mp) {
695 			snprintf(out, out_size, MSG_ARG_INVALID,
696 				"mempool_name");
697 			return;
698 		}
699 		params.pool = mp->m;
700 
701 		params.file_name = tokens[t0 + 2];
702 
703 		t0 += 3;
704 
705 		status = rte_swx_pipeline_port_in_config(p->p,
706 			port_id,
707 			"source",
708 			&params);
709 	} else if (strcmp(tokens[t0], "tap") == 0) {
710 		struct rte_swx_port_fd_reader_params params;
711 		struct tap *tap;
712 		struct mempool *mp;
713 
714 		if (n_tokens < t0 + 8) {
715 			snprintf(out, out_size, MSG_ARG_MISMATCH,
716 				"pipeline port in tap");
717 			return;
718 		}
719 
720 		tap = tap_find(obj, tokens[t0 + 1]);
721 		if (!tap) {
722 			snprintf(out, out_size, MSG_ARG_INVALID,
723 				"tap_name");
724 			return;
725 		}
726 		params.fd = tap->fd;
727 
728 		if (strcmp(tokens[t0 + 2], "mempool") != 0) {
729 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
730 				"mempool");
731 			return;
732 		}
733 
734 		mp = mempool_find(obj, tokens[t0 + 3]);
735 		if (!mp) {
736 			snprintf(out, out_size, MSG_ARG_INVALID,
737 				"mempool_name");
738 			return;
739 		}
740 		params.mempool = mp->m;
741 
742 		if (strcmp(tokens[t0 + 4], "mtu") != 0) {
743 			snprintf(out, out_size, MSG_ARG_NOT_FOUND,
744 				"mtu");
745 			return;
746 		}
747 
748 		if (parser_read_uint32(&params.mtu, tokens[t0 + 5]) != 0) {
749 			snprintf(out, out_size, MSG_ARG_INVALID, "mtu");
750 			return;
751 		}
752 
753 		if (strcmp(tokens[t0 + 6], "bsz") != 0) {
754 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
755 			return;
756 		}
757 
758 		if (parser_read_uint32(&params.burst_size, tokens[t0 + 7])) {
759 			snprintf(out, out_size, MSG_ARG_INVALID,
760 				"burst_size");
761 			return;
762 		}
763 
764 		t0 += 8;
765 
766 		status = rte_swx_pipeline_port_in_config(p->p,
767 			port_id,
768 			"fd",
769 			&params);
770 
771 	} else {
772 		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
773 		return;
774 	}
775 
776 	if (status) {
777 		snprintf(out, out_size, "port in error.");
778 		return;
779 	}
780 
781 	if (n_tokens != t0) {
782 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
783 		return;
784 	}
785 }
786 
787 static const char cmd_pipeline_port_out_help[] =
788 "pipeline <pipeline_name> port out <port_id>\n"
789 "   link <link_name> txq <txq_id> bsz <burst_size>\n"
790 "   ring <ring_name> bsz <burst_size>\n"
791 "   | sink <file_name> | none\n"
792 "   | tap <tap_name> bsz <burst_size>\n";
793 
794 static void
795 cmd_pipeline_port_out(char **tokens,
796 	uint32_t n_tokens,
797 	char *out,
798 	size_t out_size,
799 	void *obj)
800 {
801 	struct pipeline *p;
802 	int status;
803 	uint32_t port_id = 0, t0;
804 
805 	if (n_tokens < 6) {
806 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
807 		return;
808 	}
809 
810 	p = pipeline_find(obj, tokens[1]);
811 	if (!p || p->ctl) {
812 		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
813 		return;
814 	}
815 
816 	if (strcmp(tokens[2], "port") != 0) {
817 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
818 		return;
819 	}
820 
821 	if (strcmp(tokens[3], "out") != 0) {
822 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "out");
823 		return;
824 	}
825 
826 	if (parser_read_uint32(&port_id, tokens[4]) != 0) {
827 		snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
828 		return;
829 	}
830 
831 	t0 = 5;
832 
833 	if (strcmp(tokens[t0], "link") == 0) {
834 		struct rte_swx_port_ethdev_writer_params params;
835 		struct link *link;
836 
837 		if (n_tokens < t0 + 6) {
838 			snprintf(out, out_size, MSG_ARG_MISMATCH,
839 				"pipeline port out link");
840 			return;
841 		}
842 
843 		link = link_find(obj, tokens[t0 + 1]);
844 		if (!link) {
845 			snprintf(out, out_size, MSG_ARG_INVALID,
846 				"link_name");
847 			return;
848 		}
849 		params.dev_name = link->dev_name;
850 
851 		if (strcmp(tokens[t0 + 2], "txq") != 0) {
852 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "txq");
853 			return;
854 		}
855 
856 		if (parser_read_uint16(&params.queue_id, tokens[t0 + 3]) != 0) {
857 			snprintf(out, out_size, MSG_ARG_INVALID,
858 				"queue_id");
859 			return;
860 		}
861 
862 		if (strcmp(tokens[t0 + 4], "bsz") != 0) {
863 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
864 			return;
865 		}
866 
867 		if (parser_read_uint32(&params.burst_size, tokens[t0 + 5])) {
868 			snprintf(out, out_size, MSG_ARG_INVALID,
869 				"burst_size");
870 			return;
871 		}
872 
873 		t0 += 6;
874 
875 		status = rte_swx_pipeline_port_out_config(p->p,
876 			port_id,
877 			"ethdev",
878 			&params);
879 	} else if (strcmp(tokens[t0], "ring") == 0) {
880 		struct rte_swx_port_ring_writer_params params;
881 		struct ring *ring;
882 
883 		if (n_tokens < t0 + 4) {
884 			snprintf(out, out_size, MSG_ARG_MISMATCH,
885 				"pipeline port out link");
886 			return;
887 		}
888 
889 		ring = ring_find(obj, tokens[t0 + 1]);
890 		if (!ring) {
891 			snprintf(out, out_size, MSG_ARG_INVALID,
892 				"ring_name");
893 			return;
894 		}
895 		params.name = ring->name;
896 
897 		if (strcmp(tokens[t0 + 2], "bsz") != 0) {
898 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
899 			return;
900 		}
901 
902 		if (parser_read_uint32(&params.burst_size, tokens[t0 + 3])) {
903 			snprintf(out, out_size, MSG_ARG_INVALID,
904 				"burst_size");
905 			return;
906 		}
907 
908 		t0 += 4;
909 
910 		status = rte_swx_pipeline_port_out_config(p->p,
911 			port_id,
912 			"ring",
913 			&params);
914 	} else if (strcmp(tokens[t0], "sink") == 0) {
915 		struct rte_swx_port_sink_params params;
916 
917 		params.file_name = strcmp(tokens[t0 + 1], "none") ?
918 			tokens[t0 + 1] : NULL;
919 
920 		t0 += 2;
921 
922 		status = rte_swx_pipeline_port_out_config(p->p,
923 			port_id,
924 			"sink",
925 			&params);
926 	} else if (strcmp(tokens[t0], "tap") == 0) {
927 		struct rte_swx_port_fd_writer_params params;
928 		struct tap *tap;
929 
930 		if (n_tokens < t0 + 4) {
931 			snprintf(out, out_size, MSG_ARG_MISMATCH,
932 				"pipeline port out tap");
933 			return;
934 		}
935 
936 		tap = tap_find(obj, tokens[t0 + 1]);
937 		if (!tap) {
938 			snprintf(out, out_size, MSG_ARG_INVALID,
939 				"tap_name");
940 			return;
941 		}
942 		params.fd = tap->fd;
943 
944 		if (strcmp(tokens[t0 + 2], "bsz") != 0) {
945 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
946 			return;
947 		}
948 
949 		if (parser_read_uint32(&params.burst_size, tokens[t0 + 3])) {
950 			snprintf(out, out_size, MSG_ARG_INVALID,
951 				"burst_size");
952 			return;
953 		}
954 
955 		t0 += 4;
956 
957 		status = rte_swx_pipeline_port_out_config(p->p,
958 			port_id,
959 			"fd",
960 			&params);
961 	} else {
962 		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
963 		return;
964 	}
965 
966 	if (status) {
967 		snprintf(out, out_size, "port out error.");
968 		return;
969 	}
970 
971 	if (n_tokens != t0) {
972 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
973 		return;
974 	}
975 }
976 
977 static const char cmd_pipeline_build_help[] =
978 "pipeline <pipeline_name> build <spec_file>\n";
979 
980 static void
981 cmd_pipeline_build(char **tokens,
982 	uint32_t n_tokens,
983 	char *out,
984 	size_t out_size,
985 	void *obj)
986 {
987 	struct pipeline *p = NULL;
988 	FILE *spec = NULL;
989 	uint32_t err_line;
990 	const char *err_msg;
991 	int status;
992 
993 	if (n_tokens != 4) {
994 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
995 		return;
996 	}
997 
998 	p = pipeline_find(obj, tokens[1]);
999 	if (!p || p->ctl) {
1000 		snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]);
1001 		return;
1002 	}
1003 
1004 	spec = fopen(tokens[3], "r");
1005 	if (!spec) {
1006 		snprintf(out, out_size, "Cannot open file %s.\n", tokens[3]);
1007 		return;
1008 	}
1009 
1010 	status = rte_swx_pipeline_build_from_spec(p->p,
1011 		spec,
1012 		&err_line,
1013 		&err_msg);
1014 	fclose(spec);
1015 	if (status) {
1016 		snprintf(out, out_size, "Error %d at line %u: %s\n.",
1017 			status, err_line, err_msg);
1018 		return;
1019 	}
1020 
1021 	p->ctl = rte_swx_ctl_pipeline_create(p->p);
1022 	if (!p->ctl) {
1023 		snprintf(out, out_size, "Pipeline control create failed.");
1024 		rte_swx_pipeline_free(p->p);
1025 		return;
1026 	}
1027 }
1028 
1029 static void
1030 table_entry_free(struct rte_swx_table_entry *entry)
1031 {
1032 	if (!entry)
1033 		return;
1034 
1035 	free(entry->key);
1036 	free(entry->key_mask);
1037 	free(entry->action_data);
1038 	free(entry);
1039 }
1040 
1041 static const char cmd_pipeline_table_update_help[] =
1042 "pipeline <pipeline_name> table <table_name> update <file_name_add> "
1043 "<file_name_delete> <file_name_default>";
1044 
1045 static void
1046 cmd_pipeline_table_update(char **tokens,
1047 	uint32_t n_tokens,
1048 	char *out,
1049 	size_t out_size,
1050 	void *obj)
1051 {
1052 	struct pipeline *p;
1053 	char *pipeline_name, *table_name, *line = NULL;
1054 	char *file_name_add, *file_name_delete, *file_name_default;
1055 	FILE *file_add = NULL, *file_delete = NULL, *file_default = NULL;
1056 	uint32_t line_id;
1057 	int status;
1058 
1059 	if (n_tokens != 8) {
1060 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1061 		return;
1062 	}
1063 
1064 	pipeline_name = tokens[1];
1065 	p = pipeline_find(obj, pipeline_name);
1066 	if (!p || !p->ctl) {
1067 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1068 		return;
1069 	}
1070 
1071 	if (strcmp(tokens[2], "table") != 0) {
1072 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table");
1073 		return;
1074 	}
1075 
1076 	table_name = tokens[3];
1077 
1078 	if (strcmp(tokens[4], "update") != 0) {
1079 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "update");
1080 		return;
1081 	}
1082 
1083 	file_name_add = tokens[5];
1084 	file_name_delete = tokens[6];
1085 	file_name_default = tokens[7];
1086 
1087 	/* File open. */
1088 	if (strcmp(file_name_add, "none")) {
1089 		file_add = fopen(file_name_add, "r");
1090 		if (!file_add) {
1091 			snprintf(out, out_size, "Cannot open file %s",
1092 				file_name_add);
1093 			goto error;
1094 		}
1095 	}
1096 
1097 	if (strcmp(file_name_delete, "none")) {
1098 		file_delete = fopen(file_name_delete, "r");
1099 		if (!file_delete) {
1100 			snprintf(out, out_size, "Cannot open file %s",
1101 				file_name_delete);
1102 			goto error;
1103 		}
1104 	}
1105 
1106 	if (strcmp(file_name_default, "none")) {
1107 		file_default = fopen(file_name_default, "r");
1108 		if (!file_default) {
1109 			snprintf(out, out_size, "Cannot open file %s",
1110 				file_name_default);
1111 			goto error;
1112 		}
1113 	}
1114 
1115 	if (!file_add && !file_delete && !file_default) {
1116 		snprintf(out, out_size, "Nothing to be done.");
1117 		return;
1118 	}
1119 
1120 	/* Buffer allocation. */
1121 	line = malloc(2048);
1122 	if (!line) {
1123 		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
1124 		goto error;
1125 	}
1126 
1127 	/* Add. */
1128 	if (file_add)
1129 		for (line_id = 1; ; line_id++) {
1130 			struct rte_swx_table_entry *entry;
1131 			int is_blank_or_comment;
1132 
1133 			if (fgets(line, 2048, file_add) == NULL)
1134 				break;
1135 
1136 			entry = rte_swx_ctl_pipeline_table_entry_read(p->ctl,
1137 				table_name,
1138 				line,
1139 				&is_blank_or_comment);
1140 			if (!entry) {
1141 				if (is_blank_or_comment)
1142 					continue;
1143 
1144 				snprintf(out, out_size, MSG_FILE_ERR,
1145 					file_name_add, line_id);
1146 				goto error;
1147 			}
1148 
1149 			status = rte_swx_ctl_pipeline_table_entry_add(p->ctl,
1150 				table_name,
1151 				entry);
1152 			table_entry_free(entry);
1153 			if (status) {
1154 				snprintf(out, out_size,
1155 					"Invalid entry in file %s at line %u",
1156 					file_name_add, line_id);
1157 				goto error;
1158 			}
1159 		}
1160 
1161 
1162 	/* Delete. */
1163 	if (file_delete)
1164 		for (line_id = 1; ; line_id++) {
1165 			struct rte_swx_table_entry *entry;
1166 			int is_blank_or_comment;
1167 
1168 			if (fgets(line, 2048, file_delete) == NULL)
1169 				break;
1170 
1171 			entry = rte_swx_ctl_pipeline_table_entry_read(p->ctl,
1172 				table_name,
1173 				line,
1174 				&is_blank_or_comment);
1175 			if (!entry) {
1176 				if (is_blank_or_comment)
1177 					continue;
1178 
1179 				snprintf(out, out_size, MSG_FILE_ERR,
1180 					file_name_delete, line_id);
1181 				goto error;
1182 			}
1183 
1184 			status = rte_swx_ctl_pipeline_table_entry_delete(p->ctl,
1185 				table_name,
1186 				entry);
1187 			table_entry_free(entry);
1188 			if (status)  {
1189 				snprintf(out, out_size,
1190 					"Invalid entry in file %s at line %u",
1191 					file_name_delete, line_id);
1192 				goto error;
1193 			}
1194 		}
1195 
1196 	/* Default. */
1197 	if (file_default)
1198 		for (line_id = 1; ; line_id++) {
1199 			struct rte_swx_table_entry *entry;
1200 			int is_blank_or_comment;
1201 
1202 			if (fgets(line, 2048, file_default) == NULL)
1203 				break;
1204 
1205 			entry = rte_swx_ctl_pipeline_table_entry_read(p->ctl,
1206 				table_name,
1207 				line,
1208 				&is_blank_or_comment);
1209 			if (!entry) {
1210 				if (is_blank_or_comment)
1211 					continue;
1212 
1213 				snprintf(out, out_size, MSG_FILE_ERR,
1214 					file_name_default, line_id);
1215 				goto error;
1216 			}
1217 
1218 			status = rte_swx_ctl_pipeline_table_default_entry_add(p->ctl,
1219 				table_name,
1220 				entry);
1221 			table_entry_free(entry);
1222 			if (status) {
1223 				snprintf(out, out_size,
1224 					"Invalid entry in file %s at line %u",
1225 					file_name_default, line_id);
1226 				goto error;
1227 			}
1228 		}
1229 
1230 	status = rte_swx_ctl_pipeline_commit(p->ctl, 1);
1231 	if (status) {
1232 		snprintf(out, out_size, "Commit failed.");
1233 		goto error;
1234 	}
1235 
1236 
1237 	rte_swx_ctl_pipeline_table_fprintf(stdout, p->ctl, table_name);
1238 
1239 	free(line);
1240 	if (file_add)
1241 		fclose(file_add);
1242 	if (file_delete)
1243 		fclose(file_delete);
1244 	if (file_default)
1245 		fclose(file_default);
1246 	return;
1247 
1248 error:
1249 	rte_swx_ctl_pipeline_abort(p->ctl);
1250 	free(line);
1251 	if (file_add)
1252 		fclose(file_add);
1253 	if (file_delete)
1254 		fclose(file_delete);
1255 	if (file_default)
1256 		fclose(file_default);
1257 }
1258 
1259 static const char cmd_pipeline_regrd_help[] =
1260 "pipeline <pipeline_name> regrd <register_array_name> <index>\n";
1261 
1262 static void
1263 cmd_pipeline_regrd(char **tokens,
1264 	uint32_t n_tokens,
1265 	char *out,
1266 	size_t out_size,
1267 	void *obj)
1268 {
1269 	struct pipeline *p;
1270 	const char *name;
1271 	uint64_t value;
1272 	uint32_t idx;
1273 	int status;
1274 
1275 	if (n_tokens != 5) {
1276 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1277 		return;
1278 	}
1279 
1280 	p = pipeline_find(obj, tokens[1]);
1281 	if (!p || !p->ctl) {
1282 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1283 		return;
1284 	}
1285 
1286 	if (strcmp(tokens[2], "regrd")) {
1287 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "regrd");
1288 		return;
1289 	}
1290 
1291 	name = tokens[3];
1292 
1293 	if (parser_read_uint32(&idx, tokens[4])) {
1294 		snprintf(out, out_size, MSG_ARG_INVALID, "index");
1295 		return;
1296 	}
1297 
1298 	status = rte_swx_ctl_pipeline_regarray_read(p->p, name, idx, &value);
1299 	if (status) {
1300 		snprintf(out, out_size, "Command failed.\n");
1301 		return;
1302 	}
1303 
1304 	snprintf(out, out_size, "0x%" PRIx64 "\n", value);
1305 }
1306 
1307 static const char cmd_pipeline_regwr_help[] =
1308 "pipeline <pipeline_name> regwr <register_array_name> <index> <value>\n";
1309 
1310 static void
1311 cmd_pipeline_regwr(char **tokens,
1312 	uint32_t n_tokens,
1313 	char *out,
1314 	size_t out_size,
1315 	void *obj)
1316 {
1317 	struct pipeline *p;
1318 	const char *name;
1319 	uint64_t value;
1320 	uint32_t idx;
1321 	int status;
1322 
1323 	if (n_tokens != 6) {
1324 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1325 		return;
1326 	}
1327 
1328 	p = pipeline_find(obj, tokens[1]);
1329 	if (!p || !p->ctl) {
1330 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1331 		return;
1332 	}
1333 
1334 	if (strcmp(tokens[2], "regwr")) {
1335 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "regwr");
1336 		return;
1337 	}
1338 
1339 	name = tokens[3];
1340 
1341 	if (parser_read_uint32(&idx, tokens[4])) {
1342 		snprintf(out, out_size, MSG_ARG_INVALID, "index");
1343 		return;
1344 	}
1345 
1346 	if (parser_read_uint64(&value, tokens[5])) {
1347 		snprintf(out, out_size, MSG_ARG_INVALID, "value");
1348 		return;
1349 	}
1350 
1351 	status = rte_swx_ctl_pipeline_regarray_write(p->p, name, idx, value);
1352 	if (status) {
1353 		snprintf(out, out_size, "Command failed.\n");
1354 		return;
1355 	}
1356 }
1357 
1358 static const char cmd_pipeline_meter_profile_add_help[] =
1359 "pipeline <pipeline_name> meter profile <profile_name> add "
1360 	"cir <cir> pir <pir> cbs <cbs> pbs <pbs>\n";
1361 
1362 static void
1363 cmd_pipeline_meter_profile_add(char **tokens,
1364 	uint32_t n_tokens,
1365 	char *out,
1366 	size_t out_size,
1367 	void *obj)
1368 {
1369 	struct rte_meter_trtcm_params params;
1370 	struct pipeline *p;
1371 	const char *profile_name;
1372 	int status;
1373 
1374 	if (n_tokens != 14) {
1375 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1376 		return;
1377 	}
1378 
1379 	p = pipeline_find(obj, tokens[1]);
1380 	if (!p || !p->ctl) {
1381 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1382 		return;
1383 	}
1384 
1385 	if (strcmp(tokens[2], "meter")) {
1386 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
1387 		return;
1388 	}
1389 
1390 	if (strcmp(tokens[3], "profile")) {
1391 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
1392 		return;
1393 	}
1394 
1395 	profile_name = tokens[4];
1396 
1397 	if (strcmp(tokens[5], "add")) {
1398 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
1399 		return;
1400 	}
1401 
1402 	if (strcmp(tokens[6], "cir")) {
1403 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cir");
1404 		return;
1405 	}
1406 
1407 	if (parser_read_uint64(&params.cir, tokens[7])) {
1408 		snprintf(out, out_size, MSG_ARG_INVALID, "cir");
1409 		return;
1410 	}
1411 
1412 	if (strcmp(tokens[8], "pir")) {
1413 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pir");
1414 		return;
1415 	}
1416 
1417 	if (parser_read_uint64(&params.pir, tokens[9])) {
1418 		snprintf(out, out_size, MSG_ARG_INVALID, "pir");
1419 		return;
1420 	}
1421 
1422 	if (strcmp(tokens[10], "cbs")) {
1423 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cbs");
1424 		return;
1425 	}
1426 
1427 	if (parser_read_uint64(&params.cbs, tokens[11])) {
1428 		snprintf(out, out_size, MSG_ARG_INVALID, "cbs");
1429 		return;
1430 	}
1431 
1432 	if (strcmp(tokens[12], "pbs")) {
1433 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pbs");
1434 		return;
1435 	}
1436 
1437 	if (parser_read_uint64(&params.pbs, tokens[13])) {
1438 		snprintf(out, out_size, MSG_ARG_INVALID, "pbs");
1439 		return;
1440 	}
1441 
1442 	status = rte_swx_ctl_meter_profile_add(p->p, profile_name, &params);
1443 	if (status) {
1444 		snprintf(out, out_size, "Command failed.\n");
1445 		return;
1446 	}
1447 }
1448 
1449 static const char cmd_pipeline_meter_profile_delete_help[] =
1450 "pipeline <pipeline_name> meter profile <profile_name> delete\n";
1451 
1452 static void
1453 cmd_pipeline_meter_profile_delete(char **tokens,
1454 	uint32_t n_tokens,
1455 	char *out,
1456 	size_t out_size,
1457 	void *obj)
1458 {
1459 	struct pipeline *p;
1460 	const char *profile_name;
1461 	int status;
1462 
1463 	if (n_tokens != 6) {
1464 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1465 		return;
1466 	}
1467 
1468 	p = pipeline_find(obj, tokens[1]);
1469 	if (!p || !p->ctl) {
1470 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1471 		return;
1472 	}
1473 
1474 	if (strcmp(tokens[2], "meter")) {
1475 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
1476 		return;
1477 	}
1478 
1479 	if (strcmp(tokens[3], "profile")) {
1480 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
1481 		return;
1482 	}
1483 
1484 	profile_name = tokens[4];
1485 
1486 	if (strcmp(tokens[5], "delete")) {
1487 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
1488 		return;
1489 	}
1490 
1491 	status = rte_swx_ctl_meter_profile_delete(p->p, profile_name);
1492 	if (status) {
1493 		snprintf(out, out_size, "Command failed.\n");
1494 		return;
1495 	}
1496 }
1497 
1498 static const char cmd_pipeline_meter_reset_help[] =
1499 "pipeline <pipeline_name> meter <meter_array_name> from <index0> to <index1> "
1500 	"reset\n";
1501 
1502 static void
1503 cmd_pipeline_meter_reset(char **tokens,
1504 	uint32_t n_tokens,
1505 	char *out,
1506 	size_t out_size,
1507 	void *obj)
1508 {
1509 	struct pipeline *p;
1510 	const char *name;
1511 	uint32_t idx0, idx1;
1512 
1513 	if (n_tokens != 9) {
1514 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1515 		return;
1516 	}
1517 
1518 	p = pipeline_find(obj, tokens[1]);
1519 	if (!p || !p->ctl) {
1520 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1521 		return;
1522 	}
1523 
1524 	if (strcmp(tokens[2], "meter")) {
1525 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
1526 		return;
1527 	}
1528 
1529 	name = tokens[3];
1530 
1531 	if (strcmp(tokens[4], "from")) {
1532 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "from");
1533 		return;
1534 	}
1535 
1536 	if (parser_read_uint32(&idx0, tokens[5])) {
1537 		snprintf(out, out_size, MSG_ARG_INVALID, "index0");
1538 		return;
1539 	}
1540 
1541 	if (strcmp(tokens[6], "to")) {
1542 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "to");
1543 		return;
1544 	}
1545 
1546 	if (parser_read_uint32(&idx1, tokens[7]) || (idx1 < idx0)) {
1547 		snprintf(out, out_size, MSG_ARG_INVALID, "index1");
1548 		return;
1549 	}
1550 
1551 	if (strcmp(tokens[8], "reset")) {
1552 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "reset");
1553 		return;
1554 	}
1555 
1556 	for ( ; idx0 <= idx1; idx0++) {
1557 		int status;
1558 
1559 		status = rte_swx_ctl_meter_reset(p->p, name, idx0);
1560 		if (status) {
1561 			snprintf(out, out_size, "Command failed for index %u.\n", idx0);
1562 			return;
1563 		}
1564 	}
1565 }
1566 
1567 static const char cmd_pipeline_meter_set_help[] =
1568 "pipeline <pipeline_name> meter <meter_array_name> from <index0> to <index1> "
1569 	"set profile <profile_name>\n";
1570 
1571 static void
1572 cmd_pipeline_meter_set(char **tokens,
1573 	uint32_t n_tokens,
1574 	char *out,
1575 	size_t out_size,
1576 	void *obj)
1577 {
1578 	struct pipeline *p;
1579 	const char *name, *profile_name;
1580 	uint32_t idx0, idx1;
1581 
1582 	if (n_tokens != 11) {
1583 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1584 		return;
1585 	}
1586 
1587 	p = pipeline_find(obj, tokens[1]);
1588 	if (!p || !p->ctl) {
1589 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1590 		return;
1591 	}
1592 
1593 	if (strcmp(tokens[2], "meter")) {
1594 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
1595 		return;
1596 	}
1597 
1598 	name = tokens[3];
1599 
1600 	if (strcmp(tokens[4], "from")) {
1601 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "from");
1602 		return;
1603 	}
1604 
1605 	if (parser_read_uint32(&idx0, tokens[5])) {
1606 		snprintf(out, out_size, MSG_ARG_INVALID, "index0");
1607 		return;
1608 	}
1609 
1610 	if (strcmp(tokens[6], "to")) {
1611 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "to");
1612 		return;
1613 	}
1614 
1615 	if (parser_read_uint32(&idx1, tokens[7]) || (idx1 < idx0)) {
1616 		snprintf(out, out_size, MSG_ARG_INVALID, "index1");
1617 		return;
1618 	}
1619 
1620 	if (strcmp(tokens[8], "set")) {
1621 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "set");
1622 		return;
1623 	}
1624 
1625 	if (strcmp(tokens[9], "profile")) {
1626 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
1627 		return;
1628 	}
1629 
1630 	profile_name = tokens[10];
1631 
1632 	for ( ; idx0 <= idx1; idx0++) {
1633 		int status;
1634 
1635 		status = rte_swx_ctl_meter_set(p->p, name, idx0, profile_name);
1636 		if (status) {
1637 			snprintf(out, out_size, "Command failed for index %u.\n", idx0);
1638 			return;
1639 		}
1640 	}
1641 }
1642 
1643 static const char cmd_pipeline_meter_stats_help[] =
1644 "pipeline <pipeline_name> meter <meter_array_name> from <index0> to <index1> "
1645 	"stats\n";
1646 
1647 static void
1648 cmd_pipeline_meter_stats(char **tokens,
1649 	uint32_t n_tokens,
1650 	char *out,
1651 	size_t out_size,
1652 	void *obj)
1653 {
1654 	struct rte_swx_ctl_meter_stats stats;
1655 	struct pipeline *p;
1656 	const char *name;
1657 	uint32_t idx0, idx1;
1658 
1659 	if (n_tokens != 9) {
1660 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1661 		return;
1662 	}
1663 
1664 	p = pipeline_find(obj, tokens[1]);
1665 	if (!p || !p->ctl) {
1666 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1667 		return;
1668 	}
1669 
1670 	if (strcmp(tokens[2], "meter")) {
1671 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
1672 		return;
1673 	}
1674 
1675 	name = tokens[3];
1676 
1677 	if (strcmp(tokens[4], "from")) {
1678 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "from");
1679 		return;
1680 	}
1681 
1682 	if (parser_read_uint32(&idx0, tokens[5])) {
1683 		snprintf(out, out_size, MSG_ARG_INVALID, "index0");
1684 		return;
1685 	}
1686 
1687 	if (strcmp(tokens[6], "to")) {
1688 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "to");
1689 		return;
1690 	}
1691 
1692 	if (parser_read_uint32(&idx1, tokens[7]) || (idx1 < idx0)) {
1693 		snprintf(out, out_size, MSG_ARG_INVALID, "index1");
1694 		return;
1695 	}
1696 
1697 	if (strcmp(tokens[8], "stats")) {
1698 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
1699 		return;
1700 	}
1701 
1702 	/* Table header. */
1703 	snprintf(out, out_size, "+-%7s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+\n",
1704 		 "-------",
1705 		 "----------------", "----------------", "----------------",
1706 		 "----------------", "----------------", "----------------");
1707 	out_size -= strlen(out);
1708 	out += strlen(out);
1709 
1710 	snprintf(out, out_size, "| %4s | %16s | %16s | %16s | %16s | %16s | %16s |\n",
1711 		 "METER #",
1712 		 "GREEN (packets)", "YELLOW (packets)", "RED (packets)",
1713 		 "GREEN (bytes)", "YELLOW (bytes)", "RED (bytes)");
1714 	out_size -= strlen(out);
1715 	out += strlen(out);
1716 
1717 	snprintf(out, out_size, "+-%7s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+\n",
1718 		 "-------",
1719 		 "----------------", "----------------", "----------------",
1720 		 "----------------", "----------------", "----------------");
1721 	out_size -= strlen(out);
1722 	out += strlen(out);
1723 
1724 	/* Table rows. */
1725 	for ( ; idx0 <= idx1; idx0++) {
1726 		int status;
1727 
1728 		status = rte_swx_ctl_meter_stats_read(p->p, name, idx0, &stats);
1729 		if (status) {
1730 			snprintf(out, out_size, "Pipeline meter stats error at index %u.\n", idx0);
1731 			out_size -= strlen(out);
1732 			out += strlen(out);
1733 			return;
1734 		}
1735 
1736 		snprintf(out, out_size, "| %7d | %16" PRIx64 " | %16" PRIx64 " | %16" PRIx64
1737 			 " | %16" PRIx64 " | %16" PRIx64 " | %16" PRIx64 " |\n",
1738 			 idx0,
1739 			 stats.n_pkts[RTE_COLOR_GREEN],
1740 			 stats.n_pkts[RTE_COLOR_YELLOW],
1741 			 stats.n_pkts[RTE_COLOR_RED],
1742 			 stats.n_bytes[RTE_COLOR_GREEN],
1743 			 stats.n_bytes[RTE_COLOR_YELLOW],
1744 			 stats.n_bytes[RTE_COLOR_RED]);
1745 		out_size -= strlen(out);
1746 		out += strlen(out);
1747 	}
1748 }
1749 
1750 static const char cmd_pipeline_stats_help[] =
1751 "pipeline <pipeline_name> stats\n";
1752 
1753 static void
1754 cmd_pipeline_stats(char **tokens,
1755 	uint32_t n_tokens,
1756 	char *out,
1757 	size_t out_size,
1758 	void *obj)
1759 {
1760 	struct rte_swx_ctl_pipeline_info info;
1761 	struct pipeline *p;
1762 	uint32_t i;
1763 	int status;
1764 
1765 	if (n_tokens != 3) {
1766 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1767 		return;
1768 	}
1769 
1770 	p = pipeline_find(obj, tokens[1]);
1771 	if (!p || !p->ctl) {
1772 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1773 		return;
1774 	}
1775 
1776 	if (strcmp(tokens[2], "stats")) {
1777 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
1778 		return;
1779 	}
1780 
1781 	status = rte_swx_ctl_pipeline_info_get(p->p, &info);
1782 	if (status) {
1783 		snprintf(out, out_size, "Pipeline info get error.");
1784 		return;
1785 	}
1786 
1787 	snprintf(out, out_size, "Input ports:\n");
1788 	out_size -= strlen(out);
1789 	out += strlen(out);
1790 
1791 	for (i = 0; i < info.n_ports_in; i++) {
1792 		struct rte_swx_port_in_stats stats;
1793 
1794 		rte_swx_ctl_pipeline_port_in_stats_read(p->p, i, &stats);
1795 
1796 		snprintf(out, out_size, "\tPort %u:"
1797 			" packets %" PRIu64
1798 			" bytes %" PRIu64
1799 			" empty %" PRIu64 "\n",
1800 			i, stats.n_pkts, stats.n_bytes, stats.n_empty);
1801 		out_size -= strlen(out);
1802 		out += strlen(out);
1803 	}
1804 
1805 	snprintf(out, out_size, "Output ports:\n");
1806 	out_size -= strlen(out);
1807 	out += strlen(out);
1808 
1809 	for (i = 0; i < info.n_ports_out; i++) {
1810 		struct rte_swx_port_out_stats stats;
1811 
1812 		rte_swx_ctl_pipeline_port_out_stats_read(p->p, i, &stats);
1813 
1814 		snprintf(out, out_size, "\tPort %u:"
1815 			" packets %" PRIu64
1816 			" bytes %" PRIu64 "\n",
1817 			i, stats.n_pkts, stats.n_bytes);
1818 		out_size -= strlen(out);
1819 		out += strlen(out);
1820 	}
1821 }
1822 
1823 static const char cmd_thread_pipeline_enable_help[] =
1824 "thread <thread_id> pipeline <pipeline_name> enable\n";
1825 
1826 static void
1827 cmd_thread_pipeline_enable(char **tokens,
1828 	uint32_t n_tokens,
1829 	char *out,
1830 	size_t out_size,
1831 	void *obj)
1832 {
1833 	char *pipeline_name;
1834 	struct pipeline *p;
1835 	uint32_t thread_id;
1836 	int status;
1837 
1838 	if (n_tokens != 5) {
1839 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1840 		return;
1841 	}
1842 
1843 	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
1844 		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
1845 		return;
1846 	}
1847 
1848 	if (strcmp(tokens[2], "pipeline") != 0) {
1849 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
1850 		return;
1851 	}
1852 
1853 	pipeline_name = tokens[3];
1854 	p = pipeline_find(obj, pipeline_name);
1855 	if (!p || !p->ctl) {
1856 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1857 		return;
1858 	}
1859 
1860 	if (strcmp(tokens[4], "enable") != 0) {
1861 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
1862 		return;
1863 	}
1864 
1865 	status = thread_pipeline_enable(thread_id, obj, pipeline_name);
1866 	if (status) {
1867 		snprintf(out, out_size, MSG_CMD_FAIL, "thread pipeline enable");
1868 		return;
1869 	}
1870 }
1871 
1872 static const char cmd_thread_pipeline_disable_help[] =
1873 "thread <thread_id> pipeline <pipeline_name> disable\n";
1874 
1875 static void
1876 cmd_thread_pipeline_disable(char **tokens,
1877 	uint32_t n_tokens,
1878 	char *out,
1879 	size_t out_size,
1880 	void *obj)
1881 {
1882 	struct pipeline *p;
1883 	char *pipeline_name;
1884 	uint32_t thread_id;
1885 	int status;
1886 
1887 	if (n_tokens != 5) {
1888 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1889 		return;
1890 	}
1891 
1892 	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
1893 		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
1894 		return;
1895 	}
1896 
1897 	if (strcmp(tokens[2], "pipeline") != 0) {
1898 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
1899 		return;
1900 	}
1901 
1902 	pipeline_name = tokens[3];
1903 	p = pipeline_find(obj, pipeline_name);
1904 	if (!p || !p->ctl) {
1905 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1906 		return;
1907 	}
1908 
1909 	if (strcmp(tokens[4], "disable") != 0) {
1910 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
1911 		return;
1912 	}
1913 
1914 	status = thread_pipeline_disable(thread_id, obj, pipeline_name);
1915 	if (status) {
1916 		snprintf(out, out_size, MSG_CMD_FAIL,
1917 			"thread pipeline disable");
1918 		return;
1919 	}
1920 }
1921 
1922 static void
1923 cmd_help(char **tokens,
1924 	 uint32_t n_tokens,
1925 	 char *out,
1926 	 size_t out_size,
1927 	 void *arg __rte_unused)
1928 {
1929 	tokens++;
1930 	n_tokens--;
1931 
1932 	if (n_tokens == 0) {
1933 		snprintf(out, out_size,
1934 			"Type 'help <command>' for command details.\n\n"
1935 			"List of commands:\n"
1936 			"\tmempool\n"
1937 			"\tlink\n"
1938 			"\ttap\n"
1939 			"\tpipeline create\n"
1940 			"\tpipeline port in\n"
1941 			"\tpipeline port out\n"
1942 			"\tpipeline build\n"
1943 			"\tpipeline table update\n"
1944 			"\tpipeline regrd\n"
1945 			"\tpipeline regwr\n"
1946 			"\tpipeline meter profile add\n"
1947 			"\tpipeline meter profile delete\n"
1948 			"\tpipeline meter reset\n"
1949 			"\tpipeline meter set\n"
1950 			"\tpipeline meter stats\n"
1951 			"\tpipeline stats\n"
1952 			"\tthread pipeline enable\n"
1953 			"\tthread pipeline disable\n\n");
1954 		return;
1955 	}
1956 
1957 	if (strcmp(tokens[0], "mempool") == 0) {
1958 		snprintf(out, out_size, "\n%s\n", cmd_mempool_help);
1959 		return;
1960 	}
1961 
1962 	if (strcmp(tokens[0], "link") == 0) {
1963 		snprintf(out, out_size, "\n%s\n", cmd_link_help);
1964 		return;
1965 	}
1966 
1967 	if (strcmp(tokens[0], "ring") == 0) {
1968 		snprintf(out, out_size, "\n%s\n", cmd_ring_help);
1969 		return;
1970 	}
1971 
1972 	if (strcmp(tokens[0], "tap") == 0) {
1973 		snprintf(out, out_size, "\n%s\n", cmd_tap_help);
1974 		return;
1975 	}
1976 
1977 	if ((strcmp(tokens[0], "pipeline") == 0) &&
1978 		(n_tokens == 2) && (strcmp(tokens[1], "create") == 0)) {
1979 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_create_help);
1980 		return;
1981 	}
1982 
1983 	if ((strcmp(tokens[0], "pipeline") == 0) &&
1984 		(n_tokens == 3) && (strcmp(tokens[1], "port") == 0)) {
1985 		if (strcmp(tokens[2], "in") == 0) {
1986 			snprintf(out, out_size, "\n%s\n",
1987 				cmd_pipeline_port_in_help);
1988 			return;
1989 		}
1990 
1991 		if (strcmp(tokens[2], "out") == 0) {
1992 			snprintf(out, out_size, "\n%s\n",
1993 				cmd_pipeline_port_out_help);
1994 			return;
1995 		}
1996 	}
1997 
1998 	if ((strcmp(tokens[0], "pipeline") == 0) &&
1999 		(n_tokens == 2) && (strcmp(tokens[1], "build") == 0)) {
2000 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_build_help);
2001 		return;
2002 	}
2003 
2004 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2005 		(n_tokens == 3) &&
2006 		(strcmp(tokens[1], "table") == 0) &&
2007 		(strcmp(tokens[2], "update") == 0)) {
2008 		snprintf(out, out_size, "\n%s\n",
2009 			cmd_pipeline_table_update_help);
2010 		return;
2011 	}
2012 
2013 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2014 		(n_tokens == 2) && (strcmp(tokens[1], "regrd") == 0)) {
2015 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_regrd_help);
2016 		return;
2017 	}
2018 
2019 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2020 		(n_tokens == 2) && (strcmp(tokens[1], "regwr") == 0)) {
2021 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_regwr_help);
2022 		return;
2023 	}
2024 
2025 	if (!strcmp(tokens[0], "pipeline") &&
2026 		(n_tokens == 4) && !strcmp(tokens[1], "meter")
2027 		&& !strcmp(tokens[2], "profile")
2028 		&& !strcmp(tokens[3], "add")) {
2029 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_profile_add_help);
2030 		return;
2031 	}
2032 
2033 	if (!strcmp(tokens[0], "pipeline") &&
2034 		(n_tokens == 4) && !strcmp(tokens[1], "meter")
2035 		&& !strcmp(tokens[2], "profile")
2036 		&& !strcmp(tokens[3], "delete")) {
2037 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_profile_delete_help);
2038 		return;
2039 	}
2040 
2041 	if (!strcmp(tokens[0], "pipeline") &&
2042 		(n_tokens == 3) && !strcmp(tokens[1], "meter")
2043 		&& !strcmp(tokens[2], "reset")) {
2044 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_reset_help);
2045 		return;
2046 	}
2047 
2048 	if (!strcmp(tokens[0], "pipeline") &&
2049 		(n_tokens == 3) && !strcmp(tokens[1], "meter")
2050 		&& !strcmp(tokens[2], "set")) {
2051 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_set_help);
2052 		return;
2053 	}
2054 
2055 	if (!strcmp(tokens[0], "pipeline") &&
2056 		(n_tokens == 3) && !strcmp(tokens[1], "meter")
2057 		&& !strcmp(tokens[2], "stats")) {
2058 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_stats_help);
2059 		return;
2060 	}
2061 
2062 	if ((strcmp(tokens[0], "pipeline") == 0) &&
2063 		(n_tokens == 2) && (strcmp(tokens[1], "stats") == 0)) {
2064 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_stats_help);
2065 		return;
2066 	}
2067 
2068 	if ((n_tokens == 3) &&
2069 		(strcmp(tokens[0], "thread") == 0) &&
2070 		(strcmp(tokens[1], "pipeline") == 0)) {
2071 		if (strcmp(tokens[2], "enable") == 0) {
2072 			snprintf(out, out_size, "\n%s\n",
2073 				cmd_thread_pipeline_enable_help);
2074 			return;
2075 		}
2076 
2077 		if (strcmp(tokens[2], "disable") == 0) {
2078 			snprintf(out, out_size, "\n%s\n",
2079 				cmd_thread_pipeline_disable_help);
2080 			return;
2081 		}
2082 	}
2083 
2084 	snprintf(out, out_size, "Invalid command\n");
2085 }
2086 
2087 void
2088 cli_process(char *in, char *out, size_t out_size, void *obj)
2089 {
2090 	char *tokens[CMD_MAX_TOKENS];
2091 	uint32_t n_tokens = RTE_DIM(tokens);
2092 	int status;
2093 
2094 	if (is_comment(in))
2095 		return;
2096 
2097 	status = parse_tokenize_string(in, tokens, &n_tokens);
2098 	if (status) {
2099 		snprintf(out, out_size, MSG_ARG_TOO_MANY, "");
2100 		return;
2101 	}
2102 
2103 	if (n_tokens == 0)
2104 		return;
2105 
2106 	if (strcmp(tokens[0], "help") == 0) {
2107 		cmd_help(tokens, n_tokens, out, out_size, obj);
2108 		return;
2109 	}
2110 
2111 	if (strcmp(tokens[0], "mempool") == 0) {
2112 		cmd_mempool(tokens, n_tokens, out, out_size, obj);
2113 		return;
2114 	}
2115 
2116 	if (strcmp(tokens[0], "link") == 0) {
2117 		if ((n_tokens >= 2) && (strcmp(tokens[1], "show") == 0)) {
2118 			cmd_link_show(tokens, n_tokens, out, out_size, obj);
2119 			return;
2120 		}
2121 
2122 		cmd_link(tokens, n_tokens, out, out_size, obj);
2123 		return;
2124 	}
2125 
2126 	if (strcmp(tokens[0], "ring") == 0) {
2127 		cmd_ring(tokens, n_tokens, out, out_size, obj);
2128 		return;
2129 	}
2130 
2131 	if (strcmp(tokens[0], "tap") == 0) {
2132 		cmd_tap(tokens, n_tokens, out, out_size, obj);
2133 		return;
2134 	}
2135 
2136 	if (strcmp(tokens[0], "pipeline") == 0) {
2137 		if ((n_tokens >= 3) &&
2138 			(strcmp(tokens[2], "create") == 0)) {
2139 			cmd_pipeline_create(tokens, n_tokens, out, out_size,
2140 				obj);
2141 			return;
2142 		}
2143 
2144 		if ((n_tokens >= 4) &&
2145 			(strcmp(tokens[2], "port") == 0) &&
2146 			(strcmp(tokens[3], "in") == 0)) {
2147 			cmd_pipeline_port_in(tokens, n_tokens, out, out_size,
2148 				obj);
2149 			return;
2150 		}
2151 
2152 		if ((n_tokens >= 4) &&
2153 			(strcmp(tokens[2], "port") == 0) &&
2154 			(strcmp(tokens[3], "out") == 0)) {
2155 			cmd_pipeline_port_out(tokens, n_tokens, out, out_size,
2156 				obj);
2157 			return;
2158 		}
2159 
2160 		if ((n_tokens >= 3) &&
2161 			(strcmp(tokens[2], "build") == 0)) {
2162 			cmd_pipeline_build(tokens, n_tokens, out, out_size,
2163 				obj);
2164 			return;
2165 		}
2166 
2167 		if ((n_tokens >= 3) &&
2168 			(strcmp(tokens[2], "table") == 0)) {
2169 			cmd_pipeline_table_update(tokens, n_tokens, out,
2170 				out_size, obj);
2171 			return;
2172 		}
2173 
2174 		if ((n_tokens >= 3) &&
2175 			(strcmp(tokens[2], "regrd") == 0)) {
2176 			cmd_pipeline_regrd(tokens, n_tokens, out, out_size, obj);
2177 			return;
2178 		}
2179 
2180 		if ((n_tokens >= 3) &&
2181 			(strcmp(tokens[2], "regwr") == 0)) {
2182 			cmd_pipeline_regwr(tokens, n_tokens, out, out_size, obj);
2183 			return;
2184 		}
2185 
2186 		if ((n_tokens >= 6) &&
2187 			(strcmp(tokens[2], "meter") == 0) &&
2188 			(strcmp(tokens[3], "profile") == 0) &&
2189 			(strcmp(tokens[5], "add") == 0)) {
2190 			cmd_pipeline_meter_profile_add(tokens, n_tokens, out, out_size, obj);
2191 			return;
2192 		}
2193 
2194 		if ((n_tokens >= 6) &&
2195 			(strcmp(tokens[2], "meter") == 0) &&
2196 			(strcmp(tokens[3], "profile") == 0) &&
2197 			(strcmp(tokens[5], "delete") == 0)) {
2198 			cmd_pipeline_meter_profile_delete(tokens, n_tokens, out, out_size, obj);
2199 			return;
2200 		}
2201 
2202 		if ((n_tokens >= 9) &&
2203 			(strcmp(tokens[2], "meter") == 0) &&
2204 			(strcmp(tokens[8], "reset") == 0)) {
2205 			cmd_pipeline_meter_reset(tokens, n_tokens, out, out_size, obj);
2206 			return;
2207 		}
2208 
2209 		if ((n_tokens >= 9) &&
2210 			(strcmp(tokens[2], "meter") == 0) &&
2211 			(strcmp(tokens[8], "set") == 0)) {
2212 			cmd_pipeline_meter_set(tokens, n_tokens, out, out_size, obj);
2213 			return;
2214 		}
2215 
2216 		if ((n_tokens >= 9) &&
2217 			(strcmp(tokens[2], "meter") == 0) &&
2218 			(strcmp(tokens[8], "stats") == 0)) {
2219 			cmd_pipeline_meter_stats(tokens, n_tokens, out, out_size, obj);
2220 			return;
2221 		}
2222 
2223 		if ((n_tokens >= 3) &&
2224 			(strcmp(tokens[2], "stats") == 0)) {
2225 			cmd_pipeline_stats(tokens, n_tokens, out, out_size,
2226 				obj);
2227 			return;
2228 		}
2229 	}
2230 
2231 	if (strcmp(tokens[0], "thread") == 0) {
2232 		if ((n_tokens >= 5) &&
2233 			(strcmp(tokens[4], "enable") == 0)) {
2234 			cmd_thread_pipeline_enable(tokens, n_tokens,
2235 				out, out_size, obj);
2236 			return;
2237 		}
2238 
2239 		if ((n_tokens >= 5) &&
2240 			(strcmp(tokens[4], "disable") == 0)) {
2241 			cmd_thread_pipeline_disable(tokens, n_tokens,
2242 				out, out_size, obj);
2243 			return;
2244 		}
2245 	}
2246 
2247 	snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
2248 }
2249 
2250 int
2251 cli_script_process(const char *file_name,
2252 	size_t msg_in_len_max,
2253 	size_t msg_out_len_max,
2254 	void *obj)
2255 {
2256 	char *msg_in = NULL, *msg_out = NULL;
2257 	FILE *f = NULL;
2258 
2259 	/* Check input arguments */
2260 	if ((file_name == NULL) ||
2261 		(strlen(file_name) == 0) ||
2262 		(msg_in_len_max == 0) ||
2263 		(msg_out_len_max == 0))
2264 		return -EINVAL;
2265 
2266 	msg_in = malloc(msg_in_len_max + 1);
2267 	msg_out = malloc(msg_out_len_max + 1);
2268 	if ((msg_in == NULL) ||
2269 		(msg_out == NULL)) {
2270 		free(msg_out);
2271 		free(msg_in);
2272 		return -ENOMEM;
2273 	}
2274 
2275 	/* Open input file */
2276 	f = fopen(file_name, "r");
2277 	if (f == NULL) {
2278 		free(msg_out);
2279 		free(msg_in);
2280 		return -EIO;
2281 	}
2282 
2283 	/* Read file */
2284 	for ( ; ; ) {
2285 		if (fgets(msg_in, msg_in_len_max + 1, f) == NULL)
2286 			break;
2287 
2288 		printf("%s", msg_in);
2289 		msg_out[0] = 0;
2290 
2291 		cli_process(msg_in,
2292 			msg_out,
2293 			msg_out_len_max,
2294 			obj);
2295 
2296 		if (strlen(msg_out))
2297 			printf("%s", msg_out);
2298 	}
2299 
2300 	/* Close file */
2301 	fclose(f);
2302 	free(msg_out);
2303 	free(msg_in);
2304 	return 0;
2305 }
2306