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