xref: /dpdk/examples/ipsec-secgw/parser.c (revision a49342abbb5d68fafab1d2ba4c669c0e76e32c65)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2016 Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 #include <rte_common.h>
34 #include <rte_crypto.h>
35 
36 #include <cmdline_parse_string.h>
37 #include <cmdline_parse_num.h>
38 #include <cmdline_parse_ipaddr.h>
39 #include <cmdline_socket.h>
40 #include <cmdline.h>
41 
42 #include "ipsec.h"
43 #include "parser.h"
44 
45 #define PARSE_DELIMITER		" \f\n\r\t\v"
46 static int
47 parse_tokenize_string(char *string, char *tokens[], uint32_t *n_tokens)
48 {
49 	uint32_t i;
50 
51 	if ((string == NULL) ||
52 		(tokens == NULL) ||
53 		(*n_tokens < 1))
54 		return -EINVAL;
55 
56 	for (i = 0; i < *n_tokens; i++) {
57 		tokens[i] = strtok_r(string, PARSE_DELIMITER, &string);
58 		if (tokens[i] == NULL)
59 			break;
60 	}
61 
62 	if ((i == *n_tokens) &&
63 		(NULL != strtok_r(string, PARSE_DELIMITER, &string)))
64 		return -E2BIG;
65 
66 	*n_tokens = i;
67 	return 0;
68 }
69 
70 #define INADDRSZ 4
71 #define IN6ADDRSZ 16
72 
73 /* int
74  * inet_pton4(src, dst)
75  *      like inet_aton() but without all the hexadecimal and shorthand.
76  * return:
77  *      1 if `src' is a valid dotted quad, else 0.
78  * notice:
79  *      does not touch `dst' unless it's returning 1.
80  * author:
81  *      Paul Vixie, 1996.
82  */
83 static int
84 inet_pton4(const char *src, unsigned char *dst)
85 {
86 	static const char digits[] = "0123456789";
87 	int saw_digit, octets, ch;
88 	unsigned char tmp[INADDRSZ], *tp;
89 
90 	saw_digit = 0;
91 	octets = 0;
92 	*(tp = tmp) = 0;
93 	while ((ch = *src++) != '\0') {
94 		const char *pch;
95 
96 		pch = strchr(digits, ch);
97 		if (pch != NULL) {
98 			unsigned int new = *tp * 10 + (pch - digits);
99 
100 			if (new > 255)
101 				return 0;
102 			if (!saw_digit) {
103 				if (++octets > 4)
104 					return 0;
105 				saw_digit = 1;
106 			}
107 			*tp = (unsigned char)new;
108 		} else if (ch == '.' && saw_digit) {
109 			if (octets == 4)
110 				return 0;
111 			*++tp = 0;
112 			saw_digit = 0;
113 		} else
114 			return 0;
115 	}
116 	if (octets < 4)
117 		return 0;
118 
119 	memcpy(dst, tmp, INADDRSZ);
120 	return 1;
121 }
122 
123 /* int
124  * inet_pton6(src, dst)
125  *      convert presentation level address to network order binary form.
126  * return:
127  *      1 if `src' is a valid [RFC1884 2.2] address, else 0.
128  * notice:
129  *      (1) does not touch `dst' unless it's returning 1.
130  *      (2) :: in a full address is silently ignored.
131  * credit:
132  *      inspired by Mark Andrews.
133  * author:
134  *      Paul Vixie, 1996.
135  */
136 static int
137 inet_pton6(const char *src, unsigned char *dst)
138 {
139 	static const char xdigits_l[] = "0123456789abcdef",
140 		xdigits_u[] = "0123456789ABCDEF";
141 	unsigned char tmp[IN6ADDRSZ], *tp = 0, *endp = 0, *colonp = 0;
142 	const char *xdigits = 0, *curtok = 0;
143 	int ch = 0, saw_xdigit = 0, count_xdigit = 0;
144 	unsigned int val = 0;
145 	unsigned dbloct_count = 0;
146 
147 	memset((tp = tmp), '\0', IN6ADDRSZ);
148 	endp = tp + IN6ADDRSZ;
149 	colonp = NULL;
150 	/* Leading :: requires some special handling. */
151 	if (*src == ':')
152 		if (*++src != ':')
153 			return 0;
154 	curtok = src;
155 	saw_xdigit = count_xdigit = 0;
156 	val = 0;
157 
158 	while ((ch = *src++) != '\0') {
159 		const char *pch;
160 
161 		pch = strchr((xdigits = xdigits_l), ch);
162 		if (pch == NULL)
163 			pch = strchr((xdigits = xdigits_u), ch);
164 		if (pch != NULL) {
165 			if (count_xdigit >= 4)
166 				return 0;
167 			val <<= 4;
168 			val |= (pch - xdigits);
169 			if (val > 0xffff)
170 				return 0;
171 			saw_xdigit = 1;
172 			count_xdigit++;
173 			continue;
174 		}
175 		if (ch == ':') {
176 			curtok = src;
177 			if (!saw_xdigit) {
178 				if (colonp)
179 					return 0;
180 				colonp = tp;
181 				continue;
182 			} else if (*src == '\0') {
183 				return 0;
184 			}
185 			if (tp + sizeof(int16_t) > endp)
186 				return 0;
187 			*tp++ = (unsigned char) ((val >> 8) & 0xff);
188 			*tp++ = (unsigned char) (val & 0xff);
189 			saw_xdigit = 0;
190 			count_xdigit = 0;
191 			val = 0;
192 			dbloct_count++;
193 			continue;
194 		}
195 		if (ch == '.' && ((tp + INADDRSZ) <= endp) &&
196 		    inet_pton4(curtok, tp) > 0) {
197 			tp += INADDRSZ;
198 			saw_xdigit = 0;
199 			dbloct_count += 2;
200 			break;  /* '\0' was seen by inet_pton4(). */
201 		}
202 		return 0;
203 	}
204 	if (saw_xdigit) {
205 		if (tp + sizeof(int16_t) > endp)
206 			return 0;
207 		*tp++ = (unsigned char) ((val >> 8) & 0xff);
208 		*tp++ = (unsigned char) (val & 0xff);
209 		dbloct_count++;
210 	}
211 	if (colonp != NULL) {
212 		/* if we already have 8 double octets, having a colon
213 		 * means error */
214 		if (dbloct_count == 8)
215 			return 0;
216 
217 		/*
218 		 * Since some memmove()'s erroneously fail to handle
219 		 * overlapping regions, we'll do the shift by hand.
220 		 */
221 		const int n = tp - colonp;
222 		int i;
223 
224 		for (i = 1; i <= n; i++) {
225 			endp[-i] = colonp[n - i];
226 			colonp[n - i] = 0;
227 		}
228 		tp = endp;
229 	}
230 	if (tp != endp)
231 		return 0;
232 	memcpy(dst, tmp, IN6ADDRSZ);
233 	return 1;
234 }
235 
236 int
237 parse_ipv4_addr(const char *token, struct in_addr *ipv4, uint32_t *mask)
238 {
239 	char ip_str[256] = {0};
240 	char *pch;
241 
242 	pch = strchr(token, '/');
243 	if (pch != NULL) {
244 		strncpy(ip_str, token, pch - token);
245 		pch += 1;
246 		if (is_str_num(pch) != 0)
247 			return -EINVAL;
248 		if (mask)
249 			*mask = atoi(pch);
250 	} else {
251 		strncpy(ip_str, token, sizeof(ip_str) - 1);
252 		if (mask)
253 			*mask = 0;
254 	}
255 
256 	if (strlen(ip_str) >= INET_ADDRSTRLEN)
257 		return -EINVAL;
258 
259 	if (inet_pton4(ip_str, (unsigned char *)ipv4) != 1)
260 		return -EINVAL;
261 
262 	return 0;
263 }
264 
265 int
266 parse_ipv6_addr(const char *token, struct in6_addr *ipv6, uint32_t *mask)
267 {
268 	char ip_str[256] = {0};
269 	char *pch;
270 
271 	pch = strchr(token, '/');
272 	if (pch != NULL) {
273 		strncpy(ip_str, token, pch - token);
274 		pch += 1;
275 		if (is_str_num(pch) != 0)
276 			return -EINVAL;
277 		if (mask)
278 			*mask = atoi(pch);
279 	} else {
280 		strncpy(ip_str, token, sizeof(ip_str) - 1);
281 		if (mask)
282 			*mask = 0;
283 	}
284 
285 	if (strlen(ip_str) >= INET6_ADDRSTRLEN)
286 		return -EINVAL;
287 
288 	if (inet_pton6(ip_str, (unsigned char *)ipv6) != 1)
289 		return -EINVAL;
290 
291 	return 0;
292 }
293 
294 int
295 parse_range(const char *token, uint16_t *low, uint16_t *high)
296 {
297 	char ch;
298 	char num_str[20];
299 	uint32_t pos;
300 	int range_low = -1;
301 	int range_high = -1;
302 
303 	if (!low || !high)
304 		return -1;
305 
306 	memset(num_str, 0, 20);
307 	pos = 0;
308 
309 	while ((ch = *token++) != '\0') {
310 		if (isdigit(ch)) {
311 			if (pos >= 19)
312 				return -1;
313 			num_str[pos++] = ch;
314 		} else if (ch == ':') {
315 			if (range_low != -1)
316 				return -1;
317 			range_low = atoi(num_str);
318 			memset(num_str, 0, 20);
319 			pos = 0;
320 		}
321 	}
322 
323 	if (strlen(num_str) == 0)
324 		return -1;
325 
326 	range_high = atoi(num_str);
327 
328 	*low = (uint16_t)range_low;
329 	*high = (uint16_t)range_high;
330 
331 	return 0;
332 }
333 
334 /** sp add parse */
335 struct cfg_sp_add_cfg_item {
336 	cmdline_fixed_string_t sp_keyword;
337 	cmdline_multi_string_t multi_string;
338 };
339 
340 static void
341 cfg_sp_add_cfg_item_parsed(void *parsed_result,
342 	__rte_unused struct cmdline *cl, void *data)
343 {
344 	struct cfg_sp_add_cfg_item *params = parsed_result;
345 	char *tokens[32];
346 	uint32_t n_tokens = RTE_DIM(tokens);
347 	struct parse_status *status = (struct parse_status *)data;
348 
349 	APP_CHECK((parse_tokenize_string(params->multi_string, tokens,
350 		&n_tokens) == 0), status, "too many arguments");
351 
352 	if (status->status < 0)
353 		return;
354 
355 	if (strcmp(tokens[0], "ipv4") == 0) {
356 		parse_sp4_tokens(tokens, n_tokens, status);
357 		if (status->status < 0)
358 			return;
359 	} else if (strcmp(tokens[0], "ipv6") == 0) {
360 		parse_sp6_tokens(tokens, n_tokens, status);
361 		if (status->status < 0)
362 			return;
363 	} else {
364 		APP_CHECK(0, status, "unrecognizable input %s\n",
365 			tokens[0]);
366 		return;
367 	}
368 }
369 
370 static cmdline_parse_token_string_t cfg_sp_add_sp_str =
371 	TOKEN_STRING_INITIALIZER(struct cfg_sp_add_cfg_item,
372 		sp_keyword, "sp");
373 
374 static cmdline_parse_token_string_t cfg_sp_add_multi_str =
375 	TOKEN_STRING_INITIALIZER(struct cfg_sp_add_cfg_item, multi_string,
376 		TOKEN_STRING_MULTI);
377 
378 cmdline_parse_inst_t cfg_sp_add_rule = {
379 	.f = cfg_sp_add_cfg_item_parsed,
380 	.data = NULL,
381 	.help_str = "",
382 	.tokens = {
383 		(void *) &cfg_sp_add_sp_str,
384 		(void *) &cfg_sp_add_multi_str,
385 		NULL,
386 	},
387 };
388 
389 /* sa add parse */
390 struct cfg_sa_add_cfg_item {
391 	cmdline_fixed_string_t sa_keyword;
392 	cmdline_multi_string_t multi_string;
393 };
394 
395 static void
396 cfg_sa_add_cfg_item_parsed(void *parsed_result,
397 	__rte_unused struct cmdline *cl, void *data)
398 {
399 	struct cfg_sa_add_cfg_item *params = parsed_result;
400 	char *tokens[32];
401 	uint32_t n_tokens = RTE_DIM(tokens);
402 	struct parse_status *status = (struct parse_status *)data;
403 
404 	APP_CHECK(parse_tokenize_string(params->multi_string, tokens,
405 		&n_tokens) == 0, status, "too many arguments\n");
406 
407 	parse_sa_tokens(tokens, n_tokens, status);
408 }
409 
410 static cmdline_parse_token_string_t cfg_sa_add_sa_str =
411 	TOKEN_STRING_INITIALIZER(struct cfg_sa_add_cfg_item,
412 		sa_keyword, "sa");
413 
414 static cmdline_parse_token_string_t cfg_sa_add_multi_str =
415 	TOKEN_STRING_INITIALIZER(struct cfg_sa_add_cfg_item, multi_string,
416 		TOKEN_STRING_MULTI);
417 
418 cmdline_parse_inst_t cfg_sa_add_rule = {
419 	.f = cfg_sa_add_cfg_item_parsed,
420 	.data = NULL,
421 	.help_str = "",
422 	.tokens = {
423 		(void *) &cfg_sa_add_sa_str,
424 		(void *) &cfg_sa_add_multi_str,
425 		NULL,
426 	},
427 };
428 
429 /* rt add parse */
430 struct cfg_rt_add_cfg_item {
431 	cmdline_fixed_string_t rt_keyword;
432 	cmdline_multi_string_t multi_string;
433 };
434 
435 static void
436 cfg_rt_add_cfg_item_parsed(void *parsed_result,
437 	__rte_unused struct cmdline *cl, void *data)
438 {
439 	struct cfg_rt_add_cfg_item *params = parsed_result;
440 	char *tokens[32];
441 	uint32_t n_tokens = RTE_DIM(tokens);
442 	struct parse_status *status = (struct parse_status *)data;
443 
444 	APP_CHECK(parse_tokenize_string(
445 		params->multi_string, tokens, &n_tokens) == 0,
446 		status, "too many arguments\n");
447 	if (status->status < 0)
448 		return;
449 
450 	parse_rt_tokens(tokens, n_tokens, status);
451 }
452 
453 static cmdline_parse_token_string_t cfg_rt_add_rt_str =
454 	TOKEN_STRING_INITIALIZER(struct cfg_rt_add_cfg_item,
455 		rt_keyword, "rt");
456 
457 static cmdline_parse_token_string_t cfg_rt_add_multi_str =
458 	TOKEN_STRING_INITIALIZER(struct cfg_rt_add_cfg_item, multi_string,
459 		TOKEN_STRING_MULTI);
460 
461 cmdline_parse_inst_t cfg_rt_add_rule = {
462 	.f = cfg_rt_add_cfg_item_parsed,
463 	.data = NULL,
464 	.help_str = "",
465 	.tokens = {
466 		(void *) &cfg_rt_add_rt_str,
467 		(void *) &cfg_rt_add_multi_str,
468 		NULL,
469 	},
470 };
471 
472 /** set of cfg items */
473 cmdline_parse_ctx_t ipsec_ctx[] = {
474 	(cmdline_parse_inst_t *)&cfg_sp_add_rule,
475 	(cmdline_parse_inst_t *)&cfg_sa_add_rule,
476 	(cmdline_parse_inst_t *)&cfg_rt_add_rule,
477 	NULL,
478 };
479 
480 int
481 parse_cfg_file(const char *cfg_filename)
482 {
483 	struct cmdline *cl = cmdline_stdin_new(ipsec_ctx, "");
484 	FILE *f = fopen(cfg_filename, "r");
485 	char str[1024] = {0}, *get_s = NULL;
486 	uint32_t line_num = 0;
487 	struct parse_status status = {0};
488 
489 	if (f == NULL) {
490 		rte_panic("Error: invalid file descriptor %s\n", cfg_filename);
491 		goto error_exit;
492 	}
493 
494 	if (cl == NULL) {
495 		rte_panic("Error: cannot create cmdline instance\n");
496 		goto error_exit;
497 	}
498 
499 	cfg_sp_add_rule.data = &status;
500 	cfg_sa_add_rule.data = &status;
501 	cfg_rt_add_rule.data = &status;
502 
503 	do {
504 		char oneline[1024];
505 		char *pos;
506 		get_s = fgets(oneline, 1024, f);
507 
508 		if (!get_s)
509 			break;
510 
511 		line_num++;
512 
513 		if (strlen(oneline) > 1022) {
514 			rte_panic("%s:%u: error: "
515 				"the line contains more characters the parser can handle\n",
516 				cfg_filename, line_num);
517 			goto error_exit;
518 		}
519 
520 		/* process comment char '#' */
521 		if (oneline[0] == '#')
522 			continue;
523 
524 		pos = strchr(oneline, '#');
525 		if (pos != NULL)
526 			*pos = '\0';
527 
528 		/* process line concatenator '\' */
529 		pos = strchr(oneline, 92);
530 		if (pos != NULL) {
531 			if (pos != oneline+strlen(oneline) - 2) {
532 				rte_panic("%s:%u: error: "
533 					"no character should exist after '\\'\n",
534 					cfg_filename, line_num);
535 				goto error_exit;
536 			}
537 
538 			*pos = '\0';
539 
540 			if (strlen(oneline) + strlen(str) > 1022) {
541 				rte_panic("%s:%u: error: "
542 					"the concatenated line contains more characters the parser can handle\n",
543 					cfg_filename, line_num);
544 				goto error_exit;
545 			}
546 
547 			strncpy(str + strlen(str), oneline,
548 				strlen(oneline));
549 
550 			continue;
551 		}
552 
553 		/* copy the line to str and process */
554 		if (strlen(oneline) + strlen(str) > 1022) {
555 			rte_panic("%s:%u: error: "
556 				"the line contains more characters the parser can handle\n",
557 				cfg_filename, line_num);
558 			goto error_exit;
559 		}
560 		strncpy(str + strlen(str), oneline,
561 			strlen(oneline));
562 
563 		str[strlen(str)] = '\n';
564 		if (cmdline_parse(cl, str) < 0) {
565 			rte_panic("%s:%u: error: parsing \"%s\" failed\n",
566 				cfg_filename, line_num, str);
567 			goto error_exit;
568 		}
569 
570 		if (status.status < 0) {
571 			rte_panic("%s:%u: error: %s", cfg_filename,
572 				line_num, status.parse_msg);
573 			goto error_exit;
574 		}
575 
576 		memset(str, 0, 1024);
577 	} while (1);
578 
579 	cmdline_stdin_exit(cl);
580 	fclose(f);
581 
582 	return 0;
583 
584 error_exit:
585 	if (cl)
586 		cmdline_stdin_exit(cl);
587 	if (f)
588 		fclose(f);
589 
590 	return -1;
591 }
592