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: stable/12/usr.sbin/valectl/valectl.c 368036 2020-11-25 21:24:39Z vmaffione $ */
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
dump_port_info(struct nmreq_port_info_get * v)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
dump_newif(struct nmreq_vale_newif * v)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
dump_vale_list(struct nmreq_vale_list * v)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
parse_ring_config(const char * conf,uint32_t * nr_tx_slots,uint32_t * nr_rx_slots,uint16_t * nr_tx_rings,uint16_t * nr_rx_rings)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
parse_poll_config(const char * conf,struct nmreq_vale_polling * v)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
parse_mem_id(const char * mem_id)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
list_all(int fd,struct nmreq_header * hdr)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
bdg_ctl(struct args * a)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
usage(int errcode)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
main(int argc,char * argv[])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 }
446