New upstream version 18.02
[deb_dpdk.git] / examples / ipsec-secgw / parser.c
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
7 #include <cmdline_parse_string.h>
8 #include <cmdline_parse_num.h>
9 #include <cmdline_parse_ipaddr.h>
10 #include <cmdline_socket.h>
11 #include <cmdline.h>
12
13 #include "ipsec.h"
14 #include "parser.h"
15
16 #define PARSE_DELIMITER         " \f\n\r\t\v"
17 static int
18 parse_tokenize_string(char *string, char *tokens[], uint32_t *n_tokens)
19 {
20         uint32_t i;
21
22         if ((string == NULL) ||
23                 (tokens == NULL) ||
24                 (*n_tokens < 1))
25                 return -EINVAL;
26
27         for (i = 0; i < *n_tokens; i++) {
28                 tokens[i] = strtok_r(string, PARSE_DELIMITER, &string);
29                 if (tokens[i] == NULL)
30                         break;
31         }
32
33         if ((i == *n_tokens) &&
34                 (NULL != strtok_r(string, PARSE_DELIMITER, &string)))
35                 return -E2BIG;
36
37         *n_tokens = i;
38         return 0;
39 }
40
41 #define INADDRSZ 4
42 #define IN6ADDRSZ 16
43
44 /* int
45  * inet_pton4(src, dst)
46  *      like inet_aton() but without all the hexadecimal and shorthand.
47  * return:
48  *      1 if `src' is a valid dotted quad, else 0.
49  * notice:
50  *      does not touch `dst' unless it's returning 1.
51  * author:
52  *      Paul Vixie, 1996.
53  */
54 static int
55 inet_pton4(const char *src, unsigned char *dst)
56 {
57         static const char digits[] = "0123456789";
58         int saw_digit, octets, ch;
59         unsigned char tmp[INADDRSZ], *tp;
60
61         saw_digit = 0;
62         octets = 0;
63         *(tp = tmp) = 0;
64         while ((ch = *src++) != '\0') {
65                 const char *pch;
66
67                 pch = strchr(digits, ch);
68                 if (pch != NULL) {
69                         unsigned int new = *tp * 10 + (pch - digits);
70
71                         if (new > 255)
72                                 return 0;
73                         if (!saw_digit) {
74                                 if (++octets > 4)
75                                         return 0;
76                                 saw_digit = 1;
77                         }
78                         *tp = (unsigned char)new;
79                 } else if (ch == '.' && saw_digit) {
80                         if (octets == 4)
81                                 return 0;
82                         *++tp = 0;
83                         saw_digit = 0;
84                 } else
85                         return 0;
86         }
87         if (octets < 4)
88                 return 0;
89
90         memcpy(dst, tmp, INADDRSZ);
91         return 1;
92 }
93
94 /* int
95  * inet_pton6(src, dst)
96  *      convert presentation level address to network order binary form.
97  * return:
98  *      1 if `src' is a valid [RFC1884 2.2] address, else 0.
99  * notice:
100  *      (1) does not touch `dst' unless it's returning 1.
101  *      (2) :: in a full address is silently ignored.
102  * credit:
103  *      inspired by Mark Andrews.
104  * author:
105  *      Paul Vixie, 1996.
106  */
107 static int
108 inet_pton6(const char *src, unsigned char *dst)
109 {
110         static const char xdigits_l[] = "0123456789abcdef",
111                 xdigits_u[] = "0123456789ABCDEF";
112         unsigned char tmp[IN6ADDRSZ], *tp = 0, *endp = 0, *colonp = 0;
113         const char *xdigits = 0, *curtok = 0;
114         int ch = 0, saw_xdigit = 0, count_xdigit = 0;
115         unsigned int val = 0;
116         unsigned dbloct_count = 0;
117
118         memset((tp = tmp), '\0', IN6ADDRSZ);
119         endp = tp + IN6ADDRSZ;
120         colonp = NULL;
121         /* Leading :: requires some special handling. */
122         if (*src == ':')
123                 if (*++src != ':')
124                         return 0;
125         curtok = src;
126         saw_xdigit = count_xdigit = 0;
127         val = 0;
128
129         while ((ch = *src++) != '\0') {
130                 const char *pch;
131
132                 pch = strchr((xdigits = xdigits_l), ch);
133                 if (pch == NULL)
134                         pch = strchr((xdigits = xdigits_u), ch);
135                 if (pch != NULL) {
136                         if (count_xdigit >= 4)
137                                 return 0;
138                         val <<= 4;
139                         val |= (pch - xdigits);
140                         if (val > 0xffff)
141                                 return 0;
142                         saw_xdigit = 1;
143                         count_xdigit++;
144                         continue;
145                 }
146                 if (ch == ':') {
147                         curtok = src;
148                         if (!saw_xdigit) {
149                                 if (colonp)
150                                         return 0;
151                                 colonp = tp;
152                                 continue;
153                         } else if (*src == '\0') {
154                                 return 0;
155                         }
156                         if (tp + sizeof(int16_t) > endp)
157                                 return 0;
158                         *tp++ = (unsigned char) ((val >> 8) & 0xff);
159                         *tp++ = (unsigned char) (val & 0xff);
160                         saw_xdigit = 0;
161                         count_xdigit = 0;
162                         val = 0;
163                         dbloct_count++;
164                         continue;
165                 }
166                 if (ch == '.' && ((tp + INADDRSZ) <= endp) &&
167                     inet_pton4(curtok, tp) > 0) {
168                         tp += INADDRSZ;
169                         saw_xdigit = 0;
170                         dbloct_count += 2;
171                         break;  /* '\0' was seen by inet_pton4(). */
172                 }
173                 return 0;
174         }
175         if (saw_xdigit) {
176                 if (tp + sizeof(int16_t) > endp)
177                         return 0;
178                 *tp++ = (unsigned char) ((val >> 8) & 0xff);
179                 *tp++ = (unsigned char) (val & 0xff);
180                 dbloct_count++;
181         }
182         if (colonp != NULL) {
183                 /* if we already have 8 double octets, having a colon
184                  * means error */
185                 if (dbloct_count == 8)
186                         return 0;
187
188                 /*
189                  * Since some memmove()'s erroneously fail to handle
190                  * overlapping regions, we'll do the shift by hand.
191                  */
192                 const int n = tp - colonp;
193                 int i;
194
195                 for (i = 1; i <= n; i++) {
196                         endp[-i] = colonp[n - i];
197                         colonp[n - i] = 0;
198                 }
199                 tp = endp;
200         }
201         if (tp != endp)
202                 return 0;
203         memcpy(dst, tmp, IN6ADDRSZ);
204         return 1;
205 }
206
207 int
208 parse_ipv4_addr(const char *token, struct in_addr *ipv4, uint32_t *mask)
209 {
210         char ip_str[256] = {0};
211         char *pch;
212
213         pch = strchr(token, '/');
214         if (pch != NULL) {
215                 strncpy(ip_str, token, pch - token);
216                 pch += 1;
217                 if (is_str_num(pch) != 0)
218                         return -EINVAL;
219                 if (mask)
220                         *mask = atoi(pch);
221         } else {
222                 strncpy(ip_str, token, sizeof(ip_str) - 1);
223                 if (mask)
224                         *mask = 0;
225         }
226
227         if (strlen(ip_str) >= INET_ADDRSTRLEN)
228                 return -EINVAL;
229
230         if (inet_pton4(ip_str, (unsigned char *)ipv4) != 1)
231                 return -EINVAL;
232
233         return 0;
234 }
235
236 int
237 parse_ipv6_addr(const char *token, struct in6_addr *ipv6, 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) >= INET6_ADDRSTRLEN)
257                 return -EINVAL;
258
259         if (inet_pton6(ip_str, (unsigned char *)ipv6) != 1)
260                 return -EINVAL;
261
262         return 0;
263 }
264
265 int
266 parse_range(const char *token, uint16_t *low, uint16_t *high)
267 {
268         char ch;
269         char num_str[20];
270         uint32_t pos;
271         int range_low = -1;
272         int range_high = -1;
273
274         if (!low || !high)
275                 return -1;
276
277         memset(num_str, 0, 20);
278         pos = 0;
279
280         while ((ch = *token++) != '\0') {
281                 if (isdigit(ch)) {
282                         if (pos >= 19)
283                                 return -1;
284                         num_str[pos++] = ch;
285                 } else if (ch == ':') {
286                         if (range_low != -1)
287                                 return -1;
288                         range_low = atoi(num_str);
289                         memset(num_str, 0, 20);
290                         pos = 0;
291                 }
292         }
293
294         if (strlen(num_str) == 0)
295                 return -1;
296
297         range_high = atoi(num_str);
298
299         *low = (uint16_t)range_low;
300         *high = (uint16_t)range_high;
301
302         return 0;
303 }
304
305 /** sp add parse */
306 struct cfg_sp_add_cfg_item {
307         cmdline_fixed_string_t sp_keyword;
308         cmdline_multi_string_t multi_string;
309 };
310
311 static void
312 cfg_sp_add_cfg_item_parsed(void *parsed_result,
313         __rte_unused struct cmdline *cl, void *data)
314 {
315         struct cfg_sp_add_cfg_item *params = parsed_result;
316         char *tokens[32];
317         uint32_t n_tokens = RTE_DIM(tokens);
318         struct parse_status *status = (struct parse_status *)data;
319
320         APP_CHECK((parse_tokenize_string(params->multi_string, tokens,
321                 &n_tokens) == 0), status, "too many arguments");
322
323         if (status->status < 0)
324                 return;
325
326         if (strcmp(tokens[0], "ipv4") == 0) {
327                 parse_sp4_tokens(tokens, n_tokens, status);
328                 if (status->status < 0)
329                         return;
330         } else if (strcmp(tokens[0], "ipv6") == 0) {
331                 parse_sp6_tokens(tokens, n_tokens, status);
332                 if (status->status < 0)
333                         return;
334         } else {
335                 APP_CHECK(0, status, "unrecognizable input %s\n",
336                         tokens[0]);
337                 return;
338         }
339 }
340
341 static cmdline_parse_token_string_t cfg_sp_add_sp_str =
342         TOKEN_STRING_INITIALIZER(struct cfg_sp_add_cfg_item,
343                 sp_keyword, "sp");
344
345 static cmdline_parse_token_string_t cfg_sp_add_multi_str =
346         TOKEN_STRING_INITIALIZER(struct cfg_sp_add_cfg_item, multi_string,
347                 TOKEN_STRING_MULTI);
348
349 cmdline_parse_inst_t cfg_sp_add_rule = {
350         .f = cfg_sp_add_cfg_item_parsed,
351         .data = NULL,
352         .help_str = "",
353         .tokens = {
354                 (void *) &cfg_sp_add_sp_str,
355                 (void *) &cfg_sp_add_multi_str,
356                 NULL,
357         },
358 };
359
360 /* sa add parse */
361 struct cfg_sa_add_cfg_item {
362         cmdline_fixed_string_t sa_keyword;
363         cmdline_multi_string_t multi_string;
364 };
365
366 static void
367 cfg_sa_add_cfg_item_parsed(void *parsed_result,
368         __rte_unused struct cmdline *cl, void *data)
369 {
370         struct cfg_sa_add_cfg_item *params = parsed_result;
371         char *tokens[32];
372         uint32_t n_tokens = RTE_DIM(tokens);
373         struct parse_status *status = (struct parse_status *)data;
374
375         APP_CHECK(parse_tokenize_string(params->multi_string, tokens,
376                 &n_tokens) == 0, status, "too many arguments\n");
377
378         parse_sa_tokens(tokens, n_tokens, status);
379 }
380
381 static cmdline_parse_token_string_t cfg_sa_add_sa_str =
382         TOKEN_STRING_INITIALIZER(struct cfg_sa_add_cfg_item,
383                 sa_keyword, "sa");
384
385 static cmdline_parse_token_string_t cfg_sa_add_multi_str =
386         TOKEN_STRING_INITIALIZER(struct cfg_sa_add_cfg_item, multi_string,
387                 TOKEN_STRING_MULTI);
388
389 cmdline_parse_inst_t cfg_sa_add_rule = {
390         .f = cfg_sa_add_cfg_item_parsed,
391         .data = NULL,
392         .help_str = "",
393         .tokens = {
394                 (void *) &cfg_sa_add_sa_str,
395                 (void *) &cfg_sa_add_multi_str,
396                 NULL,
397         },
398 };
399
400 /* rt add parse */
401 struct cfg_rt_add_cfg_item {
402         cmdline_fixed_string_t rt_keyword;
403         cmdline_multi_string_t multi_string;
404 };
405
406 static void
407 cfg_rt_add_cfg_item_parsed(void *parsed_result,
408         __rte_unused struct cmdline *cl, void *data)
409 {
410         struct cfg_rt_add_cfg_item *params = parsed_result;
411         char *tokens[32];
412         uint32_t n_tokens = RTE_DIM(tokens);
413         struct parse_status *status = (struct parse_status *)data;
414
415         APP_CHECK(parse_tokenize_string(
416                 params->multi_string, tokens, &n_tokens) == 0,
417                 status, "too many arguments\n");
418         if (status->status < 0)
419                 return;
420
421         parse_rt_tokens(tokens, n_tokens, status);
422 }
423
424 static cmdline_parse_token_string_t cfg_rt_add_rt_str =
425         TOKEN_STRING_INITIALIZER(struct cfg_rt_add_cfg_item,
426                 rt_keyword, "rt");
427
428 static cmdline_parse_token_string_t cfg_rt_add_multi_str =
429         TOKEN_STRING_INITIALIZER(struct cfg_rt_add_cfg_item, multi_string,
430                 TOKEN_STRING_MULTI);
431
432 cmdline_parse_inst_t cfg_rt_add_rule = {
433         .f = cfg_rt_add_cfg_item_parsed,
434         .data = NULL,
435         .help_str = "",
436         .tokens = {
437                 (void *) &cfg_rt_add_rt_str,
438                 (void *) &cfg_rt_add_multi_str,
439                 NULL,
440         },
441 };
442
443 /** set of cfg items */
444 cmdline_parse_ctx_t ipsec_ctx[] = {
445         (cmdline_parse_inst_t *)&cfg_sp_add_rule,
446         (cmdline_parse_inst_t *)&cfg_sa_add_rule,
447         (cmdline_parse_inst_t *)&cfg_rt_add_rule,
448         NULL,
449 };
450
451 int
452 parse_cfg_file(const char *cfg_filename)
453 {
454         struct cmdline *cl = cmdline_stdin_new(ipsec_ctx, "");
455         FILE *f = fopen(cfg_filename, "r");
456         char str[1024] = {0}, *get_s = NULL;
457         uint32_t line_num = 0;
458         struct parse_status status = {0};
459
460         if (f == NULL) {
461                 rte_panic("Error: invalid file descriptor %s\n", cfg_filename);
462                 goto error_exit;
463         }
464
465         if (cl == NULL) {
466                 rte_panic("Error: cannot create cmdline instance\n");
467                 goto error_exit;
468         }
469
470         cfg_sp_add_rule.data = &status;
471         cfg_sa_add_rule.data = &status;
472         cfg_rt_add_rule.data = &status;
473
474         do {
475                 char oneline[1024];
476                 char *pos;
477                 get_s = fgets(oneline, 1024, f);
478
479                 if (!get_s)
480                         break;
481
482                 line_num++;
483
484                 if (strlen(oneline) > 1022) {
485                         rte_panic("%s:%u: error: "
486                                 "the line contains more characters the parser can handle\n",
487                                 cfg_filename, line_num);
488                         goto error_exit;
489                 }
490
491                 /* process comment char '#' */
492                 if (oneline[0] == '#')
493                         continue;
494
495                 pos = strchr(oneline, '#');
496                 if (pos != NULL)
497                         *pos = '\0';
498
499                 /* process line concatenator '\' */
500                 pos = strchr(oneline, 92);
501                 if (pos != NULL) {
502                         if (pos != oneline+strlen(oneline) - 2) {
503                                 rte_panic("%s:%u: error: "
504                                         "no character should exist after '\\'\n",
505                                         cfg_filename, line_num);
506                                 goto error_exit;
507                         }
508
509                         *pos = '\0';
510
511                         if (strlen(oneline) + strlen(str) > 1022) {
512                                 rte_panic("%s:%u: error: "
513                                         "the concatenated line contains more characters the parser can handle\n",
514                                         cfg_filename, line_num);
515                                 goto error_exit;
516                         }
517
518                         strncpy(str + strlen(str), oneline,
519                                 strlen(oneline));
520
521                         continue;
522                 }
523
524                 /* copy the line to str and process */
525                 if (strlen(oneline) + strlen(str) > 1022) {
526                         rte_panic("%s:%u: error: "
527                                 "the line contains more characters the parser can handle\n",
528                                 cfg_filename, line_num);
529                         goto error_exit;
530                 }
531                 strncpy(str + strlen(str), oneline,
532                         strlen(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 }