examples/ipsec-secgw: enable flow based distribution
[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 #include <rte_string_fns.h>
7
8 #include <cmdline_parse_string.h>
9 #include <cmdline_parse_num.h>
10 #include <cmdline_parse_ipaddr.h>
11 #include <cmdline_socket.h>
12 #include <cmdline.h>
13
14 #include "flow.h"
15 #include "ipsec.h"
16 #include "parser.h"
17
18 #define PARSE_DELIMITER         " \f\n\r\t\v"
19 static int
20 parse_tokenize_string(char *string, char *tokens[], uint32_t *n_tokens)
21 {
22         uint32_t i;
23
24         if ((string == NULL) ||
25                 (tokens == NULL) ||
26                 (*n_tokens < 1))
27                 return -EINVAL;
28
29         for (i = 0; i < *n_tokens; i++) {
30                 tokens[i] = strtok_r(string, PARSE_DELIMITER, &string);
31                 if (tokens[i] == NULL)
32                         break;
33         }
34
35         if ((i == *n_tokens) &&
36                 (NULL != strtok_r(string, PARSE_DELIMITER, &string)))
37                 return -E2BIG;
38
39         *n_tokens = i;
40         return 0;
41 }
42
43 #define INADDRSZ 4
44 #define IN6ADDRSZ 16
45
46 /* int
47  * inet_pton4(src, dst)
48  *      like inet_aton() but without all the hexadecimal and shorthand.
49  * return:
50  *      1 if `src' is a valid dotted quad, else 0.
51  * notice:
52  *      does not touch `dst' unless it's returning 1.
53  * author:
54  *      Paul Vixie, 1996.
55  */
56 static int
57 inet_pton4(const char *src, unsigned char *dst)
58 {
59         static const char digits[] = "0123456789";
60         int saw_digit, octets, ch;
61         unsigned char tmp[INADDRSZ], *tp;
62
63         saw_digit = 0;
64         octets = 0;
65         *(tp = tmp) = 0;
66         while ((ch = *src++) != '\0') {
67                 const char *pch;
68
69                 pch = strchr(digits, ch);
70                 if (pch != NULL) {
71                         unsigned int new = *tp * 10 + (pch - digits);
72
73                         if (new > 255)
74                                 return 0;
75                         if (!saw_digit) {
76                                 if (++octets > 4)
77                                         return 0;
78                                 saw_digit = 1;
79                         }
80                         *tp = (unsigned char)new;
81                 } else if (ch == '.' && saw_digit) {
82                         if (octets == 4)
83                                 return 0;
84                         *++tp = 0;
85                         saw_digit = 0;
86                 } else
87                         return 0;
88         }
89         if (octets < 4)
90                 return 0;
91
92         memcpy(dst, tmp, INADDRSZ);
93         return 1;
94 }
95
96 /* int
97  * inet_pton6(src, dst)
98  *      convert presentation level address to network order binary form.
99  * return:
100  *      1 if `src' is a valid [RFC1884 2.2] address, else 0.
101  * notice:
102  *      (1) does not touch `dst' unless it's returning 1.
103  *      (2) :: in a full address is silently ignored.
104  * credit:
105  *      inspired by Mark Andrews.
106  * author:
107  *      Paul Vixie, 1996.
108  */
109 static int
110 inet_pton6(const char *src, unsigned char *dst)
111 {
112         static const char xdigits_l[] = "0123456789abcdef",
113                 xdigits_u[] = "0123456789ABCDEF";
114         unsigned char tmp[IN6ADDRSZ], *tp = 0, *endp = 0, *colonp = 0;
115         const char *xdigits = 0, *curtok = 0;
116         int ch = 0, saw_xdigit = 0, count_xdigit = 0;
117         unsigned int val = 0;
118         unsigned dbloct_count = 0;
119
120         memset((tp = tmp), '\0', IN6ADDRSZ);
121         endp = tp + IN6ADDRSZ;
122         colonp = NULL;
123         /* Leading :: requires some special handling. */
124         if (*src == ':')
125                 if (*++src != ':')
126                         return 0;
127         curtok = src;
128         saw_xdigit = count_xdigit = 0;
129         val = 0;
130
131         while ((ch = *src++) != '\0') {
132                 const char *pch;
133
134                 pch = strchr((xdigits = xdigits_l), ch);
135                 if (pch == NULL)
136                         pch = strchr((xdigits = xdigits_u), ch);
137                 if (pch != NULL) {
138                         if (count_xdigit >= 4)
139                                 return 0;
140                         val <<= 4;
141                         val |= (pch - xdigits);
142                         if (val > 0xffff)
143                                 return 0;
144                         saw_xdigit = 1;
145                         count_xdigit++;
146                         continue;
147                 }
148                 if (ch == ':') {
149                         curtok = src;
150                         if (!saw_xdigit) {
151                                 if (colonp)
152                                         return 0;
153                                 colonp = tp;
154                                 continue;
155                         } else if (*src == '\0') {
156                                 return 0;
157                         }
158                         if (tp + sizeof(int16_t) > endp)
159                                 return 0;
160                         *tp++ = (unsigned char) ((val >> 8) & 0xff);
161                         *tp++ = (unsigned char) (val & 0xff);
162                         saw_xdigit = 0;
163                         count_xdigit = 0;
164                         val = 0;
165                         dbloct_count++;
166                         continue;
167                 }
168                 if (ch == '.' && ((tp + INADDRSZ) <= endp) &&
169                     inet_pton4(curtok, tp) > 0) {
170                         tp += INADDRSZ;
171                         saw_xdigit = 0;
172                         dbloct_count += 2;
173                         break;  /* '\0' was seen by inet_pton4(). */
174                 }
175                 return 0;
176         }
177         if (saw_xdigit) {
178                 if (tp + sizeof(int16_t) > endp)
179                         return 0;
180                 *tp++ = (unsigned char) ((val >> 8) & 0xff);
181                 *tp++ = (unsigned char) (val & 0xff);
182                 dbloct_count++;
183         }
184         if (colonp != NULL) {
185                 /* if we already have 8 double octets, having a colon
186                  * means error */
187                 if (dbloct_count == 8)
188                         return 0;
189
190                 /*
191                  * Since some memmove()'s erroneously fail to handle
192                  * overlapping regions, we'll do the shift by hand.
193                  */
194                 const int n = tp - colonp;
195                 int i;
196
197                 for (i = 1; i <= n; i++) {
198                         endp[-i] = colonp[n - i];
199                         colonp[n - i] = 0;
200                 }
201                 tp = endp;
202         }
203         if (tp != endp)
204                 return 0;
205         memcpy(dst, tmp, IN6ADDRSZ);
206         return 1;
207 }
208
209 int
210 parse_ipv4_addr(const char *token, struct in_addr *ipv4, uint32_t *mask)
211 {
212         char ip_str[INET_ADDRSTRLEN] = {0};
213         char *pch;
214
215         pch = strchr(token, '/');
216         if (pch != NULL) {
217                 strlcpy(ip_str, token,
218                         RTE_MIN((unsigned int long)(pch - token + 1),
219                         sizeof(ip_str)));
220                 pch += 1;
221                 if (is_str_num(pch) != 0)
222                         return -EINVAL;
223                 if (mask)
224                         *mask = atoi(pch);
225         } else {
226                 strlcpy(ip_str, token, sizeof(ip_str));
227                 if (mask)
228                         *mask = 0;
229         }
230         if (strlen(ip_str) >= INET_ADDRSTRLEN)
231                 return -EINVAL;
232
233         if (inet_pton4(ip_str, (unsigned char *)ipv4) != 1)
234                 return -EINVAL;
235
236         return 0;
237 }
238
239 int
240 parse_ipv6_addr(const char *token, struct in6_addr *ipv6, uint32_t *mask)
241 {
242         char ip_str[256] = {0};
243         char *pch;
244
245         pch = strchr(token, '/');
246         if (pch != NULL) {
247                 strlcpy(ip_str, token,
248                         RTE_MIN((unsigned int long)(pch - token + 1),
249                                         sizeof(ip_str)));
250                 pch += 1;
251                 if (is_str_num(pch) != 0)
252                         return -EINVAL;
253                 if (mask)
254                         *mask = atoi(pch);
255         } else {
256                 strlcpy(ip_str, token, sizeof(ip_str));
257                 if (mask)
258                         *mask = 0;
259         }
260
261         if (strlen(ip_str) >= INET6_ADDRSTRLEN)
262                 return -EINVAL;
263
264         if (inet_pton6(ip_str, (unsigned char *)ipv6) != 1)
265                 return -EINVAL;
266
267         return 0;
268 }
269
270 int
271 parse_range(const char *token, uint16_t *low, uint16_t *high)
272 {
273         char ch;
274         char num_str[20];
275         uint32_t pos;
276         int range_low = -1;
277         int range_high = -1;
278
279         if (!low || !high)
280                 return -1;
281
282         memset(num_str, 0, 20);
283         pos = 0;
284
285         while ((ch = *token++) != '\0') {
286                 if (isdigit(ch)) {
287                         if (pos >= 19)
288                                 return -1;
289                         num_str[pos++] = ch;
290                 } else if (ch == ':') {
291                         if (range_low != -1)
292                                 return -1;
293                         range_low = atoi(num_str);
294                         memset(num_str, 0, 20);
295                         pos = 0;
296                 }
297         }
298
299         if (strlen(num_str) == 0)
300                 return -1;
301
302         range_high = atoi(num_str);
303
304         *low = (uint16_t)range_low;
305         *high = (uint16_t)range_high;
306
307         return 0;
308 }
309
310 /*
311  * helper function for parse_mac, parse one section of the ether addr.
312  */
313 static const char *
314 parse_uint8x16(const char *s, uint8_t *v, uint8_t ls)
315 {
316         char *end;
317         unsigned long t;
318
319         errno = 0;
320         t = strtoul(s, &end, 16);
321         if (errno != 0 || end[0] != ls || t > UINT8_MAX)
322                 return NULL;
323         v[0] = t;
324         return end + 1;
325 }
326
327 static int
328 parse_mac(const char *str, struct rte_ether_addr *addr)
329 {
330         uint32_t i;
331
332         static const uint8_t stop_sym[RTE_DIM(addr->addr_bytes)] = {
333                 [0] = ':',
334                 [1] = ':',
335                 [2] = ':',
336                 [3] = ':',
337                 [4] = ':',
338                 [5] = 0,
339         };
340
341         for (i = 0; i != RTE_DIM(addr->addr_bytes); i++) {
342                 str = parse_uint8x16(str, addr->addr_bytes + i, stop_sym[i]);
343                 if (str == NULL)
344                         return -EINVAL;
345         }
346
347         return 0;
348 }
349
350 /** sp add parse */
351 struct cfg_sp_add_cfg_item {
352         cmdline_fixed_string_t sp_keyword;
353         cmdline_multi_string_t multi_string;
354 };
355
356 static void
357 cfg_sp_add_cfg_item_parsed(void *parsed_result,
358         __rte_unused struct cmdline *cl, void *data)
359 {
360         struct cfg_sp_add_cfg_item *params = parsed_result;
361         char *tokens[32];
362         uint32_t n_tokens = RTE_DIM(tokens);
363         struct parse_status *status = (struct parse_status *)data;
364
365         APP_CHECK((parse_tokenize_string(params->multi_string, tokens,
366                 &n_tokens) == 0), status, "too many arguments");
367
368         if (status->status < 0)
369                 return;
370
371         if (strcmp(tokens[0], "ipv4") == 0) {
372                 parse_sp4_tokens(tokens, n_tokens, status);
373                 if (status->status < 0)
374                         return;
375         } else if (strcmp(tokens[0], "ipv6") == 0) {
376                 parse_sp6_tokens(tokens, n_tokens, status);
377                 if (status->status < 0)
378                         return;
379         } else {
380                 APP_CHECK(0, status, "unrecognizable input %s\n",
381                         tokens[0]);
382                 return;
383         }
384 }
385
386 static cmdline_parse_token_string_t cfg_sp_add_sp_str =
387         TOKEN_STRING_INITIALIZER(struct cfg_sp_add_cfg_item,
388                 sp_keyword, "sp");
389
390 static cmdline_parse_token_string_t cfg_sp_add_multi_str =
391         TOKEN_STRING_INITIALIZER(struct cfg_sp_add_cfg_item, multi_string,
392                 TOKEN_STRING_MULTI);
393
394 cmdline_parse_inst_t cfg_sp_add_rule = {
395         .f = cfg_sp_add_cfg_item_parsed,
396         .data = NULL,
397         .help_str = "",
398         .tokens = {
399                 (void *) &cfg_sp_add_sp_str,
400                 (void *) &cfg_sp_add_multi_str,
401                 NULL,
402         },
403 };
404
405 /* sa add parse */
406 struct cfg_sa_add_cfg_item {
407         cmdline_fixed_string_t sa_keyword;
408         cmdline_multi_string_t multi_string;
409 };
410
411 static void
412 cfg_sa_add_cfg_item_parsed(void *parsed_result,
413         __rte_unused struct cmdline *cl, void *data)
414 {
415         struct cfg_sa_add_cfg_item *params = parsed_result;
416         char *tokens[32];
417         uint32_t n_tokens = RTE_DIM(tokens);
418         struct parse_status *status = (struct parse_status *)data;
419
420         APP_CHECK(parse_tokenize_string(params->multi_string, tokens,
421                 &n_tokens) == 0, status, "too many arguments\n");
422
423         parse_sa_tokens(tokens, n_tokens, status);
424 }
425
426 static cmdline_parse_token_string_t cfg_sa_add_sa_str =
427         TOKEN_STRING_INITIALIZER(struct cfg_sa_add_cfg_item,
428                 sa_keyword, "sa");
429
430 static cmdline_parse_token_string_t cfg_sa_add_multi_str =
431         TOKEN_STRING_INITIALIZER(struct cfg_sa_add_cfg_item, multi_string,
432                 TOKEN_STRING_MULTI);
433
434 cmdline_parse_inst_t cfg_sa_add_rule = {
435         .f = cfg_sa_add_cfg_item_parsed,
436         .data = NULL,
437         .help_str = "",
438         .tokens = {
439                 (void *) &cfg_sa_add_sa_str,
440                 (void *) &cfg_sa_add_multi_str,
441                 NULL,
442         },
443 };
444
445 /* rt add parse */
446 struct cfg_rt_add_cfg_item {
447         cmdline_fixed_string_t rt_keyword;
448         cmdline_multi_string_t multi_string;
449 };
450
451 static void
452 cfg_rt_add_cfg_item_parsed(void *parsed_result,
453         __rte_unused struct cmdline *cl, void *data)
454 {
455         struct cfg_rt_add_cfg_item *params = parsed_result;
456         char *tokens[32];
457         uint32_t n_tokens = RTE_DIM(tokens);
458         struct parse_status *status = (struct parse_status *)data;
459
460         APP_CHECK(parse_tokenize_string(
461                 params->multi_string, tokens, &n_tokens) == 0,
462                 status, "too many arguments\n");
463         if (status->status < 0)
464                 return;
465
466         parse_rt_tokens(tokens, n_tokens, status);
467 }
468
469 static cmdline_parse_token_string_t cfg_rt_add_rt_str =
470         TOKEN_STRING_INITIALIZER(struct cfg_rt_add_cfg_item,
471                 rt_keyword, "rt");
472
473 static cmdline_parse_token_string_t cfg_rt_add_multi_str =
474         TOKEN_STRING_INITIALIZER(struct cfg_rt_add_cfg_item, multi_string,
475                 TOKEN_STRING_MULTI);
476
477 cmdline_parse_inst_t cfg_rt_add_rule = {
478         .f = cfg_rt_add_cfg_item_parsed,
479         .data = NULL,
480         .help_str = "",
481         .tokens = {
482                 (void *) &cfg_rt_add_rt_str,
483                 (void *) &cfg_rt_add_multi_str,
484                 NULL,
485         },
486 };
487
488 /* flow add parse */
489 struct cfg_flow_add_cfg_item {
490         cmdline_fixed_string_t flow_keyword;
491         cmdline_multi_string_t multi_string;
492 };
493
494 static void
495 cfg_flow_add_cfg_item_parsed(void *parsed_result,
496         __rte_unused struct cmdline *cl, void *data)
497 {
498         struct cfg_flow_add_cfg_item *params = parsed_result;
499         char *tokens[32];
500         uint32_t n_tokens = RTE_DIM(tokens);
501         struct parse_status *status = (struct parse_status *)data;
502
503         APP_CHECK(parse_tokenize_string(
504                 params->multi_string, tokens, &n_tokens) == 0,
505                 status, "too many arguments\n");
506         if (status->status < 0)
507                 return;
508
509         parse_flow_tokens(tokens, n_tokens, status);
510 }
511
512 static cmdline_parse_token_string_t cfg_flow_add_flow_str =
513         TOKEN_STRING_INITIALIZER(struct cfg_flow_add_cfg_item,
514                 flow_keyword, "flow");
515
516 static cmdline_parse_token_string_t cfg_flow_add_multi_str =
517         TOKEN_STRING_INITIALIZER(struct cfg_flow_add_cfg_item, multi_string,
518                 TOKEN_STRING_MULTI);
519
520 cmdline_parse_inst_t cfg_flow_add_rule = {
521         .f = cfg_flow_add_cfg_item_parsed,
522         .data = NULL,
523         .help_str = "",
524         .tokens = {
525                 (void *) &cfg_flow_add_flow_str,
526                 (void *) &cfg_flow_add_multi_str,
527                 NULL,
528         },
529 };
530
531 /* neigh add parse */
532 struct cfg_neigh_add_item {
533         cmdline_fixed_string_t neigh;
534         cmdline_fixed_string_t pstr;
535         uint16_t port;
536         cmdline_fixed_string_t mac;
537 };
538
539 static void
540 cfg_parse_neigh(void *parsed_result, __rte_unused struct cmdline *cl,
541         void *data)
542 {
543         int32_t rc;
544         struct cfg_neigh_add_item *res;
545         struct parse_status *st;
546         struct rte_ether_addr mac;
547
548         st = data;
549         res = parsed_result;
550         rc = parse_mac(res->mac, &mac);
551         APP_CHECK(rc == 0, st, "invalid ether addr:%s", res->mac);
552         rc = add_dst_ethaddr(res->port, &mac);
553         APP_CHECK(rc == 0, st, "invalid port numer:%hu", res->port);
554         if (st->status < 0)
555                 return;
556 }
557
558 cmdline_parse_token_string_t cfg_add_neigh_start =
559         TOKEN_STRING_INITIALIZER(struct cfg_neigh_add_item, neigh, "neigh");
560 cmdline_parse_token_string_t cfg_add_neigh_pstr =
561         TOKEN_STRING_INITIALIZER(struct cfg_neigh_add_item, pstr, "port");
562 cmdline_parse_token_num_t cfg_add_neigh_port =
563         TOKEN_NUM_INITIALIZER(struct cfg_neigh_add_item, port, UINT16);
564 cmdline_parse_token_string_t cfg_add_neigh_mac =
565         TOKEN_STRING_INITIALIZER(struct cfg_neigh_add_item, mac, NULL);
566
567 cmdline_parse_inst_t cfg_neigh_add_rule = {
568         .f = cfg_parse_neigh,
569         .data = NULL,
570         .help_str = "",
571         .tokens = {
572                 (void *)&cfg_add_neigh_start,
573                 (void *)&cfg_add_neigh_pstr,
574                 (void *)&cfg_add_neigh_port,
575                 (void *)&cfg_add_neigh_mac,
576                 NULL,
577         },
578 };
579
580 /** set of cfg items */
581 cmdline_parse_ctx_t ipsec_ctx[] = {
582         (cmdline_parse_inst_t *)&cfg_sp_add_rule,
583         (cmdline_parse_inst_t *)&cfg_sa_add_rule,
584         (cmdline_parse_inst_t *)&cfg_rt_add_rule,
585         (cmdline_parse_inst_t *)&cfg_flow_add_rule,
586         (cmdline_parse_inst_t *)&cfg_neigh_add_rule,
587         NULL,
588 };
589
590 int
591 parse_cfg_file(const char *cfg_filename)
592 {
593         struct cmdline *cl = cmdline_stdin_new(ipsec_ctx, "");
594         FILE *f = fopen(cfg_filename, "r");
595         char str[1024] = {0}, *get_s = NULL;
596         uint32_t line_num = 0;
597         struct parse_status status = {0};
598
599         if (f == NULL) {
600                 rte_panic("Error: invalid file descriptor %s\n", cfg_filename);
601                 goto error_exit;
602         }
603
604         if (cl == NULL) {
605                 rte_panic("Error: cannot create cmdline instance\n");
606                 goto error_exit;
607         }
608
609         cfg_sp_add_rule.data = &status;
610         cfg_sa_add_rule.data = &status;
611         cfg_rt_add_rule.data = &status;
612         cfg_flow_add_rule.data = &status;
613         cfg_neigh_add_rule.data = &status;
614
615         do {
616                 char oneline[1024];
617                 char *pos;
618                 get_s = fgets(oneline, 1024, f);
619
620                 if (!get_s)
621                         break;
622
623                 line_num++;
624
625                 if (strlen(oneline) > 1022) {
626                         rte_panic("%s:%u: error: "
627                                 "the line contains more characters the parser can handle\n",
628                                 cfg_filename, line_num);
629                         goto error_exit;
630                 }
631
632                 /* process comment char '#' */
633                 if (oneline[0] == '#')
634                         continue;
635
636                 pos = strchr(oneline, '#');
637                 if (pos != NULL)
638                         *pos = '\0';
639
640                 /* process line concatenator '\' */
641                 pos = strchr(oneline, 92);
642                 if (pos != NULL) {
643                         if (pos != oneline+strlen(oneline) - 2) {
644                                 rte_panic("%s:%u: error: "
645                                         "no character should exist after '\\'\n",
646                                         cfg_filename, line_num);
647                                 goto error_exit;
648                         }
649
650                         *pos = '\0';
651
652                         if (strlen(oneline) + strlen(str) > 1022) {
653                                 rte_panic("%s:%u: error: "
654                                         "the concatenated line contains more characters the parser can handle\n",
655                                         cfg_filename, line_num);
656                                 goto error_exit;
657                         }
658
659                         strcpy(str + strlen(str), oneline);
660                         continue;
661                 }
662
663                 /* copy the line to str and process */
664                 if (strlen(oneline) + strlen(str) > 1022) {
665                         rte_panic("%s:%u: error: "
666                                 "the line contains more characters the parser can handle\n",
667                                 cfg_filename, line_num);
668                         goto error_exit;
669                 }
670                 strcpy(str + strlen(str), oneline);
671
672                 str[strlen(str)] = '\n';
673                 if (cmdline_parse(cl, str) < 0) {
674                         rte_panic("%s:%u: error: parsing \"%s\" failed\n",
675                                 cfg_filename, line_num, str);
676                         goto error_exit;
677                 }
678
679                 if (status.status < 0) {
680                         rte_panic("%s:%u: error: %s", cfg_filename,
681                                 line_num, status.parse_msg);
682                         goto error_exit;
683                 }
684
685                 memset(str, 0, 1024);
686         } while (1);
687
688         cmdline_stdin_exit(cl);
689         fclose(f);
690
691         sa_sort_arr();
692         sp4_sort_arr();
693         sp6_sort_arr();
694
695         return 0;
696
697 error_exit:
698         if (cl)
699                 cmdline_stdin_exit(cl);
700         if (f)
701                 fclose(f);
702
703         return -1;
704 }