xref: /freebsd/usr.sbin/valectl/valectl.c (revision 19261079)
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 *)(uintptr_t)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 }
446