1 /*- 2 * BSD LICENSE 3 * 4 * Copyright(c) 2016 Intel Corporation. All rights reserved. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * * Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * * Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * * Neither the name of Intel Corporation nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 #include <rte_common.h> 34 #include <rte_crypto.h> 35 36 #include <cmdline_parse_string.h> 37 #include <cmdline_parse_num.h> 38 #include <cmdline_parse_ipaddr.h> 39 #include <cmdline_socket.h> 40 #include <cmdline.h> 41 42 #include "ipsec.h" 43 #include "parser.h" 44 45 #define PARSE_DELIMITER " \f\n\r\t\v" 46 static int 47 parse_tokenize_string(char *string, char *tokens[], uint32_t *n_tokens) 48 { 49 uint32_t i; 50 51 if ((string == NULL) || 52 (tokens == NULL) || 53 (*n_tokens < 1)) 54 return -EINVAL; 55 56 for (i = 0; i < *n_tokens; i++) { 57 tokens[i] = strtok_r(string, PARSE_DELIMITER, &string); 58 if (tokens[i] == NULL) 59 break; 60 } 61 62 if ((i == *n_tokens) && 63 (NULL != strtok_r(string, PARSE_DELIMITER, &string))) 64 return -E2BIG; 65 66 *n_tokens = i; 67 return 0; 68 } 69 70 #define INADDRSZ 4 71 #define IN6ADDRSZ 16 72 73 /* int 74 * inet_pton4(src, dst) 75 * like inet_aton() but without all the hexadecimal and shorthand. 76 * return: 77 * 1 if `src' is a valid dotted quad, else 0. 78 * notice: 79 * does not touch `dst' unless it's returning 1. 80 * author: 81 * Paul Vixie, 1996. 82 */ 83 static int 84 inet_pton4(const char *src, unsigned char *dst) 85 { 86 static const char digits[] = "0123456789"; 87 int saw_digit, octets, ch; 88 unsigned char tmp[INADDRSZ], *tp; 89 90 saw_digit = 0; 91 octets = 0; 92 *(tp = tmp) = 0; 93 while ((ch = *src++) != '\0') { 94 const char *pch; 95 96 pch = strchr(digits, ch); 97 if (pch != NULL) { 98 unsigned int new = *tp * 10 + (pch - digits); 99 100 if (new > 255) 101 return 0; 102 if (!saw_digit) { 103 if (++octets > 4) 104 return 0; 105 saw_digit = 1; 106 } 107 *tp = (unsigned char)new; 108 } else if (ch == '.' && saw_digit) { 109 if (octets == 4) 110 return 0; 111 *++tp = 0; 112 saw_digit = 0; 113 } else 114 return 0; 115 } 116 if (octets < 4) 117 return 0; 118 119 memcpy(dst, tmp, INADDRSZ); 120 return 1; 121 } 122 123 /* int 124 * inet_pton6(src, dst) 125 * convert presentation level address to network order binary form. 126 * return: 127 * 1 if `src' is a valid [RFC1884 2.2] address, else 0. 128 * notice: 129 * (1) does not touch `dst' unless it's returning 1. 130 * (2) :: in a full address is silently ignored. 131 * credit: 132 * inspired by Mark Andrews. 133 * author: 134 * Paul Vixie, 1996. 135 */ 136 static int 137 inet_pton6(const char *src, unsigned char *dst) 138 { 139 static const char xdigits_l[] = "0123456789abcdef", 140 xdigits_u[] = "0123456789ABCDEF"; 141 unsigned char tmp[IN6ADDRSZ], *tp = 0, *endp = 0, *colonp = 0; 142 const char *xdigits = 0, *curtok = 0; 143 int ch = 0, saw_xdigit = 0, count_xdigit = 0; 144 unsigned int val = 0; 145 unsigned dbloct_count = 0; 146 147 memset((tp = tmp), '\0', IN6ADDRSZ); 148 endp = tp + IN6ADDRSZ; 149 colonp = NULL; 150 /* Leading :: requires some special handling. */ 151 if (*src == ':') 152 if (*++src != ':') 153 return 0; 154 curtok = src; 155 saw_xdigit = count_xdigit = 0; 156 val = 0; 157 158 while ((ch = *src++) != '\0') { 159 const char *pch; 160 161 pch = strchr((xdigits = xdigits_l), ch); 162 if (pch == NULL) 163 pch = strchr((xdigits = xdigits_u), ch); 164 if (pch != NULL) { 165 if (count_xdigit >= 4) 166 return 0; 167 val <<= 4; 168 val |= (pch - xdigits); 169 if (val > 0xffff) 170 return 0; 171 saw_xdigit = 1; 172 count_xdigit++; 173 continue; 174 } 175 if (ch == ':') { 176 curtok = src; 177 if (!saw_xdigit) { 178 if (colonp) 179 return 0; 180 colonp = tp; 181 continue; 182 } else if (*src == '\0') { 183 return 0; 184 } 185 if (tp + sizeof(int16_t) > endp) 186 return 0; 187 *tp++ = (unsigned char) ((val >> 8) & 0xff); 188 *tp++ = (unsigned char) (val & 0xff); 189 saw_xdigit = 0; 190 count_xdigit = 0; 191 val = 0; 192 dbloct_count++; 193 continue; 194 } 195 if (ch == '.' && ((tp + INADDRSZ) <= endp) && 196 inet_pton4(curtok, tp) > 0) { 197 tp += INADDRSZ; 198 saw_xdigit = 0; 199 dbloct_count += 2; 200 break; /* '\0' was seen by inet_pton4(). */ 201 } 202 return 0; 203 } 204 if (saw_xdigit) { 205 if (tp + sizeof(int16_t) > endp) 206 return 0; 207 *tp++ = (unsigned char) ((val >> 8) & 0xff); 208 *tp++ = (unsigned char) (val & 0xff); 209 dbloct_count++; 210 } 211 if (colonp != NULL) { 212 /* if we already have 8 double octets, having a colon 213 * means error */ 214 if (dbloct_count == 8) 215 return 0; 216 217 /* 218 * Since some memmove()'s erroneously fail to handle 219 * overlapping regions, we'll do the shift by hand. 220 */ 221 const int n = tp - colonp; 222 int i; 223 224 for (i = 1; i <= n; i++) { 225 endp[-i] = colonp[n - i]; 226 colonp[n - i] = 0; 227 } 228 tp = endp; 229 } 230 if (tp != endp) 231 return 0; 232 memcpy(dst, tmp, IN6ADDRSZ); 233 return 1; 234 } 235 236 int 237 parse_ipv4_addr(const char *token, struct in_addr *ipv4, 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) >= INET_ADDRSTRLEN) 257 return -EINVAL; 258 259 if (inet_pton4(ip_str, (unsigned char *)ipv4) != 1) 260 return -EINVAL; 261 262 return 0; 263 } 264 265 int 266 parse_ipv6_addr(const char *token, struct in6_addr *ipv6, uint32_t *mask) 267 { 268 char ip_str[256] = {0}; 269 char *pch; 270 271 pch = strchr(token, '/'); 272 if (pch != NULL) { 273 strncpy(ip_str, token, pch - token); 274 pch += 1; 275 if (is_str_num(pch) != 0) 276 return -EINVAL; 277 if (mask) 278 *mask = atoi(pch); 279 } else { 280 strncpy(ip_str, token, sizeof(ip_str) - 1); 281 if (mask) 282 *mask = 0; 283 } 284 285 if (strlen(ip_str) >= INET6_ADDRSTRLEN) 286 return -EINVAL; 287 288 if (inet_pton6(ip_str, (unsigned char *)ipv6) != 1) 289 return -EINVAL; 290 291 return 0; 292 } 293 294 int 295 parse_range(const char *token, uint16_t *low, uint16_t *high) 296 { 297 char ch; 298 char num_str[20]; 299 uint32_t pos; 300 int range_low = -1; 301 int range_high = -1; 302 303 if (!low || !high) 304 return -1; 305 306 memset(num_str, 0, 20); 307 pos = 0; 308 309 while ((ch = *token++) != '\0') { 310 if (isdigit(ch)) { 311 if (pos >= 19) 312 return -1; 313 num_str[pos++] = ch; 314 } else if (ch == ':') { 315 if (range_low != -1) 316 return -1; 317 range_low = atoi(num_str); 318 memset(num_str, 0, 20); 319 pos = 0; 320 } 321 } 322 323 if (strlen(num_str) == 0) 324 return -1; 325 326 range_high = atoi(num_str); 327 328 *low = (uint16_t)range_low; 329 *high = (uint16_t)range_high; 330 331 return 0; 332 } 333 334 /** sp add parse */ 335 struct cfg_sp_add_cfg_item { 336 cmdline_fixed_string_t sp_keyword; 337 cmdline_multi_string_t multi_string; 338 }; 339 340 static void 341 cfg_sp_add_cfg_item_parsed(void *parsed_result, 342 __rte_unused struct cmdline *cl, void *data) 343 { 344 struct cfg_sp_add_cfg_item *params = parsed_result; 345 char *tokens[32]; 346 uint32_t n_tokens = RTE_DIM(tokens); 347 struct parse_status *status = (struct parse_status *)data; 348 349 APP_CHECK((parse_tokenize_string(params->multi_string, tokens, 350 &n_tokens) == 0), status, "too many arguments"); 351 352 if (status->status < 0) 353 return; 354 355 if (strcmp(tokens[0], "ipv4") == 0) { 356 parse_sp4_tokens(tokens, n_tokens, status); 357 if (status->status < 0) 358 return; 359 } else if (strcmp(tokens[0], "ipv6") == 0) { 360 parse_sp6_tokens(tokens, n_tokens, status); 361 if (status->status < 0) 362 return; 363 } else { 364 APP_CHECK(0, status, "unrecognizable input %s\n", 365 tokens[0]); 366 return; 367 } 368 } 369 370 static cmdline_parse_token_string_t cfg_sp_add_sp_str = 371 TOKEN_STRING_INITIALIZER(struct cfg_sp_add_cfg_item, 372 sp_keyword, "sp"); 373 374 static cmdline_parse_token_string_t cfg_sp_add_multi_str = 375 TOKEN_STRING_INITIALIZER(struct cfg_sp_add_cfg_item, multi_string, 376 TOKEN_STRING_MULTI); 377 378 cmdline_parse_inst_t cfg_sp_add_rule = { 379 .f = cfg_sp_add_cfg_item_parsed, 380 .data = NULL, 381 .help_str = "", 382 .tokens = { 383 (void *) &cfg_sp_add_sp_str, 384 (void *) &cfg_sp_add_multi_str, 385 NULL, 386 }, 387 }; 388 389 /* sa add parse */ 390 struct cfg_sa_add_cfg_item { 391 cmdline_fixed_string_t sa_keyword; 392 cmdline_multi_string_t multi_string; 393 }; 394 395 static void 396 cfg_sa_add_cfg_item_parsed(void *parsed_result, 397 __rte_unused struct cmdline *cl, void *data) 398 { 399 struct cfg_sa_add_cfg_item *params = parsed_result; 400 char *tokens[32]; 401 uint32_t n_tokens = RTE_DIM(tokens); 402 struct parse_status *status = (struct parse_status *)data; 403 404 APP_CHECK(parse_tokenize_string(params->multi_string, tokens, 405 &n_tokens) == 0, status, "too many arguments\n"); 406 407 parse_sa_tokens(tokens, n_tokens, status); 408 } 409 410 static cmdline_parse_token_string_t cfg_sa_add_sa_str = 411 TOKEN_STRING_INITIALIZER(struct cfg_sa_add_cfg_item, 412 sa_keyword, "sa"); 413 414 static cmdline_parse_token_string_t cfg_sa_add_multi_str = 415 TOKEN_STRING_INITIALIZER(struct cfg_sa_add_cfg_item, multi_string, 416 TOKEN_STRING_MULTI); 417 418 cmdline_parse_inst_t cfg_sa_add_rule = { 419 .f = cfg_sa_add_cfg_item_parsed, 420 .data = NULL, 421 .help_str = "", 422 .tokens = { 423 (void *) &cfg_sa_add_sa_str, 424 (void *) &cfg_sa_add_multi_str, 425 NULL, 426 }, 427 }; 428 429 /* rt add parse */ 430 struct cfg_rt_add_cfg_item { 431 cmdline_fixed_string_t rt_keyword; 432 cmdline_multi_string_t multi_string; 433 }; 434 435 static void 436 cfg_rt_add_cfg_item_parsed(void *parsed_result, 437 __rte_unused struct cmdline *cl, void *data) 438 { 439 struct cfg_rt_add_cfg_item *params = parsed_result; 440 char *tokens[32]; 441 uint32_t n_tokens = RTE_DIM(tokens); 442 struct parse_status *status = (struct parse_status *)data; 443 444 APP_CHECK(parse_tokenize_string( 445 params->multi_string, tokens, &n_tokens) == 0, 446 status, "too many arguments\n"); 447 if (status->status < 0) 448 return; 449 450 parse_rt_tokens(tokens, n_tokens, status); 451 } 452 453 static cmdline_parse_token_string_t cfg_rt_add_rt_str = 454 TOKEN_STRING_INITIALIZER(struct cfg_rt_add_cfg_item, 455 rt_keyword, "rt"); 456 457 static cmdline_parse_token_string_t cfg_rt_add_multi_str = 458 TOKEN_STRING_INITIALIZER(struct cfg_rt_add_cfg_item, multi_string, 459 TOKEN_STRING_MULTI); 460 461 cmdline_parse_inst_t cfg_rt_add_rule = { 462 .f = cfg_rt_add_cfg_item_parsed, 463 .data = NULL, 464 .help_str = "", 465 .tokens = { 466 (void *) &cfg_rt_add_rt_str, 467 (void *) &cfg_rt_add_multi_str, 468 NULL, 469 }, 470 }; 471 472 /** set of cfg items */ 473 cmdline_parse_ctx_t ipsec_ctx[] = { 474 (cmdline_parse_inst_t *)&cfg_sp_add_rule, 475 (cmdline_parse_inst_t *)&cfg_sa_add_rule, 476 (cmdline_parse_inst_t *)&cfg_rt_add_rule, 477 NULL, 478 }; 479 480 int 481 parse_cfg_file(const char *cfg_filename) 482 { 483 struct cmdline *cl = cmdline_stdin_new(ipsec_ctx, ""); 484 FILE *f = fopen(cfg_filename, "r"); 485 char str[1024] = {0}, *get_s = NULL; 486 uint32_t line_num = 0; 487 struct parse_status status = {0}; 488 489 if (f == NULL) { 490 rte_panic("Error: invalid file descriptor %s\n", cfg_filename); 491 goto error_exit; 492 } 493 494 if (cl == NULL) { 495 rte_panic("Error: cannot create cmdline instance\n"); 496 goto error_exit; 497 } 498 499 cfg_sp_add_rule.data = &status; 500 cfg_sa_add_rule.data = &status; 501 cfg_rt_add_rule.data = &status; 502 503 do { 504 char oneline[1024]; 505 char *pos; 506 get_s = fgets(oneline, 1024, f); 507 508 if (!get_s) 509 break; 510 511 line_num++; 512 513 if (strlen(oneline) > 1022) { 514 rte_panic("%s:%u: error: " 515 "the line contains more characters the parser can handle\n", 516 cfg_filename, line_num); 517 goto error_exit; 518 } 519 520 /* process comment char '#' */ 521 if (oneline[0] == '#') 522 continue; 523 524 pos = strchr(oneline, '#'); 525 if (pos != NULL) 526 *pos = '\0'; 527 528 /* process line concatenator '\' */ 529 pos = strchr(oneline, 92); 530 if (pos != NULL) { 531 if (pos != oneline+strlen(oneline) - 2) { 532 rte_panic("%s:%u: error: " 533 "no character should exist after '\\'\n", 534 cfg_filename, line_num); 535 goto error_exit; 536 } 537 538 *pos = '\0'; 539 540 if (strlen(oneline) + strlen(str) > 1022) { 541 rte_panic("%s:%u: error: " 542 "the concatenated line contains more characters the parser can handle\n", 543 cfg_filename, line_num); 544 goto error_exit; 545 } 546 547 strncpy(str + strlen(str), oneline, 548 strlen(oneline)); 549 550 continue; 551 } 552 553 /* copy the line to str and process */ 554 if (strlen(oneline) + strlen(str) > 1022) { 555 rte_panic("%s:%u: error: " 556 "the line contains more characters the parser can handle\n", 557 cfg_filename, line_num); 558 goto error_exit; 559 } 560 strncpy(str + strlen(str), oneline, 561 strlen(oneline)); 562 563 str[strlen(str)] = '\n'; 564 if (cmdline_parse(cl, str) < 0) { 565 rte_panic("%s:%u: error: parsing \"%s\" failed\n", 566 cfg_filename, line_num, str); 567 goto error_exit; 568 } 569 570 if (status.status < 0) { 571 rte_panic("%s:%u: error: %s", cfg_filename, 572 line_num, status.parse_msg); 573 goto error_exit; 574 } 575 576 memset(str, 0, 1024); 577 } while (1); 578 579 cmdline_stdin_exit(cl); 580 fclose(f); 581 582 return 0; 583 584 error_exit: 585 if (cl) 586 cmdline_stdin_exit(cl); 587 if (f) 588 fclose(f); 589 590 return -1; 591 } 592