xref: /dpdk/examples/ipsec-secgw/parser.c (revision 2808423a9ce42a748aed77a4b487be27d2b6acfa)
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 /** sp add parse */
310 struct cfg_sp_add_cfg_item {
311 	cmdline_fixed_string_t sp_keyword;
312 	cmdline_multi_string_t multi_string;
313 };
314 
315 static void
316 cfg_sp_add_cfg_item_parsed(void *parsed_result,
317 	__rte_unused struct cmdline *cl, void *data)
318 {
319 	struct cfg_sp_add_cfg_item *params = parsed_result;
320 	char *tokens[32];
321 	uint32_t n_tokens = RTE_DIM(tokens);
322 	struct parse_status *status = (struct parse_status *)data;
323 
324 	APP_CHECK((parse_tokenize_string(params->multi_string, tokens,
325 		&n_tokens) == 0), status, "too many arguments");
326 
327 	if (status->status < 0)
328 		return;
329 
330 	if (strcmp(tokens[0], "ipv4") == 0) {
331 		parse_sp4_tokens(tokens, n_tokens, status);
332 		if (status->status < 0)
333 			return;
334 	} else if (strcmp(tokens[0], "ipv6") == 0) {
335 		parse_sp6_tokens(tokens, n_tokens, status);
336 		if (status->status < 0)
337 			return;
338 	} else {
339 		APP_CHECK(0, status, "unrecognizable input %s\n",
340 			tokens[0]);
341 		return;
342 	}
343 }
344 
345 static cmdline_parse_token_string_t cfg_sp_add_sp_str =
346 	TOKEN_STRING_INITIALIZER(struct cfg_sp_add_cfg_item,
347 		sp_keyword, "sp");
348 
349 static cmdline_parse_token_string_t cfg_sp_add_multi_str =
350 	TOKEN_STRING_INITIALIZER(struct cfg_sp_add_cfg_item, multi_string,
351 		TOKEN_STRING_MULTI);
352 
353 cmdline_parse_inst_t cfg_sp_add_rule = {
354 	.f = cfg_sp_add_cfg_item_parsed,
355 	.data = NULL,
356 	.help_str = "",
357 	.tokens = {
358 		(void *) &cfg_sp_add_sp_str,
359 		(void *) &cfg_sp_add_multi_str,
360 		NULL,
361 	},
362 };
363 
364 /* sa add parse */
365 struct cfg_sa_add_cfg_item {
366 	cmdline_fixed_string_t sa_keyword;
367 	cmdline_multi_string_t multi_string;
368 };
369 
370 static void
371 cfg_sa_add_cfg_item_parsed(void *parsed_result,
372 	__rte_unused struct cmdline *cl, void *data)
373 {
374 	struct cfg_sa_add_cfg_item *params = parsed_result;
375 	char *tokens[32];
376 	uint32_t n_tokens = RTE_DIM(tokens);
377 	struct parse_status *status = (struct parse_status *)data;
378 
379 	APP_CHECK(parse_tokenize_string(params->multi_string, tokens,
380 		&n_tokens) == 0, status, "too many arguments\n");
381 
382 	parse_sa_tokens(tokens, n_tokens, status);
383 }
384 
385 static cmdline_parse_token_string_t cfg_sa_add_sa_str =
386 	TOKEN_STRING_INITIALIZER(struct cfg_sa_add_cfg_item,
387 		sa_keyword, "sa");
388 
389 static cmdline_parse_token_string_t cfg_sa_add_multi_str =
390 	TOKEN_STRING_INITIALIZER(struct cfg_sa_add_cfg_item, multi_string,
391 		TOKEN_STRING_MULTI);
392 
393 cmdline_parse_inst_t cfg_sa_add_rule = {
394 	.f = cfg_sa_add_cfg_item_parsed,
395 	.data = NULL,
396 	.help_str = "",
397 	.tokens = {
398 		(void *) &cfg_sa_add_sa_str,
399 		(void *) &cfg_sa_add_multi_str,
400 		NULL,
401 	},
402 };
403 
404 /* rt add parse */
405 struct cfg_rt_add_cfg_item {
406 	cmdline_fixed_string_t rt_keyword;
407 	cmdline_multi_string_t multi_string;
408 };
409 
410 static void
411 cfg_rt_add_cfg_item_parsed(void *parsed_result,
412 	__rte_unused struct cmdline *cl, void *data)
413 {
414 	struct cfg_rt_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(
420 		params->multi_string, tokens, &n_tokens) == 0,
421 		status, "too many arguments\n");
422 	if (status->status < 0)
423 		return;
424 
425 	parse_rt_tokens(tokens, n_tokens, status);
426 }
427 
428 static cmdline_parse_token_string_t cfg_rt_add_rt_str =
429 	TOKEN_STRING_INITIALIZER(struct cfg_rt_add_cfg_item,
430 		rt_keyword, "rt");
431 
432 static cmdline_parse_token_string_t cfg_rt_add_multi_str =
433 	TOKEN_STRING_INITIALIZER(struct cfg_rt_add_cfg_item, multi_string,
434 		TOKEN_STRING_MULTI);
435 
436 cmdline_parse_inst_t cfg_rt_add_rule = {
437 	.f = cfg_rt_add_cfg_item_parsed,
438 	.data = NULL,
439 	.help_str = "",
440 	.tokens = {
441 		(void *) &cfg_rt_add_rt_str,
442 		(void *) &cfg_rt_add_multi_str,
443 		NULL,
444 	},
445 };
446 
447 /** set of cfg items */
448 cmdline_parse_ctx_t ipsec_ctx[] = {
449 	(cmdline_parse_inst_t *)&cfg_sp_add_rule,
450 	(cmdline_parse_inst_t *)&cfg_sa_add_rule,
451 	(cmdline_parse_inst_t *)&cfg_rt_add_rule,
452 	NULL,
453 };
454 
455 int
456 parse_cfg_file(const char *cfg_filename)
457 {
458 	struct cmdline *cl = cmdline_stdin_new(ipsec_ctx, "");
459 	FILE *f = fopen(cfg_filename, "r");
460 	char str[1024] = {0}, *get_s = NULL;
461 	uint32_t line_num = 0;
462 	struct parse_status status = {0};
463 
464 	if (f == NULL) {
465 		rte_panic("Error: invalid file descriptor %s\n", cfg_filename);
466 		goto error_exit;
467 	}
468 
469 	if (cl == NULL) {
470 		rte_panic("Error: cannot create cmdline instance\n");
471 		goto error_exit;
472 	}
473 
474 	cfg_sp_add_rule.data = &status;
475 	cfg_sa_add_rule.data = &status;
476 	cfg_rt_add_rule.data = &status;
477 
478 	do {
479 		char oneline[1024];
480 		char *pos;
481 		get_s = fgets(oneline, 1024, f);
482 
483 		if (!get_s)
484 			break;
485 
486 		line_num++;
487 
488 		if (strlen(oneline) > 1022) {
489 			rte_panic("%s:%u: error: "
490 				"the line contains more characters the parser can handle\n",
491 				cfg_filename, line_num);
492 			goto error_exit;
493 		}
494 
495 		/* process comment char '#' */
496 		if (oneline[0] == '#')
497 			continue;
498 
499 		pos = strchr(oneline, '#');
500 		if (pos != NULL)
501 			*pos = '\0';
502 
503 		/* process line concatenator '\' */
504 		pos = strchr(oneline, 92);
505 		if (pos != NULL) {
506 			if (pos != oneline+strlen(oneline) - 2) {
507 				rte_panic("%s:%u: error: "
508 					"no character should exist after '\\'\n",
509 					cfg_filename, line_num);
510 				goto error_exit;
511 			}
512 
513 			*pos = '\0';
514 
515 			if (strlen(oneline) + strlen(str) > 1022) {
516 				rte_panic("%s:%u: error: "
517 					"the concatenated line contains more characters the parser can handle\n",
518 					cfg_filename, line_num);
519 				goto error_exit;
520 			}
521 
522 			strcpy(str + strlen(str), oneline);
523 			continue;
524 		}
525 
526 		/* copy the line to str and process */
527 		if (strlen(oneline) + strlen(str) > 1022) {
528 			rte_panic("%s:%u: error: "
529 				"the line contains more characters the parser can handle\n",
530 				cfg_filename, line_num);
531 			goto error_exit;
532 		}
533 		strcpy(str + strlen(str), oneline);
534 
535 		str[strlen(str)] = '\n';
536 		if (cmdline_parse(cl, str) < 0) {
537 			rte_panic("%s:%u: error: parsing \"%s\" failed\n",
538 				cfg_filename, line_num, str);
539 			goto error_exit;
540 		}
541 
542 		if (status.status < 0) {
543 			rte_panic("%s:%u: error: %s", cfg_filename,
544 				line_num, status.parse_msg);
545 			goto error_exit;
546 		}
547 
548 		memset(str, 0, 1024);
549 	} while (1);
550 
551 	cmdline_stdin_exit(cl);
552 	fclose(f);
553 
554 	return 0;
555 
556 error_exit:
557 	if (cl)
558 		cmdline_stdin_exit(cl);
559 	if (f)
560 		fclose(f);
561 
562 	return -1;
563 }
564