xref: /dpdk/examples/pipeline/cli.c (revision 09442498ef736d0a96632cf8b8c15d8ca78a6468)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2020 Intel Corporation
3  */
4 
5 #include <ctype.h>
6 #include <stdio.h>
7 #include <stdint.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <unistd.h>
11 
12 #include <rte_common.h>
13 #include <rte_ethdev.h>
14 #include <rte_ring.h>
15 #include <rte_swx_port_ethdev.h>
16 #include <rte_swx_port_ring.h>
17 #include <rte_swx_port_source_sink.h>
18 #include <rte_swx_port_fd.h>
19 #include <rte_swx_pipeline.h>
20 #include <rte_swx_ctl.h>
21 #include <rte_swx_ipsec.h>
22 
23 #include "cli.h"
24 
25 #include "obj.h"
26 #include "thread.h"
27 
28 #ifndef CMD_MAX_TOKENS
29 #define CMD_MAX_TOKENS     256
30 #endif
31 
32 #ifndef MAX_LINE_SIZE
33 #define MAX_LINE_SIZE 2048
34 #endif
35 
36 #define MSG_OUT_OF_MEMORY   "Not enough memory.\n"
37 #define MSG_CMD_UNKNOWN     "Unknown command \"%s\".\n"
38 #define MSG_CMD_UNIMPLEM    "Command \"%s\" not implemented.\n"
39 #define MSG_ARG_NOT_ENOUGH  "Not enough arguments for command \"%s\".\n"
40 #define MSG_ARG_TOO_MANY    "Too many arguments for command \"%s\".\n"
41 #define MSG_ARG_MISMATCH    "Wrong number of arguments for command \"%s\".\n"
42 #define MSG_ARG_NOT_FOUND   "Argument \"%s\" not found.\n"
43 #define MSG_ARG_INVALID     "Invalid value for argument \"%s\".\n"
44 #define MSG_FILE_ERR        "Error in file \"%s\" at line %u.\n"
45 #define MSG_FILE_NOT_ENOUGH "Not enough rules in file \"%s\".\n"
46 #define MSG_CMD_FAIL        "Command \"%s\" failed.\n"
47 
48 #define skip_white_spaces(pos)			\
49 ({						\
50 	__typeof__(pos) _p = (pos);		\
51 	for ( ; isspace(*_p); _p++)		\
52 		;				\
53 	_p;					\
54 })
55 
56 static int
57 parser_read_uint64(uint64_t *value, const char *p)
58 {
59 	char *next;
60 	uint64_t val;
61 
62 	p = skip_white_spaces(p);
63 	if (!isdigit(*p))
64 		return -EINVAL;
65 
66 	val = strtoul(p, &next, 0);
67 	if (p == next)
68 		return -EINVAL;
69 
70 	p = next;
71 	switch (*p) {
72 	case 'T':
73 		val *= 1024ULL;
74 		/* fall through */
75 	case 'G':
76 		val *= 1024ULL;
77 		/* fall through */
78 	case 'M':
79 		val *= 1024ULL;
80 		/* fall through */
81 	case 'k':
82 	case 'K':
83 		val *= 1024ULL;
84 		p++;
85 		break;
86 	}
87 
88 	p = skip_white_spaces(p);
89 	if (*p != '\0')
90 		return -EINVAL;
91 
92 	*value = val;
93 	return 0;
94 }
95 
96 static int
97 parser_read_uint32(uint32_t *value, const char *p)
98 {
99 	uint64_t val = 0;
100 	int ret = parser_read_uint64(&val, p);
101 
102 	if (ret < 0)
103 		return ret;
104 
105 	if (val > UINT32_MAX)
106 		return -ERANGE;
107 
108 	*value = val;
109 	return 0;
110 }
111 
112 #define PARSE_DELIMITER " \f\n\r\t\v"
113 
114 static int
115 parse_tokenize_string(char *string, char *tokens[], uint32_t *n_tokens)
116 {
117 	uint32_t i;
118 
119 	if ((string == NULL) ||
120 		(tokens == NULL) ||
121 		(*n_tokens < 1))
122 		return -EINVAL;
123 
124 	for (i = 0; i < *n_tokens; i++) {
125 		tokens[i] = strtok_r(string, PARSE_DELIMITER, &string);
126 		if (tokens[i] == NULL)
127 			break;
128 	}
129 
130 	if ((i == *n_tokens) && strtok_r(string, PARSE_DELIMITER, &string))
131 		return -E2BIG;
132 
133 	*n_tokens = i;
134 	return 0;
135 }
136 
137 static int
138 is_comment(char *in)
139 {
140 	if ((strlen(in) && index("!#%;", in[0])) ||
141 		(strncmp(in, "//", 2) == 0) ||
142 		(strncmp(in, "--", 2) == 0))
143 		return 1;
144 
145 	return 0;
146 }
147 
148 static void
149 table_entry_free(struct rte_swx_table_entry *entry)
150 {
151 	if (!entry)
152 		return;
153 
154 	free(entry->key);
155 	free(entry->key_mask);
156 	free(entry->action_data);
157 	free(entry);
158 }
159 
160 static struct rte_swx_table_entry *
161 parse_table_entry(struct rte_swx_ctl_pipeline *p,
162 		  char *table_name,
163 		  char **tokens,
164 		  uint32_t n_tokens)
165 {
166 	struct rte_swx_table_entry *entry;
167 	char *line;
168 	uint32_t i;
169 
170 	/* Buffer allocation. */
171 	line = malloc(MAX_LINE_SIZE);
172 	if (!line)
173 		return NULL;
174 
175 	/* Copy tokens to buffer. Since the tokens were initially part of a buffer of size
176 	 * MAX_LINE_LENGTH, it is guaranteed that putting back some of them into a buffer of the
177 	 * same size separated by a single space will not result in buffer overrun.
178 	 */
179 	line[0] = 0;
180 	for (i = 0; i < n_tokens; i++) {
181 		if (i)
182 			strcat(line, " ");
183 
184 		strcat(line, tokens[i]);
185 	}
186 
187 	/* Read the table entry from the input buffer. */
188 	entry = rte_swx_ctl_pipeline_table_entry_read(p, table_name, line, NULL);
189 
190 	/* Buffer free. */
191 	free(line);
192 
193 	return entry;
194 }
195 
196 static const char cmd_mempool_help[] =
197 "mempool <mempool_name> "
198 "meta <mbuf_private_size> "
199 "pkt <pkt_buffer_size> "
200 "pool <pool_size> "
201 "cache <cache_size> "
202 "numa <numa_node>\n";
203 
204 static void
205 cmd_mempool(char **tokens,
206 	    uint32_t n_tokens,
207 	    char *out,
208 	    size_t out_size,
209 	    void *obj __rte_unused)
210 {
211 	struct rte_mempool *mp;
212 	char *mempool_name;
213 	uint32_t mbuf_private_size, pkt_buffer_size, pool_size, cache_size, numa_node;
214 
215 	if (n_tokens != 12) {
216 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
217 		return;
218 	}
219 
220 	mempool_name = tokens[1];
221 
222 	if (strcmp(tokens[2], "meta")) {
223 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meta");
224 		return;
225 	}
226 
227 	if (parser_read_uint32(&mbuf_private_size, tokens[3])) {
228 		snprintf(out, out_size, MSG_ARG_INVALID, "mbuf_private_size");
229 		return;
230 	}
231 
232 	if (strcmp(tokens[4], "pkt")) {
233 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pkt");
234 		return;
235 	}
236 
237 	if (parser_read_uint32(&pkt_buffer_size, tokens[5])) {
238 		snprintf(out, out_size, MSG_ARG_INVALID, "pkt_buffer_size");
239 		return;
240 	}
241 
242 	if (strcmp(tokens[6], "pool")) {
243 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pool");
244 		return;
245 	}
246 
247 	if (parser_read_uint32(&pool_size, tokens[7])) {
248 		snprintf(out, out_size, MSG_ARG_INVALID, "pool_size");
249 		return;
250 	}
251 
252 	if (strcmp(tokens[8], "cache")) {
253 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cache");
254 		return;
255 	}
256 
257 	if (parser_read_uint32(&cache_size, tokens[9])) {
258 		snprintf(out, out_size, MSG_ARG_INVALID, "cache_size");
259 		return;
260 	}
261 
262 	if (strcmp(tokens[10], "numa")) {
263 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
264 		return;
265 	}
266 
267 	if (parser_read_uint32(&numa_node, tokens[11])) {
268 		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
269 		return;
270 	}
271 
272 	mp = rte_pktmbuf_pool_create(mempool_name,
273 				     pool_size,
274 				     cache_size,
275 				     mbuf_private_size,
276 				     pkt_buffer_size,
277 				     numa_node);
278 	if (!mp) {
279 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
280 		return;
281 	}
282 }
283 
284 static const char cmd_ethdev_help[] =
285 "ethdev <ethdev_name>\n"
286 "   rxq <n_queues> <queue_size> <mempool_name>\n"
287 "   txq <n_queues> <queue_size>\n"
288 "   promiscuous on | off\n"
289 "   [rss <qid_0> ... <qid_n>]\n";
290 
291 static void
292 cmd_ethdev(char **tokens,
293 	uint32_t n_tokens,
294 	char *out,
295 	size_t out_size,
296 	void *obj __rte_unused)
297 {
298 	struct ethdev_params p;
299 	struct ethdev_params_rss rss;
300 	char *name;
301 	int status;
302 
303 	memset(&p, 0, sizeof(p));
304 	memset(&rss, 0, sizeof(rss));
305 
306 	if (n_tokens < 11 || n_tokens > 12 + ETHDEV_RXQ_RSS_MAX) {
307 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
308 		return;
309 	}
310 	name = tokens[1];
311 
312 	if (strcmp(tokens[2], "rxq") != 0) {
313 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq");
314 		return;
315 	}
316 
317 	if (parser_read_uint32(&p.rx.n_queues, tokens[3]) != 0) {
318 		snprintf(out, out_size, MSG_ARG_INVALID, "n_queues");
319 		return;
320 	}
321 	if (parser_read_uint32(&p.rx.queue_size, tokens[4]) != 0) {
322 		snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
323 		return;
324 	}
325 
326 	p.rx.mempool_name = tokens[5];
327 
328 	if (strcmp(tokens[6], "txq") != 0) {
329 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "txq");
330 		return;
331 	}
332 
333 	if (parser_read_uint32(&p.tx.n_queues, tokens[7]) != 0) {
334 		snprintf(out, out_size, MSG_ARG_INVALID, "n_queues");
335 		return;
336 	}
337 
338 	if (parser_read_uint32(&p.tx.queue_size, tokens[8]) != 0) {
339 		snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
340 		return;
341 	}
342 
343 	if (strcmp(tokens[9], "promiscuous") != 0) {
344 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "promiscuous");
345 		return;
346 	}
347 
348 	if (strcmp(tokens[10], "on") == 0)
349 		p.promiscuous = 1;
350 	else if (strcmp(tokens[10], "off") == 0)
351 		p.promiscuous = 0;
352 	else {
353 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "on or off");
354 		return;
355 	}
356 
357 	/* RSS */
358 	p.rx.rss = NULL;
359 	if (n_tokens > 11) {
360 		uint32_t queue_id, i;
361 
362 		if (strcmp(tokens[11], "rss") != 0) {
363 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rss");
364 			return;
365 		}
366 
367 		p.rx.rss = &rss;
368 
369 		rss.n_queues = 0;
370 		for (i = 12; i < n_tokens; i++) {
371 			if (parser_read_uint32(&queue_id, tokens[i]) != 0) {
372 				snprintf(out, out_size, MSG_ARG_INVALID,
373 					"queue_id");
374 				return;
375 			}
376 
377 			rss.queue_id[rss.n_queues] = queue_id;
378 			rss.n_queues++;
379 		}
380 	}
381 
382 	status = ethdev_config(name, &p);
383 	if (status) {
384 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
385 		return;
386 	}
387 }
388 
389 static void
390 ethdev_show(uint16_t port_id, char **out, size_t *out_size)
391 {
392 	char name[RTE_ETH_NAME_MAX_LEN];
393 	struct rte_eth_dev_info info;
394 	struct rte_eth_stats stats;
395 	struct rte_ether_addr addr;
396 	struct rte_eth_link link;
397 	uint32_t length;
398 	uint16_t mtu = 0;
399 
400 	if (!rte_eth_dev_is_valid_port(port_id))
401 		return;
402 
403 	rte_eth_dev_get_name_by_port(port_id, name);
404 	rte_eth_dev_info_get(port_id, &info);
405 	rte_eth_stats_get(port_id, &stats);
406 	rte_eth_macaddr_get(port_id, &addr);
407 	rte_eth_link_get(port_id, &link);
408 	rte_eth_dev_get_mtu(port_id, &mtu);
409 
410 	snprintf(*out, *out_size,
411 		 "%s: flags=<%s> mtu %u\n"
412 		 "\tether " RTE_ETHER_ADDR_PRT_FMT " rxqueues %u txqueues %u\n"
413 		 "\tport# %u  speed %s\n"
414 		 "\tRX packets %" PRIu64"  bytes %" PRIu64"\n"
415 		 "\tRX errors %" PRIu64"  missed %" PRIu64"  no-mbuf %" PRIu64"\n"
416 		 "\tTX packets %" PRIu64"  bytes %" PRIu64"\n"
417 		 "\tTX errors %" PRIu64"\n\n",
418 		 name,
419 		 link.link_status ? "UP" : "DOWN",
420 		 mtu,
421 		 RTE_ETHER_ADDR_BYTES(&addr),
422 		 info.nb_rx_queues,
423 		 info.nb_tx_queues,
424 		 port_id,
425 		 rte_eth_link_speed_to_str(link.link_speed),
426 		 stats.ipackets,
427 		 stats.ibytes,
428 		 stats.ierrors,
429 		 stats.imissed,
430 		 stats.rx_nombuf,
431 		 stats.opackets,
432 		 stats.obytes,
433 		 stats.oerrors);
434 
435 	length = strlen(*out);
436 	*out_size -= length;
437 	*out += length;
438 }
439 
440 
441 static char cmd_ethdev_show_help[] =
442 "ethdev show [ <ethdev_name> ]\n";
443 
444 static void
445 cmd_ethdev_show(char **tokens,
446 	      uint32_t n_tokens,
447 	      char *out,
448 	      size_t out_size,
449 	      void *obj __rte_unused)
450 {
451 	uint16_t port_id;
452 
453 	if (n_tokens != 2 && n_tokens != 3) {
454 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
455 		return;
456 	}
457 
458 	/* Single device. */
459 	if (n_tokens == 3) {
460 		int status;
461 
462 		status = rte_eth_dev_get_port_by_name(tokens[2], &port_id);
463 		if (status)
464 			snprintf(out, out_size, "Error: Invalid Ethernet device name.\n");
465 
466 		ethdev_show(port_id, &out, &out_size);
467 		return;
468 	}
469 
470 	/*  All devices. */
471 	for (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++)
472 		if (rte_eth_dev_is_valid_port(port_id))
473 			ethdev_show(port_id, &out, &out_size);
474 }
475 
476 static const char cmd_ring_help[] =
477 "ring <ring_name> size <size> numa <numa_node>\n";
478 
479 static void
480 cmd_ring(char **tokens,
481 	uint32_t n_tokens,
482 	char *out,
483 	size_t out_size,
484 	void *obj __rte_unused)
485 {
486 	struct rte_ring *r;
487 	char *name;
488 	uint32_t size, numa_node;
489 
490 	if (n_tokens != 6) {
491 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
492 		return;
493 	}
494 
495 	name = tokens[1];
496 
497 	if (strcmp(tokens[2], "size")) {
498 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size");
499 		return;
500 	}
501 
502 	if (parser_read_uint32(&size, tokens[3])) {
503 		snprintf(out, out_size, MSG_ARG_INVALID, "size");
504 		return;
505 	}
506 
507 	if (strcmp(tokens[4], "numa")) {
508 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
509 		return;
510 	}
511 
512 	if (parser_read_uint32(&numa_node, tokens[5])) {
513 		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
514 		return;
515 	}
516 
517 	r = rte_ring_create(
518 		name,
519 		size,
520 		(int)numa_node,
521 		RING_F_SP_ENQ | RING_F_SC_DEQ);
522 	if (!r) {
523 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
524 		return;
525 	}
526 }
527 
528 static const char cmd_cryptodev_help[] =
529 "cryptodev <cryptodev_name> queues <n_queue_pairs> qsize <queue_size>\n";
530 
531 static void
532 cmd_cryptodev(char **tokens,
533 	      uint32_t n_tokens,
534 	      char *out,
535 	      size_t out_size,
536 	      void *obj __rte_unused)
537 {
538 	struct cryptodev_params params;
539 	char *cryptodev_name;
540 	int status;
541 
542 	if (n_tokens != 6) {
543 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
544 		return;
545 	}
546 
547 	if (strcmp(tokens[0], "cryptodev")) {
548 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cryptodev");
549 		return;
550 	}
551 
552 	cryptodev_name = tokens[1];
553 
554 	if (strcmp(tokens[2], "queues")) {
555 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "queues");
556 		return;
557 	}
558 
559 	if (parser_read_uint32(&params.n_queue_pairs, tokens[3])) {
560 		snprintf(out, out_size, MSG_ARG_INVALID, "n_queue_pairs");
561 		return;
562 	}
563 
564 	if (strcmp(tokens[4], "qsize")) {
565 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "qsize");
566 		return;
567 	}
568 
569 
570 	if (parser_read_uint32(&params.queue_size, tokens[5])) {
571 		snprintf(out, out_size, MSG_ARG_INVALID, "queue_size");
572 		return;
573 	}
574 
575 	status = cryptodev_config(cryptodev_name, &params);
576 	if (status)
577 		snprintf(out, out_size, "Crypto device configuration failed (%d).\n", status);
578 }
579 
580 static const char cmd_pipeline_codegen_help[] =
581 "pipeline codegen <spec_file> <code_file>\n";
582 
583 static void
584 cmd_pipeline_codegen(char **tokens,
585 	uint32_t n_tokens,
586 	char *out,
587 	size_t out_size,
588 	void *obj __rte_unused)
589 {
590 	FILE *spec_file = NULL;
591 	FILE *code_file = NULL;
592 	uint32_t err_line;
593 	const char *err_msg;
594 	int status;
595 
596 	if (n_tokens != 4) {
597 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
598 		return;
599 	}
600 
601 	spec_file = fopen(tokens[2], "r");
602 	if (!spec_file) {
603 		snprintf(out, out_size, "Cannot open file %s.\n", tokens[2]);
604 		return;
605 	}
606 
607 	code_file = fopen(tokens[3], "w");
608 	if (!code_file) {
609 		snprintf(out, out_size, "Cannot open file %s.\n", tokens[3]);
610 		fclose(spec_file);
611 		return;
612 	}
613 
614 	status = rte_swx_pipeline_codegen(spec_file,
615 					  code_file,
616 					  &err_line,
617 					  &err_msg);
618 
619 	fclose(spec_file);
620 	fclose(code_file);
621 
622 	if (status) {
623 		snprintf(out, out_size, "Error %d at line %u: %s\n.",
624 			status, err_line, err_msg);
625 		return;
626 	}
627 }
628 
629 static const char cmd_pipeline_libbuild_help[] =
630 "pipeline libbuild <code_file> <lib_file>\n";
631 
632 static void
633 cmd_pipeline_libbuild(char **tokens,
634 	uint32_t n_tokens,
635 	char *out,
636 	size_t out_size,
637 	void *obj __rte_unused)
638 {
639 	char *code_file, *lib_file, *obj_file = NULL, *log_file = NULL;
640 	char *install_dir, *cwd = NULL, *buffer = NULL;
641 	size_t length;
642 	int status = 0;
643 
644 	if (n_tokens != 4) {
645 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
646 		goto free;
647 	}
648 
649 	install_dir = getenv("RTE_INSTALL_DIR");
650 	if (!install_dir) {
651 		cwd = malloc(MAX_LINE_SIZE);
652 		if (!cwd) {
653 			snprintf(out, out_size, MSG_OUT_OF_MEMORY);
654 			goto free;
655 		}
656 
657 		install_dir = getcwd(cwd, MAX_LINE_SIZE);
658 		if (!install_dir) {
659 			snprintf(out, out_size, "Error: Path too long.\n");
660 			goto free;
661 		}
662 	}
663 
664 	snprintf(out, out_size, "Using DPDK source code from \"%s\".\n", install_dir);
665 	out_size -= strlen(out);
666 	out += strlen(out);
667 
668 	code_file = tokens[2];
669 	length = strnlen(code_file, MAX_LINE_SIZE);
670 	if ((length < 3) ||
671 	    (code_file[length - 2] != '.') ||
672 	    (code_file[length - 1] != 'c')) {
673 		snprintf(out, out_size, MSG_ARG_INVALID, "code_file");
674 		goto free;
675 	}
676 
677 	lib_file = tokens[3];
678 	length = strnlen(lib_file, MAX_LINE_SIZE);
679 	if ((length < 4) ||
680 	    (lib_file[length - 3] != '.') ||
681 	    (lib_file[length - 2] != 's') ||
682 	    (lib_file[length - 1] != 'o')) {
683 		snprintf(out, out_size, MSG_ARG_INVALID, "lib_file");
684 		goto free;
685 	}
686 
687 	obj_file = malloc(length);
688 	log_file = malloc(length + 2);
689 	if (!obj_file || !log_file) {
690 		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
691 		goto free;
692 	}
693 
694 	memcpy(obj_file, lib_file, length - 2);
695 	obj_file[length - 2] = 'o';
696 	obj_file[length - 1] = 0;
697 
698 	memcpy(log_file, lib_file, length - 2);
699 	log_file[length - 2] = 'l';
700 	log_file[length - 1] = 'o';
701 	log_file[length] = 'g';
702 	log_file[length + 1] = 0;
703 
704 	buffer = malloc(MAX_LINE_SIZE);
705 	if (!buffer) {
706 		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
707 		goto free;
708 	}
709 
710 	snprintf(buffer,
711 		 MAX_LINE_SIZE,
712 		 "gcc -c -O3 -fpic -Wno-deprecated-declarations -o %s %s "
713 		 "-I %s/lib/pipeline "
714 		 "-I %s/lib/eal/include "
715 		 "-I %s/lib/eal/x86/include "
716 		 "-I %s/lib/eal/include/generic "
717 		 "-I %s/lib/meter "
718 		 "-I %s/lib/port "
719 		 "-I %s/lib/table "
720 		 "-I %s/lib/pipeline "
721 		 "-I %s/config "
722 		 "-I %s/build "
723 		 "-I %s/lib/eal/linux/include "
724 		 ">%s 2>&1 "
725 		 "&& "
726 		 "gcc -shared %s -o %s "
727 		 ">>%s 2>&1",
728 		 obj_file,
729 		 code_file,
730 		 install_dir,
731 		 install_dir,
732 		 install_dir,
733 		 install_dir,
734 		 install_dir,
735 		 install_dir,
736 		 install_dir,
737 		 install_dir,
738 		 install_dir,
739 		 install_dir,
740 		 install_dir,
741 		 log_file,
742 		 obj_file,
743 		 lib_file,
744 		 log_file);
745 
746 	status = system(buffer);
747 	if (status) {
748 		snprintf(out,
749 			 out_size,
750 			 "Library build failed, see file \"%s\" for details.\n",
751 			 log_file);
752 		goto free;
753 	}
754 
755 free:
756 	free(cwd);
757 	free(obj_file);
758 	free(log_file);
759 	free(buffer);
760 }
761 
762 static const char cmd_pipeline_build_help[] =
763 "pipeline <pipeline_name> build lib <lib_file> io <iospec_file> numa <numa_node>\n";
764 
765 static void
766 cmd_pipeline_build(char **tokens,
767 	uint32_t n_tokens,
768 	char *out,
769 	size_t out_size,
770 	void *obj __rte_unused)
771 {
772 	struct rte_swx_pipeline *p = NULL;
773 	struct rte_swx_ctl_pipeline *ctl = NULL;
774 	char *pipeline_name, *lib_file_name, *iospec_file_name;
775 	FILE *iospec_file = NULL;
776 	uint32_t numa_node = 0;
777 	int status = 0;
778 
779 	/* Parsing. */
780 	if (n_tokens != 9) {
781 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
782 		return;
783 	}
784 
785 	pipeline_name = tokens[1];
786 
787 	if (strcmp(tokens[2], "build")) {
788 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "build");
789 		return;
790 	}
791 
792 	if (strcmp(tokens[3], "lib")) {
793 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "lib");
794 		return;
795 	}
796 
797 	lib_file_name = tokens[4];
798 
799 	if (strcmp(tokens[5], "io")) {
800 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "io");
801 		return;
802 	}
803 
804 	iospec_file_name = tokens[6];
805 
806 	if (strcmp(tokens[7], "numa")) {
807 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
808 		return;
809 	}
810 
811 	if (parser_read_uint32(&numa_node, tokens[8])) {
812 		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
813 		return;
814 	}
815 
816 	/* I/O spec file open. */
817 	iospec_file = fopen(iospec_file_name, "r");
818 	if (!iospec_file) {
819 		snprintf(out, out_size, "Cannot open file \"%s\".\n", iospec_file_name);
820 		return;
821 	}
822 
823 	status = rte_swx_pipeline_build_from_lib(&p,
824 						 pipeline_name,
825 						 lib_file_name,
826 						 iospec_file,
827 						 (int)numa_node);
828 	if (status) {
829 		snprintf(out, out_size, "Pipeline build failed (%d).", status);
830 		goto free;
831 	}
832 
833 	ctl = rte_swx_ctl_pipeline_create(p);
834 	if (!ctl) {
835 		snprintf(out, out_size, "Pipeline control create failed.");
836 		goto free;
837 	}
838 
839 free:
840 	if (status)
841 		rte_swx_pipeline_free(p);
842 
843 	if (iospec_file)
844 		fclose(iospec_file);
845 }
846 
847 static int
848 pipeline_table_entries_add(struct rte_swx_ctl_pipeline *p,
849 			   const char *table_name,
850 			   FILE *file,
851 			   uint32_t *file_line_number)
852 {
853 	char *line = NULL;
854 	uint32_t line_id = 0;
855 	int status = 0;
856 
857 	/* Buffer allocation. */
858 	line = malloc(MAX_LINE_SIZE);
859 	if (!line)
860 		return -ENOMEM;
861 
862 	/* File read. */
863 	for (line_id = 1; ; line_id++) {
864 		struct rte_swx_table_entry *entry;
865 		int is_blank_or_comment;
866 
867 		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
868 			break;
869 
870 		entry = rte_swx_ctl_pipeline_table_entry_read(p,
871 							      table_name,
872 							      line,
873 							      &is_blank_or_comment);
874 		if (!entry) {
875 			if (is_blank_or_comment)
876 				continue;
877 
878 			status = -EINVAL;
879 			goto error;
880 		}
881 
882 		status = rte_swx_ctl_pipeline_table_entry_add(p,
883 							      table_name,
884 							      entry);
885 		table_entry_free(entry);
886 		if (status)
887 			goto error;
888 	}
889 
890 error:
891 	free(line);
892 	*file_line_number = line_id;
893 	return status;
894 }
895 
896 static const char cmd_pipeline_table_add_help[] =
897 "pipeline <pipeline_name> table <table_name> add <file_name>\n";
898 
899 static void
900 cmd_pipeline_table_add(char **tokens,
901 		       uint32_t n_tokens,
902 		       char *out,
903 		       size_t out_size,
904 		       void *obj __rte_unused)
905 {
906 	struct rte_swx_ctl_pipeline *ctl;
907 	char *pipeline_name, *table_name, *file_name;
908 	FILE *file = NULL;
909 	uint32_t file_line_number = 0;
910 	int status;
911 
912 	if (n_tokens != 6) {
913 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
914 		return;
915 	}
916 
917 	pipeline_name = tokens[1];
918 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
919 	if (!ctl) {
920 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
921 		return;
922 	}
923 
924 	table_name = tokens[3];
925 
926 	file_name = tokens[5];
927 	file = fopen(file_name, "r");
928 	if (!file) {
929 		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
930 		return;
931 	}
932 
933 	status = pipeline_table_entries_add(ctl,
934 					    table_name,
935 					    file,
936 					    &file_line_number);
937 	if (status)
938 		snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
939 			 file_name,
940 			 file_line_number);
941 
942 	fclose(file);
943 }
944 
945 static int
946 pipeline_table_entries_delete(struct rte_swx_ctl_pipeline *p,
947 			      const char *table_name,
948 			      FILE *file,
949 			      uint32_t *file_line_number)
950 {
951 	char *line = NULL;
952 	uint32_t line_id = 0;
953 	int status = 0;
954 
955 	/* Buffer allocation. */
956 	line = malloc(MAX_LINE_SIZE);
957 	if (!line)
958 		return -ENOMEM;
959 
960 	/* File read. */
961 	for (line_id = 1; ; line_id++) {
962 		struct rte_swx_table_entry *entry;
963 		int is_blank_or_comment;
964 
965 		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
966 			break;
967 
968 		entry = rte_swx_ctl_pipeline_table_entry_read(p,
969 							      table_name,
970 							      line,
971 							      &is_blank_or_comment);
972 		if (!entry) {
973 			if (is_blank_or_comment)
974 				continue;
975 
976 			status = -EINVAL;
977 			goto error;
978 		}
979 
980 		status = rte_swx_ctl_pipeline_table_entry_delete(p,
981 								 table_name,
982 								 entry);
983 		table_entry_free(entry);
984 		if (status)
985 			goto error;
986 	}
987 
988 error:
989 	*file_line_number = line_id;
990 	free(line);
991 	return status;
992 }
993 
994 static const char cmd_pipeline_table_delete_help[] =
995 "pipeline <pipeline_name> table <table_name> delete <file_name>\n";
996 
997 static void
998 cmd_pipeline_table_delete(char **tokens,
999 			  uint32_t n_tokens,
1000 			  char *out,
1001 			  size_t out_size,
1002 			  void *obj __rte_unused)
1003 {
1004 	struct rte_swx_ctl_pipeline *ctl;
1005 	char *pipeline_name, *table_name, *file_name;
1006 	FILE *file = NULL;
1007 	uint32_t file_line_number = 0;
1008 	int status;
1009 
1010 	if (n_tokens != 6) {
1011 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1012 		return;
1013 	}
1014 
1015 	pipeline_name = tokens[1];
1016 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
1017 	if (!ctl) {
1018 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1019 		return;
1020 	}
1021 
1022 	table_name = tokens[3];
1023 
1024 	file_name = tokens[5];
1025 	file = fopen(file_name, "r");
1026 	if (!file) {
1027 		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
1028 		return;
1029 	}
1030 
1031 	status = pipeline_table_entries_delete(ctl,
1032 					       table_name,
1033 					       file,
1034 					       &file_line_number);
1035 	if (status)
1036 		snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
1037 			 file_name,
1038 			 file_line_number);
1039 
1040 	fclose(file);
1041 }
1042 
1043 static int
1044 pipeline_table_default_entry_add(struct rte_swx_ctl_pipeline *p,
1045 				 const char *table_name,
1046 				 FILE *file,
1047 				 uint32_t *file_line_number)
1048 {
1049 	char *line = NULL;
1050 	uint32_t line_id = 0;
1051 	int status = 0;
1052 
1053 	/* Buffer allocation. */
1054 	line = malloc(MAX_LINE_SIZE);
1055 	if (!line)
1056 		return -ENOMEM;
1057 
1058 	/* File read. */
1059 	for (line_id = 1; ; line_id++) {
1060 		struct rte_swx_table_entry *entry;
1061 		int is_blank_or_comment;
1062 
1063 		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
1064 			break;
1065 
1066 		entry = rte_swx_ctl_pipeline_table_entry_read(p,
1067 							      table_name,
1068 							      line,
1069 							      &is_blank_or_comment);
1070 		if (!entry) {
1071 			if (is_blank_or_comment)
1072 				continue;
1073 
1074 			status = -EINVAL;
1075 			goto error;
1076 		}
1077 
1078 		status = rte_swx_ctl_pipeline_table_default_entry_add(p,
1079 								      table_name,
1080 								      entry);
1081 		table_entry_free(entry);
1082 		if (status)
1083 			goto error;
1084 	}
1085 
1086 error:
1087 	*file_line_number = line_id;
1088 	free(line);
1089 	return status;
1090 }
1091 
1092 static const char cmd_pipeline_table_default_help[] =
1093 "pipeline <pipeline_name> table <table_name> default <file_name>\n";
1094 
1095 static void
1096 cmd_pipeline_table_default(char **tokens,
1097 			   uint32_t n_tokens,
1098 			   char *out,
1099 			   size_t out_size,
1100 			   void *obj __rte_unused)
1101 {
1102 	struct rte_swx_ctl_pipeline *ctl;
1103 	char *pipeline_name, *table_name, *file_name;
1104 	FILE *file = NULL;
1105 	uint32_t file_line_number = 0;
1106 	int status;
1107 
1108 	if (n_tokens != 6) {
1109 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1110 		return;
1111 	}
1112 
1113 	pipeline_name = tokens[1];
1114 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
1115 	if (!ctl) {
1116 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1117 		return;
1118 	}
1119 
1120 	table_name = tokens[3];
1121 
1122 	file_name = tokens[5];
1123 	file = fopen(file_name, "r");
1124 	if (!file) {
1125 		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
1126 		return;
1127 	}
1128 
1129 	status = pipeline_table_default_entry_add(ctl,
1130 						  table_name,
1131 						  file,
1132 						  &file_line_number);
1133 	if (status)
1134 		snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
1135 			 file_name,
1136 			 file_line_number);
1137 
1138 	fclose(file);
1139 }
1140 
1141 static const char cmd_pipeline_table_show_help[] =
1142 "pipeline <pipeline_name> table <table_name> show [filename]\n";
1143 
1144 static void
1145 cmd_pipeline_table_show(char **tokens,
1146 	uint32_t n_tokens,
1147 	char *out,
1148 	size_t out_size,
1149 	void *obj __rte_unused)
1150 {
1151 	struct rte_swx_ctl_pipeline *ctl;
1152 	char *pipeline_name, *table_name;
1153 	FILE *file = NULL;
1154 	int status;
1155 
1156 	if (n_tokens != 5 && n_tokens != 6) {
1157 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1158 		return;
1159 	}
1160 
1161 	pipeline_name = tokens[1];
1162 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
1163 	if (!ctl) {
1164 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1165 		return;
1166 	}
1167 
1168 	table_name = tokens[3];
1169 	file = (n_tokens == 6) ? fopen(tokens[5], "w") : stdout;
1170 	if (!file) {
1171 		snprintf(out, out_size, "Cannot open file %s.\n", tokens[5]);
1172 		return;
1173 	}
1174 
1175 	status = rte_swx_ctl_pipeline_table_fprintf(file, ctl, table_name);
1176 	if (status)
1177 		snprintf(out, out_size, MSG_ARG_INVALID, "table_name");
1178 
1179 	if (file)
1180 		fclose(file);
1181 }
1182 
1183 static const char cmd_pipeline_selector_group_add_help[] =
1184 "pipeline <pipeline_name> selector <selector_name> group add\n";
1185 
1186 static void
1187 cmd_pipeline_selector_group_add(char **tokens,
1188 	uint32_t n_tokens,
1189 	char *out,
1190 	size_t out_size,
1191 	void *obj __rte_unused)
1192 {
1193 	struct rte_swx_ctl_pipeline *ctl;
1194 	char *pipeline_name, *selector_name;
1195 	uint32_t group_id;
1196 	int status;
1197 
1198 	if (n_tokens != 6) {
1199 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1200 		return;
1201 	}
1202 
1203 	pipeline_name = tokens[1];
1204 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
1205 	if (!ctl) {
1206 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1207 		return;
1208 	}
1209 
1210 	if (strcmp(tokens[2], "selector") != 0) {
1211 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector");
1212 		return;
1213 	}
1214 
1215 	selector_name = tokens[3];
1216 
1217 	if (strcmp(tokens[4], "group") ||
1218 		strcmp(tokens[5], "add")) {
1219 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group add");
1220 		return;
1221 	}
1222 
1223 	status = rte_swx_ctl_pipeline_selector_group_add(ctl,
1224 		selector_name,
1225 		&group_id);
1226 	if (status)
1227 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
1228 	else
1229 		snprintf(out, out_size, "Group ID: %u\n", group_id);
1230 }
1231 
1232 static const char cmd_pipeline_selector_group_delete_help[] =
1233 "pipeline <pipeline_name> selector <selector_name> group delete <group_id>\n";
1234 
1235 static void
1236 cmd_pipeline_selector_group_delete(char **tokens,
1237 	uint32_t n_tokens,
1238 	char *out,
1239 	size_t out_size,
1240 	void *obj __rte_unused)
1241 {
1242 	struct rte_swx_ctl_pipeline *ctl;
1243 	char *pipeline_name, *selector_name;
1244 	uint32_t group_id;
1245 	int status;
1246 
1247 	if (n_tokens != 7) {
1248 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1249 		return;
1250 	}
1251 
1252 	pipeline_name = tokens[1];
1253 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
1254 	if (!ctl) {
1255 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1256 		return;
1257 	}
1258 
1259 	if (strcmp(tokens[2], "selector") != 0) {
1260 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector");
1261 		return;
1262 	}
1263 
1264 	selector_name = tokens[3];
1265 
1266 	if (strcmp(tokens[4], "group") ||
1267 		strcmp(tokens[5], "delete")) {
1268 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group delete");
1269 		return;
1270 	}
1271 
1272 	if (parser_read_uint32(&group_id, tokens[6]) != 0) {
1273 		snprintf(out, out_size, MSG_ARG_INVALID, "group_id");
1274 		return;
1275 	}
1276 
1277 	status = rte_swx_ctl_pipeline_selector_group_delete(ctl,
1278 		selector_name,
1279 		group_id);
1280 	if (status)
1281 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
1282 }
1283 
1284 #define GROUP_MEMBER_INFO_TOKENS_MAX 6
1285 
1286 static int
1287 token_is_comment(const char *token)
1288 {
1289 	if ((token[0] == '#') ||
1290 	    (token[0] == ';') ||
1291 	    ((token[0] == '/') && (token[1] == '/')))
1292 		return 1; /* TRUE. */
1293 
1294 	return 0; /* FALSE. */
1295 }
1296 
1297 static int
1298 pipeline_selector_group_member_read(const char *string,
1299 				      uint32_t *group_id,
1300 				      uint32_t *member_id,
1301 				      uint32_t *weight,
1302 				      int *is_blank_or_comment)
1303 {
1304 	char *token_array[GROUP_MEMBER_INFO_TOKENS_MAX], **tokens;
1305 	char *s0 = NULL, *s;
1306 	uint32_t n_tokens = 0, group_id_val = 0, member_id_val = 0, weight_val = 0;
1307 	int blank_or_comment = 0;
1308 
1309 	/* Check input arguments. */
1310 	if (!string || !string[0])
1311 		goto error;
1312 
1313 	/* Memory allocation. */
1314 	s0 = strdup(string);
1315 	if (!s0)
1316 		goto error;
1317 
1318 	/* Parse the string into tokens. */
1319 	for (s = s0; ; ) {
1320 		char *token;
1321 
1322 		token = strtok_r(s, " \f\n\r\t\v", &s);
1323 		if (!token || token_is_comment(token))
1324 			break;
1325 
1326 		if (n_tokens >= GROUP_MEMBER_INFO_TOKENS_MAX)
1327 			goto error;
1328 
1329 		token_array[n_tokens] = token;
1330 		n_tokens++;
1331 	}
1332 
1333 	if (!n_tokens) {
1334 		blank_or_comment = 1;
1335 		goto error;
1336 	}
1337 
1338 	tokens = token_array;
1339 
1340 	if (n_tokens < 4 ||
1341 		strcmp(tokens[0], "group") ||
1342 		strcmp(tokens[2], "member"))
1343 		goto error;
1344 
1345 	/*
1346 	 * Group ID.
1347 	 */
1348 	if (parser_read_uint32(&group_id_val, tokens[1]) != 0)
1349 		goto error;
1350 	*group_id = group_id_val;
1351 
1352 	/*
1353 	 * Member ID.
1354 	 */
1355 	if (parser_read_uint32(&member_id_val, tokens[3]) != 0)
1356 		goto error;
1357 	*member_id = member_id_val;
1358 
1359 	tokens += 4;
1360 	n_tokens -= 4;
1361 
1362 	/*
1363 	 * Weight.
1364 	 */
1365 	if (n_tokens && !strcmp(tokens[0], "weight")) {
1366 		if (n_tokens < 2)
1367 			goto error;
1368 
1369 		if (parser_read_uint32(&weight_val, tokens[1]) != 0)
1370 			goto error;
1371 		*weight = weight_val;
1372 
1373 		tokens += 2;
1374 		n_tokens -= 2;
1375 	}
1376 
1377 	if (n_tokens)
1378 		goto error;
1379 
1380 	free(s0);
1381 	return 0;
1382 
1383 error:
1384 	free(s0);
1385 	if (is_blank_or_comment)
1386 		*is_blank_or_comment = blank_or_comment;
1387 	return -EINVAL;
1388 }
1389 
1390 static int
1391 pipeline_selector_group_members_add(struct rte_swx_ctl_pipeline *p,
1392 			   const char *selector_name,
1393 			   FILE *file,
1394 			   uint32_t *file_line_number)
1395 {
1396 	char *line = NULL;
1397 	uint32_t line_id = 0;
1398 	int status = 0;
1399 
1400 	/* Buffer allocation. */
1401 	line = malloc(MAX_LINE_SIZE);
1402 	if (!line)
1403 		return -ENOMEM;
1404 
1405 	/* File read. */
1406 	for (line_id = 1; ; line_id++) {
1407 		uint32_t group_id, member_id, weight;
1408 		int is_blank_or_comment;
1409 
1410 		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
1411 			break;
1412 
1413 		status = pipeline_selector_group_member_read(line,
1414 							      &group_id,
1415 							      &member_id,
1416 							      &weight,
1417 							      &is_blank_or_comment);
1418 		if (status) {
1419 			if (is_blank_or_comment)
1420 				continue;
1421 
1422 			goto error;
1423 		}
1424 
1425 		status = rte_swx_ctl_pipeline_selector_group_member_add(p,
1426 			selector_name,
1427 			group_id,
1428 			member_id,
1429 			weight);
1430 		if (status)
1431 			goto error;
1432 	}
1433 
1434 error:
1435 	free(line);
1436 	*file_line_number = line_id;
1437 	return status;
1438 }
1439 
1440 static const char cmd_pipeline_selector_group_member_add_help[] =
1441 "pipeline <pipeline_name> selector <selector_name> group member add <file_name>";
1442 
1443 static void
1444 cmd_pipeline_selector_group_member_add(char **tokens,
1445 	uint32_t n_tokens,
1446 	char *out,
1447 	size_t out_size,
1448 	void *obj __rte_unused)
1449 {
1450 	struct rte_swx_ctl_pipeline *ctl;
1451 	char *pipeline_name, *selector_name, *file_name;
1452 	FILE *file = NULL;
1453 	uint32_t file_line_number = 0;
1454 	int status;
1455 
1456 	if (n_tokens != 8) {
1457 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1458 		return;
1459 	}
1460 
1461 	pipeline_name = tokens[1];
1462 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
1463 	if (!ctl) {
1464 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1465 		return;
1466 	}
1467 
1468 	if (strcmp(tokens[2], "selector") != 0) {
1469 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector");
1470 		return;
1471 	}
1472 
1473 	selector_name = tokens[3];
1474 
1475 	if (strcmp(tokens[4], "group") ||
1476 		strcmp(tokens[5], "member") ||
1477 		strcmp(tokens[6], "add")) {
1478 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group member add");
1479 		return;
1480 	}
1481 
1482 	file_name = tokens[7];
1483 	file = fopen(file_name, "r");
1484 	if (!file) {
1485 		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
1486 		return;
1487 	}
1488 
1489 	status = pipeline_selector_group_members_add(ctl,
1490 					    selector_name,
1491 					    file,
1492 					    &file_line_number);
1493 	if (status)
1494 		snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
1495 			 file_name,
1496 			 file_line_number);
1497 
1498 	fclose(file);
1499 }
1500 
1501 static int
1502 pipeline_selector_group_members_delete(struct rte_swx_ctl_pipeline *p,
1503 			   const char *selector_name,
1504 			   FILE *file,
1505 			   uint32_t *file_line_number)
1506 {
1507 	char *line = NULL;
1508 	uint32_t line_id = 0;
1509 	int status = 0;
1510 
1511 	/* Buffer allocation. */
1512 	line = malloc(MAX_LINE_SIZE);
1513 	if (!line)
1514 		return -ENOMEM;
1515 
1516 	/* File read. */
1517 	for (line_id = 1; ; line_id++) {
1518 		uint32_t group_id, member_id, weight;
1519 		int is_blank_or_comment;
1520 
1521 		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
1522 			break;
1523 
1524 		status = pipeline_selector_group_member_read(line,
1525 							      &group_id,
1526 							      &member_id,
1527 							      &weight,
1528 							      &is_blank_or_comment);
1529 		if (status) {
1530 			if (is_blank_or_comment)
1531 				continue;
1532 
1533 			goto error;
1534 		}
1535 
1536 		status = rte_swx_ctl_pipeline_selector_group_member_delete(p,
1537 			selector_name,
1538 			group_id,
1539 			member_id);
1540 		if (status)
1541 			goto error;
1542 	}
1543 
1544 error:
1545 	free(line);
1546 	*file_line_number = line_id;
1547 	return status;
1548 }
1549 
1550 static const char cmd_pipeline_selector_group_member_delete_help[] =
1551 "pipeline <pipeline_name> selector <selector_name> group member delete <file_name>";
1552 
1553 static void
1554 cmd_pipeline_selector_group_member_delete(char **tokens,
1555 	uint32_t n_tokens,
1556 	char *out,
1557 	size_t out_size,
1558 	void *obj __rte_unused)
1559 {
1560 	struct rte_swx_ctl_pipeline *ctl;
1561 	char *pipeline_name, *selector_name, *file_name;
1562 	FILE *file = NULL;
1563 	uint32_t file_line_number = 0;
1564 	int status;
1565 
1566 	if (n_tokens != 8) {
1567 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1568 		return;
1569 	}
1570 
1571 	pipeline_name = tokens[1];
1572 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
1573 	if (!ctl) {
1574 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1575 		return;
1576 	}
1577 
1578 	if (strcmp(tokens[2], "selector") != 0) {
1579 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "selector");
1580 		return;
1581 	}
1582 
1583 	selector_name = tokens[3];
1584 
1585 	if (strcmp(tokens[4], "group") ||
1586 		strcmp(tokens[5], "member") ||
1587 		strcmp(tokens[6], "delete")) {
1588 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "group member delete");
1589 		return;
1590 	}
1591 
1592 	file_name = tokens[7];
1593 	file = fopen(file_name, "r");
1594 	if (!file) {
1595 		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
1596 		return;
1597 	}
1598 
1599 	status = pipeline_selector_group_members_delete(ctl,
1600 					    selector_name,
1601 					    file,
1602 					    &file_line_number);
1603 	if (status)
1604 		snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
1605 			 file_name,
1606 			 file_line_number);
1607 
1608 	fclose(file);
1609 }
1610 
1611 static const char cmd_pipeline_selector_show_help[] =
1612 "pipeline <pipeline_name> selector <selector_name> show [filename]\n";
1613 
1614 static void
1615 cmd_pipeline_selector_show(char **tokens,
1616 	uint32_t n_tokens,
1617 	char *out,
1618 	size_t out_size,
1619 	void *obj __rte_unused)
1620 {
1621 	struct rte_swx_ctl_pipeline *ctl;
1622 	char *pipeline_name, *selector_name;
1623 	FILE *file = NULL;
1624 	int status;
1625 
1626 	if (n_tokens != 5 && n_tokens != 6) {
1627 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1628 		return;
1629 	}
1630 
1631 	pipeline_name = tokens[1];
1632 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
1633 	if (!ctl) {
1634 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1635 		return;
1636 	}
1637 
1638 	selector_name = tokens[3];
1639 
1640 	file = (n_tokens == 6) ? fopen(tokens[5], "w") : stdout;
1641 	if (!file) {
1642 		snprintf(out, out_size, "Cannot open file %s.\n", tokens[5]);
1643 		return;
1644 	}
1645 
1646 	status = rte_swx_ctl_pipeline_selector_fprintf(file, ctl, selector_name);
1647 	if (status)
1648 		snprintf(out, out_size, MSG_ARG_INVALID, "selector_name");
1649 
1650 	if (file)
1651 		fclose(file);
1652 }
1653 
1654 static int
1655 pipeline_learner_default_entry_add(struct rte_swx_ctl_pipeline *p,
1656 				   const char *learner_name,
1657 				   FILE *file,
1658 				   uint32_t *file_line_number)
1659 {
1660 	char *line = NULL;
1661 	uint32_t line_id = 0;
1662 	int status = 0;
1663 
1664 	/* Buffer allocation. */
1665 	line = malloc(MAX_LINE_SIZE);
1666 	if (!line)
1667 		return -ENOMEM;
1668 
1669 	/* File read. */
1670 	for (line_id = 1; ; line_id++) {
1671 		struct rte_swx_table_entry *entry;
1672 		int is_blank_or_comment;
1673 
1674 		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
1675 			break;
1676 
1677 		entry = rte_swx_ctl_pipeline_learner_default_entry_read(p,
1678 									learner_name,
1679 									line,
1680 									&is_blank_or_comment);
1681 		if (!entry) {
1682 			if (is_blank_or_comment)
1683 				continue;
1684 
1685 			status = -EINVAL;
1686 			goto error;
1687 		}
1688 
1689 		status = rte_swx_ctl_pipeline_learner_default_entry_add(p,
1690 									learner_name,
1691 									entry);
1692 		table_entry_free(entry);
1693 		if (status)
1694 			goto error;
1695 	}
1696 
1697 error:
1698 	*file_line_number = line_id;
1699 	free(line);
1700 	return status;
1701 }
1702 
1703 static const char cmd_pipeline_learner_default_help[] =
1704 "pipeline <pipeline_name> learner <learner_name> default <file_name>\n";
1705 
1706 static void
1707 cmd_pipeline_learner_default(char **tokens,
1708 			     uint32_t n_tokens,
1709 			     char *out,
1710 			     size_t out_size,
1711 			     void *obj __rte_unused)
1712 {
1713 	struct rte_swx_ctl_pipeline *ctl;
1714 	char *pipeline_name, *learner_name, *file_name;
1715 	FILE *file = NULL;
1716 	uint32_t file_line_number = 0;
1717 	int status;
1718 
1719 	if (n_tokens != 6) {
1720 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1721 		return;
1722 	}
1723 
1724 	pipeline_name = tokens[1];
1725 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
1726 	if (!ctl) {
1727 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1728 		return;
1729 	}
1730 
1731 	learner_name = tokens[3];
1732 
1733 	file_name = tokens[5];
1734 	file = fopen(file_name, "r");
1735 	if (!file) {
1736 		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
1737 		return;
1738 	}
1739 
1740 	status = pipeline_learner_default_entry_add(ctl,
1741 						    learner_name,
1742 						    file,
1743 						    &file_line_number);
1744 	if (status)
1745 		snprintf(out, out_size, "Invalid entry in file %s at line %u\n",
1746 			 file_name,
1747 			 file_line_number);
1748 
1749 	fclose(file);
1750 }
1751 
1752 static const char cmd_pipeline_commit_help[] =
1753 "pipeline <pipeline_name> commit\n";
1754 
1755 static void
1756 cmd_pipeline_commit(char **tokens,
1757 	uint32_t n_tokens,
1758 	char *out,
1759 	size_t out_size,
1760 	void *obj __rte_unused)
1761 {
1762 	struct rte_swx_ctl_pipeline *ctl;
1763 	char *pipeline_name;
1764 	int status;
1765 
1766 	if (n_tokens != 3) {
1767 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1768 		return;
1769 	}
1770 
1771 	pipeline_name = tokens[1];
1772 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
1773 	if (!ctl) {
1774 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1775 		return;
1776 	}
1777 
1778 	status = rte_swx_ctl_pipeline_commit(ctl, 1);
1779 	if (status)
1780 		snprintf(out, out_size, "Commit failed. "
1781 			"Use \"commit\" to retry or \"abort\" to discard the pending work.\n");
1782 }
1783 
1784 static const char cmd_pipeline_abort_help[] =
1785 "pipeline <pipeline_name> abort\n";
1786 
1787 static void
1788 cmd_pipeline_abort(char **tokens,
1789 	uint32_t n_tokens,
1790 	char *out,
1791 	size_t out_size,
1792 	void *obj __rte_unused)
1793 {
1794 	struct rte_swx_ctl_pipeline *ctl;
1795 	char *pipeline_name;
1796 
1797 	if (n_tokens != 3) {
1798 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1799 		return;
1800 	}
1801 
1802 	pipeline_name = tokens[1];
1803 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
1804 	if (!ctl) {
1805 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1806 		return;
1807 	}
1808 
1809 	rte_swx_ctl_pipeline_abort(ctl);
1810 }
1811 
1812 static const char cmd_pipeline_regrd_help[] =
1813 "pipeline <pipeline_name> regrd <register_array_name>\n"
1814 	"index <index>\n"
1815 	" | table <table_name> match <field0> ...\n";
1816 
1817 static void
1818 cmd_pipeline_regrd(char **tokens,
1819 	uint32_t n_tokens,
1820 	char *out,
1821 	size_t out_size,
1822 	void *obj __rte_unused)
1823 {
1824 	struct rte_swx_pipeline *p;
1825 	struct rte_swx_ctl_pipeline *ctl;
1826 	const char *pipeline_name, *name;
1827 	uint64_t value;
1828 	int status;
1829 
1830 	if (n_tokens < 5) {
1831 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1832 		return;
1833 	}
1834 
1835 	pipeline_name = tokens[1];
1836 	p = rte_swx_pipeline_find(pipeline_name);
1837 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
1838 	if (!p || !ctl) {
1839 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1840 		return;
1841 	}
1842 
1843 	if (strcmp(tokens[2], "regrd")) {
1844 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "regrd");
1845 		return;
1846 	}
1847 
1848 	name = tokens[3];
1849 
1850 	/* index. */
1851 	if (!strcmp(tokens[4], "index")) {
1852 		uint32_t idx = 0;
1853 
1854 		if (n_tokens != 6) {
1855 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1856 			return;
1857 		}
1858 
1859 		if (parser_read_uint32(&idx, tokens[5])) {
1860 			snprintf(out, out_size, MSG_ARG_INVALID, "index");
1861 			return;
1862 		}
1863 
1864 		status = rte_swx_ctl_pipeline_regarray_read(p, name, idx, &value);
1865 		if (status) {
1866 			snprintf(out, out_size, "Command failed.\n");
1867 			return;
1868 		}
1869 
1870 		snprintf(out, out_size, "0x%" PRIx64 "\n", value);
1871 		return;
1872 	}
1873 
1874 	/* table. */
1875 	if (!strcmp(tokens[4], "table")) {
1876 		struct rte_swx_table_entry *entry;
1877 		char *table_name;
1878 
1879 		if (n_tokens < 8) {
1880 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1881 			return;
1882 		}
1883 
1884 		table_name = tokens[5];
1885 
1886 		if (strcmp(tokens[6], "match")) {
1887 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "match");
1888 			return;
1889 		}
1890 
1891 		entry = parse_table_entry(ctl, table_name, &tokens[6], n_tokens - 6);
1892 		if (!entry) {
1893 			snprintf(out, out_size, "Invalid match tokens.\n");
1894 			return;
1895 		}
1896 
1897 		status = rte_swx_ctl_pipeline_regarray_read_with_key(p,
1898 								     name,
1899 								     table_name,
1900 								     entry->key,
1901 								     &value);
1902 		table_entry_free(entry);
1903 		if (status) {
1904 			snprintf(out, out_size, "Command failed.\n");
1905 			return;
1906 		}
1907 
1908 		snprintf(out, out_size, "0x%" PRIx64 "\n", value);
1909 		return;
1910 	}
1911 
1912 	/* anything else. */
1913 	snprintf(out, out_size, "Invalid token %s\n.", tokens[4]);
1914 	return;
1915 }
1916 
1917 static const char cmd_pipeline_regwr_help[] =
1918 "pipeline <pipeline_name> regwr <register_array_name> value <value>\n"
1919 	"index <index>\n"
1920 	" | table <table_name> match <field0> ...\n";
1921 
1922 static void
1923 cmd_pipeline_regwr(char **tokens,
1924 	uint32_t n_tokens,
1925 	char *out,
1926 	size_t out_size,
1927 	void *obj __rte_unused)
1928 {
1929 	struct rte_swx_pipeline *p;
1930 	struct rte_swx_ctl_pipeline *ctl;
1931 	const char *pipeline_name, *name;
1932 	uint64_t value = 0;
1933 	int status;
1934 
1935 	if (n_tokens < 7) {
1936 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1937 		return;
1938 	}
1939 
1940 	pipeline_name = tokens[1];
1941 	p = rte_swx_pipeline_find(pipeline_name);
1942 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
1943 	if (!p || !ctl) {
1944 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
1945 		return;
1946 	}
1947 
1948 	if (strcmp(tokens[2], "regwr")) {
1949 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "regwr");
1950 		return;
1951 	}
1952 
1953 	name = tokens[3];
1954 
1955 	if (strcmp(tokens[4], "value")) {
1956 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "value");
1957 		return;
1958 	}
1959 
1960 	if (parser_read_uint64(&value, tokens[5])) {
1961 		snprintf(out, out_size, MSG_ARG_INVALID, "value");
1962 		return;
1963 	}
1964 
1965 	/* index. */
1966 	if (!strcmp(tokens[6], "index")) {
1967 		uint32_t idx = 0;
1968 
1969 		if (n_tokens != 8) {
1970 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1971 			return;
1972 		}
1973 
1974 		if (parser_read_uint32(&idx, tokens[7])) {
1975 			snprintf(out, out_size, MSG_ARG_INVALID, "index");
1976 			return;
1977 		}
1978 
1979 		status = rte_swx_ctl_pipeline_regarray_write(p, name, idx, value);
1980 		if (status) {
1981 			snprintf(out, out_size, "Command failed.\n");
1982 			return;
1983 		}
1984 
1985 		snprintf(out, out_size, "0x%" PRIx64 "\n", value);
1986 		return;
1987 	}
1988 
1989 	/* table. */
1990 	if (!strcmp(tokens[6], "table")) {
1991 		struct rte_swx_table_entry *entry;
1992 		char *table_name;
1993 
1994 		if (n_tokens < 10) {
1995 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
1996 			return;
1997 		}
1998 
1999 		table_name = tokens[7];
2000 
2001 		if (strcmp(tokens[8], "match")) {
2002 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "match");
2003 			return;
2004 		}
2005 
2006 		entry = parse_table_entry(ctl, table_name, &tokens[8], n_tokens - 8);
2007 		if (!entry) {
2008 			snprintf(out, out_size, "Invalid match tokens.\n");
2009 			return;
2010 		}
2011 
2012 		status = rte_swx_ctl_pipeline_regarray_write_with_key(p,
2013 								      name,
2014 								      table_name,
2015 								      entry->key,
2016 								      value);
2017 		table_entry_free(entry);
2018 		if (status) {
2019 			snprintf(out, out_size, "Command failed.\n");
2020 			return;
2021 		}
2022 
2023 		return;
2024 	}
2025 
2026 	/* anything else. */
2027 	snprintf(out, out_size, "Invalid token %s\n.", tokens[6]);
2028 	return;
2029 }
2030 
2031 static const char cmd_pipeline_meter_profile_add_help[] =
2032 "pipeline <pipeline_name> meter profile <profile_name> add "
2033 	"cir <cir> pir <pir> cbs <cbs> pbs <pbs>\n";
2034 
2035 static void
2036 cmd_pipeline_meter_profile_add(char **tokens,
2037 	uint32_t n_tokens,
2038 	char *out,
2039 	size_t out_size,
2040 	void *obj __rte_unused)
2041 {
2042 	struct rte_meter_trtcm_params params;
2043 	struct rte_swx_pipeline *p;
2044 	const char *profile_name;
2045 	int status;
2046 
2047 	if (n_tokens != 14) {
2048 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2049 		return;
2050 	}
2051 
2052 	p = rte_swx_pipeline_find(tokens[1]);
2053 	if (!p) {
2054 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2055 		return;
2056 	}
2057 
2058 	if (strcmp(tokens[2], "meter")) {
2059 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
2060 		return;
2061 	}
2062 
2063 	if (strcmp(tokens[3], "profile")) {
2064 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
2065 		return;
2066 	}
2067 
2068 	profile_name = tokens[4];
2069 
2070 	if (strcmp(tokens[5], "add")) {
2071 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
2072 		return;
2073 	}
2074 
2075 	if (strcmp(tokens[6], "cir")) {
2076 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cir");
2077 		return;
2078 	}
2079 
2080 	if (parser_read_uint64(&params.cir, tokens[7])) {
2081 		snprintf(out, out_size, MSG_ARG_INVALID, "cir");
2082 		return;
2083 	}
2084 
2085 	if (strcmp(tokens[8], "pir")) {
2086 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pir");
2087 		return;
2088 	}
2089 
2090 	if (parser_read_uint64(&params.pir, tokens[9])) {
2091 		snprintf(out, out_size, MSG_ARG_INVALID, "pir");
2092 		return;
2093 	}
2094 
2095 	if (strcmp(tokens[10], "cbs")) {
2096 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cbs");
2097 		return;
2098 	}
2099 
2100 	if (parser_read_uint64(&params.cbs, tokens[11])) {
2101 		snprintf(out, out_size, MSG_ARG_INVALID, "cbs");
2102 		return;
2103 	}
2104 
2105 	if (strcmp(tokens[12], "pbs")) {
2106 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pbs");
2107 		return;
2108 	}
2109 
2110 	if (parser_read_uint64(&params.pbs, tokens[13])) {
2111 		snprintf(out, out_size, MSG_ARG_INVALID, "pbs");
2112 		return;
2113 	}
2114 
2115 	status = rte_swx_ctl_meter_profile_add(p, profile_name, &params);
2116 	if (status) {
2117 		snprintf(out, out_size, "Command failed.\n");
2118 		return;
2119 	}
2120 }
2121 
2122 static const char cmd_pipeline_meter_profile_delete_help[] =
2123 "pipeline <pipeline_name> meter profile <profile_name> delete\n";
2124 
2125 static void
2126 cmd_pipeline_meter_profile_delete(char **tokens,
2127 	uint32_t n_tokens,
2128 	char *out,
2129 	size_t out_size,
2130 	void *obj __rte_unused)
2131 {
2132 	struct rte_swx_pipeline *p;
2133 	const char *profile_name;
2134 	int status;
2135 
2136 	if (n_tokens != 6) {
2137 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2138 		return;
2139 	}
2140 
2141 	p = rte_swx_pipeline_find(tokens[1]);
2142 	if (!p) {
2143 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2144 		return;
2145 	}
2146 
2147 	if (strcmp(tokens[2], "meter")) {
2148 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
2149 		return;
2150 	}
2151 
2152 	if (strcmp(tokens[3], "profile")) {
2153 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
2154 		return;
2155 	}
2156 
2157 	profile_name = tokens[4];
2158 
2159 	if (strcmp(tokens[5], "delete")) {
2160 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
2161 		return;
2162 	}
2163 
2164 	status = rte_swx_ctl_meter_profile_delete(p, profile_name);
2165 	if (status) {
2166 		snprintf(out, out_size, "Command failed.\n");
2167 		return;
2168 	}
2169 }
2170 
2171 static const char cmd_pipeline_meter_reset_help[] =
2172 "pipeline <pipeline_name> meter <meter_array_name> reset\n"
2173 	"index from <index0> to <index1>\n"
2174 	" | table <table_name> match <field0> ...\n";
2175 
2176 static void
2177 cmd_pipeline_meter_reset(char **tokens,
2178 	uint32_t n_tokens,
2179 	char *out,
2180 	size_t out_size,
2181 	void *obj __rte_unused)
2182 {
2183 	struct rte_swx_pipeline *p;
2184 	struct rte_swx_ctl_pipeline *ctl;
2185 	const char *pipeline_name, *name;
2186 
2187 	if (n_tokens < 6) {
2188 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2189 		return;
2190 	}
2191 
2192 	pipeline_name = tokens[1];
2193 	p = rte_swx_pipeline_find(pipeline_name);
2194 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
2195 	if (!p || !ctl) {
2196 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2197 		return;
2198 	}
2199 
2200 	if (strcmp(tokens[2], "meter")) {
2201 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
2202 		return;
2203 	}
2204 
2205 	name = tokens[3];
2206 
2207 	if (strcmp(tokens[4], "reset")) {
2208 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "reset");
2209 		return;
2210 	}
2211 
2212 	/* index. */
2213 	if (!strcmp(tokens[5], "index")) {
2214 		uint32_t idx0 = 0, idx1 = 0;
2215 
2216 		if (n_tokens != 10) {
2217 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2218 			return;
2219 		}
2220 
2221 		if (strcmp(tokens[6], "from")) {
2222 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "from");
2223 			return;
2224 		}
2225 
2226 		if (parser_read_uint32(&idx0, tokens[7])) {
2227 			snprintf(out, out_size, MSG_ARG_INVALID, "index0");
2228 			return;
2229 		}
2230 
2231 		if (strcmp(tokens[8], "to")) {
2232 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "to");
2233 			return;
2234 		}
2235 
2236 		if (parser_read_uint32(&idx1, tokens[9]) || (idx1 < idx0)) {
2237 			snprintf(out, out_size, MSG_ARG_INVALID, "index1");
2238 			return;
2239 		}
2240 
2241 		for ( ; idx0 <= idx1; idx0++) {
2242 			int status;
2243 
2244 			status = rte_swx_ctl_meter_reset(p, name, idx0);
2245 			if (status) {
2246 				snprintf(out, out_size, "Command failed for index %u.\n", idx0);
2247 				return;
2248 			}
2249 		}
2250 
2251 		return;
2252 	}
2253 
2254 	/* table. */
2255 	if (!strcmp(tokens[5], "table")) {
2256 		struct rte_swx_table_entry *entry;
2257 		char *table_name;
2258 		int status;
2259 
2260 		if (n_tokens < 9) {
2261 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2262 			return;
2263 		}
2264 
2265 		table_name = tokens[6];
2266 
2267 		if (strcmp(tokens[7], "match")) {
2268 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "match");
2269 			return;
2270 		}
2271 
2272 		entry = parse_table_entry(ctl, table_name, &tokens[7], n_tokens - 7);
2273 		if (!entry) {
2274 			snprintf(out, out_size, "Invalid match tokens.\n");
2275 			return;
2276 		}
2277 
2278 		status = rte_swx_ctl_meter_reset_with_key(p, name, table_name, entry->key);
2279 		table_entry_free(entry);
2280 		if (status) {
2281 			snprintf(out, out_size, "Command failed.\n");
2282 			return;
2283 		}
2284 
2285 		return;
2286 	}
2287 
2288 	/* anything else. */
2289 	snprintf(out, out_size, "Invalid token %s\n.", tokens[5]);
2290 	return;
2291 }
2292 
2293 static const char cmd_pipeline_meter_set_help[] =
2294 "pipeline <pipeline_name> meter <meter_array_name> set profile <profile_name>\n"
2295 	"index from <index0> to <index1>\n"
2296 	" | table <table_name> match <field0> ...\n";
2297 
2298 static void
2299 cmd_pipeline_meter_set(char **tokens,
2300 	uint32_t n_tokens,
2301 	char *out,
2302 	size_t out_size,
2303 	void *obj __rte_unused)
2304 {
2305 	struct rte_swx_pipeline *p;
2306 	struct rte_swx_ctl_pipeline *ctl;
2307 	const char *pipeline_name, *name, *profile_name;
2308 
2309 	if (n_tokens < 8) {
2310 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2311 		return;
2312 	}
2313 
2314 	pipeline_name = tokens[1];
2315 	p = rte_swx_pipeline_find(pipeline_name);
2316 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
2317 	if (!p || !ctl) {
2318 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2319 		return;
2320 	}
2321 
2322 	if (strcmp(tokens[2], "meter")) {
2323 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
2324 		return;
2325 	}
2326 
2327 	name = tokens[3];
2328 
2329 	if (strcmp(tokens[4], "set")) {
2330 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "set");
2331 		return;
2332 	}
2333 
2334 	if (strcmp(tokens[5], "profile")) {
2335 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "profile");
2336 		return;
2337 	}
2338 
2339 	profile_name = tokens[6];
2340 
2341 	/* index. */
2342 	if (!strcmp(tokens[7], "index")) {
2343 		uint32_t idx0 = 0, idx1 = 0;
2344 
2345 		if (n_tokens != 12) {
2346 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2347 			return;
2348 		}
2349 
2350 		if (strcmp(tokens[8], "from")) {
2351 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "from");
2352 			return;
2353 		}
2354 
2355 		if (parser_read_uint32(&idx0, tokens[9])) {
2356 			snprintf(out, out_size, MSG_ARG_INVALID, "index0");
2357 			return;
2358 		}
2359 
2360 		if (strcmp(tokens[10], "to")) {
2361 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "to");
2362 			return;
2363 		}
2364 
2365 		if (parser_read_uint32(&idx1, tokens[11]) || (idx1 < idx0)) {
2366 			snprintf(out, out_size, MSG_ARG_INVALID, "index1");
2367 			return;
2368 		}
2369 
2370 		for ( ; idx0 <= idx1; idx0++) {
2371 			int status;
2372 
2373 			status = rte_swx_ctl_meter_set(p, name, idx0, profile_name);
2374 			if (status) {
2375 				snprintf(out, out_size, "Command failed for index %u.\n", idx0);
2376 				return;
2377 			}
2378 		}
2379 
2380 		return;
2381 	}
2382 
2383 	/* table. */
2384 	if (!strcmp(tokens[7], "table")) {
2385 		struct rte_swx_table_entry *entry;
2386 		char *table_name;
2387 		int status;
2388 
2389 		if (n_tokens < 11) {
2390 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2391 			return;
2392 		}
2393 
2394 		table_name = tokens[8];
2395 
2396 		if (strcmp(tokens[9], "match")) {
2397 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "match");
2398 			return;
2399 		}
2400 
2401 		entry = parse_table_entry(ctl, table_name, &tokens[9], n_tokens - 9);
2402 		if (!entry) {
2403 			snprintf(out, out_size, "Invalid match tokens.\n");
2404 			return;
2405 		}
2406 
2407 		status = rte_swx_ctl_meter_set_with_key(p,
2408 							name,
2409 							table_name,
2410 							entry->key,
2411 							profile_name);
2412 		table_entry_free(entry);
2413 		if (status) {
2414 			snprintf(out, out_size, "Command failed.\n");
2415 			return;
2416 		}
2417 
2418 		return;
2419 	}
2420 
2421 	/* anything else. */
2422 	snprintf(out, out_size, "Invalid token %s\n.", tokens[7]);
2423 	return;
2424 }
2425 
2426 static const char cmd_pipeline_meter_stats_help[] =
2427 "pipeline <pipeline_name> meter <meter_array_name> stats\n"
2428 	"index from <index0> to <index1>\n"
2429 	" | table <table_name> match <field0> ...\n";
2430 
2431 static void
2432 cmd_pipeline_meter_stats(char **tokens,
2433 	uint32_t n_tokens,
2434 	char *out,
2435 	size_t out_size,
2436 	void *obj __rte_unused)
2437 {
2438 	struct rte_swx_ctl_meter_stats stats;
2439 	struct rte_swx_pipeline *p;
2440 	struct rte_swx_ctl_pipeline *ctl;
2441 	const char *pipeline_name, *name;
2442 
2443 	if (n_tokens < 6) {
2444 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2445 		return;
2446 	}
2447 
2448 	pipeline_name = tokens[1];
2449 	p = rte_swx_pipeline_find(pipeline_name);
2450 	ctl = rte_swx_ctl_pipeline_find(pipeline_name);
2451 	if (!p || !ctl) {
2452 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2453 		return;
2454 	}
2455 
2456 	if (strcmp(tokens[2], "meter")) {
2457 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "meter");
2458 		return;
2459 	}
2460 
2461 	name = tokens[3];
2462 
2463 	if (strcmp(tokens[4], "stats")) {
2464 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
2465 		return;
2466 	}
2467 
2468 	/* index. */
2469 	if (!strcmp(tokens[5], "index")) {
2470 		uint32_t idx0 = 0, idx1 = 0;
2471 
2472 		if (n_tokens != 10) {
2473 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2474 			return;
2475 		}
2476 
2477 		if (strcmp(tokens[6], "from")) {
2478 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "from");
2479 			return;
2480 		}
2481 
2482 		if (parser_read_uint32(&idx0, tokens[7])) {
2483 			snprintf(out, out_size, MSG_ARG_INVALID, "index0");
2484 			return;
2485 		}
2486 
2487 		if (strcmp(tokens[8], "to")) {
2488 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "to");
2489 			return;
2490 		}
2491 
2492 		if (parser_read_uint32(&idx1, tokens[9]) || (idx1 < idx0)) {
2493 			snprintf(out, out_size, MSG_ARG_INVALID, "index1");
2494 			return;
2495 		}
2496 
2497 		/* Table header. */
2498 		snprintf(out, out_size, "+-%7s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+\n",
2499 			 "-------",
2500 			 "----------------", "----------------", "----------------",
2501 			 "----------------", "----------------", "----------------");
2502 		out_size -= strlen(out);
2503 		out += strlen(out);
2504 
2505 		snprintf(out, out_size, "| %4s | %16s | %16s | %16s | %16s | %16s | %16s |\n",
2506 			 "METER #",
2507 			 "GREEN (packets)", "YELLOW (packets)", "RED (packets)",
2508 			 "GREEN (bytes)", "YELLOW (bytes)", "RED (bytes)");
2509 		out_size -= strlen(out);
2510 		out += strlen(out);
2511 
2512 		snprintf(out, out_size, "+-%7s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+\n",
2513 			 "-------",
2514 			 "----------------", "----------------", "----------------",
2515 			 "----------------", "----------------", "----------------");
2516 		out_size -= strlen(out);
2517 		out += strlen(out);
2518 
2519 		/* Table rows. */
2520 		for ( ; idx0 <= idx1; idx0++) {
2521 			int status;
2522 
2523 			status = rte_swx_ctl_meter_stats_read(p, name, idx0, &stats);
2524 			if (status) {
2525 				snprintf(out, out_size, "Meter stats error at index %u.\n", idx0);
2526 				out_size -= strlen(out);
2527 				out += strlen(out);
2528 				return;
2529 			}
2530 
2531 			snprintf(out, out_size, "| %7d | %16" PRIx64 " | %16" PRIx64 " | %16" PRIx64
2532 				 " | %16" PRIx64 " | %16" PRIx64 " | %16" PRIx64 " |\n",
2533 				 idx0,
2534 				 stats.n_pkts[RTE_COLOR_GREEN],
2535 				 stats.n_pkts[RTE_COLOR_YELLOW],
2536 				 stats.n_pkts[RTE_COLOR_RED],
2537 				 stats.n_bytes[RTE_COLOR_GREEN],
2538 				 stats.n_bytes[RTE_COLOR_YELLOW],
2539 				 stats.n_bytes[RTE_COLOR_RED]);
2540 			out_size -= strlen(out);
2541 			out += strlen(out);
2542 		}
2543 
2544 		return;
2545 	}
2546 
2547 	/* table. */
2548 	if (!strcmp(tokens[5], "table")) {
2549 		struct rte_swx_table_entry *entry;
2550 		char *table_name;
2551 		int status;
2552 
2553 		if (n_tokens < 9) {
2554 			snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2555 			return;
2556 		}
2557 
2558 		table_name = tokens[6];
2559 
2560 		if (strcmp(tokens[7], "match")) {
2561 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "match");
2562 			return;
2563 		}
2564 
2565 		entry = parse_table_entry(ctl, table_name, &tokens[7], n_tokens - 7);
2566 		if (!entry) {
2567 			snprintf(out, out_size, "Invalid match tokens.\n");
2568 			return;
2569 		}
2570 
2571 		status = rte_swx_ctl_meter_stats_read_with_key(p,
2572 							name,
2573 							table_name,
2574 							entry->key,
2575 							&stats);
2576 		table_entry_free(entry);
2577 		if (status) {
2578 			snprintf(out, out_size, "Command failed.\n");
2579 			return;
2580 		}
2581 
2582 		/* Table header. */
2583 		snprintf(out, out_size, "+-%7s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+\n",
2584 			 "-------",
2585 			 "----------------", "----------------", "----------------",
2586 			 "----------------", "----------------", "----------------");
2587 		out_size -= strlen(out);
2588 		out += strlen(out);
2589 
2590 		snprintf(out, out_size, "| %4s | %16s | %16s | %16s | %16s | %16s | %16s |\n",
2591 			 "METER #",
2592 			 "GREEN (packets)", "YELLOW (packets)", "RED (packets)",
2593 			 "GREEN (bytes)", "YELLOW (bytes)", "RED (bytes)");
2594 		out_size -= strlen(out);
2595 		out += strlen(out);
2596 
2597 		snprintf(out, out_size, "+-%7s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+-%16s-+\n",
2598 			 "-------",
2599 			 "----------------", "----------------", "----------------",
2600 			 "----------------", "----------------", "----------------");
2601 		out_size -= strlen(out);
2602 		out += strlen(out);
2603 
2604 		/* Table row. */
2605 		snprintf(out, out_size, "| %7d | %16" PRIx64 " | %16" PRIx64 " | %16" PRIx64
2606 			 " | %16" PRIx64 " | %16" PRIx64 " | %16" PRIx64 " |\n",
2607 			 0,
2608 			 stats.n_pkts[RTE_COLOR_GREEN],
2609 			 stats.n_pkts[RTE_COLOR_YELLOW],
2610 			 stats.n_pkts[RTE_COLOR_RED],
2611 			 stats.n_bytes[RTE_COLOR_GREEN],
2612 			 stats.n_bytes[RTE_COLOR_YELLOW],
2613 			 stats.n_bytes[RTE_COLOR_RED]);
2614 		out_size -= strlen(out);
2615 		out += strlen(out);
2616 
2617 		return;
2618 	}
2619 
2620 	/* anything else. */
2621 	snprintf(out, out_size, "Invalid token %s\n.", tokens[5]);
2622 	return;
2623 }
2624 
2625 static const char cmd_pipeline_stats_help[] =
2626 "pipeline <pipeline_name> stats\n";
2627 
2628 static void
2629 cmd_pipeline_stats(char **tokens,
2630 	uint32_t n_tokens,
2631 	char *out,
2632 	size_t out_size,
2633 	void *obj __rte_unused)
2634 {
2635 	struct rte_swx_ctl_pipeline_info info;
2636 	struct rte_swx_pipeline *p;
2637 	uint32_t i;
2638 	int status;
2639 
2640 	if (n_tokens != 3) {
2641 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2642 		return;
2643 	}
2644 
2645 	p = rte_swx_pipeline_find(tokens[1]);
2646 	if (!p) {
2647 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2648 		return;
2649 	}
2650 
2651 	if (strcmp(tokens[2], "stats")) {
2652 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "stats");
2653 		return;
2654 	}
2655 
2656 	status = rte_swx_ctl_pipeline_info_get(p, &info);
2657 	if (status) {
2658 		snprintf(out, out_size, "Pipeline info get error.");
2659 		return;
2660 	}
2661 
2662 	snprintf(out, out_size, "Input ports:\n");
2663 	out_size -= strlen(out);
2664 	out += strlen(out);
2665 
2666 	for (i = 0; i < info.n_ports_in; i++) {
2667 		struct rte_swx_port_in_stats stats;
2668 
2669 		rte_swx_ctl_pipeline_port_in_stats_read(p, i, &stats);
2670 
2671 		snprintf(out, out_size, "\tPort %u:"
2672 			" packets %" PRIu64
2673 			" bytes %" PRIu64
2674 			" empty %" PRIu64 "\n",
2675 			i, stats.n_pkts, stats.n_bytes, stats.n_empty);
2676 		out_size -= strlen(out);
2677 		out += strlen(out);
2678 	}
2679 
2680 	snprintf(out, out_size, "\nOutput ports:\n");
2681 	out_size -= strlen(out);
2682 	out += strlen(out);
2683 
2684 	for (i = 0; i < info.n_ports_out; i++) {
2685 		struct rte_swx_port_out_stats stats;
2686 
2687 		rte_swx_ctl_pipeline_port_out_stats_read(p, i, &stats);
2688 
2689 		if (i != info.n_ports_out - 1)
2690 			snprintf(out, out_size, "\tPort %u:", i);
2691 		else
2692 			snprintf(out, out_size, "\tDROP:");
2693 
2694 		out_size -= strlen(out);
2695 		out += strlen(out);
2696 
2697 		snprintf(out,
2698 			out_size,
2699 			" packets %" PRIu64
2700 			" bytes %" PRIu64
2701 			" packets dropped %" PRIu64
2702 			" bytes dropped %" PRIu64
2703 			" clone %" PRIu64
2704 			" clonerr %" PRIu64 "\n",
2705 			stats.n_pkts,
2706 			stats.n_bytes,
2707 			stats.n_pkts_drop,
2708 			stats.n_bytes_drop,
2709 			stats.n_pkts_clone,
2710 			stats.n_pkts_clone_err);
2711 
2712 		out_size -= strlen(out);
2713 		out += strlen(out);
2714 	}
2715 
2716 	snprintf(out, out_size, "\nTables:\n");
2717 	out_size -= strlen(out);
2718 	out += strlen(out);
2719 
2720 	for (i = 0; i < info.n_tables; i++) {
2721 		struct rte_swx_ctl_table_info table_info;
2722 		uint64_t n_pkts_action[info.n_actions];
2723 		struct rte_swx_table_stats stats = {
2724 			.n_pkts_hit = 0,
2725 			.n_pkts_miss = 0,
2726 			.n_pkts_action = n_pkts_action,
2727 		};
2728 		uint32_t j;
2729 
2730 		status = rte_swx_ctl_table_info_get(p, i, &table_info);
2731 		if (status) {
2732 			snprintf(out, out_size, "Table info get error.");
2733 			return;
2734 		}
2735 
2736 		status = rte_swx_ctl_pipeline_table_stats_read(p, table_info.name, &stats);
2737 		if (status) {
2738 			snprintf(out, out_size, "Table stats read error.");
2739 			return;
2740 		}
2741 
2742 		snprintf(out, out_size, "\tTable %s:\n"
2743 			"\t\tHit (packets): %" PRIu64 "\n"
2744 			"\t\tMiss (packets): %" PRIu64 "\n",
2745 			table_info.name,
2746 			stats.n_pkts_hit,
2747 			stats.n_pkts_miss);
2748 		out_size -= strlen(out);
2749 		out += strlen(out);
2750 
2751 		for (j = 0; j < info.n_actions; j++) {
2752 			struct rte_swx_ctl_action_info action_info;
2753 
2754 			status = rte_swx_ctl_action_info_get(p, j, &action_info);
2755 			if (status) {
2756 				snprintf(out, out_size, "Action info get error.");
2757 				return;
2758 			}
2759 
2760 			snprintf(out, out_size, "\t\tAction %s (packets): %" PRIu64 "\n",
2761 				action_info.name,
2762 				stats.n_pkts_action[j]);
2763 			out_size -= strlen(out);
2764 			out += strlen(out);
2765 		}
2766 	}
2767 
2768 	snprintf(out, out_size, "\nLearner tables:\n");
2769 	out_size -= strlen(out);
2770 	out += strlen(out);
2771 
2772 	for (i = 0; i < info.n_learners; i++) {
2773 		struct rte_swx_ctl_learner_info learner_info;
2774 		uint64_t n_pkts_action[info.n_actions];
2775 		struct rte_swx_learner_stats stats = {
2776 			.n_pkts_hit = 0,
2777 			.n_pkts_miss = 0,
2778 			.n_pkts_action = n_pkts_action,
2779 		};
2780 		uint32_t j;
2781 
2782 		status = rte_swx_ctl_learner_info_get(p, i, &learner_info);
2783 		if (status) {
2784 			snprintf(out, out_size, "Learner table info get error.");
2785 			return;
2786 		}
2787 
2788 		status = rte_swx_ctl_pipeline_learner_stats_read(p, learner_info.name, &stats);
2789 		if (status) {
2790 			snprintf(out, out_size, "Learner table stats read error.");
2791 			return;
2792 		}
2793 
2794 		snprintf(out, out_size, "\tLearner table %s:\n"
2795 			"\t\tHit (packets): %" PRIu64 "\n"
2796 			"\t\tMiss (packets): %" PRIu64 "\n"
2797 			"\t\tLearn OK (packets): %" PRIu64 "\n"
2798 			"\t\tLearn error (packets): %" PRIu64 "\n"
2799 			"\t\tRearm (packets): %" PRIu64 "\n"
2800 			"\t\tForget (packets): %" PRIu64 "\n",
2801 			learner_info.name,
2802 			stats.n_pkts_hit,
2803 			stats.n_pkts_miss,
2804 			stats.n_pkts_learn_ok,
2805 			stats.n_pkts_learn_err,
2806 			stats.n_pkts_rearm,
2807 			stats.n_pkts_forget);
2808 		out_size -= strlen(out);
2809 		out += strlen(out);
2810 
2811 		for (j = 0; j < info.n_actions; j++) {
2812 			struct rte_swx_ctl_action_info action_info;
2813 
2814 			status = rte_swx_ctl_action_info_get(p, j, &action_info);
2815 			if (status) {
2816 				snprintf(out, out_size, "Action info get error.");
2817 				return;
2818 			}
2819 
2820 			snprintf(out, out_size, "\t\tAction %s (packets): %" PRIu64 "\n",
2821 				action_info.name,
2822 				stats.n_pkts_action[j]);
2823 			out_size -= strlen(out);
2824 			out += strlen(out);
2825 		}
2826 	}
2827 }
2828 
2829 static const char cmd_pipeline_mirror_session_help[] =
2830 "pipeline <pipeline_name> mirror session <session_id> port <port_id> clone fast | slow "
2831 "truncate <truncation_length>\n";
2832 
2833 static void
2834 cmd_pipeline_mirror_session(char **tokens,
2835 	uint32_t n_tokens,
2836 	char *out,
2837 	size_t out_size,
2838 	void *obj __rte_unused)
2839 {
2840 	struct rte_swx_pipeline_mirroring_session_params params;
2841 	struct rte_swx_pipeline *p;
2842 	uint32_t session_id = 0;
2843 	int status;
2844 
2845 	if (n_tokens != 11) {
2846 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2847 		return;
2848 	}
2849 
2850 	if (strcmp(tokens[0], "pipeline")) {
2851 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
2852 		return;
2853 	}
2854 
2855 	p = rte_swx_pipeline_find(tokens[1]);
2856 	if (!p) {
2857 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
2858 		return;
2859 	}
2860 
2861 	if (strcmp(tokens[2], "mirror")) {
2862 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mirror");
2863 		return;
2864 	}
2865 
2866 	if (strcmp(tokens[3], "session")) {
2867 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "session");
2868 		return;
2869 	}
2870 
2871 	if (parser_read_uint32(&session_id, tokens[4])) {
2872 		snprintf(out, out_size, MSG_ARG_INVALID, "session_id");
2873 		return;
2874 	}
2875 
2876 	if (strcmp(tokens[5], "port")) {
2877 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "port");
2878 		return;
2879 	}
2880 
2881 	if (parser_read_uint32(&params.port_id, tokens[6])) {
2882 		snprintf(out, out_size, MSG_ARG_INVALID, "port_id");
2883 		return;
2884 	}
2885 
2886 	if (strcmp(tokens[7], "clone")) {
2887 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "clone");
2888 		return;
2889 	}
2890 
2891 	if (!strcmp(tokens[8], "fast"))
2892 		params.fast_clone = 1;
2893 	else if (!strcmp(tokens[8], "slow"))
2894 		params.fast_clone = 0;
2895 	else {
2896 		snprintf(out, out_size, MSG_ARG_INVALID, "clone");
2897 		return;
2898 	}
2899 
2900 	if (strcmp(tokens[9], "truncate")) {
2901 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "truncate");
2902 		return;
2903 	}
2904 
2905 	if (parser_read_uint32(&params.truncation_length, tokens[10])) {
2906 		snprintf(out, out_size, MSG_ARG_INVALID, "truncation_length");
2907 		return;
2908 	}
2909 
2910 	status = rte_swx_ctl_pipeline_mirroring_session_set(p, session_id, &params);
2911 	if (status) {
2912 		snprintf(out, out_size, "Command failed!\n");
2913 		return;
2914 	}
2915 }
2916 
2917 static const char cmd_ipsec_create_help[] =
2918 "ipsec <ipsec_instance_name> create "
2919 "in <ring_in_name> out <ring_out_name> "
2920 "cryptodev <crypto_dev_name> cryptoq <crypto_dev_queue_pair_id> "
2921 "bsz <ring_rd_bsz> <ring_wr_bsz> <crypto_wr_bsz> <crypto_rd_bsz> "
2922 "samax <n_sa_max> "
2923 "numa <numa_node>\n";
2924 
2925 static void
2926 cmd_ipsec_create(char **tokens,
2927 		 uint32_t n_tokens,
2928 		 char *out,
2929 		 size_t out_size,
2930 		 void *obj __rte_unused)
2931 {
2932 	struct rte_swx_ipsec_params p;
2933 	struct rte_swx_ipsec *ipsec;
2934 	char *ipsec_instance_name;
2935 	uint32_t numa_node;
2936 	int status;
2937 
2938 	if (n_tokens != 20) {
2939 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
2940 		return;
2941 	}
2942 
2943 	ipsec_instance_name = tokens[1];
2944 
2945 	if (strcmp(tokens[2], "create")) {
2946 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "create");
2947 		return;
2948 	}
2949 
2950 	if (strcmp(tokens[3], "in")) {
2951 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "in");
2952 		return;
2953 	}
2954 
2955 	p.ring_in_name = tokens[4];
2956 
2957 	if (strcmp(tokens[5], "out")) {
2958 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "out");
2959 		return;
2960 	}
2961 
2962 	p.ring_out_name = tokens[6];
2963 
2964 	if (strcmp(tokens[7], "cryptodev")) {
2965 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cryptodev");
2966 		return;
2967 	}
2968 
2969 	p.crypto_dev_name = tokens[8];
2970 
2971 	if (strcmp(tokens[9], "cryptoq")) {
2972 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cryptoq");
2973 		return;
2974 	}
2975 
2976 	if (parser_read_uint32(&p.crypto_dev_queue_pair_id, tokens[10])) {
2977 		snprintf(out, out_size, MSG_ARG_INVALID, "crypto_dev_queue_pair_id");
2978 		return;
2979 	}
2980 
2981 	if (strcmp(tokens[11], "bsz")) {
2982 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "bsz");
2983 		return;
2984 	}
2985 
2986 	if (parser_read_uint32(&p.bsz.ring_rd, tokens[12])) {
2987 		snprintf(out, out_size, MSG_ARG_INVALID, "ring_rd_bsz");
2988 		return;
2989 	}
2990 
2991 	if (parser_read_uint32(&p.bsz.ring_wr, tokens[13])) {
2992 		snprintf(out, out_size, MSG_ARG_INVALID, "ring_wr_bsz");
2993 		return;
2994 	}
2995 
2996 	if (parser_read_uint32(&p.bsz.crypto_wr, tokens[14])) {
2997 		snprintf(out, out_size, MSG_ARG_INVALID, "crypto_wr_bsz");
2998 		return;
2999 	}
3000 
3001 	if (parser_read_uint32(&p.bsz.crypto_rd, tokens[15])) {
3002 		snprintf(out, out_size, MSG_ARG_INVALID, "crypto_rd_bsz");
3003 		return;
3004 	}
3005 
3006 	if (strcmp(tokens[16], "samax")) {
3007 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "samax");
3008 		return;
3009 	}
3010 
3011 	if (parser_read_uint32(&p.n_sa_max, tokens[17])) {
3012 		snprintf(out, out_size, MSG_ARG_INVALID, "n_sa_max");
3013 		return;
3014 	}
3015 
3016 	if (strcmp(tokens[18], "numa")) {
3017 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa");
3018 		return;
3019 	}
3020 
3021 	if (parser_read_uint32(&numa_node, tokens[19])) {
3022 		snprintf(out, out_size, MSG_ARG_INVALID, "numa_node");
3023 		return;
3024 	}
3025 
3026 	status = rte_swx_ipsec_create(&ipsec,
3027 				      ipsec_instance_name,
3028 				      &p,
3029 				      (int)numa_node);
3030 	if (status)
3031 		snprintf(out, out_size, "IPsec instance creation failed (%d).\n", status);
3032 }
3033 
3034 static const char cmd_ipsec_sa_add_help[] =
3035 "ipsec <ipsec_instance_name> sa add <file_name>\n";
3036 
3037 static void
3038 cmd_ipsec_sa_add(char **tokens,
3039 		 uint32_t n_tokens,
3040 		 char *out,
3041 		 size_t out_size,
3042 		 void *obj __rte_unused)
3043 {
3044 	struct rte_swx_ipsec *ipsec;
3045 	char *ipsec_instance_name, *file_name, *line = NULL;
3046 	FILE *file = NULL;
3047 	uint32_t line_id = 0;
3048 
3049 	if (n_tokens != 5) {
3050 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3051 		return;
3052 	}
3053 
3054 	ipsec_instance_name = tokens[1];
3055 	ipsec = rte_swx_ipsec_find(ipsec_instance_name);
3056 	if (!ipsec) {
3057 		snprintf(out, out_size, MSG_ARG_INVALID, "ipsec_instance_name");
3058 		goto free;
3059 	}
3060 
3061 	if (strcmp(tokens[2], "sa")) {
3062 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "sa");
3063 		goto free;
3064 	}
3065 
3066 	if (strcmp(tokens[3], "add")) {
3067 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add");
3068 		goto free;
3069 	}
3070 
3071 	file_name = tokens[4];
3072 	file = fopen(file_name, "r");
3073 	if (!file) {
3074 		snprintf(out, out_size, "Cannot open file %s.\n", file_name);
3075 		goto free;
3076 	}
3077 
3078 	/* Buffer allocation. */
3079 	line = malloc(MAX_LINE_SIZE);
3080 	if (!line) {
3081 		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
3082 		goto free;
3083 	}
3084 
3085 	/* File read. */
3086 	for (line_id = 1; ; line_id++) {
3087 		struct rte_swx_ipsec_sa_params *sa;
3088 		const char *err_msg;
3089 		uint32_t sa_id = 0;
3090 		int is_blank_or_comment, status = 0;
3091 
3092 		if (fgets(line, MAX_LINE_SIZE, file) == NULL)
3093 			break;
3094 
3095 		/* Read SA from file. */
3096 		sa = rte_swx_ipsec_sa_read(ipsec, line, &is_blank_or_comment, &err_msg);
3097 		if (!sa) {
3098 			if (is_blank_or_comment)
3099 				continue;
3100 
3101 			snprintf(out, out_size, "Invalid SA in file \"%s\" at line %u: \"%s\"\n",
3102 				file_name, line_id, err_msg);
3103 			goto free;
3104 		}
3105 
3106 		snprintf(out, out_size, "%s", line);
3107 		out_size -= strlen(out);
3108 		out += strlen(out);
3109 
3110 		/* Add the SA to the IPsec instance. Free the SA. */
3111 		status = rte_swx_ipsec_sa_add(ipsec, sa, &sa_id);
3112 		if (status)
3113 			snprintf(out, out_size, "\t: Error (%d)\n", status);
3114 		else
3115 			snprintf(out, out_size, "\t: OK (SA ID = %u)\n", sa_id);
3116 		out_size -= strlen(out);
3117 		out += strlen(out);
3118 
3119 		free(sa);
3120 		if (status)
3121 			goto free;
3122 	}
3123 
3124 free:
3125 	if (file)
3126 		fclose(file);
3127 	free(line);
3128 }
3129 
3130 static const char cmd_ipsec_sa_delete_help[] =
3131 "ipsec <ipsec_instance_name> sa delete <sa_id>\n";
3132 
3133 static void
3134 cmd_ipsec_sa_delete(char **tokens,
3135 		    uint32_t n_tokens,
3136 		    char *out,
3137 		    size_t out_size,
3138 		    void *obj __rte_unused)
3139 {
3140 	struct rte_swx_ipsec *ipsec;
3141 	char *ipsec_instance_name;
3142 	uint32_t sa_id;
3143 
3144 	if (n_tokens != 5) {
3145 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3146 		return;
3147 	}
3148 
3149 	ipsec_instance_name = tokens[1];
3150 	ipsec = rte_swx_ipsec_find(ipsec_instance_name);
3151 	if (!ipsec) {
3152 		snprintf(out, out_size, MSG_ARG_INVALID, "ipsec_instance_name");
3153 		return;
3154 	}
3155 
3156 	if (strcmp(tokens[2], "sa")) {
3157 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "sa");
3158 		return;
3159 	}
3160 
3161 	if (strcmp(tokens[3], "delete")) {
3162 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete");
3163 		return;
3164 	}
3165 
3166 	if (parser_read_uint32(&sa_id, tokens[4])) {
3167 		snprintf(out, out_size, MSG_ARG_INVALID, "sa_id");
3168 		return;
3169 	}
3170 
3171 	rte_swx_ipsec_sa_delete(ipsec, sa_id);
3172 }
3173 
3174 static const char cmd_pipeline_enable_help[] =
3175 "pipeline <pipeline_name> enable thread <thread_id>\n";
3176 
3177 static void
3178 cmd_pipeline_enable(char **tokens,
3179 		    uint32_t n_tokens,
3180 		    char *out,
3181 		    size_t out_size,
3182 		    void *obj __rte_unused)
3183 {
3184 	char *pipeline_name;
3185 	struct rte_swx_pipeline *p;
3186 	uint32_t thread_id;
3187 	int status;
3188 
3189 	if (n_tokens != 5) {
3190 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3191 		return;
3192 	}
3193 
3194 	pipeline_name = tokens[1];
3195 	p = rte_swx_pipeline_find(pipeline_name);
3196 	if (!p) {
3197 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
3198 		return;
3199 	}
3200 
3201 	if (strcmp(tokens[2], "enable") != 0) {
3202 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
3203 		return;
3204 	}
3205 
3206 	if (strcmp(tokens[3], "thread") != 0) {
3207 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "thread");
3208 		return;
3209 	}
3210 
3211 	if (parser_read_uint32(&thread_id, tokens[4]) != 0) {
3212 		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
3213 		return;
3214 	}
3215 
3216 	status = pipeline_enable(p, thread_id);
3217 	if (status) {
3218 		snprintf(out, out_size, MSG_CMD_FAIL, "pipeline enable");
3219 		return;
3220 	}
3221 }
3222 
3223 static const char cmd_pipeline_disable_help[] =
3224 "pipeline <pipeline_name> disable\n";
3225 
3226 static void
3227 cmd_pipeline_disable(char **tokens,
3228 		     uint32_t n_tokens,
3229 		     char *out,
3230 		     size_t out_size,
3231 		     void *obj __rte_unused)
3232 {
3233 	struct rte_swx_pipeline *p;
3234 	char *pipeline_name;
3235 
3236 	if (n_tokens != 3) {
3237 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3238 		return;
3239 	}
3240 
3241 	pipeline_name = tokens[1];
3242 	p = rte_swx_pipeline_find(pipeline_name);
3243 	if (!p) {
3244 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
3245 		return;
3246 	}
3247 
3248 	if (strcmp(tokens[2], "disable") != 0) {
3249 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
3250 		return;
3251 	}
3252 
3253 	pipeline_disable(p);
3254 }
3255 
3256 static const char cmd_block_enable_help[] =
3257 "block type <block_type> instance <block_name> enable thread <thread_id>\n";
3258 
3259 static void
3260 cmd_block_enable(char **tokens,
3261 		 uint32_t n_tokens,
3262 		 char *out,
3263 		 size_t out_size,
3264 		 void *obj __rte_unused)
3265 {
3266 	char *block_type, *block_name;
3267 	block_run_f block_func = NULL;
3268 	void *block = NULL;
3269 	uint32_t thread_id;
3270 	int status;
3271 
3272 	if (n_tokens != 8) {
3273 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3274 		return;
3275 	}
3276 
3277 	if (strcmp(tokens[1], "type") != 0) {
3278 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "type");
3279 		return;
3280 	}
3281 
3282 	block_type = tokens[2];
3283 
3284 	if (strcmp(tokens[3], "instance") != 0) {
3285 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "instance");
3286 		return;
3287 	}
3288 
3289 	block_name = tokens[4];
3290 
3291 	if (strcmp(tokens[5], "enable") != 0) {
3292 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
3293 		return;
3294 	}
3295 
3296 	if (strcmp(tokens[6], "thread") != 0) {
3297 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "thread");
3298 		return;
3299 	}
3300 
3301 	if (parser_read_uint32(&thread_id, tokens[7]) != 0) {
3302 		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
3303 		return;
3304 	}
3305 
3306 	if (!strcmp(block_type, "ipsec")) {
3307 		struct rte_swx_ipsec *ipsec;
3308 
3309 		ipsec = rte_swx_ipsec_find(block_name);
3310 		if (!ipsec) {
3311 			snprintf(out, out_size, MSG_ARG_INVALID, "block_name");
3312 			return;
3313 		}
3314 
3315 		block_func = (block_run_f)rte_swx_ipsec_run;
3316 		block = (void *)ipsec;
3317 	} else {
3318 		snprintf(out, out_size, MSG_ARG_INVALID, "block_type");
3319 		return;
3320 	}
3321 
3322 	status = block_enable(block_func, block, thread_id);
3323 	if (status) {
3324 		snprintf(out, out_size, MSG_CMD_FAIL, "block enable");
3325 		return;
3326 	}
3327 }
3328 
3329 static const char cmd_block_disable_help[] =
3330 "block type <block_type> instance <block_name> disable\n";
3331 
3332 static void
3333 cmd_block_disable(char **tokens,
3334 		  uint32_t n_tokens,
3335 		  char *out,
3336 		  size_t out_size,
3337 		  void *obj __rte_unused)
3338 {
3339 	char *block_type, *block_name;
3340 	void *block = NULL;
3341 
3342 	if (n_tokens != 6) {
3343 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3344 		return;
3345 	}
3346 
3347 	if (strcmp(tokens[1], "type") != 0) {
3348 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "type");
3349 		return;
3350 	}
3351 
3352 	block_type = tokens[2];
3353 
3354 	if (strcmp(tokens[3], "instance") != 0) {
3355 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "instance");
3356 		return;
3357 	}
3358 
3359 	block_name = tokens[4];
3360 
3361 	if (strcmp(tokens[5], "disable") != 0) {
3362 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
3363 		return;
3364 	}
3365 
3366 	if (!strcmp(block_type, "ipsec")) {
3367 		struct rte_swx_ipsec *ipsec;
3368 
3369 		ipsec = rte_swx_ipsec_find(block_name);
3370 		if (!ipsec) {
3371 			snprintf(out, out_size, MSG_ARG_INVALID, "block_name");
3372 			return;
3373 		}
3374 
3375 		block = (void *)ipsec;
3376 	} else {
3377 		snprintf(out, out_size, MSG_ARG_INVALID, "block_type");
3378 		return;
3379 	}
3380 
3381 	block_disable(block);
3382 }
3383 
3384 static void
3385 cmd_help(char **tokens,
3386 	 uint32_t n_tokens,
3387 	 char *out,
3388 	 size_t out_size,
3389 	 void *arg __rte_unused)
3390 {
3391 	tokens++;
3392 	n_tokens--;
3393 
3394 	if (n_tokens == 0) {
3395 		snprintf(out, out_size,
3396 			"Type 'help <command>' for command details.\n\n"
3397 			"List of commands:\n"
3398 			"\tmempool\n"
3399 			"\tethdev\n"
3400 			"\tethdev show\n"
3401 			"\tring\n"
3402 			"\tcryptodev\n"
3403 			"\tpipeline codegen\n"
3404 			"\tpipeline libbuild\n"
3405 			"\tpipeline build\n"
3406 			"\tpipeline table add\n"
3407 			"\tpipeline table delete\n"
3408 			"\tpipeline table default\n"
3409 			"\tpipeline table show\n"
3410 			"\tpipeline selector group add\n"
3411 			"\tpipeline selector group delete\n"
3412 			"\tpipeline selector group member add\n"
3413 			"\tpipeline selector group member delete\n"
3414 			"\tpipeline selector show\n"
3415 			"\tpipeline learner default\n"
3416 			"\tpipeline commit\n"
3417 			"\tpipeline abort\n"
3418 			"\tpipeline regrd\n"
3419 			"\tpipeline regwr\n"
3420 			"\tpipeline meter profile add\n"
3421 			"\tpipeline meter profile delete\n"
3422 			"\tpipeline meter reset\n"
3423 			"\tpipeline meter set\n"
3424 			"\tpipeline meter stats\n"
3425 			"\tpipeline stats\n"
3426 			"\tpipeline mirror session\n"
3427 			"\tpipeline enable\n"
3428 			"\tpipeline disable\n\n"
3429 			"\tipsec create\n"
3430 			"\tipsec sa add\n"
3431 			"\tipsec sa delete\n"
3432 			"\tblock enable\n"
3433 			"\tblock disable\n"
3434 			);
3435 		return;
3436 	}
3437 
3438 	if (strcmp(tokens[0], "mempool") == 0) {
3439 		snprintf(out, out_size, "\n%s\n", cmd_mempool_help);
3440 		return;
3441 	}
3442 
3443 	if (!strcmp(tokens[0], "ethdev")) {
3444 		if (n_tokens == 1) {
3445 			snprintf(out, out_size, "\n%s\n", cmd_ethdev_help);
3446 			return;
3447 		}
3448 
3449 		if (n_tokens == 2 && !strcmp(tokens[1], "show")) {
3450 			snprintf(out, out_size, "\n%s\n", cmd_ethdev_show_help);
3451 			return;
3452 		}
3453 	}
3454 
3455 	if (strcmp(tokens[0], "ring") == 0) {
3456 		snprintf(out, out_size, "\n%s\n", cmd_ring_help);
3457 		return;
3458 	}
3459 
3460 	if (!strcmp(tokens[0], "cryptodev")) {
3461 		snprintf(out, out_size, "\n%s\n", cmd_cryptodev_help);
3462 		return;
3463 	}
3464 
3465 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3466 		(n_tokens == 2) && (strcmp(tokens[1], "codegen") == 0)) {
3467 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_codegen_help);
3468 		return;
3469 	}
3470 
3471 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3472 		(n_tokens == 2) && (strcmp(tokens[1], "libbuild") == 0)) {
3473 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_libbuild_help);
3474 		return;
3475 	}
3476 
3477 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3478 		(n_tokens == 2) && (strcmp(tokens[1], "build") == 0)) {
3479 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_build_help);
3480 		return;
3481 	}
3482 
3483 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3484 		(n_tokens == 3) &&
3485 		(strcmp(tokens[1], "table") == 0) &&
3486 		(strcmp(tokens[2], "add") == 0)) {
3487 		snprintf(out, out_size, "\n%s\n",
3488 			cmd_pipeline_table_add_help);
3489 		return;
3490 	}
3491 
3492 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3493 		(n_tokens == 3) &&
3494 		(strcmp(tokens[1], "table") == 0) &&
3495 		(strcmp(tokens[2], "delete") == 0)) {
3496 		snprintf(out, out_size, "\n%s\n",
3497 			cmd_pipeline_table_delete_help);
3498 		return;
3499 	}
3500 
3501 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3502 		(n_tokens == 3) &&
3503 		(strcmp(tokens[1], "table") == 0) &&
3504 		(strcmp(tokens[2], "default") == 0)) {
3505 		snprintf(out, out_size, "\n%s\n",
3506 			cmd_pipeline_table_default_help);
3507 		return;
3508 	}
3509 
3510 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3511 		(n_tokens == 3) &&
3512 		(strcmp(tokens[1], "table") == 0) &&
3513 		(strcmp(tokens[2], "show") == 0)) {
3514 		snprintf(out, out_size, "\n%s\n",
3515 			cmd_pipeline_table_show_help);
3516 		return;
3517 	}
3518 
3519 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3520 		(n_tokens == 4) &&
3521 		(strcmp(tokens[1], "selector") == 0) &&
3522 		(strcmp(tokens[2], "group") == 0) &&
3523 		(strcmp(tokens[3], "add") == 0)) {
3524 		snprintf(out, out_size, "\n%s\n",
3525 			cmd_pipeline_selector_group_add_help);
3526 		return;
3527 	}
3528 
3529 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3530 		(n_tokens == 4) &&
3531 		(strcmp(tokens[1], "selector") == 0) &&
3532 		(strcmp(tokens[2], "group") == 0) &&
3533 		(strcmp(tokens[3], "delete") == 0)) {
3534 		snprintf(out, out_size, "\n%s\n",
3535 			cmd_pipeline_selector_group_delete_help);
3536 		return;
3537 	}
3538 
3539 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3540 		(n_tokens == 5) &&
3541 		(strcmp(tokens[1], "selector") == 0) &&
3542 		(strcmp(tokens[2], "group") == 0) &&
3543 		(strcmp(tokens[3], "member") == 0) &&
3544 		(strcmp(tokens[4], "add") == 0)) {
3545 		snprintf(out, out_size, "\n%s\n",
3546 			cmd_pipeline_selector_group_member_add_help);
3547 		return;
3548 	}
3549 
3550 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3551 		(n_tokens == 5) &&
3552 		(strcmp(tokens[1], "selector") == 0) &&
3553 		(strcmp(tokens[2], "group") == 0) &&
3554 		(strcmp(tokens[3], "member") == 0) &&
3555 		(strcmp(tokens[4], "delete") == 0)) {
3556 		snprintf(out, out_size, "\n%s\n",
3557 			cmd_pipeline_selector_group_member_delete_help);
3558 		return;
3559 	}
3560 
3561 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3562 		(n_tokens == 3) &&
3563 		(strcmp(tokens[1], "selector") == 0) &&
3564 		(strcmp(tokens[2], "show") == 0)) {
3565 		snprintf(out, out_size, "\n%s\n",
3566 			cmd_pipeline_selector_show_help);
3567 		return;
3568 	}
3569 
3570 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3571 		(n_tokens == 3) &&
3572 		(strcmp(tokens[1], "learner") == 0) &&
3573 		(strcmp(tokens[2], "default") == 0)) {
3574 		snprintf(out, out_size, "\n%s\n",
3575 			cmd_pipeline_learner_default_help);
3576 		return;
3577 	}
3578 
3579 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3580 		(n_tokens == 2) &&
3581 		(strcmp(tokens[1], "commit") == 0)) {
3582 		snprintf(out, out_size, "\n%s\n",
3583 			cmd_pipeline_commit_help);
3584 		return;
3585 	}
3586 
3587 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3588 		(n_tokens == 2) &&
3589 		(strcmp(tokens[1], "abort") == 0)) {
3590 		snprintf(out, out_size, "\n%s\n",
3591 			cmd_pipeline_abort_help);
3592 		return;
3593 	}
3594 
3595 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3596 		(n_tokens == 2) && (strcmp(tokens[1], "regrd") == 0)) {
3597 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_regrd_help);
3598 		return;
3599 	}
3600 
3601 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3602 		(n_tokens == 2) && (strcmp(tokens[1], "regwr") == 0)) {
3603 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_regwr_help);
3604 		return;
3605 	}
3606 
3607 	if (!strcmp(tokens[0], "pipeline") &&
3608 		(n_tokens == 4) && !strcmp(tokens[1], "meter")
3609 		&& !strcmp(tokens[2], "profile")
3610 		&& !strcmp(tokens[3], "add")) {
3611 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_profile_add_help);
3612 		return;
3613 	}
3614 
3615 	if (!strcmp(tokens[0], "pipeline") &&
3616 		(n_tokens == 4) && !strcmp(tokens[1], "meter")
3617 		&& !strcmp(tokens[2], "profile")
3618 		&& !strcmp(tokens[3], "delete")) {
3619 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_profile_delete_help);
3620 		return;
3621 	}
3622 
3623 	if (!strcmp(tokens[0], "pipeline") &&
3624 		(n_tokens == 3) && !strcmp(tokens[1], "meter")
3625 		&& !strcmp(tokens[2], "reset")) {
3626 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_reset_help);
3627 		return;
3628 	}
3629 
3630 	if (!strcmp(tokens[0], "pipeline") &&
3631 		(n_tokens == 3) && !strcmp(tokens[1], "meter")
3632 		&& !strcmp(tokens[2], "set")) {
3633 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_set_help);
3634 		return;
3635 	}
3636 
3637 	if (!strcmp(tokens[0], "pipeline") &&
3638 		(n_tokens == 3) && !strcmp(tokens[1], "meter")
3639 		&& !strcmp(tokens[2], "stats")) {
3640 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_stats_help);
3641 		return;
3642 	}
3643 
3644 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3645 		(n_tokens == 2) && (strcmp(tokens[1], "stats") == 0)) {
3646 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_stats_help);
3647 		return;
3648 	}
3649 
3650 	if (!strcmp(tokens[0], "pipeline") &&
3651 		(n_tokens == 3) && !strcmp(tokens[1], "mirror")
3652 		&& !strcmp(tokens[2], "session")) {
3653 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_mirror_session_help);
3654 		return;
3655 	}
3656 
3657 	if (!strcmp(tokens[0], "pipeline") &&
3658 		(n_tokens == 2) && !strcmp(tokens[1], "enable")) {
3659 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_enable_help);
3660 		return;
3661 	}
3662 
3663 	if (!strcmp(tokens[0], "pipeline") &&
3664 		(n_tokens == 2) && !strcmp(tokens[1], "disable")) {
3665 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_disable_help);
3666 		return;
3667 	}
3668 
3669 	if (!strcmp(tokens[0], "ipsec") &&
3670 		(n_tokens == 2) && !strcmp(tokens[1], "create")) {
3671 		snprintf(out, out_size, "\n%s\n", cmd_ipsec_create_help);
3672 		return;
3673 	}
3674 
3675 	if (!strcmp(tokens[0], "ipsec") &&
3676 		(n_tokens == 3) && !strcmp(tokens[1], "sa")
3677 		&& !strcmp(tokens[2], "add")) {
3678 		snprintf(out, out_size, "\n%s\n", cmd_ipsec_sa_add_help);
3679 		return;
3680 	}
3681 
3682 	if (!strcmp(tokens[0], "ipsec") &&
3683 		(n_tokens == 3) && !strcmp(tokens[1], "sa")
3684 		&& !strcmp(tokens[2], "delete")) {
3685 		snprintf(out, out_size, "\n%s\n", cmd_ipsec_sa_delete_help);
3686 		return;
3687 	}
3688 
3689 	if (!strcmp(tokens[0], "block") &&
3690 		(n_tokens == 2) && !strcmp(tokens[1], "enable")) {
3691 		snprintf(out, out_size, "\n%s\n", cmd_block_enable_help);
3692 		return;
3693 	}
3694 
3695 	if (!strcmp(tokens[0], "block") &&
3696 		(n_tokens == 2) && !strcmp(tokens[1], "disable")) {
3697 		snprintf(out, out_size, "\n%s\n", cmd_block_disable_help);
3698 		return;
3699 	}
3700 
3701 	snprintf(out, out_size, "Invalid command\n");
3702 }
3703 
3704 void
3705 cli_process(char *in, char *out, size_t out_size, void *obj)
3706 {
3707 	char *tokens[CMD_MAX_TOKENS];
3708 	uint32_t n_tokens = RTE_DIM(tokens);
3709 	int status;
3710 
3711 	if (is_comment(in))
3712 		return;
3713 
3714 	status = parse_tokenize_string(in, tokens, &n_tokens);
3715 	if (status) {
3716 		snprintf(out, out_size, MSG_ARG_TOO_MANY, "");
3717 		return;
3718 	}
3719 
3720 	if (n_tokens == 0)
3721 		return;
3722 
3723 	if (strcmp(tokens[0], "help") == 0) {
3724 		cmd_help(tokens, n_tokens, out, out_size, obj);
3725 		return;
3726 	}
3727 
3728 	if (strcmp(tokens[0], "mempool") == 0) {
3729 		cmd_mempool(tokens, n_tokens, out, out_size, obj);
3730 		return;
3731 	}
3732 
3733 	if (strcmp(tokens[0], "ethdev") == 0) {
3734 		if ((n_tokens >= 2) && (strcmp(tokens[1], "show") == 0)) {
3735 			cmd_ethdev_show(tokens, n_tokens, out, out_size, obj);
3736 			return;
3737 		}
3738 
3739 		cmd_ethdev(tokens, n_tokens, out, out_size, obj);
3740 		return;
3741 	}
3742 
3743 	if (strcmp(tokens[0], "ring") == 0) {
3744 		cmd_ring(tokens, n_tokens, out, out_size, obj);
3745 		return;
3746 	}
3747 
3748 	if (!strcmp(tokens[0], "cryptodev")) {
3749 		cmd_cryptodev(tokens, n_tokens, out, out_size, obj);
3750 		return;
3751 	}
3752 
3753 	if (strcmp(tokens[0], "pipeline") == 0) {
3754 		if ((n_tokens >= 3) &&
3755 			(strcmp(tokens[1], "codegen") == 0)) {
3756 			cmd_pipeline_codegen(tokens, n_tokens, out, out_size,
3757 				obj);
3758 			return;
3759 		}
3760 
3761 		if ((n_tokens >= 3) &&
3762 			(strcmp(tokens[1], "libbuild") == 0)) {
3763 			cmd_pipeline_libbuild(tokens, n_tokens, out, out_size,
3764 				obj);
3765 			return;
3766 		}
3767 
3768 		if ((n_tokens >= 3) &&
3769 			(strcmp(tokens[2], "build") == 0)) {
3770 			cmd_pipeline_build(tokens, n_tokens, out, out_size,
3771 				obj);
3772 			return;
3773 		}
3774 
3775 		if ((n_tokens >= 5) &&
3776 			(strcmp(tokens[2], "table") == 0) &&
3777 			(strcmp(tokens[4], "add") == 0)) {
3778 			cmd_pipeline_table_add(tokens, n_tokens, out,
3779 				out_size, obj);
3780 			return;
3781 		}
3782 
3783 		if ((n_tokens >= 5) &&
3784 			(strcmp(tokens[2], "table") == 0) &&
3785 			(strcmp(tokens[4], "delete") == 0)) {
3786 			cmd_pipeline_table_delete(tokens, n_tokens, out,
3787 				out_size, obj);
3788 			return;
3789 		}
3790 
3791 		if ((n_tokens >= 5) &&
3792 			(strcmp(tokens[2], "table") == 0) &&
3793 			(strcmp(tokens[4], "default") == 0)) {
3794 			cmd_pipeline_table_default(tokens, n_tokens, out,
3795 				out_size, obj);
3796 			return;
3797 		}
3798 
3799 		if ((n_tokens >= 5) &&
3800 			(strcmp(tokens[2], "table") == 0) &&
3801 			(strcmp(tokens[4], "show") == 0)) {
3802 			cmd_pipeline_table_show(tokens, n_tokens, out,
3803 				out_size, obj);
3804 			return;
3805 		}
3806 
3807 		if ((n_tokens >= 6) &&
3808 			(strcmp(tokens[2], "selector") == 0) &&
3809 			(strcmp(tokens[4], "group") == 0) &&
3810 			(strcmp(tokens[5], "add") == 0)) {
3811 			cmd_pipeline_selector_group_add(tokens, n_tokens, out,
3812 				out_size, obj);
3813 			return;
3814 		}
3815 
3816 		if ((n_tokens >= 6) &&
3817 			(strcmp(tokens[2], "selector") == 0) &&
3818 			(strcmp(tokens[4], "group") == 0) &&
3819 			(strcmp(tokens[5], "delete") == 0)) {
3820 			cmd_pipeline_selector_group_delete(tokens, n_tokens, out,
3821 				out_size, obj);
3822 			return;
3823 		}
3824 
3825 		if ((n_tokens >= 7) &&
3826 			(strcmp(tokens[2], "selector") == 0) &&
3827 			(strcmp(tokens[4], "group") == 0) &&
3828 			(strcmp(tokens[5], "member") == 0) &&
3829 			(strcmp(tokens[6], "add") == 0)) {
3830 			cmd_pipeline_selector_group_member_add(tokens, n_tokens, out,
3831 				out_size, obj);
3832 			return;
3833 		}
3834 
3835 		if ((n_tokens >= 7) &&
3836 			(strcmp(tokens[2], "selector") == 0) &&
3837 			(strcmp(tokens[4], "group") == 0) &&
3838 			(strcmp(tokens[5], "member") == 0) &&
3839 			(strcmp(tokens[6], "delete") == 0)) {
3840 			cmd_pipeline_selector_group_member_delete(tokens, n_tokens, out,
3841 				out_size, obj);
3842 			return;
3843 		}
3844 
3845 		if ((n_tokens >= 5) &&
3846 			(strcmp(tokens[2], "selector") == 0) &&
3847 			(strcmp(tokens[4], "show") == 0)) {
3848 			cmd_pipeline_selector_show(tokens, n_tokens, out,
3849 				out_size, obj);
3850 			return;
3851 		}
3852 
3853 		if ((n_tokens >= 5) &&
3854 			(strcmp(tokens[2], "learner") == 0) &&
3855 			(strcmp(tokens[4], "default") == 0)) {
3856 			cmd_pipeline_learner_default(tokens, n_tokens, out,
3857 				out_size, obj);
3858 			return;
3859 		}
3860 
3861 		if ((n_tokens >= 3) &&
3862 			(strcmp(tokens[2], "commit") == 0)) {
3863 			cmd_pipeline_commit(tokens, n_tokens, out,
3864 				out_size, obj);
3865 			return;
3866 		}
3867 
3868 		if ((n_tokens >= 3) &&
3869 			(strcmp(tokens[2], "abort") == 0)) {
3870 			cmd_pipeline_abort(tokens, n_tokens, out,
3871 				out_size, obj);
3872 			return;
3873 		}
3874 
3875 		if ((n_tokens >= 3) &&
3876 			(strcmp(tokens[2], "regrd") == 0)) {
3877 			cmd_pipeline_regrd(tokens, n_tokens, out, out_size, obj);
3878 			return;
3879 		}
3880 
3881 		if ((n_tokens >= 3) &&
3882 			(strcmp(tokens[2], "regwr") == 0)) {
3883 			cmd_pipeline_regwr(tokens, n_tokens, out, out_size, obj);
3884 			return;
3885 		}
3886 
3887 		if ((n_tokens >= 6) &&
3888 			(strcmp(tokens[2], "meter") == 0) &&
3889 			(strcmp(tokens[3], "profile") == 0) &&
3890 			(strcmp(tokens[5], "add") == 0)) {
3891 			cmd_pipeline_meter_profile_add(tokens, n_tokens, out, out_size, obj);
3892 			return;
3893 		}
3894 
3895 		if ((n_tokens >= 6) &&
3896 			(strcmp(tokens[2], "meter") == 0) &&
3897 			(strcmp(tokens[3], "profile") == 0) &&
3898 			(strcmp(tokens[5], "delete") == 0)) {
3899 			cmd_pipeline_meter_profile_delete(tokens, n_tokens, out, out_size, obj);
3900 			return;
3901 		}
3902 
3903 		if (n_tokens >= 9 && !strcmp(tokens[2], "meter") && !strcmp(tokens[4], "reset")) {
3904 			cmd_pipeline_meter_reset(tokens, n_tokens, out, out_size, obj);
3905 			return;
3906 		}
3907 
3908 		if (n_tokens >= 9 && !strcmp(tokens[2], "meter") && !strcmp(tokens[4], "set")) {
3909 			cmd_pipeline_meter_set(tokens, n_tokens, out, out_size, obj);
3910 			return;
3911 		}
3912 
3913 		if (n_tokens >= 9 && !strcmp(tokens[2], "meter") && !strcmp(tokens[4], "stats")) {
3914 			cmd_pipeline_meter_stats(tokens, n_tokens, out, out_size, obj);
3915 			return;
3916 		}
3917 
3918 		if ((n_tokens >= 3) &&
3919 			(strcmp(tokens[2], "stats") == 0)) {
3920 			cmd_pipeline_stats(tokens, n_tokens, out, out_size,
3921 				obj);
3922 			return;
3923 		}
3924 
3925 		if ((n_tokens >= 4) &&
3926 			(strcmp(tokens[2], "mirror") == 0) &&
3927 			(strcmp(tokens[3], "session") == 0)) {
3928 			cmd_pipeline_mirror_session(tokens, n_tokens, out, out_size, obj);
3929 			return;
3930 		}
3931 
3932 		if (n_tokens >= 3 && !strcmp(tokens[2], "enable")) {
3933 			cmd_pipeline_enable(tokens, n_tokens, out, out_size, obj);
3934 			return;
3935 		}
3936 
3937 		if (n_tokens >= 3 && !strcmp(tokens[2], "disable")) {
3938 			cmd_pipeline_disable(tokens, n_tokens, out, out_size, obj);
3939 			return;
3940 		}
3941 	}
3942 
3943 	if (!strcmp(tokens[0], "ipsec")) {
3944 		if (n_tokens >= 3 && !strcmp(tokens[2], "create")) {
3945 			cmd_ipsec_create(tokens, n_tokens, out, out_size, obj);
3946 			return;
3947 		}
3948 
3949 		if (n_tokens >= 4 && !strcmp(tokens[2], "sa") && !strcmp(tokens[3], "add")) {
3950 			cmd_ipsec_sa_add(tokens, n_tokens, out, out_size, obj);
3951 			return;
3952 		}
3953 
3954 		if (n_tokens >= 4 && !strcmp(tokens[2], "sa") && !strcmp(tokens[3], "delete")) {
3955 			cmd_ipsec_sa_delete(tokens, n_tokens, out, out_size, obj);
3956 			return;
3957 		}
3958 	}
3959 
3960 	if (!strcmp(tokens[0], "block")) {
3961 		if (n_tokens >= 6 && !strcmp(tokens[5], "enable")) {
3962 			cmd_block_enable(tokens, n_tokens, out, out_size, obj);
3963 			return;
3964 		}
3965 
3966 		if (n_tokens >= 6 && !strcmp(tokens[5], "disable")) {
3967 			cmd_block_disable(tokens, n_tokens, out, out_size, obj);
3968 			return;
3969 		}
3970 	}
3971 
3972 	snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
3973 }
3974 
3975 int
3976 cli_script_process(const char *file_name,
3977 	size_t msg_in_len_max,
3978 	size_t msg_out_len_max,
3979 	void *obj)
3980 {
3981 	char *msg_in = NULL, *msg_out = NULL;
3982 	FILE *f = NULL;
3983 
3984 	/* Check input arguments */
3985 	if ((file_name == NULL) ||
3986 		(strlen(file_name) == 0) ||
3987 		(msg_in_len_max == 0) ||
3988 		(msg_out_len_max == 0))
3989 		return -EINVAL;
3990 
3991 	msg_in = malloc(msg_in_len_max + 1);
3992 	msg_out = malloc(msg_out_len_max + 1);
3993 	if ((msg_in == NULL) ||
3994 		(msg_out == NULL)) {
3995 		free(msg_out);
3996 		free(msg_in);
3997 		return -ENOMEM;
3998 	}
3999 
4000 	/* Open input file */
4001 	f = fopen(file_name, "r");
4002 	if (f == NULL) {
4003 		free(msg_out);
4004 		free(msg_in);
4005 		return -EIO;
4006 	}
4007 
4008 	/* Read file */
4009 	for ( ; ; ) {
4010 		if (fgets(msg_in, msg_in_len_max + 1, f) == NULL)
4011 			break;
4012 
4013 		printf("%s", msg_in);
4014 		msg_out[0] = 0;
4015 
4016 		cli_process(msg_in,
4017 			msg_out,
4018 			msg_out_len_max,
4019 			obj);
4020 
4021 		if (strlen(msg_out))
4022 			printf("%s", msg_out);
4023 	}
4024 
4025 	/* Close file */
4026 	fclose(f);
4027 	free(msg_out);
4028 	free(msg_in);
4029 	return 0;
4030 }
4031