]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/valectl/valectl.c
THIS BRANCH IS OBSOLETE, PLEASE READ:
[FreeBSD/FreeBSD.git] / usr.sbin / valectl / valectl.c
1 /*
2  * Copyright (C) 2013-2014 Michio Honda. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *   1. Redistributions of source code must retain the above copyright
8  *      notice, this list of conditions and the following disclaimer.
9  *   2. Redistributions in binary form must reproduce the above copyright
10  *      notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25
26 /* $FreeBSD$ */
27
28 #define LIBNETMAP_NOTHREADSAFE
29 #include <libnetmap.h>
30
31 #include <errno.h>
32 #include <stdio.h>
33 #include <inttypes.h>   /* PRI* macros */
34 #include <string.h>     /* strcmp */
35 #include <fcntl.h>      /* open */
36 #include <unistd.h>     /* close */
37 #include <sys/ioctl.h>  /* ioctl */
38 #include <sys/param.h>
39 #include <sys/socket.h> /* apple needs sockaddr */
40 #include <net/if.h>     /* ifreq */
41 #include <libgen.h>     /* basename */
42 #include <stdlib.h>     /* atoi, free */
43
44 int verbose;
45
46 struct args {
47         const char *name;
48         const char *config;
49         const char *mem_id;
50
51         uint16_t nr_reqtype;
52         uint32_t nr_mode;
53 };
54
55 static void
56 dump_port_info(struct nmreq_port_info_get *v)
57 {
58         printf("memsize:    %"PRIu64"\n", v->nr_memsize);
59         printf("tx_slots:   %"PRIu32"\n", v->nr_tx_slots);
60         printf("rx_slots:   %"PRIu32"\n", v->nr_rx_slots);
61         printf("tx_rings:   %"PRIu16"\n", v->nr_tx_rings);
62         printf("rx_rings    %"PRIu16"\n", v->nr_rx_rings);
63         printf("mem_id:     %"PRIu16"\n", v->nr_mem_id);
64 }
65
66 static void
67 dump_newif(struct nmreq_vale_newif *v)
68 {
69         printf("tx_slots:   %"PRIu32"\n", v->nr_tx_slots);
70         printf("rx_slots:   %"PRIu32"\n", v->nr_rx_slots);
71         printf("tx_rings:   %"PRIu16"\n", v->nr_tx_rings);
72         printf("rx_ring:    %"PRIu16"\n", v->nr_rx_rings);
73         printf("mem_id:     %"PRIu16"\n", v->nr_mem_id);
74 }
75
76 static void
77 dump_vale_list(struct nmreq_vale_list *v)
78 {
79         printf("bridge_idx: %"PRIu16"\n", v->nr_bridge_idx);
80         printf("port_idx:   %"PRIu16"\n", v->nr_port_idx);
81 }
82
83
84 static void
85 parse_ring_config(const char* conf,
86                 uint32_t *nr_tx_slots,
87                 uint32_t *nr_rx_slots,
88                 uint16_t *nr_tx_rings,
89                 uint16_t *nr_rx_rings)
90 {
91         char *w, *tok;
92         int i, v;
93
94         *nr_tx_rings = *nr_rx_rings = 0;
95         *nr_tx_slots = *nr_rx_slots = 0;
96         if (conf == NULL || ! *conf)
97                 return;
98         w = strdup(conf);
99         for (i = 0, tok = strtok(w, ","); tok; i++, tok = strtok(NULL, ",")) {
100                 v = atoi(tok);
101                 switch (i) {
102                 case 0:
103                         *nr_tx_slots = *nr_rx_slots = v;
104                         break;
105                 case 1:
106                         *nr_rx_slots = v;
107                         break;
108                 case 2:
109                         *nr_tx_rings = *nr_rx_rings = v;
110                         break;
111                 case 3:
112                         *nr_rx_rings = v;
113                         break;
114                 default:
115                         fprintf(stderr, "ignored config: %s", tok);
116                         break;
117                 }
118         }
119         ND("txr %d txd %d rxr %d rxd %d",
120                         *nr_tx_rings, *nr_tx_slots,
121                         *nr_rx_rings, *nr_rx_slots);
122         free(w);
123 }
124
125 static int
126 parse_poll_config(const char *conf, struct nmreq_vale_polling *v)
127 {
128         char *w, *tok;
129         int i, p;
130
131         if (conf == NULL || ! *conf) {
132                 fprintf(stderr, "invalid null/empty config\n");
133                 return -1;
134         }
135         w = strdup(conf);
136         for (i = 0, tok = strtok(w, ","); tok; i++, tok = strtok(NULL, ",")) {
137                 p = atoi(tok);
138                 switch (i) {
139                 case 0:
140                         v->nr_mode = p ? NETMAP_POLLING_MODE_MULTI_CPU :
141                                 NETMAP_POLLING_MODE_SINGLE_CPU;
142                         break;
143                 case 1:
144                         v->nr_first_cpu_id = p;
145                         break;
146                 case 2:
147                         if (v->nr_mode != NETMAP_POLLING_MODE_MULTI_CPU) {
148                                 fprintf(stderr, "too many numbers in '%s'\n", conf);
149                                 return -1;
150                         }
151                         v->nr_num_polling_cpus = p;
152                         break;
153                 case 3:
154                         fprintf(stderr, "too many numbers in '%s'\n", conf);
155                         return -1;
156                 }
157         }
158         free(w);
159         return 0;
160 }
161
162 static int32_t
163 parse_mem_id(const char *mem_id)
164 {
165         int32_t id;
166
167         if (mem_id == NULL)
168                 return 0;
169         if (isdigit(*mem_id))
170                 return atoi(mem_id);
171         id = nmreq_get_mem_id(&mem_id, nmctx_get());
172         if (id == 0) {
173                 fprintf(stderr, "invalid format in '-m %s' (missing 'netmap:'?)\n", mem_id);
174                 return -1;
175         }
176         return id;
177 }
178
179 static int
180 list_all(int fd, struct nmreq_header *hdr)
181 {
182         int error;
183         struct nmreq_vale_list *vale_list =
184                 (struct nmreq_vale_list *)hdr->nr_body;
185
186         for (;;) {
187                 hdr->nr_name[0] = '\0';
188                 error = ioctl(fd, NIOCCTRL, hdr);
189                 if (error < 0) {
190                         if (errno == ENOENT)
191                                 break;
192
193                         fprintf(stderr, "failed to list all: %s\n", strerror(errno));
194                         return 1;
195                 }
196                 printf("%s bridge_idx %"PRIu16" port_idx %"PRIu32"\n", hdr->nr_name,
197                                 vale_list->nr_bridge_idx, vale_list->nr_port_idx);
198                 vale_list->nr_port_idx++;
199         }
200         return 1;
201 }
202
203 static int
204 bdg_ctl(struct args *a)
205 {
206         struct nmreq_header hdr;
207         struct nmreq_vale_attach   vale_attach;
208         struct nmreq_vale_detach   vale_detach;
209         struct nmreq_vale_newif    vale_newif;
210         struct nmreq_vale_list     vale_list;
211         struct nmreq_vale_polling  vale_polling;
212         struct nmreq_port_info_get port_info_get;
213         int error = 0;
214         int fd;
215         int32_t mem_id;
216         const char *action = NULL;
217
218         fd = open("/dev/netmap", O_RDWR);
219         if (fd == -1) {
220                 perror("/dev/netmap");
221                 return 1;
222         }
223
224         bzero(&hdr, sizeof(hdr));
225         hdr.nr_version = NETMAP_API;
226         if (a->name != NULL) { /* might be NULL */
227                 strncpy(hdr.nr_name, a->name, NETMAP_REQ_IFNAMSIZ - 1);
228                 hdr.nr_name[NETMAP_REQ_IFNAMSIZ - 1] = '\0';
229         }
230         hdr.nr_reqtype = a->nr_reqtype;
231
232         switch (a->nr_reqtype) {
233         case NETMAP_REQ_VALE_DELIF:
234                 /* no body */
235                 action = "remove";
236                 break;
237
238         case NETMAP_REQ_VALE_NEWIF:
239                 memset(&vale_newif, 0, sizeof(vale_newif));
240                 hdr.nr_body = (uintptr_t)&vale_newif;
241                 parse_ring_config(a->config,
242                                 &vale_newif.nr_tx_slots,
243                                 &vale_newif.nr_rx_slots,
244                                 &vale_newif.nr_tx_rings,
245                                 &vale_newif.nr_rx_rings);
246                 mem_id = parse_mem_id(a->mem_id);
247                 if (mem_id < 0)
248                         return 1;
249                 vale_newif.nr_mem_id = mem_id;
250                 action = "create";
251                 break;
252
253         case NETMAP_REQ_VALE_ATTACH:
254                 memset(&vale_attach, 0, sizeof(vale_attach));
255                 hdr.nr_body = (uintptr_t)&vale_attach;
256                 vale_attach.reg.nr_mode = a->nr_mode;
257                 parse_ring_config(a->config,
258                                 &vale_attach.reg.nr_tx_slots,
259                                 &vale_attach.reg.nr_rx_slots,
260                                 &vale_attach.reg.nr_tx_rings,
261                                 &vale_attach.reg.nr_rx_rings);
262                 mem_id = parse_mem_id(a->mem_id);
263                 if (mem_id < 0)
264                         return 1;
265                 vale_attach.reg.nr_mem_id = mem_id;
266                 action = "attach";
267                 break;
268
269         case NETMAP_REQ_VALE_DETACH:
270                 memset(&vale_detach, 0, sizeof(vale_detach));
271                 hdr.nr_body = (uintptr_t)&vale_detach;
272                 action = "detach";
273                 break;
274
275         case NETMAP_REQ_VALE_LIST:
276                 memset(&vale_list, 0, sizeof(vale_list));
277                 hdr.nr_body = (uintptr_t)&vale_list;
278                 if (a->name == NULL) {
279                         return list_all(fd, &hdr);
280                 }
281                 action = "list";
282                 break;
283
284         case NETMAP_REQ_VALE_POLLING_ENABLE:
285                 action = "enable polling on";
286                 /* fall through */
287         case NETMAP_REQ_VALE_POLLING_DISABLE:
288                 memset(&vale_polling, 0, sizeof(vale_polling));
289                 hdr.nr_body = (uintptr_t)&vale_polling;
290                 parse_poll_config(a->config, &vale_polling);
291                 if (action == NULL)
292                         action ="disable polling on";
293                 break;
294
295         case NETMAP_REQ_PORT_INFO_GET:
296                 memset(&port_info_get, 0, sizeof(port_info_get));
297                 hdr.nr_body = (uintptr_t)&port_info_get;
298                 action = "obtain info for";
299                 break;
300         }
301         error = ioctl(fd, NIOCCTRL, &hdr);
302         if (error < 0) {
303                 fprintf(stderr, "failed to %s %s: %s\n",
304                                 action, a->name, strerror(errno));
305                 return 1;
306         }
307         switch (hdr.nr_reqtype) {
308         case NETMAP_REQ_VALE_NEWIF:
309                 if (verbose) {
310                         dump_newif(&vale_newif);
311                 }
312                 break;
313
314         case NETMAP_REQ_VALE_ATTACH:
315                 if (verbose) {
316                         printf("port_index: %"PRIu32"\n", vale_attach.port_index);
317                 }
318                 break;
319
320         case NETMAP_REQ_VALE_DETACH:
321                 if (verbose) {
322                         printf("port_index: %"PRIu32"\n", vale_detach.port_index);
323                 }
324                 break;
325
326         case NETMAP_REQ_VALE_LIST:
327                 dump_vale_list(&vale_list);
328                 break;
329
330         case NETMAP_REQ_PORT_INFO_GET:
331                 dump_port_info(&port_info_get);
332                 break;
333         }
334         close(fd);
335         return error;
336 }
337
338 static void
339 usage(int errcode)
340 {
341         fprintf(stderr,
342             "Usage:\n"
343             "vale-ctl [arguments]\n"
344             "\t-g interface     interface name to get info\n"
345             "\t-d interface     interface name to be detached\n"
346             "\t-a interface     interface name to be attached\n"
347             "\t-h interface     interface name to be attached with the host stack\n"
348             "\t-n interface     interface name to be created\n"
349             "\t-r interface     interface name to be deleted\n"
350             "\t-l vale-port     show bridge and port indices\n"
351             "\t-C string ring/slot setting of an interface creating by -n\n"
352             "\t-p interface start polling. Additional -C x,y,z configures\n"
353             "\t\t x: 0 (REG_ALL_NIC) or 1 (REG_ONE_NIC),\n"
354             "\t\t y: CPU core id for ALL_NIC and core/ring for ONE_NIC\n"
355             "\t\t z: (ONE_NIC only) num of total cores/rings\n"
356             "\t-P interface stop polling\n"
357             "\t-m memid to use when creating a new interface\n"
358             "\t-v increase verbosity\n"
359             "with no arguments: list all existing vale ports\n");
360         exit(errcode);
361 }
362
363 int
364 main(int argc, char *argv[])
365 {
366         int ch;
367         struct args a = {
368                 .name = NULL,
369                 .config = NULL,
370                 .mem_id = NULL,
371                 .nr_reqtype = 0,
372                 .nr_mode = NR_REG_ALL_NIC,
373         };
374
375         while ((ch = getopt(argc, argv, "d:a:h:g:l:n:r:C:p:P:m:v")) != -1) {
376                 switch (ch) {
377                 default:
378                         fprintf(stderr, "bad option %c %s", ch, optarg);
379                         usage(1);
380                         break;
381                 case 'd':
382                         a.nr_reqtype = NETMAP_REQ_VALE_DETACH;
383                         a.name = optarg;
384                         break;
385                 case 'a':
386                         a.nr_reqtype = NETMAP_REQ_VALE_ATTACH;
387                         a.nr_mode = NR_REG_ALL_NIC;
388                         a.name = optarg;
389                         break;
390                 case 'h':
391                         a.nr_reqtype = NETMAP_REQ_VALE_ATTACH;
392                         a.nr_mode = NR_REG_NIC_SW;
393                         a.name = optarg;
394                         break;
395                 case 'n':
396                         a.nr_reqtype = NETMAP_REQ_VALE_NEWIF;
397                         a.name = optarg;
398                         break;
399                 case 'r':
400                         a.nr_reqtype = NETMAP_REQ_VALE_DELIF;
401                         a.name = optarg;
402                         break;
403                 case 'g':
404                         a.nr_reqtype = NETMAP_REQ_PORT_INFO_GET;
405                         a.name = optarg;
406                         break;
407                 case 'l':
408                         a.nr_reqtype = NETMAP_REQ_VALE_LIST;
409                         a.name = optarg;
410                         if (strncmp(a.name, NM_BDG_NAME, strlen(NM_BDG_NAME))) {
411                                 fprintf(stderr, "invalid vale port name: '%s'\n", a.name);
412                                 usage(1);
413                         }
414                         break;
415                 case 'C':
416                         a.config = optarg;
417                         break;
418                 case 'p':
419                         a.nr_reqtype = NETMAP_REQ_VALE_POLLING_ENABLE;
420                         a.name = optarg;
421                         break;
422                 case 'P':
423                         a.nr_reqtype = NETMAP_REQ_VALE_POLLING_DISABLE;
424                         a.name = optarg;
425                         break;
426                 case 'm':
427                         a.mem_id = optarg;
428                         break;
429                 case 'v':
430                         verbose++;
431                         break;
432                 }
433         }
434         if (optind != argc) {
435                 usage(1);
436         }
437         if (argc == 1) {
438                 a.nr_reqtype = NETMAP_REQ_VALE_LIST;
439                 a.name = NULL;
440         }
441         if (!a.nr_reqtype) {
442                 usage(1);
443         }
444         return bdg_ctl(&a);
445 }