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 /** sp add parse */ 310 struct cfg_sp_add_cfg_item { 311 cmdline_fixed_string_t sp_keyword; 312 cmdline_multi_string_t multi_string; 313 }; 314 315 static void 316 cfg_sp_add_cfg_item_parsed(void *parsed_result, 317 __rte_unused struct cmdline *cl, void *data) 318 { 319 struct cfg_sp_add_cfg_item *params = parsed_result; 320 char *tokens[32]; 321 uint32_t n_tokens = RTE_DIM(tokens); 322 struct parse_status *status = (struct parse_status *)data; 323 324 APP_CHECK((parse_tokenize_string(params->multi_string, tokens, 325 &n_tokens) == 0), status, "too many arguments"); 326 327 if (status->status < 0) 328 return; 329 330 if (strcmp(tokens[0], "ipv4") == 0) { 331 parse_sp4_tokens(tokens, n_tokens, status); 332 if (status->status < 0) 333 return; 334 } else if (strcmp(tokens[0], "ipv6") == 0) { 335 parse_sp6_tokens(tokens, n_tokens, status); 336 if (status->status < 0) 337 return; 338 } else { 339 APP_CHECK(0, status, "unrecognizable input %s\n", 340 tokens[0]); 341 return; 342 } 343 } 344 345 static cmdline_parse_token_string_t cfg_sp_add_sp_str = 346 TOKEN_STRING_INITIALIZER(struct cfg_sp_add_cfg_item, 347 sp_keyword, "sp"); 348 349 static cmdline_parse_token_string_t cfg_sp_add_multi_str = 350 TOKEN_STRING_INITIALIZER(struct cfg_sp_add_cfg_item, multi_string, 351 TOKEN_STRING_MULTI); 352 353 cmdline_parse_inst_t cfg_sp_add_rule = { 354 .f = cfg_sp_add_cfg_item_parsed, 355 .data = NULL, 356 .help_str = "", 357 .tokens = { 358 (void *) &cfg_sp_add_sp_str, 359 (void *) &cfg_sp_add_multi_str, 360 NULL, 361 }, 362 }; 363 364 /* sa add parse */ 365 struct cfg_sa_add_cfg_item { 366 cmdline_fixed_string_t sa_keyword; 367 cmdline_multi_string_t multi_string; 368 }; 369 370 static void 371 cfg_sa_add_cfg_item_parsed(void *parsed_result, 372 __rte_unused struct cmdline *cl, void *data) 373 { 374 struct cfg_sa_add_cfg_item *params = parsed_result; 375 char *tokens[32]; 376 uint32_t n_tokens = RTE_DIM(tokens); 377 struct parse_status *status = (struct parse_status *)data; 378 379 APP_CHECK(parse_tokenize_string(params->multi_string, tokens, 380 &n_tokens) == 0, status, "too many arguments\n"); 381 382 parse_sa_tokens(tokens, n_tokens, status); 383 } 384 385 static cmdline_parse_token_string_t cfg_sa_add_sa_str = 386 TOKEN_STRING_INITIALIZER(struct cfg_sa_add_cfg_item, 387 sa_keyword, "sa"); 388 389 static cmdline_parse_token_string_t cfg_sa_add_multi_str = 390 TOKEN_STRING_INITIALIZER(struct cfg_sa_add_cfg_item, multi_string, 391 TOKEN_STRING_MULTI); 392 393 cmdline_parse_inst_t cfg_sa_add_rule = { 394 .f = cfg_sa_add_cfg_item_parsed, 395 .data = NULL, 396 .help_str = "", 397 .tokens = { 398 (void *) &cfg_sa_add_sa_str, 399 (void *) &cfg_sa_add_multi_str, 400 NULL, 401 }, 402 }; 403 404 /* rt add parse */ 405 struct cfg_rt_add_cfg_item { 406 cmdline_fixed_string_t rt_keyword; 407 cmdline_multi_string_t multi_string; 408 }; 409 410 static void 411 cfg_rt_add_cfg_item_parsed(void *parsed_result, 412 __rte_unused struct cmdline *cl, void *data) 413 { 414 struct cfg_rt_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( 420 params->multi_string, tokens, &n_tokens) == 0, 421 status, "too many arguments\n"); 422 if (status->status < 0) 423 return; 424 425 parse_rt_tokens(tokens, n_tokens, status); 426 } 427 428 static cmdline_parse_token_string_t cfg_rt_add_rt_str = 429 TOKEN_STRING_INITIALIZER(struct cfg_rt_add_cfg_item, 430 rt_keyword, "rt"); 431 432 static cmdline_parse_token_string_t cfg_rt_add_multi_str = 433 TOKEN_STRING_INITIALIZER(struct cfg_rt_add_cfg_item, multi_string, 434 TOKEN_STRING_MULTI); 435 436 cmdline_parse_inst_t cfg_rt_add_rule = { 437 .f = cfg_rt_add_cfg_item_parsed, 438 .data = NULL, 439 .help_str = "", 440 .tokens = { 441 (void *) &cfg_rt_add_rt_str, 442 (void *) &cfg_rt_add_multi_str, 443 NULL, 444 }, 445 }; 446 447 /** set of cfg items */ 448 cmdline_parse_ctx_t ipsec_ctx[] = { 449 (cmdline_parse_inst_t *)&cfg_sp_add_rule, 450 (cmdline_parse_inst_t *)&cfg_sa_add_rule, 451 (cmdline_parse_inst_t *)&cfg_rt_add_rule, 452 NULL, 453 }; 454 455 int 456 parse_cfg_file(const char *cfg_filename) 457 { 458 struct cmdline *cl = cmdline_stdin_new(ipsec_ctx, ""); 459 FILE *f = fopen(cfg_filename, "r"); 460 char str[1024] = {0}, *get_s = NULL; 461 uint32_t line_num = 0; 462 struct parse_status status = {0}; 463 464 if (f == NULL) { 465 rte_panic("Error: invalid file descriptor %s\n", cfg_filename); 466 goto error_exit; 467 } 468 469 if (cl == NULL) { 470 rte_panic("Error: cannot create cmdline instance\n"); 471 goto error_exit; 472 } 473 474 cfg_sp_add_rule.data = &status; 475 cfg_sa_add_rule.data = &status; 476 cfg_rt_add_rule.data = &status; 477 478 do { 479 char oneline[1024]; 480 char *pos; 481 get_s = fgets(oneline, 1024, f); 482 483 if (!get_s) 484 break; 485 486 line_num++; 487 488 if (strlen(oneline) > 1022) { 489 rte_panic("%s:%u: error: " 490 "the line contains more characters the parser can handle\n", 491 cfg_filename, line_num); 492 goto error_exit; 493 } 494 495 /* process comment char '#' */ 496 if (oneline[0] == '#') 497 continue; 498 499 pos = strchr(oneline, '#'); 500 if (pos != NULL) 501 *pos = '\0'; 502 503 /* process line concatenator '\' */ 504 pos = strchr(oneline, 92); 505 if (pos != NULL) { 506 if (pos != oneline+strlen(oneline) - 2) { 507 rte_panic("%s:%u: error: " 508 "no character should exist after '\\'\n", 509 cfg_filename, line_num); 510 goto error_exit; 511 } 512 513 *pos = '\0'; 514 515 if (strlen(oneline) + strlen(str) > 1022) { 516 rte_panic("%s:%u: error: " 517 "the concatenated line contains more characters the parser can handle\n", 518 cfg_filename, line_num); 519 goto error_exit; 520 } 521 522 strcpy(str + strlen(str), oneline); 523 continue; 524 } 525 526 /* copy the line to str and process */ 527 if (strlen(oneline) + strlen(str) > 1022) { 528 rte_panic("%s:%u: error: " 529 "the line contains more characters the parser can handle\n", 530 cfg_filename, line_num); 531 goto error_exit; 532 } 533 strcpy(str + strlen(str), oneline); 534 535 str[strlen(str)] = '\n'; 536 if (cmdline_parse(cl, str) < 0) { 537 rte_panic("%s:%u: error: parsing \"%s\" failed\n", 538 cfg_filename, line_num, str); 539 goto error_exit; 540 } 541 542 if (status.status < 0) { 543 rte_panic("%s:%u: error: %s", cfg_filename, 544 line_num, status.parse_msg); 545 goto error_exit; 546 } 547 548 memset(str, 0, 1024); 549 } while (1); 550 551 cmdline_stdin_exit(cl); 552 fclose(f); 553 554 return 0; 555 556 error_exit: 557 if (cl) 558 cmdline_stdin_exit(cl); 559 if (f) 560 fclose(f); 561 562 return -1; 563 } 564