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