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