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