xref: /dpdk/examples/pipeline/cli.c (revision 3b0cc5fb3aa74c2e390b5e39f8e4e64063bbdf29)
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_thread_pipeline_enable_help[] =
3175 "thread <thread_id> pipeline <pipeline_name> enable [ period <timer_period_ms> ]\n";
3176 
3177 #ifndef TIMER_PERIOD_MS_DEFAULT
3178 #define TIMER_PERIOD_MS_DEFAULT 10
3179 #endif
3180 
3181 static void
3182 cmd_thread_pipeline_enable(char **tokens,
3183 	uint32_t n_tokens,
3184 	char *out,
3185 	size_t out_size,
3186 	void *obj __rte_unused)
3187 {
3188 	char *pipeline_name;
3189 	struct rte_swx_pipeline *p;
3190 	uint32_t thread_id, timer_period_ms = TIMER_PERIOD_MS_DEFAULT;
3191 	int status;
3192 
3193 	if ((n_tokens != 5) && (n_tokens != 7)) {
3194 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3195 		return;
3196 	}
3197 
3198 	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
3199 		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
3200 		return;
3201 	}
3202 
3203 	if (strcmp(tokens[2], "pipeline") != 0) {
3204 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
3205 		return;
3206 	}
3207 
3208 	pipeline_name = tokens[3];
3209 	p = rte_swx_pipeline_find(pipeline_name);
3210 	if (!p) {
3211 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
3212 		return;
3213 	}
3214 
3215 	if (strcmp(tokens[4], "enable") != 0) {
3216 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable");
3217 		return;
3218 	}
3219 
3220 	if (n_tokens == 7) {
3221 		if (strcmp(tokens[5], "period") != 0) {
3222 			snprintf(out, out_size, MSG_ARG_NOT_FOUND, "period");
3223 			return;
3224 		}
3225 
3226 		if (parser_read_uint32(&timer_period_ms, tokens[6]) != 0) {
3227 			snprintf(out, out_size, MSG_ARG_INVALID, "timer_period_ms");
3228 			return;
3229 		}
3230 	}
3231 
3232 	status = thread_pipeline_enable(thread_id, p, timer_period_ms);
3233 	if (status) {
3234 		snprintf(out, out_size, MSG_CMD_FAIL, "thread pipeline enable");
3235 		return;
3236 	}
3237 }
3238 
3239 static const char cmd_thread_pipeline_disable_help[] =
3240 "thread <thread_id> pipeline <pipeline_name> disable\n";
3241 
3242 static void
3243 cmd_thread_pipeline_disable(char **tokens,
3244 	uint32_t n_tokens,
3245 	char *out,
3246 	size_t out_size,
3247 	void *obj __rte_unused)
3248 {
3249 	struct rte_swx_pipeline *p;
3250 	char *pipeline_name;
3251 	uint32_t thread_id;
3252 	int status;
3253 
3254 	if (n_tokens != 5) {
3255 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
3256 		return;
3257 	}
3258 
3259 	if (parser_read_uint32(&thread_id, tokens[1]) != 0) {
3260 		snprintf(out, out_size, MSG_ARG_INVALID, "thread_id");
3261 		return;
3262 	}
3263 
3264 	if (strcmp(tokens[2], "pipeline") != 0) {
3265 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline");
3266 		return;
3267 	}
3268 
3269 	pipeline_name = tokens[3];
3270 	p = rte_swx_pipeline_find(pipeline_name);
3271 	if (!p) {
3272 		snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
3273 		return;
3274 	}
3275 
3276 	if (strcmp(tokens[4], "disable") != 0) {
3277 		snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable");
3278 		return;
3279 	}
3280 
3281 	status = thread_pipeline_disable(thread_id, p);
3282 	if (status) {
3283 		snprintf(out, out_size, MSG_CMD_FAIL,
3284 			"thread pipeline disable");
3285 		return;
3286 	}
3287 }
3288 
3289 static void
3290 cmd_help(char **tokens,
3291 	 uint32_t n_tokens,
3292 	 char *out,
3293 	 size_t out_size,
3294 	 void *arg __rte_unused)
3295 {
3296 	tokens++;
3297 	n_tokens--;
3298 
3299 	if (n_tokens == 0) {
3300 		snprintf(out, out_size,
3301 			"Type 'help <command>' for command details.\n\n"
3302 			"List of commands:\n"
3303 			"\tmempool\n"
3304 			"\tethdev\n"
3305 			"\tethdev show\n"
3306 			"\tring\n"
3307 			"\tcryptodev\n"
3308 			"\tpipeline codegen\n"
3309 			"\tpipeline libbuild\n"
3310 			"\tpipeline build\n"
3311 			"\tpipeline table add\n"
3312 			"\tpipeline table delete\n"
3313 			"\tpipeline table default\n"
3314 			"\tpipeline table show\n"
3315 			"\tpipeline selector group add\n"
3316 			"\tpipeline selector group delete\n"
3317 			"\tpipeline selector group member add\n"
3318 			"\tpipeline selector group member delete\n"
3319 			"\tpipeline selector show\n"
3320 			"\tpipeline learner default\n"
3321 			"\tpipeline commit\n"
3322 			"\tpipeline abort\n"
3323 			"\tpipeline regrd\n"
3324 			"\tpipeline regwr\n"
3325 			"\tpipeline meter profile add\n"
3326 			"\tpipeline meter profile delete\n"
3327 			"\tpipeline meter reset\n"
3328 			"\tpipeline meter set\n"
3329 			"\tpipeline meter stats\n"
3330 			"\tpipeline stats\n"
3331 			"\tpipeline mirror session\n"
3332 			"\tipsec create\n"
3333 			"\tipsec sa add\n"
3334 			"\tipsec sa delete\n"
3335 			"\tthread pipeline enable\n"
3336 			"\tthread pipeline disable\n\n");
3337 		return;
3338 	}
3339 
3340 	if (strcmp(tokens[0], "mempool") == 0) {
3341 		snprintf(out, out_size, "\n%s\n", cmd_mempool_help);
3342 		return;
3343 	}
3344 
3345 	if (!strcmp(tokens[0], "ethdev")) {
3346 		if (n_tokens == 1) {
3347 			snprintf(out, out_size, "\n%s\n", cmd_ethdev_help);
3348 			return;
3349 		}
3350 
3351 		if (n_tokens == 2 && !strcmp(tokens[1], "show")) {
3352 			snprintf(out, out_size, "\n%s\n", cmd_ethdev_show_help);
3353 			return;
3354 		}
3355 	}
3356 
3357 	if (strcmp(tokens[0], "ring") == 0) {
3358 		snprintf(out, out_size, "\n%s\n", cmd_ring_help);
3359 		return;
3360 	}
3361 
3362 	if (!strcmp(tokens[0], "cryptodev")) {
3363 		snprintf(out, out_size, "\n%s\n", cmd_cryptodev_help);
3364 		return;
3365 	}
3366 
3367 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3368 		(n_tokens == 2) && (strcmp(tokens[1], "codegen") == 0)) {
3369 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_codegen_help);
3370 		return;
3371 	}
3372 
3373 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3374 		(n_tokens == 2) && (strcmp(tokens[1], "libbuild") == 0)) {
3375 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_libbuild_help);
3376 		return;
3377 	}
3378 
3379 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3380 		(n_tokens == 2) && (strcmp(tokens[1], "build") == 0)) {
3381 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_build_help);
3382 		return;
3383 	}
3384 
3385 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3386 		(n_tokens == 3) &&
3387 		(strcmp(tokens[1], "table") == 0) &&
3388 		(strcmp(tokens[2], "add") == 0)) {
3389 		snprintf(out, out_size, "\n%s\n",
3390 			cmd_pipeline_table_add_help);
3391 		return;
3392 	}
3393 
3394 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3395 		(n_tokens == 3) &&
3396 		(strcmp(tokens[1], "table") == 0) &&
3397 		(strcmp(tokens[2], "delete") == 0)) {
3398 		snprintf(out, out_size, "\n%s\n",
3399 			cmd_pipeline_table_delete_help);
3400 		return;
3401 	}
3402 
3403 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3404 		(n_tokens == 3) &&
3405 		(strcmp(tokens[1], "table") == 0) &&
3406 		(strcmp(tokens[2], "default") == 0)) {
3407 		snprintf(out, out_size, "\n%s\n",
3408 			cmd_pipeline_table_default_help);
3409 		return;
3410 	}
3411 
3412 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3413 		(n_tokens == 3) &&
3414 		(strcmp(tokens[1], "table") == 0) &&
3415 		(strcmp(tokens[2], "show") == 0)) {
3416 		snprintf(out, out_size, "\n%s\n",
3417 			cmd_pipeline_table_show_help);
3418 		return;
3419 	}
3420 
3421 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3422 		(n_tokens == 4) &&
3423 		(strcmp(tokens[1], "selector") == 0) &&
3424 		(strcmp(tokens[2], "group") == 0) &&
3425 		(strcmp(tokens[3], "add") == 0)) {
3426 		snprintf(out, out_size, "\n%s\n",
3427 			cmd_pipeline_selector_group_add_help);
3428 		return;
3429 	}
3430 
3431 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3432 		(n_tokens == 4) &&
3433 		(strcmp(tokens[1], "selector") == 0) &&
3434 		(strcmp(tokens[2], "group") == 0) &&
3435 		(strcmp(tokens[3], "delete") == 0)) {
3436 		snprintf(out, out_size, "\n%s\n",
3437 			cmd_pipeline_selector_group_delete_help);
3438 		return;
3439 	}
3440 
3441 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3442 		(n_tokens == 5) &&
3443 		(strcmp(tokens[1], "selector") == 0) &&
3444 		(strcmp(tokens[2], "group") == 0) &&
3445 		(strcmp(tokens[3], "member") == 0) &&
3446 		(strcmp(tokens[4], "add") == 0)) {
3447 		snprintf(out, out_size, "\n%s\n",
3448 			cmd_pipeline_selector_group_member_add_help);
3449 		return;
3450 	}
3451 
3452 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3453 		(n_tokens == 5) &&
3454 		(strcmp(tokens[1], "selector") == 0) &&
3455 		(strcmp(tokens[2], "group") == 0) &&
3456 		(strcmp(tokens[3], "member") == 0) &&
3457 		(strcmp(tokens[4], "delete") == 0)) {
3458 		snprintf(out, out_size, "\n%s\n",
3459 			cmd_pipeline_selector_group_member_delete_help);
3460 		return;
3461 	}
3462 
3463 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3464 		(n_tokens == 3) &&
3465 		(strcmp(tokens[1], "selector") == 0) &&
3466 		(strcmp(tokens[2], "show") == 0)) {
3467 		snprintf(out, out_size, "\n%s\n",
3468 			cmd_pipeline_selector_show_help);
3469 		return;
3470 	}
3471 
3472 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3473 		(n_tokens == 3) &&
3474 		(strcmp(tokens[1], "learner") == 0) &&
3475 		(strcmp(tokens[2], "default") == 0)) {
3476 		snprintf(out, out_size, "\n%s\n",
3477 			cmd_pipeline_learner_default_help);
3478 		return;
3479 	}
3480 
3481 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3482 		(n_tokens == 2) &&
3483 		(strcmp(tokens[1], "commit") == 0)) {
3484 		snprintf(out, out_size, "\n%s\n",
3485 			cmd_pipeline_commit_help);
3486 		return;
3487 	}
3488 
3489 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3490 		(n_tokens == 2) &&
3491 		(strcmp(tokens[1], "abort") == 0)) {
3492 		snprintf(out, out_size, "\n%s\n",
3493 			cmd_pipeline_abort_help);
3494 		return;
3495 	}
3496 
3497 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3498 		(n_tokens == 2) && (strcmp(tokens[1], "regrd") == 0)) {
3499 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_regrd_help);
3500 		return;
3501 	}
3502 
3503 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3504 		(n_tokens == 2) && (strcmp(tokens[1], "regwr") == 0)) {
3505 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_regwr_help);
3506 		return;
3507 	}
3508 
3509 	if (!strcmp(tokens[0], "pipeline") &&
3510 		(n_tokens == 4) && !strcmp(tokens[1], "meter")
3511 		&& !strcmp(tokens[2], "profile")
3512 		&& !strcmp(tokens[3], "add")) {
3513 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_profile_add_help);
3514 		return;
3515 	}
3516 
3517 	if (!strcmp(tokens[0], "pipeline") &&
3518 		(n_tokens == 4) && !strcmp(tokens[1], "meter")
3519 		&& !strcmp(tokens[2], "profile")
3520 		&& !strcmp(tokens[3], "delete")) {
3521 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_profile_delete_help);
3522 		return;
3523 	}
3524 
3525 	if (!strcmp(tokens[0], "pipeline") &&
3526 		(n_tokens == 3) && !strcmp(tokens[1], "meter")
3527 		&& !strcmp(tokens[2], "reset")) {
3528 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_reset_help);
3529 		return;
3530 	}
3531 
3532 	if (!strcmp(tokens[0], "pipeline") &&
3533 		(n_tokens == 3) && !strcmp(tokens[1], "meter")
3534 		&& !strcmp(tokens[2], "set")) {
3535 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_set_help);
3536 		return;
3537 	}
3538 
3539 	if (!strcmp(tokens[0], "pipeline") &&
3540 		(n_tokens == 3) && !strcmp(tokens[1], "meter")
3541 		&& !strcmp(tokens[2], "stats")) {
3542 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_meter_stats_help);
3543 		return;
3544 	}
3545 
3546 	if ((strcmp(tokens[0], "pipeline") == 0) &&
3547 		(n_tokens == 2) && (strcmp(tokens[1], "stats") == 0)) {
3548 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_stats_help);
3549 		return;
3550 	}
3551 
3552 	if (!strcmp(tokens[0], "pipeline") &&
3553 		(n_tokens == 3) && !strcmp(tokens[1], "mirror")
3554 		&& !strcmp(tokens[2], "session")) {
3555 		snprintf(out, out_size, "\n%s\n", cmd_pipeline_mirror_session_help);
3556 		return;
3557 	}
3558 
3559 	if (!strcmp(tokens[0], "ipsec") &&
3560 		(n_tokens == 2) && !strcmp(tokens[1], "create")) {
3561 		snprintf(out, out_size, "\n%s\n", cmd_ipsec_create_help);
3562 		return;
3563 	}
3564 
3565 	if (!strcmp(tokens[0], "ipsec") &&
3566 		(n_tokens == 3) && !strcmp(tokens[1], "sa")
3567 		&& !strcmp(tokens[2], "add")) {
3568 		snprintf(out, out_size, "\n%s\n", cmd_ipsec_sa_add_help);
3569 		return;
3570 	}
3571 
3572 	if (!strcmp(tokens[0], "ipsec") &&
3573 		(n_tokens == 3) && !strcmp(tokens[1], "sa")
3574 		&& !strcmp(tokens[2], "delete")) {
3575 		snprintf(out, out_size, "\n%s\n", cmd_ipsec_sa_delete_help);
3576 		return;
3577 	}
3578 
3579 	if ((n_tokens == 3) &&
3580 		(strcmp(tokens[0], "thread") == 0) &&
3581 		(strcmp(tokens[1], "pipeline") == 0)) {
3582 		if (strcmp(tokens[2], "enable") == 0) {
3583 			snprintf(out, out_size, "\n%s\n",
3584 				cmd_thread_pipeline_enable_help);
3585 			return;
3586 		}
3587 
3588 		if (strcmp(tokens[2], "disable") == 0) {
3589 			snprintf(out, out_size, "\n%s\n",
3590 				cmd_thread_pipeline_disable_help);
3591 			return;
3592 		}
3593 	}
3594 
3595 	snprintf(out, out_size, "Invalid command\n");
3596 }
3597 
3598 void
3599 cli_process(char *in, char *out, size_t out_size, void *obj)
3600 {
3601 	char *tokens[CMD_MAX_TOKENS];
3602 	uint32_t n_tokens = RTE_DIM(tokens);
3603 	int status;
3604 
3605 	if (is_comment(in))
3606 		return;
3607 
3608 	status = parse_tokenize_string(in, tokens, &n_tokens);
3609 	if (status) {
3610 		snprintf(out, out_size, MSG_ARG_TOO_MANY, "");
3611 		return;
3612 	}
3613 
3614 	if (n_tokens == 0)
3615 		return;
3616 
3617 	if (strcmp(tokens[0], "help") == 0) {
3618 		cmd_help(tokens, n_tokens, out, out_size, obj);
3619 		return;
3620 	}
3621 
3622 	if (strcmp(tokens[0], "mempool") == 0) {
3623 		cmd_mempool(tokens, n_tokens, out, out_size, obj);
3624 		return;
3625 	}
3626 
3627 	if (strcmp(tokens[0], "ethdev") == 0) {
3628 		if ((n_tokens >= 2) && (strcmp(tokens[1], "show") == 0)) {
3629 			cmd_ethdev_show(tokens, n_tokens, out, out_size, obj);
3630 			return;
3631 		}
3632 
3633 		cmd_ethdev(tokens, n_tokens, out, out_size, obj);
3634 		return;
3635 	}
3636 
3637 	if (strcmp(tokens[0], "ring") == 0) {
3638 		cmd_ring(tokens, n_tokens, out, out_size, obj);
3639 		return;
3640 	}
3641 
3642 	if (!strcmp(tokens[0], "cryptodev")) {
3643 		cmd_cryptodev(tokens, n_tokens, out, out_size, obj);
3644 		return;
3645 	}
3646 
3647 	if (strcmp(tokens[0], "pipeline") == 0) {
3648 		if ((n_tokens >= 3) &&
3649 			(strcmp(tokens[1], "codegen") == 0)) {
3650 			cmd_pipeline_codegen(tokens, n_tokens, out, out_size,
3651 				obj);
3652 			return;
3653 		}
3654 
3655 		if ((n_tokens >= 3) &&
3656 			(strcmp(tokens[1], "libbuild") == 0)) {
3657 			cmd_pipeline_libbuild(tokens, n_tokens, out, out_size,
3658 				obj);
3659 			return;
3660 		}
3661 
3662 		if ((n_tokens >= 3) &&
3663 			(strcmp(tokens[2], "build") == 0)) {
3664 			cmd_pipeline_build(tokens, n_tokens, out, out_size,
3665 				obj);
3666 			return;
3667 		}
3668 
3669 		if ((n_tokens >= 5) &&
3670 			(strcmp(tokens[2], "table") == 0) &&
3671 			(strcmp(tokens[4], "add") == 0)) {
3672 			cmd_pipeline_table_add(tokens, n_tokens, out,
3673 				out_size, obj);
3674 			return;
3675 		}
3676 
3677 		if ((n_tokens >= 5) &&
3678 			(strcmp(tokens[2], "table") == 0) &&
3679 			(strcmp(tokens[4], "delete") == 0)) {
3680 			cmd_pipeline_table_delete(tokens, n_tokens, out,
3681 				out_size, obj);
3682 			return;
3683 		}
3684 
3685 		if ((n_tokens >= 5) &&
3686 			(strcmp(tokens[2], "table") == 0) &&
3687 			(strcmp(tokens[4], "default") == 0)) {
3688 			cmd_pipeline_table_default(tokens, n_tokens, out,
3689 				out_size, obj);
3690 			return;
3691 		}
3692 
3693 		if ((n_tokens >= 5) &&
3694 			(strcmp(tokens[2], "table") == 0) &&
3695 			(strcmp(tokens[4], "show") == 0)) {
3696 			cmd_pipeline_table_show(tokens, n_tokens, out,
3697 				out_size, obj);
3698 			return;
3699 		}
3700 
3701 		if ((n_tokens >= 6) &&
3702 			(strcmp(tokens[2], "selector") == 0) &&
3703 			(strcmp(tokens[4], "group") == 0) &&
3704 			(strcmp(tokens[5], "add") == 0)) {
3705 			cmd_pipeline_selector_group_add(tokens, n_tokens, out,
3706 				out_size, obj);
3707 			return;
3708 		}
3709 
3710 		if ((n_tokens >= 6) &&
3711 			(strcmp(tokens[2], "selector") == 0) &&
3712 			(strcmp(tokens[4], "group") == 0) &&
3713 			(strcmp(tokens[5], "delete") == 0)) {
3714 			cmd_pipeline_selector_group_delete(tokens, n_tokens, out,
3715 				out_size, obj);
3716 			return;
3717 		}
3718 
3719 		if ((n_tokens >= 7) &&
3720 			(strcmp(tokens[2], "selector") == 0) &&
3721 			(strcmp(tokens[4], "group") == 0) &&
3722 			(strcmp(tokens[5], "member") == 0) &&
3723 			(strcmp(tokens[6], "add") == 0)) {
3724 			cmd_pipeline_selector_group_member_add(tokens, n_tokens, out,
3725 				out_size, obj);
3726 			return;
3727 		}
3728 
3729 		if ((n_tokens >= 7) &&
3730 			(strcmp(tokens[2], "selector") == 0) &&
3731 			(strcmp(tokens[4], "group") == 0) &&
3732 			(strcmp(tokens[5], "member") == 0) &&
3733 			(strcmp(tokens[6], "delete") == 0)) {
3734 			cmd_pipeline_selector_group_member_delete(tokens, n_tokens, out,
3735 				out_size, obj);
3736 			return;
3737 		}
3738 
3739 		if ((n_tokens >= 5) &&
3740 			(strcmp(tokens[2], "selector") == 0) &&
3741 			(strcmp(tokens[4], "show") == 0)) {
3742 			cmd_pipeline_selector_show(tokens, n_tokens, out,
3743 				out_size, obj);
3744 			return;
3745 		}
3746 
3747 		if ((n_tokens >= 5) &&
3748 			(strcmp(tokens[2], "learner") == 0) &&
3749 			(strcmp(tokens[4], "default") == 0)) {
3750 			cmd_pipeline_learner_default(tokens, n_tokens, out,
3751 				out_size, obj);
3752 			return;
3753 		}
3754 
3755 		if ((n_tokens >= 3) &&
3756 			(strcmp(tokens[2], "commit") == 0)) {
3757 			cmd_pipeline_commit(tokens, n_tokens, out,
3758 				out_size, obj);
3759 			return;
3760 		}
3761 
3762 		if ((n_tokens >= 3) &&
3763 			(strcmp(tokens[2], "abort") == 0)) {
3764 			cmd_pipeline_abort(tokens, n_tokens, out,
3765 				out_size, obj);
3766 			return;
3767 		}
3768 
3769 		if ((n_tokens >= 3) &&
3770 			(strcmp(tokens[2], "regrd") == 0)) {
3771 			cmd_pipeline_regrd(tokens, n_tokens, out, out_size, obj);
3772 			return;
3773 		}
3774 
3775 		if ((n_tokens >= 3) &&
3776 			(strcmp(tokens[2], "regwr") == 0)) {
3777 			cmd_pipeline_regwr(tokens, n_tokens, out, out_size, obj);
3778 			return;
3779 		}
3780 
3781 		if ((n_tokens >= 6) &&
3782 			(strcmp(tokens[2], "meter") == 0) &&
3783 			(strcmp(tokens[3], "profile") == 0) &&
3784 			(strcmp(tokens[5], "add") == 0)) {
3785 			cmd_pipeline_meter_profile_add(tokens, n_tokens, out, out_size, obj);
3786 			return;
3787 		}
3788 
3789 		if ((n_tokens >= 6) &&
3790 			(strcmp(tokens[2], "meter") == 0) &&
3791 			(strcmp(tokens[3], "profile") == 0) &&
3792 			(strcmp(tokens[5], "delete") == 0)) {
3793 			cmd_pipeline_meter_profile_delete(tokens, n_tokens, out, out_size, obj);
3794 			return;
3795 		}
3796 
3797 		if (n_tokens >= 9 && !strcmp(tokens[2], "meter") && !strcmp(tokens[4], "reset")) {
3798 			cmd_pipeline_meter_reset(tokens, n_tokens, out, out_size, obj);
3799 			return;
3800 		}
3801 
3802 		if (n_tokens >= 9 && !strcmp(tokens[2], "meter") && !strcmp(tokens[4], "set")) {
3803 			cmd_pipeline_meter_set(tokens, n_tokens, out, out_size, obj);
3804 			return;
3805 		}
3806 
3807 		if (n_tokens >= 9 && !strcmp(tokens[2], "meter") && !strcmp(tokens[4], "stats")) {
3808 			cmd_pipeline_meter_stats(tokens, n_tokens, out, out_size, obj);
3809 			return;
3810 		}
3811 
3812 		if ((n_tokens >= 3) &&
3813 			(strcmp(tokens[2], "stats") == 0)) {
3814 			cmd_pipeline_stats(tokens, n_tokens, out, out_size,
3815 				obj);
3816 			return;
3817 		}
3818 
3819 		if ((n_tokens >= 4) &&
3820 			(strcmp(tokens[2], "mirror") == 0) &&
3821 			(strcmp(tokens[3], "session") == 0)) {
3822 			cmd_pipeline_mirror_session(tokens, n_tokens, out, out_size, obj);
3823 			return;
3824 		}
3825 	}
3826 
3827 	if (!strcmp(tokens[0], "ipsec")) {
3828 		if (n_tokens >= 3 && !strcmp(tokens[2], "create")) {
3829 			cmd_ipsec_create(tokens, n_tokens, out, out_size, obj);
3830 			return;
3831 		}
3832 
3833 		if (n_tokens >= 4 && !strcmp(tokens[2], "sa") && !strcmp(tokens[3], "add")) {
3834 			cmd_ipsec_sa_add(tokens, n_tokens, out, out_size, obj);
3835 			return;
3836 		}
3837 
3838 		if (n_tokens >= 4 && !strcmp(tokens[2], "sa") && !strcmp(tokens[3], "delete")) {
3839 			cmd_ipsec_sa_delete(tokens, n_tokens, out, out_size, obj);
3840 			return;
3841 		}
3842 	}
3843 
3844 	if (strcmp(tokens[0], "thread") == 0) {
3845 		if ((n_tokens >= 5) &&
3846 			(strcmp(tokens[4], "enable") == 0)) {
3847 			cmd_thread_pipeline_enable(tokens, n_tokens,
3848 				out, out_size, obj);
3849 			return;
3850 		}
3851 
3852 		if ((n_tokens >= 5) &&
3853 			(strcmp(tokens[4], "disable") == 0)) {
3854 			cmd_thread_pipeline_disable(tokens, n_tokens,
3855 				out, out_size, obj);
3856 			return;
3857 		}
3858 	}
3859 
3860 	snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]);
3861 }
3862 
3863 int
3864 cli_script_process(const char *file_name,
3865 	size_t msg_in_len_max,
3866 	size_t msg_out_len_max,
3867 	void *obj)
3868 {
3869 	char *msg_in = NULL, *msg_out = NULL;
3870 	FILE *f = NULL;
3871 
3872 	/* Check input arguments */
3873 	if ((file_name == NULL) ||
3874 		(strlen(file_name) == 0) ||
3875 		(msg_in_len_max == 0) ||
3876 		(msg_out_len_max == 0))
3877 		return -EINVAL;
3878 
3879 	msg_in = malloc(msg_in_len_max + 1);
3880 	msg_out = malloc(msg_out_len_max + 1);
3881 	if ((msg_in == NULL) ||
3882 		(msg_out == NULL)) {
3883 		free(msg_out);
3884 		free(msg_in);
3885 		return -ENOMEM;
3886 	}
3887 
3888 	/* Open input file */
3889 	f = fopen(file_name, "r");
3890 	if (f == NULL) {
3891 		free(msg_out);
3892 		free(msg_in);
3893 		return -EIO;
3894 	}
3895 
3896 	/* Read file */
3897 	for ( ; ; ) {
3898 		if (fgets(msg_in, msg_in_len_max + 1, f) == NULL)
3899 			break;
3900 
3901 		printf("%s", msg_in);
3902 		msg_out[0] = 0;
3903 
3904 		cli_process(msg_in,
3905 			msg_out,
3906 			msg_out_len_max,
3907 			obj);
3908 
3909 		if (strlen(msg_out))
3910 			printf("%s", msg_out);
3911 	}
3912 
3913 	/* Close file */
3914 	fclose(f);
3915 	free(msg_out);
3916 	free(msg_in);
3917 	return 0;
3918 }
3919