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
43int verbose;
44
45struct 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
54static void
55dump_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
65static void
66dump_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
75static void
76dump_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
83static void
84parse_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
124static int
125parse_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
161static int32_t
162parse_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
178static int
179list_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
202static int
203bdg_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
337static void
338usage(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
362int
363main(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