xref: /dpdk/examples/ipsec-secgw/parser.c (revision ce6b8c31548b4d71a986d9807cd06cf3a616d1ab)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2016 Intel Corporation
3  */
4 #include <rte_common.h>
5 #include <rte_crypto.h>
6 #include <rte_string_fns.h>
7 
8 #include <cmdline_parse_string.h>
9 #include <cmdline_parse_num.h>
10 #include <cmdline_parse_ipaddr.h>
11 #include <cmdline_socket.h>
12 #include <cmdline.h>
13 
14 #include "flow.h"
15 #include "ipsec.h"
16 #include "parser.h"
17 
18 #define PARSE_DELIMITER		" \f\n\r\t\v"
19 static int
20 parse_tokenize_string(char *string, char *tokens[], uint32_t *n_tokens)
21 {
22 	uint32_t i;
23 
24 	if ((string == NULL) ||
25 		(tokens == NULL) ||
26 		(*n_tokens < 1))
27 		return -EINVAL;
28 
29 	for (i = 0; i < *n_tokens; i++) {
30 		tokens[i] = strtok_r(string, PARSE_DELIMITER, &string);
31 		if (tokens[i] == NULL)
32 			break;
33 	}
34 
35 	if ((i == *n_tokens) &&
36 		(NULL != strtok_r(string, PARSE_DELIMITER, &string)))
37 		return -E2BIG;
38 
39 	*n_tokens = i;
40 	return 0;
41 }
42 
43 #define INADDRSZ 4
44 #define IN6ADDRSZ 16
45 
46 /* int
47  * inet_pton4(src, dst)
48  *      like inet_aton() but without all the hexadecimal and shorthand.
49  * return:
50  *      1 if `src' is a valid dotted quad, else 0.
51  * notice:
52  *      does not touch `dst' unless it's returning 1.
53  * author:
54  *      Paul Vixie, 1996.
55  */
56 static int
57 inet_pton4(const char *src, unsigned char *dst)
58 {
59 	static const char digits[] = "0123456789";
60 	int saw_digit, octets, ch;
61 	unsigned char tmp[INADDRSZ], *tp;
62 
63 	saw_digit = 0;
64 	octets = 0;
65 	*(tp = tmp) = 0;
66 	while ((ch = *src++) != '\0') {
67 		const char *pch;
68 
69 		pch = strchr(digits, ch);
70 		if (pch != NULL) {
71 			unsigned int new = *tp * 10 + (pch - digits);
72 
73 			if (new > 255)
74 				return 0;
75 			if (!saw_digit) {
76 				if (++octets > 4)
77 					return 0;
78 				saw_digit = 1;
79 			}
80 			*tp = (unsigned char)new;
81 		} else if (ch == '.' && saw_digit) {
82 			if (octets == 4)
83 				return 0;
84 			*++tp = 0;
85 			saw_digit = 0;
86 		} else
87 			return 0;
88 	}
89 	if (octets < 4)
90 		return 0;
91 
92 	memcpy(dst, tmp, INADDRSZ);
93 	return 1;
94 }
95 
96 /* int
97  * inet_pton6(src, dst)
98  *      convert presentation level address to network order binary form.
99  * return:
100  *      1 if `src' is a valid [RFC1884 2.2] address, else 0.
101  * notice:
102  *      (1) does not touch `dst' unless it's returning 1.
103  *      (2) :: in a full address is silently ignored.
104  * credit:
105  *      inspired by Mark Andrews.
106  * author:
107  *      Paul Vixie, 1996.
108  */
109 static int
110 inet_pton6(const char *src, unsigned char *dst)
111 {
112 	static const char xdigits_l[] = "0123456789abcdef",
113 		xdigits_u[] = "0123456789ABCDEF";
114 	unsigned char tmp[IN6ADDRSZ], *tp = 0, *endp = 0, *colonp = 0;
115 	const char *xdigits = 0, *curtok = 0;
116 	int ch = 0, saw_xdigit = 0, count_xdigit = 0;
117 	unsigned int val = 0;
118 	unsigned dbloct_count = 0;
119 
120 	memset((tp = tmp), '\0', IN6ADDRSZ);
121 	endp = tp + IN6ADDRSZ;
122 	colonp = NULL;
123 	/* Leading :: requires some special handling. */
124 	if (*src == ':')
125 		if (*++src != ':')
126 			return 0;
127 	curtok = src;
128 	saw_xdigit = count_xdigit = 0;
129 	val = 0;
130 
131 	while ((ch = *src++) != '\0') {
132 		const char *pch;
133 
134 		pch = strchr((xdigits = xdigits_l), ch);
135 		if (pch == NULL)
136 			pch = strchr((xdigits = xdigits_u), ch);
137 		if (pch != NULL) {
138 			if (count_xdigit >= 4)
139 				return 0;
140 			val <<= 4;
141 			val |= (pch - xdigits);
142 			if (val > 0xffff)
143 				return 0;
144 			saw_xdigit = 1;
145 			count_xdigit++;
146 			continue;
147 		}
148 		if (ch == ':') {
149 			curtok = src;
150 			if (!saw_xdigit) {
151 				if (colonp)
152 					return 0;
153 				colonp = tp;
154 				continue;
155 			} else if (*src == '\0') {
156 				return 0;
157 			}
158 			if (tp + sizeof(int16_t) > endp)
159 				return 0;
160 			*tp++ = (unsigned char) ((val >> 8) & 0xff);
161 			*tp++ = (unsigned char) (val & 0xff);
162 			saw_xdigit = 0;
163 			count_xdigit = 0;
164 			val = 0;
165 			dbloct_count++;
166 			continue;
167 		}
168 		if (ch == '.' && ((tp + INADDRSZ) <= endp) &&
169 		    inet_pton4(curtok, tp) > 0) {
170 			tp += INADDRSZ;
171 			saw_xdigit = 0;
172 			dbloct_count += 2;
173 			break;  /* '\0' was seen by inet_pton4(). */
174 		}
175 		return 0;
176 	}
177 	if (saw_xdigit) {
178 		if (tp + sizeof(int16_t) > endp)
179 			return 0;
180 		*tp++ = (unsigned char) ((val >> 8) & 0xff);
181 		*tp++ = (unsigned char) (val & 0xff);
182 		dbloct_count++;
183 	}
184 	if (colonp != NULL) {
185 		/* if we already have 8 double octets, having a colon
186 		 * means error */
187 		if (dbloct_count == 8)
188 			return 0;
189 
190 		/*
191 		 * Since some memmove()'s erroneously fail to handle
192 		 * overlapping regions, we'll do the shift by hand.
193 		 */
194 		const int n = tp - colonp;
195 		int i;
196 
197 		for (i = 1; i <= n; i++) {
198 			endp[-i] = colonp[n - i];
199 			colonp[n - i] = 0;
200 		}
201 		tp = endp;
202 	}
203 	if (tp != endp)
204 		return 0;
205 	memcpy(dst, tmp, IN6ADDRSZ);
206 	return 1;
207 }
208 
209 int
210 parse_ipv4_addr(const char *token, struct in_addr *ipv4, uint32_t *mask)
211 {
212 	char ip_str[INET_ADDRSTRLEN] = {0};
213 	char *pch;
214 
215 	pch = strchr(token, '/');
216 	if (pch != NULL) {
217 		strlcpy(ip_str, token,
218 			RTE_MIN((unsigned int long)(pch - token + 1),
219 			sizeof(ip_str)));
220 		pch += 1;
221 		if (is_str_num(pch) != 0)
222 			return -EINVAL;
223 		if (mask)
224 			*mask = atoi(pch);
225 	} else {
226 		strlcpy(ip_str, token, sizeof(ip_str));
227 		if (mask)
228 			*mask = 0;
229 	}
230 	if (strlen(ip_str) >= INET_ADDRSTRLEN)
231 		return -EINVAL;
232 
233 	if (inet_pton4(ip_str, (unsigned char *)ipv4) != 1)
234 		return -EINVAL;
235 
236 	return 0;
237 }
238 
239 int
240 parse_ipv6_addr(const char *token, struct in6_addr *ipv6, uint32_t *mask)
241 {
242 	char ip_str[256] = {0};
243 	char *pch;
244 
245 	pch = strchr(token, '/');
246 	if (pch != NULL) {
247 		strlcpy(ip_str, token,
248 			RTE_MIN((unsigned int long)(pch - token + 1),
249 					sizeof(ip_str)));
250 		pch += 1;
251 		if (is_str_num(pch) != 0)
252 			return -EINVAL;
253 		if (mask)
254 			*mask = atoi(pch);
255 	} else {
256 		strlcpy(ip_str, token, sizeof(ip_str));
257 		if (mask)
258 			*mask = 0;
259 	}
260 
261 	if (strlen(ip_str) >= INET6_ADDRSTRLEN)
262 		return -EINVAL;
263 
264 	if (inet_pton6(ip_str, (unsigned char *)ipv6) != 1)
265 		return -EINVAL;
266 
267 	return 0;
268 }
269 
270 int
271 parse_range(const char *token, uint16_t *low, uint16_t *high)
272 {
273 	char ch;
274 	char num_str[20];
275 	uint32_t pos;
276 	int range_low = -1;
277 	int range_high = -1;
278 
279 	if (!low || !high)
280 		return -1;
281 
282 	memset(num_str, 0, 20);
283 	pos = 0;
284 
285 	while ((ch = *token++) != '\0') {
286 		if (isdigit(ch)) {
287 			if (pos >= 19)
288 				return -1;
289 			num_str[pos++] = ch;
290 		} else if (ch == ':') {
291 			if (range_low != -1)
292 				return -1;
293 			range_low = atoi(num_str);
294 			memset(num_str, 0, 20);
295 			pos = 0;
296 		}
297 	}
298 
299 	if (strlen(num_str) == 0)
300 		return -1;
301 
302 	range_high = atoi(num_str);
303 
304 	*low = (uint16_t)range_low;
305 	*high = (uint16_t)range_high;
306 
307 	return 0;
308 }
309 
310 /*
311  * helper function for parse_mac, parse one section of the ether addr.
312  */
313 static const char *
314 parse_uint8x16(const char *s, uint8_t *v, uint8_t ls)
315 {
316 	char *end;
317 	unsigned long t;
318 
319 	errno = 0;
320 	t = strtoul(s, &end, 16);
321 	if (errno != 0 || end[0] != ls || t > UINT8_MAX)
322 		return NULL;
323 	v[0] = t;
324 	return end + 1;
325 }
326 
327 static int
328 parse_mac(const char *str, struct rte_ether_addr *addr)
329 {
330 	uint32_t i;
331 
332 	static const uint8_t stop_sym[RTE_DIM(addr->addr_bytes)] = {
333 		[0] = ':',
334 		[1] = ':',
335 		[2] = ':',
336 		[3] = ':',
337 		[4] = ':',
338 		[5] = 0,
339 	};
340 
341 	for (i = 0; i != RTE_DIM(addr->addr_bytes); i++) {
342 		str = parse_uint8x16(str, addr->addr_bytes + i, stop_sym[i]);
343 		if (str == NULL)
344 			return -EINVAL;
345 	}
346 
347 	return 0;
348 }
349 
350 /** sp add parse */
351 struct cfg_sp_add_cfg_item {
352 	cmdline_fixed_string_t sp_keyword;
353 	cmdline_multi_string_t multi_string;
354 };
355 
356 static void
357 cfg_sp_add_cfg_item_parsed(void *parsed_result,
358 	__rte_unused struct cmdline *cl, void *data)
359 {
360 	struct cfg_sp_add_cfg_item *params = parsed_result;
361 	char *tokens[32];
362 	uint32_t n_tokens = RTE_DIM(tokens);
363 	struct parse_status *status = (struct parse_status *)data;
364 
365 	APP_CHECK((parse_tokenize_string(params->multi_string, tokens,
366 		&n_tokens) == 0), status, "too many arguments");
367 
368 	if (status->status < 0)
369 		return;
370 
371 	if (strcmp(tokens[0], "ipv4") == 0) {
372 		parse_sp4_tokens(tokens, n_tokens, status);
373 		if (status->status < 0)
374 			return;
375 	} else if (strcmp(tokens[0], "ipv6") == 0) {
376 		parse_sp6_tokens(tokens, n_tokens, status);
377 		if (status->status < 0)
378 			return;
379 	} else {
380 		APP_CHECK(0, status, "unrecognizable input %s\n",
381 			tokens[0]);
382 		return;
383 	}
384 }
385 
386 static cmdline_parse_token_string_t cfg_sp_add_sp_str =
387 	TOKEN_STRING_INITIALIZER(struct cfg_sp_add_cfg_item,
388 		sp_keyword, "sp");
389 
390 static cmdline_parse_token_string_t cfg_sp_add_multi_str =
391 	TOKEN_STRING_INITIALIZER(struct cfg_sp_add_cfg_item, multi_string,
392 		TOKEN_STRING_MULTI);
393 
394 cmdline_parse_inst_t cfg_sp_add_rule = {
395 	.f = cfg_sp_add_cfg_item_parsed,
396 	.data = NULL,
397 	.help_str = "",
398 	.tokens = {
399 		(void *) &cfg_sp_add_sp_str,
400 		(void *) &cfg_sp_add_multi_str,
401 		NULL,
402 	},
403 };
404 
405 /* sa add parse */
406 struct cfg_sa_add_cfg_item {
407 	cmdline_fixed_string_t sa_keyword;
408 	cmdline_multi_string_t multi_string;
409 };
410 
411 static void
412 cfg_sa_add_cfg_item_parsed(void *parsed_result,
413 	__rte_unused struct cmdline *cl, void *data)
414 {
415 	struct cfg_sa_add_cfg_item *params = parsed_result;
416 	char *tokens[32];
417 	uint32_t n_tokens = RTE_DIM(tokens);
418 	struct parse_status *status = (struct parse_status *)data;
419 
420 	APP_CHECK(parse_tokenize_string(params->multi_string, tokens,
421 		&n_tokens) == 0, status, "too many arguments\n");
422 
423 	parse_sa_tokens(tokens, n_tokens, status);
424 }
425 
426 static cmdline_parse_token_string_t cfg_sa_add_sa_str =
427 	TOKEN_STRING_INITIALIZER(struct cfg_sa_add_cfg_item,
428 		sa_keyword, "sa");
429 
430 static cmdline_parse_token_string_t cfg_sa_add_multi_str =
431 	TOKEN_STRING_INITIALIZER(struct cfg_sa_add_cfg_item, multi_string,
432 		TOKEN_STRING_MULTI);
433 
434 cmdline_parse_inst_t cfg_sa_add_rule = {
435 	.f = cfg_sa_add_cfg_item_parsed,
436 	.data = NULL,
437 	.help_str = "",
438 	.tokens = {
439 		(void *) &cfg_sa_add_sa_str,
440 		(void *) &cfg_sa_add_multi_str,
441 		NULL,
442 	},
443 };
444 
445 /* rt add parse */
446 struct cfg_rt_add_cfg_item {
447 	cmdline_fixed_string_t rt_keyword;
448 	cmdline_multi_string_t multi_string;
449 };
450 
451 static void
452 cfg_rt_add_cfg_item_parsed(void *parsed_result,
453 	__rte_unused struct cmdline *cl, void *data)
454 {
455 	struct cfg_rt_add_cfg_item *params = parsed_result;
456 	char *tokens[32];
457 	uint32_t n_tokens = RTE_DIM(tokens);
458 	struct parse_status *status = (struct parse_status *)data;
459 
460 	APP_CHECK(parse_tokenize_string(
461 		params->multi_string, tokens, &n_tokens) == 0,
462 		status, "too many arguments\n");
463 	if (status->status < 0)
464 		return;
465 
466 	parse_rt_tokens(tokens, n_tokens, status);
467 }
468 
469 static cmdline_parse_token_string_t cfg_rt_add_rt_str =
470 	TOKEN_STRING_INITIALIZER(struct cfg_rt_add_cfg_item,
471 		rt_keyword, "rt");
472 
473 static cmdline_parse_token_string_t cfg_rt_add_multi_str =
474 	TOKEN_STRING_INITIALIZER(struct cfg_rt_add_cfg_item, multi_string,
475 		TOKEN_STRING_MULTI);
476 
477 cmdline_parse_inst_t cfg_rt_add_rule = {
478 	.f = cfg_rt_add_cfg_item_parsed,
479 	.data = NULL,
480 	.help_str = "",
481 	.tokens = {
482 		(void *) &cfg_rt_add_rt_str,
483 		(void *) &cfg_rt_add_multi_str,
484 		NULL,
485 	},
486 };
487 
488 /* flow add parse */
489 struct cfg_flow_add_cfg_item {
490 	cmdline_fixed_string_t flow_keyword;
491 	cmdline_multi_string_t multi_string;
492 };
493 
494 static void
495 cfg_flow_add_cfg_item_parsed(void *parsed_result,
496 	__rte_unused struct cmdline *cl, void *data)
497 {
498 	struct cfg_flow_add_cfg_item *params = parsed_result;
499 	char *tokens[32];
500 	uint32_t n_tokens = RTE_DIM(tokens);
501 	struct parse_status *status = (struct parse_status *)data;
502 
503 	APP_CHECK(parse_tokenize_string(
504 		params->multi_string, tokens, &n_tokens) == 0,
505 		status, "too many arguments\n");
506 	if (status->status < 0)
507 		return;
508 
509 	parse_flow_tokens(tokens, n_tokens, status);
510 }
511 
512 static cmdline_parse_token_string_t cfg_flow_add_flow_str =
513 	TOKEN_STRING_INITIALIZER(struct cfg_flow_add_cfg_item,
514 		flow_keyword, "flow");
515 
516 static cmdline_parse_token_string_t cfg_flow_add_multi_str =
517 	TOKEN_STRING_INITIALIZER(struct cfg_flow_add_cfg_item, multi_string,
518 		TOKEN_STRING_MULTI);
519 
520 cmdline_parse_inst_t cfg_flow_add_rule = {
521 	.f = cfg_flow_add_cfg_item_parsed,
522 	.data = NULL,
523 	.help_str = "",
524 	.tokens = {
525 		(void *) &cfg_flow_add_flow_str,
526 		(void *) &cfg_flow_add_multi_str,
527 		NULL,
528 	},
529 };
530 
531 /* neigh add parse */
532 struct cfg_neigh_add_item {
533 	cmdline_fixed_string_t neigh;
534 	cmdline_fixed_string_t pstr;
535 	uint16_t port;
536 	cmdline_fixed_string_t mac;
537 };
538 
539 static void
540 cfg_parse_neigh(void *parsed_result, __rte_unused struct cmdline *cl,
541 	void *data)
542 {
543 	int32_t rc;
544 	struct cfg_neigh_add_item *res;
545 	struct parse_status *st;
546 	struct rte_ether_addr mac;
547 
548 	st = data;
549 	res = parsed_result;
550 	rc = parse_mac(res->mac, &mac);
551 	APP_CHECK(rc == 0, st, "invalid ether addr:%s", res->mac);
552 	rc = add_dst_ethaddr(res->port, &mac);
553 	APP_CHECK(rc == 0, st, "invalid port numer:%hu", res->port);
554 	if (st->status < 0)
555 		return;
556 }
557 
558 cmdline_parse_token_string_t cfg_add_neigh_start =
559 	TOKEN_STRING_INITIALIZER(struct cfg_neigh_add_item, neigh, "neigh");
560 cmdline_parse_token_string_t cfg_add_neigh_pstr =
561 	TOKEN_STRING_INITIALIZER(struct cfg_neigh_add_item, pstr, "port");
562 cmdline_parse_token_num_t cfg_add_neigh_port =
563 	TOKEN_NUM_INITIALIZER(struct cfg_neigh_add_item, port, UINT16);
564 cmdline_parse_token_string_t cfg_add_neigh_mac =
565 	TOKEN_STRING_INITIALIZER(struct cfg_neigh_add_item, mac, NULL);
566 
567 cmdline_parse_inst_t cfg_neigh_add_rule = {
568 	.f = cfg_parse_neigh,
569 	.data = NULL,
570 	.help_str = "",
571 	.tokens = {
572 		(void *)&cfg_add_neigh_start,
573 		(void *)&cfg_add_neigh_pstr,
574 		(void *)&cfg_add_neigh_port,
575 		(void *)&cfg_add_neigh_mac,
576 		NULL,
577 	},
578 };
579 
580 /** set of cfg items */
581 cmdline_parse_ctx_t ipsec_ctx[] = {
582 	(cmdline_parse_inst_t *)&cfg_sp_add_rule,
583 	(cmdline_parse_inst_t *)&cfg_sa_add_rule,
584 	(cmdline_parse_inst_t *)&cfg_rt_add_rule,
585 	(cmdline_parse_inst_t *)&cfg_flow_add_rule,
586 	(cmdline_parse_inst_t *)&cfg_neigh_add_rule,
587 	NULL,
588 };
589 
590 int
591 parse_cfg_file(const char *cfg_filename)
592 {
593 	struct cmdline *cl = cmdline_stdin_new(ipsec_ctx, "");
594 	FILE *f = fopen(cfg_filename, "r");
595 	char str[1024] = {0}, *get_s = NULL;
596 	uint32_t line_num = 0;
597 	struct parse_status status = {0};
598 
599 	if (f == NULL) {
600 		rte_panic("Error: invalid file descriptor %s\n", cfg_filename);
601 		goto error_exit;
602 	}
603 
604 	if (cl == NULL) {
605 		rte_panic("Error: cannot create cmdline instance\n");
606 		goto error_exit;
607 	}
608 
609 	cfg_sp_add_rule.data = &status;
610 	cfg_sa_add_rule.data = &status;
611 	cfg_rt_add_rule.data = &status;
612 	cfg_flow_add_rule.data = &status;
613 	cfg_neigh_add_rule.data = &status;
614 
615 	do {
616 		char oneline[1024];
617 		char *pos;
618 		get_s = fgets(oneline, 1024, f);
619 
620 		if (!get_s)
621 			break;
622 
623 		line_num++;
624 
625 		if (strlen(oneline) > 1022) {
626 			rte_panic("%s:%u: error: "
627 				"the line contains more characters the parser can handle\n",
628 				cfg_filename, line_num);
629 			goto error_exit;
630 		}
631 
632 		/* process comment char '#' */
633 		if (oneline[0] == '#')
634 			continue;
635 
636 		pos = strchr(oneline, '#');
637 		if (pos != NULL)
638 			*pos = '\0';
639 
640 		/* process line concatenator '\' */
641 		pos = strchr(oneline, 92);
642 		if (pos != NULL) {
643 			if (pos != oneline+strlen(oneline) - 2) {
644 				rte_panic("%s:%u: error: "
645 					"no character should exist after '\\'\n",
646 					cfg_filename, line_num);
647 				goto error_exit;
648 			}
649 
650 			*pos = '\0';
651 
652 			if (strlen(oneline) + strlen(str) > 1022) {
653 				rte_panic("%s:%u: error: "
654 					"the concatenated line contains more characters the parser can handle\n",
655 					cfg_filename, line_num);
656 				goto error_exit;
657 			}
658 
659 			strcpy(str + strlen(str), oneline);
660 			continue;
661 		}
662 
663 		/* copy the line to str and process */
664 		if (strlen(oneline) + strlen(str) > 1022) {
665 			rte_panic("%s:%u: error: "
666 				"the line contains more characters the parser can handle\n",
667 				cfg_filename, line_num);
668 			goto error_exit;
669 		}
670 		strcpy(str + strlen(str), oneline);
671 
672 		str[strlen(str)] = '\n';
673 		if (cmdline_parse(cl, str) < 0) {
674 			rte_panic("%s:%u: error: parsing \"%s\" failed\n",
675 				cfg_filename, line_num, str);
676 			goto error_exit;
677 		}
678 
679 		if (status.status < 0) {
680 			rte_panic("%s:%u: error: %s", cfg_filename,
681 				line_num, status.parse_msg);
682 			goto error_exit;
683 		}
684 
685 		memset(str, 0, 1024);
686 	} while (1);
687 
688 	cmdline_stdin_exit(cl);
689 	fclose(f);
690 
691 	sa_sort_arr();
692 	sp4_sort_arr();
693 	sp6_sort_arr();
694 
695 	return 0;
696 
697 error_exit:
698 	if (cl)
699 		cmdline_stdin_exit(cl);
700 	if (f)
701 		fclose(f);
702 
703 	return -1;
704 }
705