1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2016 Intel Corporation 3 */ 4 #include <stdlib.h> 5 #include <arpa/inet.h> 6 #include <sys/socket.h> 7 8 #include <rte_common.h> 9 #include <rte_crypto.h> 10 #include <rte_string_fns.h> 11 12 #include <cmdline_parse_string.h> 13 #include <cmdline_parse_num.h> 14 #include <cmdline_parse_ipaddr.h> 15 #include <cmdline_socket.h> 16 #include <cmdline.h> 17 18 #include "flow.h" 19 #include "ipsec.h" 20 #include "parser.h" 21 22 #define PARSE_DELIMITER " \f\n\r\t\v" 23 static int 24 parse_tokenize_string(char *string, char *tokens[], uint32_t *n_tokens) 25 { 26 uint32_t i; 27 28 if ((string == NULL) || 29 (tokens == NULL) || 30 (*n_tokens < 1)) 31 return -EINVAL; 32 33 for (i = 0; i < *n_tokens; i++) { 34 tokens[i] = strtok_r(string, PARSE_DELIMITER, &string); 35 if (tokens[i] == NULL) 36 break; 37 } 38 39 if ((i == *n_tokens) && 40 (NULL != strtok_r(string, PARSE_DELIMITER, &string))) 41 return -E2BIG; 42 43 *n_tokens = i; 44 return 0; 45 } 46 47 int 48 parse_ipv4_addr(const char *token, struct in_addr *ipv4, uint32_t *mask) 49 { 50 char ip_str[INET_ADDRSTRLEN] = {0}; 51 char *pch; 52 53 pch = strchr(token, '/'); 54 if (pch != NULL) { 55 strlcpy(ip_str, token, 56 RTE_MIN((unsigned int long)(pch - token + 1), 57 sizeof(ip_str))); 58 pch += 1; 59 if (is_str_num(pch) != 0) 60 return -EINVAL; 61 if (mask) 62 *mask = atoi(pch); 63 } else { 64 strlcpy(ip_str, token, sizeof(ip_str)); 65 if (mask) 66 *mask = 0; 67 } 68 if (strlen(ip_str) >= INET_ADDRSTRLEN) 69 return -EINVAL; 70 71 if (inet_pton(AF_INET, ip_str, ipv4) != 1) 72 return -EINVAL; 73 74 return 0; 75 } 76 77 int 78 parse_ipv6_addr(const char *token, struct rte_ipv6_addr *ipv6, uint32_t *mask) 79 { 80 char ip_str[256] = {0}; 81 char *pch; 82 83 pch = strchr(token, '/'); 84 if (pch != NULL) { 85 strlcpy(ip_str, token, 86 RTE_MIN((unsigned int long)(pch - token + 1), 87 sizeof(ip_str))); 88 pch += 1; 89 if (is_str_num(pch) != 0) 90 return -EINVAL; 91 if (mask) 92 *mask = atoi(pch); 93 } else { 94 strlcpy(ip_str, token, sizeof(ip_str)); 95 if (mask) 96 *mask = 0; 97 } 98 99 if (strlen(ip_str) >= INET6_ADDRSTRLEN) 100 return -EINVAL; 101 102 if (inet_pton(AF_INET6, ip_str, ipv6) != 1) 103 return -EINVAL; 104 105 return 0; 106 } 107 108 int 109 parse_range(const char *token, uint16_t *low, uint16_t *high) 110 { 111 char ch; 112 char num_str[20]; 113 uint32_t pos; 114 int range_low = -1; 115 int range_high = -1; 116 117 if (!low || !high) 118 return -1; 119 120 memset(num_str, 0, 20); 121 pos = 0; 122 123 while ((ch = *token++) != '\0') { 124 if (isdigit(ch)) { 125 if (pos >= 19) 126 return -1; 127 num_str[pos++] = ch; 128 } else if (ch == ':') { 129 if (range_low != -1) 130 return -1; 131 range_low = atoi(num_str); 132 memset(num_str, 0, 20); 133 pos = 0; 134 } 135 } 136 137 if (strlen(num_str) == 0) 138 return -1; 139 140 range_high = atoi(num_str); 141 142 *low = (uint16_t)range_low; 143 *high = (uint16_t)range_high; 144 145 return 0; 146 } 147 148 /* 149 * helper function for parse_mac, parse one section of the ether addr. 150 */ 151 static const char * 152 parse_uint8x16(const char *s, uint8_t *v, uint8_t ls) 153 { 154 char *end; 155 unsigned long t; 156 157 errno = 0; 158 t = strtoul(s, &end, 16); 159 if (errno != 0 || end[0] != ls || t > UINT8_MAX) 160 return NULL; 161 v[0] = t; 162 return end + 1; 163 } 164 165 static int 166 parse_mac(const char *str, struct rte_ether_addr *addr) 167 { 168 uint32_t i; 169 170 static const uint8_t stop_sym[RTE_DIM(addr->addr_bytes)] = { 171 [0] = ':', 172 [1] = ':', 173 [2] = ':', 174 [3] = ':', 175 [4] = ':', 176 [5] = 0, 177 }; 178 179 for (i = 0; i != RTE_DIM(addr->addr_bytes); i++) { 180 str = parse_uint8x16(str, addr->addr_bytes + i, stop_sym[i]); 181 if (str == NULL) 182 return -EINVAL; 183 } 184 185 return 0; 186 } 187 188 /** sp add parse */ 189 struct cfg_sp_add_cfg_item { 190 cmdline_fixed_string_t sp_keyword; 191 cmdline_multi_string_t multi_string; 192 }; 193 194 static void 195 cfg_sp_add_cfg_item_parsed(void *parsed_result, 196 __rte_unused struct cmdline *cl, void *data) 197 { 198 struct cfg_sp_add_cfg_item *params = parsed_result; 199 char *tokens[32]; 200 uint32_t n_tokens = RTE_DIM(tokens); 201 struct parse_status *status = (struct parse_status *)data; 202 203 APP_CHECK((parse_tokenize_string(params->multi_string, tokens, 204 &n_tokens) == 0), status, "too many arguments"); 205 206 if (status->status < 0) 207 return; 208 209 if (strcmp(tokens[0], "ipv4") == 0) { 210 parse_sp4_tokens(tokens, n_tokens, status); 211 if (status->status < 0) 212 return; 213 } else if (strcmp(tokens[0], "ipv6") == 0) { 214 parse_sp6_tokens(tokens, n_tokens, status); 215 if (status->status < 0) 216 return; 217 } else { 218 APP_CHECK(0, status, "unrecognizable input %s\n", 219 tokens[0]); 220 return; 221 } 222 } 223 224 static cmdline_parse_token_string_t cfg_sp_add_sp_str = 225 TOKEN_STRING_INITIALIZER(struct cfg_sp_add_cfg_item, 226 sp_keyword, "sp"); 227 228 static cmdline_parse_token_string_t cfg_sp_add_multi_str = 229 TOKEN_STRING_INITIALIZER(struct cfg_sp_add_cfg_item, multi_string, 230 TOKEN_STRING_MULTI); 231 232 cmdline_parse_inst_t cfg_sp_add_rule = { 233 .f = cfg_sp_add_cfg_item_parsed, 234 .data = NULL, 235 .help_str = "", 236 .tokens = { 237 (void *) &cfg_sp_add_sp_str, 238 (void *) &cfg_sp_add_multi_str, 239 NULL, 240 }, 241 }; 242 243 /* sa add parse */ 244 struct cfg_sa_add_cfg_item { 245 cmdline_fixed_string_t sa_keyword; 246 cmdline_multi_string_t multi_string; 247 }; 248 249 static void 250 cfg_sa_add_cfg_item_parsed(void *parsed_result, 251 __rte_unused struct cmdline *cl, void *data) 252 { 253 struct cfg_sa_add_cfg_item *params = parsed_result; 254 char *tokens[32]; 255 uint32_t n_tokens = RTE_DIM(tokens); 256 struct parse_status *status = (struct parse_status *)data; 257 258 APP_CHECK(parse_tokenize_string(params->multi_string, tokens, 259 &n_tokens) == 0, status, "too many arguments\n"); 260 261 parse_sa_tokens(tokens, n_tokens, status); 262 } 263 264 static cmdline_parse_token_string_t cfg_sa_add_sa_str = 265 TOKEN_STRING_INITIALIZER(struct cfg_sa_add_cfg_item, 266 sa_keyword, "sa"); 267 268 static cmdline_parse_token_string_t cfg_sa_add_multi_str = 269 TOKEN_STRING_INITIALIZER(struct cfg_sa_add_cfg_item, multi_string, 270 TOKEN_STRING_MULTI); 271 272 cmdline_parse_inst_t cfg_sa_add_rule = { 273 .f = cfg_sa_add_cfg_item_parsed, 274 .data = NULL, 275 .help_str = "", 276 .tokens = { 277 (void *) &cfg_sa_add_sa_str, 278 (void *) &cfg_sa_add_multi_str, 279 NULL, 280 }, 281 }; 282 283 /* rt add parse */ 284 struct cfg_rt_add_cfg_item { 285 cmdline_fixed_string_t rt_keyword; 286 cmdline_multi_string_t multi_string; 287 }; 288 289 static void 290 cfg_rt_add_cfg_item_parsed(void *parsed_result, 291 __rte_unused struct cmdline *cl, void *data) 292 { 293 struct cfg_rt_add_cfg_item *params = parsed_result; 294 char *tokens[32]; 295 uint32_t n_tokens = RTE_DIM(tokens); 296 struct parse_status *status = (struct parse_status *)data; 297 298 APP_CHECK(parse_tokenize_string( 299 params->multi_string, tokens, &n_tokens) == 0, 300 status, "too many arguments\n"); 301 if (status->status < 0) 302 return; 303 304 parse_rt_tokens(tokens, n_tokens, status); 305 } 306 307 static cmdline_parse_token_string_t cfg_rt_add_rt_str = 308 TOKEN_STRING_INITIALIZER(struct cfg_rt_add_cfg_item, 309 rt_keyword, "rt"); 310 311 static cmdline_parse_token_string_t cfg_rt_add_multi_str = 312 TOKEN_STRING_INITIALIZER(struct cfg_rt_add_cfg_item, multi_string, 313 TOKEN_STRING_MULTI); 314 315 cmdline_parse_inst_t cfg_rt_add_rule = { 316 .f = cfg_rt_add_cfg_item_parsed, 317 .data = NULL, 318 .help_str = "", 319 .tokens = { 320 (void *) &cfg_rt_add_rt_str, 321 (void *) &cfg_rt_add_multi_str, 322 NULL, 323 }, 324 }; 325 326 /* flow add parse */ 327 struct cfg_flow_add_cfg_item { 328 cmdline_fixed_string_t flow_keyword; 329 cmdline_multi_string_t multi_string; 330 }; 331 332 static void 333 cfg_flow_add_cfg_item_parsed(void *parsed_result, 334 __rte_unused struct cmdline *cl, void *data) 335 { 336 struct cfg_flow_add_cfg_item *params = parsed_result; 337 char *tokens[32]; 338 uint32_t n_tokens = RTE_DIM(tokens); 339 struct parse_status *status = (struct parse_status *)data; 340 341 APP_CHECK(parse_tokenize_string( 342 params->multi_string, tokens, &n_tokens) == 0, 343 status, "too many arguments\n"); 344 if (status->status < 0) 345 return; 346 347 parse_flow_tokens(tokens, n_tokens, status); 348 } 349 350 static cmdline_parse_token_string_t cfg_flow_add_flow_str = 351 TOKEN_STRING_INITIALIZER(struct cfg_flow_add_cfg_item, 352 flow_keyword, "flow"); 353 354 static cmdline_parse_token_string_t cfg_flow_add_multi_str = 355 TOKEN_STRING_INITIALIZER(struct cfg_flow_add_cfg_item, multi_string, 356 TOKEN_STRING_MULTI); 357 358 cmdline_parse_inst_t cfg_flow_add_rule = { 359 .f = cfg_flow_add_cfg_item_parsed, 360 .data = NULL, 361 .help_str = "", 362 .tokens = { 363 (void *) &cfg_flow_add_flow_str, 364 (void *) &cfg_flow_add_multi_str, 365 NULL, 366 }, 367 }; 368 369 /* neigh add parse */ 370 struct cfg_neigh_add_item { 371 cmdline_fixed_string_t neigh; 372 cmdline_fixed_string_t pstr; 373 uint16_t port; 374 cmdline_fixed_string_t mac; 375 }; 376 377 static void 378 cfg_parse_neigh(void *parsed_result, __rte_unused struct cmdline *cl, 379 void *data) 380 { 381 int32_t rc; 382 struct cfg_neigh_add_item *res; 383 struct parse_status *st; 384 struct rte_ether_addr mac; 385 386 st = data; 387 res = parsed_result; 388 rc = parse_mac(res->mac, &mac); 389 APP_CHECK(rc == 0, st, "invalid ether addr:%s", res->mac); 390 rc = add_dst_ethaddr(res->port, &mac); 391 APP_CHECK(rc == 0, st, "invalid port number:%hu", res->port); 392 if (st->status < 0) 393 return; 394 } 395 396 cmdline_parse_token_string_t cfg_add_neigh_start = 397 TOKEN_STRING_INITIALIZER(struct cfg_neigh_add_item, neigh, "neigh"); 398 cmdline_parse_token_string_t cfg_add_neigh_pstr = 399 TOKEN_STRING_INITIALIZER(struct cfg_neigh_add_item, pstr, "port"); 400 cmdline_parse_token_num_t cfg_add_neigh_port = 401 TOKEN_NUM_INITIALIZER(struct cfg_neigh_add_item, port, RTE_UINT16); 402 cmdline_parse_token_string_t cfg_add_neigh_mac = 403 TOKEN_STRING_INITIALIZER(struct cfg_neigh_add_item, mac, NULL); 404 405 cmdline_parse_inst_t cfg_neigh_add_rule = { 406 .f = cfg_parse_neigh, 407 .data = NULL, 408 .help_str = "", 409 .tokens = { 410 (void *)&cfg_add_neigh_start, 411 (void *)&cfg_add_neigh_pstr, 412 (void *)&cfg_add_neigh_port, 413 (void *)&cfg_add_neigh_mac, 414 NULL, 415 }, 416 }; 417 418 /** set of cfg items */ 419 cmdline_parse_ctx_t ipsec_ctx[] = { 420 (cmdline_parse_inst_t *)&cfg_sp_add_rule, 421 (cmdline_parse_inst_t *)&cfg_sa_add_rule, 422 (cmdline_parse_inst_t *)&cfg_rt_add_rule, 423 (cmdline_parse_inst_t *)&cfg_flow_add_rule, 424 (cmdline_parse_inst_t *)&cfg_neigh_add_rule, 425 NULL, 426 }; 427 428 int 429 parse_cfg_file(const char *cfg_filename) 430 { 431 struct cmdline *cl = cmdline_stdin_new(ipsec_ctx, ""); 432 FILE *f = fopen(cfg_filename, "r"); 433 char str[1024] = {0}, *get_s = NULL; 434 uint32_t line_num = 0; 435 struct parse_status status = {0}; 436 437 if (f == NULL) { 438 rte_panic("Error: invalid file descriptor %s\n", cfg_filename); 439 goto error_exit; 440 } 441 442 if (cl == NULL) { 443 rte_panic("Error: cannot create cmdline instance\n"); 444 goto error_exit; 445 } 446 447 cfg_sp_add_rule.data = &status; 448 cfg_sa_add_rule.data = &status; 449 cfg_rt_add_rule.data = &status; 450 cfg_flow_add_rule.data = &status; 451 cfg_neigh_add_rule.data = &status; 452 453 do { 454 char oneline[1024]; 455 char *pos; 456 get_s = fgets(oneline, 1024, f); 457 458 if (!get_s) 459 break; 460 461 line_num++; 462 463 if (strlen(oneline) > 1022) { 464 rte_panic("%s:%u: error: " 465 "the line contains more characters the parser can handle\n", 466 cfg_filename, line_num); 467 goto error_exit; 468 } 469 470 /* process comment char '#' */ 471 if (oneline[0] == '#') 472 continue; 473 474 pos = strchr(oneline, '#'); 475 if (pos != NULL) 476 *pos = '\0'; 477 478 /* process line concatenator '\' */ 479 pos = strchr(oneline, 92); 480 if (pos != NULL) { 481 if (pos != oneline+strlen(oneline) - 2) { 482 rte_panic("%s:%u: error: " 483 "no character should exist after '\\'\n", 484 cfg_filename, line_num); 485 goto error_exit; 486 } 487 488 *pos = '\0'; 489 490 if (strlen(oneline) + strlen(str) > 1022) { 491 rte_panic("%s:%u: error: " 492 "the concatenated line contains more characters the parser can handle\n", 493 cfg_filename, line_num); 494 goto error_exit; 495 } 496 497 strcpy(str + strlen(str), oneline); 498 continue; 499 } 500 501 /* copy the line to str and process */ 502 if (strlen(oneline) + strlen(str) > 1022) { 503 rte_panic("%s:%u: error: " 504 "the line contains more characters the parser can handle\n", 505 cfg_filename, line_num); 506 goto error_exit; 507 } 508 strcpy(str + strlen(str), oneline); 509 510 str[strlen(str)] = '\n'; 511 if (cmdline_parse(cl, str) < 0) { 512 rte_panic("%s:%u: error: parsing \"%s\" failed\n", 513 cfg_filename, line_num, str); 514 goto error_exit; 515 } 516 517 if (status.status < 0) { 518 rte_panic("%s:%u: error: %s", cfg_filename, 519 line_num, status.parse_msg); 520 goto error_exit; 521 } 522 523 memset(str, 0, 1024); 524 } while (1); 525 526 cmdline_stdin_exit(cl); 527 fclose(f); 528 529 sa_sort_arr(); 530 sp4_sort_arr(); 531 sp6_sort_arr(); 532 533 return 0; 534 535 error_exit: 536 if (cl) 537 cmdline_stdin_exit(cl); 538 if (f) 539 fclose(f); 540 541 return -1; 542 } 543