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