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
44int verbose;
45
46struct 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
55static void
56dump_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
66static void
67dump_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
76static void
77dump_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
84static void
85parse_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
125static int
126parse_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
162static int32_t
163parse_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
179static int
180list_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
203static int
204bdg_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
338static void
339usage(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
363int
364main(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