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, RTE_MIN((unsigned int long)(pch - token), 217 sizeof(ip_str))); 218 pch += 1; 219 if (is_str_num(pch) != 0) 220 return -EINVAL; 221 if (mask) 222 *mask = atoi(pch); 223 } else { 224 strlcpy(ip_str, token, sizeof(ip_str)); 225 if (mask) 226 *mask = 0; 227 } 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, RTE_MIN((unsigned int long)(pch - token), 247 sizeof(ip_str))); 248 pch += 1; 249 if (is_str_num(pch) != 0) 250 return -EINVAL; 251 if (mask) 252 *mask = atoi(pch); 253 } else { 254 strlcpy(ip_str, token, sizeof(ip_str)); 255 if (mask) 256 *mask = 0; 257 } 258 259 if (strlen(ip_str) >= INET6_ADDRSTRLEN) 260 return -EINVAL; 261 262 if (inet_pton6(ip_str, (unsigned char *)ipv6) != 1) 263 return -EINVAL; 264 265 return 0; 266 } 267 268 int 269 parse_range(const char *token, uint16_t *low, uint16_t *high) 270 { 271 char ch; 272 char num_str[20]; 273 uint32_t pos; 274 int range_low = -1; 275 int range_high = -1; 276 277 if (!low || !high) 278 return -1; 279 280 memset(num_str, 0, 20); 281 pos = 0; 282 283 while ((ch = *token++) != '\0') { 284 if (isdigit(ch)) { 285 if (pos >= 19) 286 return -1; 287 num_str[pos++] = ch; 288 } else if (ch == ':') { 289 if (range_low != -1) 290 return -1; 291 range_low = atoi(num_str); 292 memset(num_str, 0, 20); 293 pos = 0; 294 } 295 } 296 297 if (strlen(num_str) == 0) 298 return -1; 299 300 range_high = atoi(num_str); 301 302 *low = (uint16_t)range_low; 303 *high = (uint16_t)range_high; 304 305 return 0; 306 } 307 308 /** sp add parse */ 309 struct cfg_sp_add_cfg_item { 310 cmdline_fixed_string_t sp_keyword; 311 cmdline_multi_string_t multi_string; 312 }; 313 314 static void 315 cfg_sp_add_cfg_item_parsed(void *parsed_result, 316 __rte_unused struct cmdline *cl, void *data) 317 { 318 struct cfg_sp_add_cfg_item *params = parsed_result; 319 char *tokens[32]; 320 uint32_t n_tokens = RTE_DIM(tokens); 321 struct parse_status *status = (struct parse_status *)data; 322 323 APP_CHECK((parse_tokenize_string(params->multi_string, tokens, 324 &n_tokens) == 0), status, "too many arguments"); 325 326 if (status->status < 0) 327 return; 328 329 if (strcmp(tokens[0], "ipv4") == 0) { 330 parse_sp4_tokens(tokens, n_tokens, status); 331 if (status->status < 0) 332 return; 333 } else if (strcmp(tokens[0], "ipv6") == 0) { 334 parse_sp6_tokens(tokens, n_tokens, status); 335 if (status->status < 0) 336 return; 337 } else { 338 APP_CHECK(0, status, "unrecognizable input %s\n", 339 tokens[0]); 340 return; 341 } 342 } 343 344 static cmdline_parse_token_string_t cfg_sp_add_sp_str = 345 TOKEN_STRING_INITIALIZER(struct cfg_sp_add_cfg_item, 346 sp_keyword, "sp"); 347 348 static cmdline_parse_token_string_t cfg_sp_add_multi_str = 349 TOKEN_STRING_INITIALIZER(struct cfg_sp_add_cfg_item, multi_string, 350 TOKEN_STRING_MULTI); 351 352 cmdline_parse_inst_t cfg_sp_add_rule = { 353 .f = cfg_sp_add_cfg_item_parsed, 354 .data = NULL, 355 .help_str = "", 356 .tokens = { 357 (void *) &cfg_sp_add_sp_str, 358 (void *) &cfg_sp_add_multi_str, 359 NULL, 360 }, 361 }; 362 363 /* sa add parse */ 364 struct cfg_sa_add_cfg_item { 365 cmdline_fixed_string_t sa_keyword; 366 cmdline_multi_string_t multi_string; 367 }; 368 369 static void 370 cfg_sa_add_cfg_item_parsed(void *parsed_result, 371 __rte_unused struct cmdline *cl, void *data) 372 { 373 struct cfg_sa_add_cfg_item *params = parsed_result; 374 char *tokens[32]; 375 uint32_t n_tokens = RTE_DIM(tokens); 376 struct parse_status *status = (struct parse_status *)data; 377 378 APP_CHECK(parse_tokenize_string(params->multi_string, tokens, 379 &n_tokens) == 0, status, "too many arguments\n"); 380 381 parse_sa_tokens(tokens, n_tokens, status); 382 } 383 384 static cmdline_parse_token_string_t cfg_sa_add_sa_str = 385 TOKEN_STRING_INITIALIZER(struct cfg_sa_add_cfg_item, 386 sa_keyword, "sa"); 387 388 static cmdline_parse_token_string_t cfg_sa_add_multi_str = 389 TOKEN_STRING_INITIALIZER(struct cfg_sa_add_cfg_item, multi_string, 390 TOKEN_STRING_MULTI); 391 392 cmdline_parse_inst_t cfg_sa_add_rule = { 393 .f = cfg_sa_add_cfg_item_parsed, 394 .data = NULL, 395 .help_str = "", 396 .tokens = { 397 (void *) &cfg_sa_add_sa_str, 398 (void *) &cfg_sa_add_multi_str, 399 NULL, 400 }, 401 }; 402 403 /* rt add parse */ 404 struct cfg_rt_add_cfg_item { 405 cmdline_fixed_string_t rt_keyword; 406 cmdline_multi_string_t multi_string; 407 }; 408 409 static void 410 cfg_rt_add_cfg_item_parsed(void *parsed_result, 411 __rte_unused struct cmdline *cl, void *data) 412 { 413 struct cfg_rt_add_cfg_item *params = parsed_result; 414 char *tokens[32]; 415 uint32_t n_tokens = RTE_DIM(tokens); 416 struct parse_status *status = (struct parse_status *)data; 417 418 APP_CHECK(parse_tokenize_string( 419 params->multi_string, tokens, &n_tokens) == 0, 420 status, "too many arguments\n"); 421 if (status->status < 0) 422 return; 423 424 parse_rt_tokens(tokens, n_tokens, status); 425 } 426 427 static cmdline_parse_token_string_t cfg_rt_add_rt_str = 428 TOKEN_STRING_INITIALIZER(struct cfg_rt_add_cfg_item, 429 rt_keyword, "rt"); 430 431 static cmdline_parse_token_string_t cfg_rt_add_multi_str = 432 TOKEN_STRING_INITIALIZER(struct cfg_rt_add_cfg_item, multi_string, 433 TOKEN_STRING_MULTI); 434 435 cmdline_parse_inst_t cfg_rt_add_rule = { 436 .f = cfg_rt_add_cfg_item_parsed, 437 .data = NULL, 438 .help_str = "", 439 .tokens = { 440 (void *) &cfg_rt_add_rt_str, 441 (void *) &cfg_rt_add_multi_str, 442 NULL, 443 }, 444 }; 445 446 /** set of cfg items */ 447 cmdline_parse_ctx_t ipsec_ctx[] = { 448 (cmdline_parse_inst_t *)&cfg_sp_add_rule, 449 (cmdline_parse_inst_t *)&cfg_sa_add_rule, 450 (cmdline_parse_inst_t *)&cfg_rt_add_rule, 451 NULL, 452 }; 453 454 int 455 parse_cfg_file(const char *cfg_filename) 456 { 457 struct cmdline *cl = cmdline_stdin_new(ipsec_ctx, ""); 458 FILE *f = fopen(cfg_filename, "r"); 459 char str[1024] = {0}, *get_s = NULL; 460 uint32_t line_num = 0; 461 struct parse_status status = {0}; 462 463 if (f == NULL) { 464 rte_panic("Error: invalid file descriptor %s\n", cfg_filename); 465 goto error_exit; 466 } 467 468 if (cl == NULL) { 469 rte_panic("Error: cannot create cmdline instance\n"); 470 goto error_exit; 471 } 472 473 cfg_sp_add_rule.data = &status; 474 cfg_sa_add_rule.data = &status; 475 cfg_rt_add_rule.data = &status; 476 477 do { 478 char oneline[1024]; 479 char *pos; 480 get_s = fgets(oneline, 1024, f); 481 482 if (!get_s) 483 break; 484 485 line_num++; 486 487 if (strlen(oneline) > 1022) { 488 rte_panic("%s:%u: error: " 489 "the line contains more characters the parser can handle\n", 490 cfg_filename, line_num); 491 goto error_exit; 492 } 493 494 /* process comment char '#' */ 495 if (oneline[0] == '#') 496 continue; 497 498 pos = strchr(oneline, '#'); 499 if (pos != NULL) 500 *pos = '\0'; 501 502 /* process line concatenator '\' */ 503 pos = strchr(oneline, 92); 504 if (pos != NULL) { 505 if (pos != oneline+strlen(oneline) - 2) { 506 rte_panic("%s:%u: error: " 507 "no character should exist after '\\'\n", 508 cfg_filename, line_num); 509 goto error_exit; 510 } 511 512 *pos = '\0'; 513 514 if (strlen(oneline) + strlen(str) > 1022) { 515 rte_panic("%s:%u: error: " 516 "the concatenated line contains more characters the parser can handle\n", 517 cfg_filename, line_num); 518 goto error_exit; 519 } 520 521 strcpy(str + strlen(str), oneline); 522 continue; 523 } 524 525 /* copy the line to str and process */ 526 if (strlen(oneline) + strlen(str) > 1022) { 527 rte_panic("%s:%u: error: " 528 "the line contains more characters the parser can handle\n", 529 cfg_filename, line_num); 530 goto error_exit; 531 } 532 strcpy(str + strlen(str), 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