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