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