1 /* 2 * Copyright (C) 2017 THL A29 Limited, a Tencent company. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 * 25 */ 26 27 #include <string.h> 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <stdint.h> 31 #include <getopt.h> 32 #include <ctype.h> 33 #include <rte_config.h> 34 #include <rte_string_fns.h> 35 36 #include "ff_config.h" 37 #include "ff_ini_parser.h" 38 39 #define DEFAULT_CONFIG_FILE "config.ini" 40 41 #define BITS_PER_HEX 4 42 43 struct ff_config ff_global_cfg; 44 int dpdk_argc; 45 char *dpdk_argv[DPDK_CONFIG_NUM + 1]; 46 47 char* const short_options = "c:t:p:"; 48 struct option long_options[] = { 49 { "conf", 1, NULL, 'c'}, 50 { "proc-type", 1, NULL, 't'}, 51 { "proc-id", 1, NULL, 'p'}, 52 { 0, 0, 0, 0}, 53 }; 54 55 static int 56 xdigit2val(unsigned char c) 57 { 58 int val; 59 60 if (isdigit(c)) 61 val = c - '0'; 62 else if (isupper(c)) 63 val = c - 'A' + 10; 64 else 65 val = c - 'a' + 10; 66 return val; 67 } 68 69 static int 70 parse_lcore_mask(struct ff_config *cfg, const char *coremask) 71 { 72 int i, j, idx = 0; 73 unsigned count = 0; 74 char c; 75 int val; 76 uint16_t *proc_lcore; 77 char buf[RTE_MAX_LCORE] = {0}; 78 79 if (coremask == NULL) 80 return 0; 81 82 cfg->dpdk.proc_lcore = (uint16_t *)calloc(RTE_MAX_LCORE, sizeof(uint16_t)); 83 if (cfg->dpdk.proc_lcore == NULL) { 84 fprintf(stderr, "parse_lcore_mask malloc failed\n"); 85 return 0; 86 } 87 proc_lcore = cfg->dpdk.proc_lcore; 88 89 /* 90 * Remove all blank characters ahead and after. 91 * Remove 0x/0X if exists. 92 */ 93 while (isblank(*coremask)) 94 coremask++; 95 if (coremask[0] == '0' && ((coremask[1] == 'x') 96 || (coremask[1] == 'X'))) 97 coremask += 2; 98 99 i = strlen(coremask); 100 while ((i > 0) && isblank(coremask[i - 1])) 101 i--; 102 103 if (i == 0) 104 return 0; 105 106 for (i = i - 1; i >= 0 && idx < RTE_MAX_LCORE; i--) { 107 c = coremask[i]; 108 if (isxdigit(c) == 0) { 109 return 0; 110 } 111 val = xdigit2val(c); 112 for (j = 0; j < BITS_PER_HEX && idx < RTE_MAX_LCORE; j++, idx++) { 113 if ((1 << j) & val) { 114 proc_lcore[count] = idx; 115 if (cfg->dpdk.proc_id == count) { 116 sprintf(buf, "%x", 1<<idx); 117 cfg->dpdk.proc_mask = strdup(buf); 118 } 119 count++; 120 } 121 } 122 } 123 124 for (; i >= 0; i--) 125 if (coremask[i] != '0') 126 return 0; 127 128 if (cfg->dpdk.proc_id >= count) 129 return 0; 130 131 cfg->dpdk.nb_procs = count; 132 133 return 1; 134 } 135 136 static int 137 is_integer(const char *s) 138 { 139 if (*s == '-' || *s == '+') 140 s++; 141 if (*s < '0' || '9' < *s) 142 return 0; 143 s++; 144 while ('0' <= *s && *s <= '9') 145 s++; 146 return (*s == '\0'); 147 } 148 149 static int 150 freebsd_conf_handler(struct ff_config *cfg, const char *section, 151 const char *name, const char *value) 152 { 153 struct ff_freebsd_cfg *newconf, **cur; 154 newconf = (struct ff_freebsd_cfg *)malloc(sizeof(struct ff_freebsd_cfg)); 155 if (newconf == NULL) { 156 fprintf(stderr, "freebsd conf malloc failed\n"); 157 return 0; 158 } 159 160 newconf->name = strdup(name); 161 newconf->str = strdup(value); 162 163 if (strcmp(section, "boot") == 0) { 164 cur = &cfg->freebsd.boot; 165 166 newconf->value = (void *)newconf->str; 167 newconf->vlen = strlen(value); 168 } else if (strcmp(section, "sysctl") == 0) { 169 cur = &cfg->freebsd.sysctl; 170 171 if (is_integer(value)) { 172 int *p = (int *)malloc(sizeof(int)); 173 *p = atoi(value); 174 newconf->value = (void *)p; 175 newconf->vlen = sizeof(*p); 176 } else { 177 newconf->value = (void *)newconf->str; 178 newconf->vlen = strlen(value); 179 } 180 } else { 181 fprintf(stderr, "freebsd conf section[%s] error\n", section); 182 return 0; 183 } 184 185 if (*cur == NULL) { 186 *cur = newconf; 187 } else { 188 (*cur)->next = newconf; 189 newconf->next = NULL; 190 } 191 192 return 1; 193 } 194 // A recursive binary search function. It returns location of x in 195 // given array arr[l..r] is present, otherwise -1 196 static int 197 uint16_binary_search(uint16_t arr[], int l, int r, uint16_t x) 198 { 199 if (r >= l) { 200 int mid = l + (r - l)/2; 201 202 // If the element is present at the middle itself 203 if (arr[mid] == x) return mid; 204 205 // If element is smaller than mid, then it can only be present 206 // in left subarray 207 if (arr[mid] > x) return uint16_binary_search(arr, l, mid-1, x); 208 209 // Else the element can only be present in right subarray 210 return uint16_binary_search(arr, mid+1, r, x); 211 } 212 213 // We reach here when element is not present in array 214 return -1; 215 } 216 217 static int 218 uint16_cmp (const void * a, const void * b) 219 { 220 return ( *(uint16_t*)a - *(uint16_t*)b ); 221 } 222 223 static inline void 224 sort_uint16_array(uint16_t arr[], int n) 225 { 226 qsort(arr, n, sizeof(uint16_t), uint16_cmp); 227 } 228 229 static inline char * 230 __strstrip(char *s) 231 { 232 char *end = s + strlen(s) - 1; 233 while(*s == ' ') s++; 234 for (; end >= s; --end) { 235 if (*end != ' ') break; 236 } 237 *(++end) = '\0'; 238 return s; 239 } 240 241 static int 242 __parse_config_list(uint16_t *arr, int *sz, const char *value) { 243 int i, j; 244 char input[4096]; 245 char *tokens[128]; 246 int nTokens = 0; 247 char *endptr; 248 int nr_ele = 0; 249 int max_ele = *sz; 250 251 strncpy(input, value, 4096); 252 nTokens = rte_strsplit(input, sizeof(input), tokens, 128, ','); 253 for (i = 0; i < nTokens; i++) { 254 char *tok = tokens[i]; 255 char *middle = strchr(tok, '-'); 256 if (middle == NULL) { 257 tok = __strstrip(tok); 258 long v = strtol(tok, &endptr, 10); 259 if (*endptr != '\0') { 260 fprintf(stderr, "%s is not a integer.", tok); 261 return 0; 262 } 263 if (nr_ele > max_ele) { 264 fprintf(stderr, "too many elements in list %s\n", value); 265 return 0; 266 } 267 arr[nr_ele++] = (uint16_t)v; 268 } else { 269 *middle = '\0'; 270 char *lbound = __strstrip(tok); 271 char *rbound = __strstrip(middle+1); 272 long lv = strtol(lbound, &endptr, 10); 273 if (*endptr != '\0') { 274 fprintf(stderr, "%s is not a integer.", lbound); 275 return 0; 276 } 277 long rv = strtol(rbound, &endptr, 10); 278 if (*endptr != '\0') { 279 fprintf(stderr, "%s is not a integer.", rbound); 280 return 0; 281 } 282 for (j = lv; j <= rv; ++j) { 283 if (nr_ele > max_ele) { 284 fprintf(stderr, "too many elements in list %s.\n", value); 285 return 0; 286 } 287 arr[nr_ele++] = (uint16_t)j; 288 } 289 } 290 } 291 if (nr_ele <= 0) { 292 printf("list %s is empty\n", value); 293 return 1; 294 } 295 sort_uint16_array(arr, nr_ele); 296 *sz = nr_ele; 297 return 1; 298 } 299 300 static int 301 parse_port_lcore_list(struct ff_port_cfg *cfg, const char *v_str) 302 { 303 cfg->nb_lcores = DPDK_MAX_LCORE; 304 uint16_t *cores = cfg->lcore_list; 305 return __parse_config_list(cores, &cfg->nb_lcores, v_str); 306 } 307 308 static int 309 parse_port_list(struct ff_config *cfg, const char *v_str) 310 { 311 int res; 312 uint16_t ports[RTE_MAX_ETHPORTS]; 313 int sz = RTE_MAX_ETHPORTS; 314 315 res = __parse_config_list(ports, &sz, v_str); 316 if (! res) return res; 317 318 uint16_t *portid_list = malloc(sizeof(uint16_t)*sz); 319 320 if (portid_list == NULL) { 321 fprintf(stderr, "parse_port_list malloc failed\n"); 322 return 0; 323 } 324 memcpy(portid_list, ports, sz*sizeof(uint16_t)); 325 326 cfg->dpdk.portid_list = portid_list; 327 cfg->dpdk.nb_ports = sz; 328 cfg->dpdk.max_portid = portid_list[sz-1]; 329 return res; 330 } 331 332 static int 333 port_cfg_handler(struct ff_config *cfg, const char *section, 334 const char *name, const char *value) { 335 336 if (cfg->dpdk.nb_ports == 0) { 337 fprintf(stderr, "port_cfg_handler: must config dpdk.port_list first\n"); 338 return 0; 339 } 340 341 if (cfg->dpdk.port_cfgs == NULL) { 342 struct ff_port_cfg *pc = calloc(RTE_MAX_ETHPORTS, sizeof(struct ff_port_cfg)); 343 if (pc == NULL) { 344 fprintf(stderr, "port_cfg_handler malloc failed\n"); 345 return 0; 346 } 347 // initialize lcore list and nb_lcores 348 int i; 349 for (i = 0; i < cfg->dpdk.nb_ports; ++i) { 350 uint16_t portid = cfg->dpdk.portid_list[i]; 351 352 struct ff_port_cfg *pconf = &pc[portid]; 353 pconf->port_id = portid; 354 pconf->nb_lcores = ff_global_cfg.dpdk.nb_procs; 355 memcpy(pconf->lcore_list, ff_global_cfg.dpdk.proc_lcore, 356 pconf->nb_lcores*sizeof(uint16_t)); 357 } 358 cfg->dpdk.port_cfgs = pc; 359 } 360 361 int portid; 362 int ret = sscanf(section, "port%d", &portid); 363 if (ret != 1) { 364 fprintf(stderr, "port_cfg_handler section[%s] error\n", section); 365 return 0; 366 } 367 368 /* just return true if portid >= nb_ports because it has no effect */ 369 if (portid > cfg->dpdk.max_portid) { 370 fprintf(stderr, "port_cfg_handler section[%s] bigger than max port id\n", section); 371 return 1; 372 } 373 374 struct ff_port_cfg *cur = &cfg->dpdk.port_cfgs[portid]; 375 if (cur->name == NULL) { 376 cur->name = strdup(section); 377 cur->port_id = portid; 378 } 379 380 if (strcmp(name, "addr") == 0) { 381 cur->addr = strdup(value); 382 } else if (strcmp(name, "netmask") == 0) { 383 cur->netmask = strdup(value); 384 } else if (strcmp(name, "broadcast") == 0) { 385 cur->broadcast = strdup(value); 386 } else if (strcmp(name, "gateway") == 0) { 387 cur->gateway = strdup(value); 388 } else if (strcmp(name, "pcap") == 0) { 389 cur->pcap = strdup(value); 390 } else if (strcmp(name, "lcore_list") == 0) { 391 return parse_port_lcore_list(cur, value); 392 } 393 394 return 1; 395 } 396 397 static int 398 ini_parse_handler(void* user, const char* section, const char* name, 399 const char* value) 400 { 401 struct ff_config *pconfig = (struct ff_config*)user; 402 403 printf("[%s]: %s=%s\n", section, name, value); 404 405 #define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0 406 if (MATCH("dpdk", "channel")) { 407 pconfig->dpdk.nb_channel = atoi(value); 408 } else if (MATCH("dpdk", "memory")) { 409 pconfig->dpdk.memory = atoi(value); 410 } else if (MATCH("dpdk", "no_huge")) { 411 pconfig->dpdk.no_huge = atoi(value); 412 } else if (MATCH("dpdk", "lcore_mask")) { 413 pconfig->dpdk.lcore_mask = strdup(value); 414 return parse_lcore_mask(pconfig, pconfig->dpdk.lcore_mask); 415 } else if (MATCH("dpdk", "port_list")) { 416 return parse_port_list(pconfig, value); 417 } else if (MATCH("dpdk", "promiscuous")) { 418 pconfig->dpdk.promiscuous = atoi(value); 419 } else if (MATCH("dpdk", "numa_on")) { 420 pconfig->dpdk.numa_on = atoi(value); 421 } else if (MATCH("dpdk", "tso")) { 422 pconfig->dpdk.tso = atoi(value); 423 } else if (MATCH("dpdk", "vlan_strip")) { 424 pconfig->dpdk.vlan_strip = atoi(value); 425 } else if (MATCH("kni", "enable")) { 426 pconfig->kni.enable= atoi(value); 427 } else if (MATCH("kni", "method")) { 428 pconfig->kni.method= strdup(value); 429 } else if (MATCH("kni", "tcp_port")) { 430 pconfig->kni.tcp_port = strdup(value); 431 } else if (MATCH("kni", "udp_port")) { 432 pconfig->kni.udp_port= strdup(value); 433 } else if (strcmp(section, "freebsd.boot") == 0) { 434 if (strcmp(name, "hz") == 0) { 435 pconfig->freebsd.hz = atoi(value); 436 } else if (strcmp(name, "physmem") == 0) { 437 pconfig->freebsd.physmem = atol(value); 438 } else if (strcmp(name, "fd_reserve") == 0) { 439 pconfig->freebsd.fd_reserve = atoi(value); 440 } else { 441 return freebsd_conf_handler(pconfig, "boot", name, value); 442 } 443 } else if (strcmp(section, "freebsd.sysctl") == 0) { 444 return freebsd_conf_handler(pconfig, "sysctl", name, value); 445 } else if (strncmp(section, "port", 4) == 0) { 446 return port_cfg_handler(pconfig, section, name, value); 447 } 448 449 return 1; 450 } 451 452 static int 453 dpdk_args_setup(struct ff_config *cfg) 454 { 455 int n = 0, i; 456 dpdk_argv[n++] = strdup("f-stack"); 457 char temp[DPDK_CONFIG_MAXLEN] = {0}; 458 459 if (cfg->dpdk.no_huge) { 460 dpdk_argv[n++] = strdup("--no-huge"); 461 } 462 if (cfg->dpdk.proc_mask) { 463 sprintf(temp, "-c%s", cfg->dpdk.proc_mask); 464 dpdk_argv[n++] = strdup(temp); 465 } 466 if (cfg->dpdk.nb_channel) { 467 sprintf(temp, "-n%d", cfg->dpdk.nb_channel); 468 dpdk_argv[n++] = strdup(temp); 469 } 470 if (cfg->dpdk.memory) { 471 sprintf(temp, "-m%d", cfg->dpdk.memory); 472 dpdk_argv[n++] = strdup(temp); 473 } 474 if (cfg->dpdk.proc_type) { 475 sprintf(temp, "--proc-type=%s", cfg->dpdk.proc_type); 476 dpdk_argv[n++] = strdup(temp); 477 } 478 479 dpdk_argc = n; 480 481 return n; 482 } 483 484 static int 485 ff_parse_args(struct ff_config *cfg, int argc, char *const argv[]) 486 { 487 int c; 488 int index = 0; 489 optind = 1; 490 while((c = getopt_long(argc, argv, short_options, long_options, &index)) != -1) { 491 switch (c) { 492 case 'c': 493 cfg->filename = strdup(optarg); 494 break; 495 case 'p': 496 cfg->dpdk.proc_id = atoi(optarg); 497 break; 498 case 't': 499 cfg->dpdk.proc_type = strdup(optarg); 500 break; 501 default: 502 return -1; 503 } 504 } 505 506 if (cfg->dpdk.proc_type == NULL || 507 (strcmp(cfg->dpdk.proc_type, "primary") && 508 strcmp(cfg->dpdk.proc_type, "secondary"))) { 509 printf("invalid proc-type\n"); 510 return -1; 511 } 512 513 if ((uint16_t)cfg->dpdk.proc_id > RTE_MAX_LCORE) { 514 printf("proc_id:%d is too large\n", cfg->dpdk.proc_id); 515 return -1; 516 } 517 518 return 0; 519 } 520 521 static int 522 ff_check_config(struct ff_config *cfg) 523 { 524 if(cfg->kni.enable && !cfg->kni.method) { 525 fprintf(stderr, "conf dpdk.method is necessary\n"); 526 return -1; 527 } 528 529 if(cfg->kni.method) { 530 if(strcasecmp(cfg->kni.method,"accept") && 531 strcasecmp(cfg->kni.method,"reject")) { 532 fprintf(stderr, "conf kni.method[accept|reject] is error(%s)\n", 533 cfg->kni.method); 534 return -1; 535 } 536 } 537 538 #define CHECK_VALID(n) \ 539 do { \ 540 if (!pc->n) { \ 541 fprintf(stderr, "port%d if config error: no %s\n", \ 542 pc->port_id, #n); \ 543 return -1; \ 544 } \ 545 } while (0) 546 547 int i; 548 for (i = 0; i < cfg->dpdk.nb_ports; i++) { 549 uint16_t portid = cfg->dpdk.portid_list[i]; 550 struct ff_port_cfg *pc = &cfg->dpdk.port_cfgs[portid]; 551 CHECK_VALID(addr); 552 CHECK_VALID(netmask); 553 CHECK_VALID(broadcast); 554 CHECK_VALID(gateway); 555 // check if the lcores in lcore_list are enabled. 556 int k; 557 for (k = 0; k < pc->nb_lcores; k++) { 558 uint16_t lcore_id = pc->lcore_list[k]; 559 if (uint16_binary_search(cfg->dpdk.proc_lcore, 0, 560 cfg->dpdk.nb_procs-1, lcore_id) < 0) { 561 fprintf(stderr, "lcore %d is not enabled.\n", lcore_id); 562 return -1; 563 } 564 } 565 /* 566 * only primary process process KNI, so if KNI enabled, 567 * primary lcore must stay in every enabled ports' lcore_list 568 */ 569 if (cfg->kni.enable && 570 strcmp(cfg->dpdk.proc_type, "primary") == 0) { 571 int found = 0; 572 int j; 573 uint16_t lcore_id = cfg->dpdk.proc_lcore[cfg->dpdk.proc_id]; 574 for (j = 0; j < pc->nb_lcores; j++) { 575 if (pc->lcore_list[j] == lcore_id) { 576 found = 1; 577 } 578 } 579 if (! found) { 580 fprintf(stderr, 581 "primary lcore %d should stay in port %d's lcore_list.\n", 582 lcore_id, pc->port_id); 583 return -1; 584 } 585 } 586 } 587 588 return 0; 589 } 590 591 static void 592 ff_default_config(struct ff_config *cfg) 593 { 594 memset(cfg, 0, sizeof(struct ff_config)); 595 596 cfg->filename = DEFAULT_CONFIG_FILE; 597 598 cfg->dpdk.proc_id = -1; 599 cfg->dpdk.numa_on = 1; 600 cfg->dpdk.promiscuous = 1; 601 602 cfg->freebsd.hz = 100; 603 cfg->freebsd.physmem = 1048576*256; 604 cfg->freebsd.fd_reserve = 0; 605 } 606 607 int 608 ff_load_config(int argc, char *const argv[]) 609 { 610 ff_default_config(&ff_global_cfg); 611 612 int ret = ff_parse_args(&ff_global_cfg, argc, argv); 613 if (ret < 0) { 614 return ret; 615 } 616 617 ret = ini_parse(ff_global_cfg.filename, ini_parse_handler, 618 &ff_global_cfg); 619 if (ret != 0) { 620 printf("parse %s failed on line %d\n", ff_global_cfg.filename, ret); 621 return -1; 622 } 623 624 if (ff_check_config(&ff_global_cfg)) { 625 return -1; 626 } 627 628 if (dpdk_args_setup(&ff_global_cfg) <= 0) { 629 return -1; 630 } 631 632 return 0; 633 } 634