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