xref: /dpdk/examples/ipsec-secgw/parser.c (revision daa02b5cddbb8e11b31d41e2bf7bb1ae64dcae2f)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2016 Intel Corporation
3  */
4 #include <arpa/inet.h>
5 #include <sys/socket.h>
6 
7 #include <rte_common.h>
8 #include <rte_crypto.h>
9 #include <rte_string_fns.h>
10 
11 #include <cmdline_parse_string.h>
12 #include <cmdline_parse_num.h>
13 #include <cmdline_parse_ipaddr.h>
14 #include <cmdline_socket.h>
15 #include <cmdline.h>
16 
17 #include "flow.h"
18 #include "ipsec.h"
19 #include "parser.h"
20 
21 #define PARSE_DELIMITER		" \f\n\r\t\v"
22 static int
23 parse_tokenize_string(char *string, char *tokens[], uint32_t *n_tokens)
24 {
25 	uint32_t i;
26 
27 	if ((string == NULL) ||
28 		(tokens == NULL) ||
29 		(*n_tokens < 1))
30 		return -EINVAL;
31 
32 	for (i = 0; i < *n_tokens; i++) {
33 		tokens[i] = strtok_r(string, PARSE_DELIMITER, &string);
34 		if (tokens[i] == NULL)
35 			break;
36 	}
37 
38 	if ((i == *n_tokens) &&
39 		(NULL != strtok_r(string, PARSE_DELIMITER, &string)))
40 		return -E2BIG;
41 
42 	*n_tokens = i;
43 	return 0;
44 }
45 
46 int
47 parse_ipv4_addr(const char *token, struct in_addr *ipv4, uint32_t *mask)
48 {
49 	char ip_str[INET_ADDRSTRLEN] = {0};
50 	char *pch;
51 
52 	pch = strchr(token, '/');
53 	if (pch != NULL) {
54 		strlcpy(ip_str, token,
55 			RTE_MIN((unsigned int long)(pch - token + 1),
56 			sizeof(ip_str)));
57 		pch += 1;
58 		if (is_str_num(pch) != 0)
59 			return -EINVAL;
60 		if (mask)
61 			*mask = atoi(pch);
62 	} else {
63 		strlcpy(ip_str, token, sizeof(ip_str));
64 		if (mask)
65 			*mask = 0;
66 	}
67 	if (strlen(ip_str) >= INET_ADDRSTRLEN)
68 		return -EINVAL;
69 
70 	if (inet_pton(AF_INET, ip_str, ipv4) != 1)
71 		return -EINVAL;
72 
73 	return 0;
74 }
75 
76 int
77 parse_ipv6_addr(const char *token, struct in6_addr *ipv6, uint32_t *mask)
78 {
79 	char ip_str[256] = {0};
80 	char *pch;
81 
82 	pch = strchr(token, '/');
83 	if (pch != NULL) {
84 		strlcpy(ip_str, token,
85 			RTE_MIN((unsigned int long)(pch - token + 1),
86 					sizeof(ip_str)));
87 		pch += 1;
88 		if (is_str_num(pch) != 0)
89 			return -EINVAL;
90 		if (mask)
91 			*mask = atoi(pch);
92 	} else {
93 		strlcpy(ip_str, token, sizeof(ip_str));
94 		if (mask)
95 			*mask = 0;
96 	}
97 
98 	if (strlen(ip_str) >= INET6_ADDRSTRLEN)
99 		return -EINVAL;
100 
101 	if (inet_pton(AF_INET6, ip_str, ipv6) != 1)
102 		return -EINVAL;
103 
104 	return 0;
105 }
106 
107 int
108 parse_range(const char *token, uint16_t *low, uint16_t *high)
109 {
110 	char ch;
111 	char num_str[20];
112 	uint32_t pos;
113 	int range_low = -1;
114 	int range_high = -1;
115 
116 	if (!low || !high)
117 		return -1;
118 
119 	memset(num_str, 0, 20);
120 	pos = 0;
121 
122 	while ((ch = *token++) != '\0') {
123 		if (isdigit(ch)) {
124 			if (pos >= 19)
125 				return -1;
126 			num_str[pos++] = ch;
127 		} else if (ch == ':') {
128 			if (range_low != -1)
129 				return -1;
130 			range_low = atoi(num_str);
131 			memset(num_str, 0, 20);
132 			pos = 0;
133 		}
134 	}
135 
136 	if (strlen(num_str) == 0)
137 		return -1;
138 
139 	range_high = atoi(num_str);
140 
141 	*low = (uint16_t)range_low;
142 	*high = (uint16_t)range_high;
143 
144 	return 0;
145 }
146 
147 /*
148  * helper function for parse_mac, parse one section of the ether addr.
149  */
150 static const char *
151 parse_uint8x16(const char *s, uint8_t *v, uint8_t ls)
152 {
153 	char *end;
154 	unsigned long t;
155 
156 	errno = 0;
157 	t = strtoul(s, &end, 16);
158 	if (errno != 0 || end[0] != ls || t > UINT8_MAX)
159 		return NULL;
160 	v[0] = t;
161 	return end + 1;
162 }
163 
164 static int
165 parse_mac(const char *str, struct rte_ether_addr *addr)
166 {
167 	uint32_t i;
168 
169 	static const uint8_t stop_sym[RTE_DIM(addr->addr_bytes)] = {
170 		[0] = ':',
171 		[1] = ':',
172 		[2] = ':',
173 		[3] = ':',
174 		[4] = ':',
175 		[5] = 0,
176 	};
177 
178 	for (i = 0; i != RTE_DIM(addr->addr_bytes); i++) {
179 		str = parse_uint8x16(str, addr->addr_bytes + i, stop_sym[i]);
180 		if (str == NULL)
181 			return -EINVAL;
182 	}
183 
184 	return 0;
185 }
186 
187 /** sp add parse */
188 struct cfg_sp_add_cfg_item {
189 	cmdline_fixed_string_t sp_keyword;
190 	cmdline_multi_string_t multi_string;
191 };
192 
193 static void
194 cfg_sp_add_cfg_item_parsed(void *parsed_result,
195 	__rte_unused struct cmdline *cl, void *data)
196 {
197 	struct cfg_sp_add_cfg_item *params = parsed_result;
198 	char *tokens[32];
199 	uint32_t n_tokens = RTE_DIM(tokens);
200 	struct parse_status *status = (struct parse_status *)data;
201 
202 	APP_CHECK((parse_tokenize_string(params->multi_string, tokens,
203 		&n_tokens) == 0), status, "too many arguments");
204 
205 	if (status->status < 0)
206 		return;
207 
208 	if (strcmp(tokens[0], "ipv4") == 0) {
209 		parse_sp4_tokens(tokens, n_tokens, status);
210 		if (status->status < 0)
211 			return;
212 	} else if (strcmp(tokens[0], "ipv6") == 0) {
213 		parse_sp6_tokens(tokens, n_tokens, status);
214 		if (status->status < 0)
215 			return;
216 	} else {
217 		APP_CHECK(0, status, "unrecognizable input %s\n",
218 			tokens[0]);
219 		return;
220 	}
221 }
222 
223 static cmdline_parse_token_string_t cfg_sp_add_sp_str =
224 	TOKEN_STRING_INITIALIZER(struct cfg_sp_add_cfg_item,
225 		sp_keyword, "sp");
226 
227 static cmdline_parse_token_string_t cfg_sp_add_multi_str =
228 	TOKEN_STRING_INITIALIZER(struct cfg_sp_add_cfg_item, multi_string,
229 		TOKEN_STRING_MULTI);
230 
231 cmdline_parse_inst_t cfg_sp_add_rule = {
232 	.f = cfg_sp_add_cfg_item_parsed,
233 	.data = NULL,
234 	.help_str = "",
235 	.tokens = {
236 		(void *) &cfg_sp_add_sp_str,
237 		(void *) &cfg_sp_add_multi_str,
238 		NULL,
239 	},
240 };
241 
242 /* sa add parse */
243 struct cfg_sa_add_cfg_item {
244 	cmdline_fixed_string_t sa_keyword;
245 	cmdline_multi_string_t multi_string;
246 };
247 
248 static void
249 cfg_sa_add_cfg_item_parsed(void *parsed_result,
250 	__rte_unused struct cmdline *cl, void *data)
251 {
252 	struct cfg_sa_add_cfg_item *params = parsed_result;
253 	char *tokens[32];
254 	uint32_t n_tokens = RTE_DIM(tokens);
255 	struct parse_status *status = (struct parse_status *)data;
256 
257 	APP_CHECK(parse_tokenize_string(params->multi_string, tokens,
258 		&n_tokens) == 0, status, "too many arguments\n");
259 
260 	parse_sa_tokens(tokens, n_tokens, status);
261 }
262 
263 static cmdline_parse_token_string_t cfg_sa_add_sa_str =
264 	TOKEN_STRING_INITIALIZER(struct cfg_sa_add_cfg_item,
265 		sa_keyword, "sa");
266 
267 static cmdline_parse_token_string_t cfg_sa_add_multi_str =
268 	TOKEN_STRING_INITIALIZER(struct cfg_sa_add_cfg_item, multi_string,
269 		TOKEN_STRING_MULTI);
270 
271 cmdline_parse_inst_t cfg_sa_add_rule = {
272 	.f = cfg_sa_add_cfg_item_parsed,
273 	.data = NULL,
274 	.help_str = "",
275 	.tokens = {
276 		(void *) &cfg_sa_add_sa_str,
277 		(void *) &cfg_sa_add_multi_str,
278 		NULL,
279 	},
280 };
281 
282 /* rt add parse */
283 struct cfg_rt_add_cfg_item {
284 	cmdline_fixed_string_t rt_keyword;
285 	cmdline_multi_string_t multi_string;
286 };
287 
288 static void
289 cfg_rt_add_cfg_item_parsed(void *parsed_result,
290 	__rte_unused struct cmdline *cl, void *data)
291 {
292 	struct cfg_rt_add_cfg_item *params = parsed_result;
293 	char *tokens[32];
294 	uint32_t n_tokens = RTE_DIM(tokens);
295 	struct parse_status *status = (struct parse_status *)data;
296 
297 	APP_CHECK(parse_tokenize_string(
298 		params->multi_string, tokens, &n_tokens) == 0,
299 		status, "too many arguments\n");
300 	if (status->status < 0)
301 		return;
302 
303 	parse_rt_tokens(tokens, n_tokens, status);
304 }
305 
306 static cmdline_parse_token_string_t cfg_rt_add_rt_str =
307 	TOKEN_STRING_INITIALIZER(struct cfg_rt_add_cfg_item,
308 		rt_keyword, "rt");
309 
310 static cmdline_parse_token_string_t cfg_rt_add_multi_str =
311 	TOKEN_STRING_INITIALIZER(struct cfg_rt_add_cfg_item, multi_string,
312 		TOKEN_STRING_MULTI);
313 
314 cmdline_parse_inst_t cfg_rt_add_rule = {
315 	.f = cfg_rt_add_cfg_item_parsed,
316 	.data = NULL,
317 	.help_str = "",
318 	.tokens = {
319 		(void *) &cfg_rt_add_rt_str,
320 		(void *) &cfg_rt_add_multi_str,
321 		NULL,
322 	},
323 };
324 
325 /* flow add parse */
326 struct cfg_flow_add_cfg_item {
327 	cmdline_fixed_string_t flow_keyword;
328 	cmdline_multi_string_t multi_string;
329 };
330 
331 static void
332 cfg_flow_add_cfg_item_parsed(void *parsed_result,
333 	__rte_unused struct cmdline *cl, void *data)
334 {
335 	struct cfg_flow_add_cfg_item *params = parsed_result;
336 	char *tokens[32];
337 	uint32_t n_tokens = RTE_DIM(tokens);
338 	struct parse_status *status = (struct parse_status *)data;
339 
340 	APP_CHECK(parse_tokenize_string(
341 		params->multi_string, tokens, &n_tokens) == 0,
342 		status, "too many arguments\n");
343 	if (status->status < 0)
344 		return;
345 
346 	parse_flow_tokens(tokens, n_tokens, status);
347 }
348 
349 static cmdline_parse_token_string_t cfg_flow_add_flow_str =
350 	TOKEN_STRING_INITIALIZER(struct cfg_flow_add_cfg_item,
351 		flow_keyword, "flow");
352 
353 static cmdline_parse_token_string_t cfg_flow_add_multi_str =
354 	TOKEN_STRING_INITIALIZER(struct cfg_flow_add_cfg_item, multi_string,
355 		TOKEN_STRING_MULTI);
356 
357 cmdline_parse_inst_t cfg_flow_add_rule = {
358 	.f = cfg_flow_add_cfg_item_parsed,
359 	.data = NULL,
360 	.help_str = "",
361 	.tokens = {
362 		(void *) &cfg_flow_add_flow_str,
363 		(void *) &cfg_flow_add_multi_str,
364 		NULL,
365 	},
366 };
367 
368 /* neigh add parse */
369 struct cfg_neigh_add_item {
370 	cmdline_fixed_string_t neigh;
371 	cmdline_fixed_string_t pstr;
372 	uint16_t port;
373 	cmdline_fixed_string_t mac;
374 };
375 
376 static void
377 cfg_parse_neigh(void *parsed_result, __rte_unused struct cmdline *cl,
378 	void *data)
379 {
380 	int32_t rc;
381 	struct cfg_neigh_add_item *res;
382 	struct parse_status *st;
383 	struct rte_ether_addr mac;
384 
385 	st = data;
386 	res = parsed_result;
387 	rc = parse_mac(res->mac, &mac);
388 	APP_CHECK(rc == 0, st, "invalid ether addr:%s", res->mac);
389 	rc = add_dst_ethaddr(res->port, &mac);
390 	APP_CHECK(rc == 0, st, "invalid port numer:%hu", res->port);
391 	if (st->status < 0)
392 		return;
393 }
394 
395 cmdline_parse_token_string_t cfg_add_neigh_start =
396 	TOKEN_STRING_INITIALIZER(struct cfg_neigh_add_item, neigh, "neigh");
397 cmdline_parse_token_string_t cfg_add_neigh_pstr =
398 	TOKEN_STRING_INITIALIZER(struct cfg_neigh_add_item, pstr, "port");
399 cmdline_parse_token_num_t cfg_add_neigh_port =
400 	TOKEN_NUM_INITIALIZER(struct cfg_neigh_add_item, port, RTE_UINT16);
401 cmdline_parse_token_string_t cfg_add_neigh_mac =
402 	TOKEN_STRING_INITIALIZER(struct cfg_neigh_add_item, mac, NULL);
403 
404 cmdline_parse_inst_t cfg_neigh_add_rule = {
405 	.f = cfg_parse_neigh,
406 	.data = NULL,
407 	.help_str = "",
408 	.tokens = {
409 		(void *)&cfg_add_neigh_start,
410 		(void *)&cfg_add_neigh_pstr,
411 		(void *)&cfg_add_neigh_port,
412 		(void *)&cfg_add_neigh_mac,
413 		NULL,
414 	},
415 };
416 
417 /** set of cfg items */
418 cmdline_parse_ctx_t ipsec_ctx[] = {
419 	(cmdline_parse_inst_t *)&cfg_sp_add_rule,
420 	(cmdline_parse_inst_t *)&cfg_sa_add_rule,
421 	(cmdline_parse_inst_t *)&cfg_rt_add_rule,
422 	(cmdline_parse_inst_t *)&cfg_flow_add_rule,
423 	(cmdline_parse_inst_t *)&cfg_neigh_add_rule,
424 	NULL,
425 };
426 
427 int
428 parse_cfg_file(const char *cfg_filename)
429 {
430 	struct cmdline *cl = cmdline_stdin_new(ipsec_ctx, "");
431 	FILE *f = fopen(cfg_filename, "r");
432 	char str[1024] = {0}, *get_s = NULL;
433 	uint32_t line_num = 0;
434 	struct parse_status status = {0};
435 
436 	if (f == NULL) {
437 		rte_panic("Error: invalid file descriptor %s\n", cfg_filename);
438 		goto error_exit;
439 	}
440 
441 	if (cl == NULL) {
442 		rte_panic("Error: cannot create cmdline instance\n");
443 		goto error_exit;
444 	}
445 
446 	cfg_sp_add_rule.data = &status;
447 	cfg_sa_add_rule.data = &status;
448 	cfg_rt_add_rule.data = &status;
449 	cfg_flow_add_rule.data = &status;
450 	cfg_neigh_add_rule.data = &status;
451 
452 	do {
453 		char oneline[1024];
454 		char *pos;
455 		get_s = fgets(oneline, 1024, f);
456 
457 		if (!get_s)
458 			break;
459 
460 		line_num++;
461 
462 		if (strlen(oneline) > 1022) {
463 			rte_panic("%s:%u: error: "
464 				"the line contains more characters the parser can handle\n",
465 				cfg_filename, line_num);
466 			goto error_exit;
467 		}
468 
469 		/* process comment char '#' */
470 		if (oneline[0] == '#')
471 			continue;
472 
473 		pos = strchr(oneline, '#');
474 		if (pos != NULL)
475 			*pos = '\0';
476 
477 		/* process line concatenator '\' */
478 		pos = strchr(oneline, 92);
479 		if (pos != NULL) {
480 			if (pos != oneline+strlen(oneline) - 2) {
481 				rte_panic("%s:%u: error: "
482 					"no character should exist after '\\'\n",
483 					cfg_filename, line_num);
484 				goto error_exit;
485 			}
486 
487 			*pos = '\0';
488 
489 			if (strlen(oneline) + strlen(str) > 1022) {
490 				rte_panic("%s:%u: error: "
491 					"the concatenated line contains more characters the parser can handle\n",
492 					cfg_filename, line_num);
493 				goto error_exit;
494 			}
495 
496 			strcpy(str + strlen(str), oneline);
497 			continue;
498 		}
499 
500 		/* copy the line to str and process */
501 		if (strlen(oneline) + strlen(str) > 1022) {
502 			rte_panic("%s:%u: error: "
503 				"the line contains more characters the parser can handle\n",
504 				cfg_filename, line_num);
505 			goto error_exit;
506 		}
507 		strcpy(str + strlen(str), oneline);
508 
509 		str[strlen(str)] = '\n';
510 		if (cmdline_parse(cl, str) < 0) {
511 			rte_panic("%s:%u: error: parsing \"%s\" failed\n",
512 				cfg_filename, line_num, str);
513 			goto error_exit;
514 		}
515 
516 		if (status.status < 0) {
517 			rte_panic("%s:%u: error: %s", cfg_filename,
518 				line_num, status.parse_msg);
519 			goto error_exit;
520 		}
521 
522 		memset(str, 0, 1024);
523 	} while (1);
524 
525 	cmdline_stdin_exit(cl);
526 	fclose(f);
527 
528 	sa_sort_arr();
529 	sp4_sort_arr();
530 	sp6_sort_arr();
531 
532 	return 0;
533 
534 error_exit:
535 	if (cl)
536 		cmdline_stdin_exit(cl);
537 	if (f)
538 		fclose(f);
539 
540 	return -1;
541 }
542