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