xref: /dpdk/examples/ipsec-secgw/parser.c (revision 6e0752205bb2df36e51d2411263a89a868e2f53f)
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, RTE_MIN((unsigned int long)(pch - token),
217 					sizeof(ip_str)));
218 		pch += 1;
219 		if (is_str_num(pch) != 0)
220 			return -EINVAL;
221 		if (mask)
222 			*mask = atoi(pch);
223 	} else {
224 		strlcpy(ip_str, token, sizeof(ip_str));
225 		if (mask)
226 			*mask = 0;
227 	}
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, RTE_MIN((unsigned int long)(pch - token),
247 					sizeof(ip_str)));
248 		pch += 1;
249 		if (is_str_num(pch) != 0)
250 			return -EINVAL;
251 		if (mask)
252 			*mask = atoi(pch);
253 	} else {
254 		strlcpy(ip_str, token, sizeof(ip_str));
255 		if (mask)
256 			*mask = 0;
257 	}
258 
259 	if (strlen(ip_str) >= INET6_ADDRSTRLEN)
260 		return -EINVAL;
261 
262 	if (inet_pton6(ip_str, (unsigned char *)ipv6) != 1)
263 		return -EINVAL;
264 
265 	return 0;
266 }
267 
268 int
269 parse_range(const char *token, uint16_t *low, uint16_t *high)
270 {
271 	char ch;
272 	char num_str[20];
273 	uint32_t pos;
274 	int range_low = -1;
275 	int range_high = -1;
276 
277 	if (!low || !high)
278 		return -1;
279 
280 	memset(num_str, 0, 20);
281 	pos = 0;
282 
283 	while ((ch = *token++) != '\0') {
284 		if (isdigit(ch)) {
285 			if (pos >= 19)
286 				return -1;
287 			num_str[pos++] = ch;
288 		} else if (ch == ':') {
289 			if (range_low != -1)
290 				return -1;
291 			range_low = atoi(num_str);
292 			memset(num_str, 0, 20);
293 			pos = 0;
294 		}
295 	}
296 
297 	if (strlen(num_str) == 0)
298 		return -1;
299 
300 	range_high = atoi(num_str);
301 
302 	*low = (uint16_t)range_low;
303 	*high = (uint16_t)range_high;
304 
305 	return 0;
306 }
307 
308 /** sp add parse */
309 struct cfg_sp_add_cfg_item {
310 	cmdline_fixed_string_t sp_keyword;
311 	cmdline_multi_string_t multi_string;
312 };
313 
314 static void
315 cfg_sp_add_cfg_item_parsed(void *parsed_result,
316 	__rte_unused struct cmdline *cl, void *data)
317 {
318 	struct cfg_sp_add_cfg_item *params = parsed_result;
319 	char *tokens[32];
320 	uint32_t n_tokens = RTE_DIM(tokens);
321 	struct parse_status *status = (struct parse_status *)data;
322 
323 	APP_CHECK((parse_tokenize_string(params->multi_string, tokens,
324 		&n_tokens) == 0), status, "too many arguments");
325 
326 	if (status->status < 0)
327 		return;
328 
329 	if (strcmp(tokens[0], "ipv4") == 0) {
330 		parse_sp4_tokens(tokens, n_tokens, status);
331 		if (status->status < 0)
332 			return;
333 	} else if (strcmp(tokens[0], "ipv6") == 0) {
334 		parse_sp6_tokens(tokens, n_tokens, status);
335 		if (status->status < 0)
336 			return;
337 	} else {
338 		APP_CHECK(0, status, "unrecognizable input %s\n",
339 			tokens[0]);
340 		return;
341 	}
342 }
343 
344 static cmdline_parse_token_string_t cfg_sp_add_sp_str =
345 	TOKEN_STRING_INITIALIZER(struct cfg_sp_add_cfg_item,
346 		sp_keyword, "sp");
347 
348 static cmdline_parse_token_string_t cfg_sp_add_multi_str =
349 	TOKEN_STRING_INITIALIZER(struct cfg_sp_add_cfg_item, multi_string,
350 		TOKEN_STRING_MULTI);
351 
352 cmdline_parse_inst_t cfg_sp_add_rule = {
353 	.f = cfg_sp_add_cfg_item_parsed,
354 	.data = NULL,
355 	.help_str = "",
356 	.tokens = {
357 		(void *) &cfg_sp_add_sp_str,
358 		(void *) &cfg_sp_add_multi_str,
359 		NULL,
360 	},
361 };
362 
363 /* sa add parse */
364 struct cfg_sa_add_cfg_item {
365 	cmdline_fixed_string_t sa_keyword;
366 	cmdline_multi_string_t multi_string;
367 };
368 
369 static void
370 cfg_sa_add_cfg_item_parsed(void *parsed_result,
371 	__rte_unused struct cmdline *cl, void *data)
372 {
373 	struct cfg_sa_add_cfg_item *params = parsed_result;
374 	char *tokens[32];
375 	uint32_t n_tokens = RTE_DIM(tokens);
376 	struct parse_status *status = (struct parse_status *)data;
377 
378 	APP_CHECK(parse_tokenize_string(params->multi_string, tokens,
379 		&n_tokens) == 0, status, "too many arguments\n");
380 
381 	parse_sa_tokens(tokens, n_tokens, status);
382 }
383 
384 static cmdline_parse_token_string_t cfg_sa_add_sa_str =
385 	TOKEN_STRING_INITIALIZER(struct cfg_sa_add_cfg_item,
386 		sa_keyword, "sa");
387 
388 static cmdline_parse_token_string_t cfg_sa_add_multi_str =
389 	TOKEN_STRING_INITIALIZER(struct cfg_sa_add_cfg_item, multi_string,
390 		TOKEN_STRING_MULTI);
391 
392 cmdline_parse_inst_t cfg_sa_add_rule = {
393 	.f = cfg_sa_add_cfg_item_parsed,
394 	.data = NULL,
395 	.help_str = "",
396 	.tokens = {
397 		(void *) &cfg_sa_add_sa_str,
398 		(void *) &cfg_sa_add_multi_str,
399 		NULL,
400 	},
401 };
402 
403 /* rt add parse */
404 struct cfg_rt_add_cfg_item {
405 	cmdline_fixed_string_t rt_keyword;
406 	cmdline_multi_string_t multi_string;
407 };
408 
409 static void
410 cfg_rt_add_cfg_item_parsed(void *parsed_result,
411 	__rte_unused struct cmdline *cl, void *data)
412 {
413 	struct cfg_rt_add_cfg_item *params = parsed_result;
414 	char *tokens[32];
415 	uint32_t n_tokens = RTE_DIM(tokens);
416 	struct parse_status *status = (struct parse_status *)data;
417 
418 	APP_CHECK(parse_tokenize_string(
419 		params->multi_string, tokens, &n_tokens) == 0,
420 		status, "too many arguments\n");
421 	if (status->status < 0)
422 		return;
423 
424 	parse_rt_tokens(tokens, n_tokens, status);
425 }
426 
427 static cmdline_parse_token_string_t cfg_rt_add_rt_str =
428 	TOKEN_STRING_INITIALIZER(struct cfg_rt_add_cfg_item,
429 		rt_keyword, "rt");
430 
431 static cmdline_parse_token_string_t cfg_rt_add_multi_str =
432 	TOKEN_STRING_INITIALIZER(struct cfg_rt_add_cfg_item, multi_string,
433 		TOKEN_STRING_MULTI);
434 
435 cmdline_parse_inst_t cfg_rt_add_rule = {
436 	.f = cfg_rt_add_cfg_item_parsed,
437 	.data = NULL,
438 	.help_str = "",
439 	.tokens = {
440 		(void *) &cfg_rt_add_rt_str,
441 		(void *) &cfg_rt_add_multi_str,
442 		NULL,
443 	},
444 };
445 
446 /** set of cfg items */
447 cmdline_parse_ctx_t ipsec_ctx[] = {
448 	(cmdline_parse_inst_t *)&cfg_sp_add_rule,
449 	(cmdline_parse_inst_t *)&cfg_sa_add_rule,
450 	(cmdline_parse_inst_t *)&cfg_rt_add_rule,
451 	NULL,
452 };
453 
454 int
455 parse_cfg_file(const char *cfg_filename)
456 {
457 	struct cmdline *cl = cmdline_stdin_new(ipsec_ctx, "");
458 	FILE *f = fopen(cfg_filename, "r");
459 	char str[1024] = {0}, *get_s = NULL;
460 	uint32_t line_num = 0;
461 	struct parse_status status = {0};
462 
463 	if (f == NULL) {
464 		rte_panic("Error: invalid file descriptor %s\n", cfg_filename);
465 		goto error_exit;
466 	}
467 
468 	if (cl == NULL) {
469 		rte_panic("Error: cannot create cmdline instance\n");
470 		goto error_exit;
471 	}
472 
473 	cfg_sp_add_rule.data = &status;
474 	cfg_sa_add_rule.data = &status;
475 	cfg_rt_add_rule.data = &status;
476 
477 	do {
478 		char oneline[1024];
479 		char *pos;
480 		get_s = fgets(oneline, 1024, f);
481 
482 		if (!get_s)
483 			break;
484 
485 		line_num++;
486 
487 		if (strlen(oneline) > 1022) {
488 			rte_panic("%s:%u: error: "
489 				"the line contains more characters the parser can handle\n",
490 				cfg_filename, line_num);
491 			goto error_exit;
492 		}
493 
494 		/* process comment char '#' */
495 		if (oneline[0] == '#')
496 			continue;
497 
498 		pos = strchr(oneline, '#');
499 		if (pos != NULL)
500 			*pos = '\0';
501 
502 		/* process line concatenator '\' */
503 		pos = strchr(oneline, 92);
504 		if (pos != NULL) {
505 			if (pos != oneline+strlen(oneline) - 2) {
506 				rte_panic("%s:%u: error: "
507 					"no character should exist after '\\'\n",
508 					cfg_filename, line_num);
509 				goto error_exit;
510 			}
511 
512 			*pos = '\0';
513 
514 			if (strlen(oneline) + strlen(str) > 1022) {
515 				rte_panic("%s:%u: error: "
516 					"the concatenated line contains more characters the parser can handle\n",
517 					cfg_filename, line_num);
518 				goto error_exit;
519 			}
520 
521 			strcpy(str + strlen(str), oneline);
522 			continue;
523 		}
524 
525 		/* copy the line to str and process */
526 		if (strlen(oneline) + strlen(str) > 1022) {
527 			rte_panic("%s:%u: error: "
528 				"the line contains more characters the parser can handle\n",
529 				cfg_filename, line_num);
530 			goto error_exit;
531 		}
532 		strcpy(str + strlen(str), oneline);
533 
534 		str[strlen(str)] = '\n';
535 		if (cmdline_parse(cl, str) < 0) {
536 			rte_panic("%s:%u: error: parsing \"%s\" failed\n",
537 				cfg_filename, line_num, str);
538 			goto error_exit;
539 		}
540 
541 		if (status.status < 0) {
542 			rte_panic("%s:%u: error: %s", cfg_filename,
543 				line_num, status.parse_msg);
544 			goto error_exit;
545 		}
546 
547 		memset(str, 0, 1024);
548 	} while (1);
549 
550 	cmdline_stdin_exit(cl);
551 	fclose(f);
552 
553 	return 0;
554 
555 error_exit:
556 	if (cl)
557 		cmdline_stdin_exit(cl);
558 	if (f)
559 		fclose(f);
560 
561 	return -1;
562 }
563