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