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 } 705