xref: /f-stack/lib/ff_config.c (revision 80a6164c)
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