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#include <errno.h>
29#include <stdio.h>
30#include <inttypes.h>	/* PRI* macros */
31#include <string.h>	/* strcmp */
32#include <fcntl.h>	/* open */
33#include <unistd.h>	/* close */
34#include <sys/ioctl.h>	/* ioctl */
35#include <sys/param.h>
36#include <sys/socket.h>	/* apple needs sockaddr */
37#include <net/if.h>	/* ifreq */
38#include <net/netmap.h>
39#include <net/netmap_user.h>
40#include <libgen.h>	/* basename */
41#include <stdlib.h>	/* atoi, free */
42
43/* debug support */
44#define ND(format, ...)	do {} while(0)
45#define D(format, ...)					\
46	fprintf(stderr, "%s [%d] " format "\n",		\
47	__FUNCTION__, __LINE__, ##__VA_ARGS__)
48
49/* XXX cut and paste from pkt-gen.c because I'm not sure whether this
50 * program may include nm_util.h
51 */
52void parse_nmr_config(const char* conf, struct nmreq *nmr)
53{
54	char *w, *tok;
55	int i, v;
56
57	nmr->nr_tx_rings = nmr->nr_rx_rings = 0;
58	nmr->nr_tx_slots = nmr->nr_rx_slots = 0;
59	if (conf == NULL || ! *conf)
60		return;
61	w = strdup(conf);
62	for (i = 0, tok = strtok(w, ","); tok; i++, tok = strtok(NULL, ",")) {
63		v = atoi(tok);
64		switch (i) {
65		case 0:
66			nmr->nr_tx_slots = nmr->nr_rx_slots = v;
67			break;
68		case 1:
69			nmr->nr_rx_slots = v;
70			break;
71		case 2:
72			nmr->nr_tx_rings = nmr->nr_rx_rings = v;
73			break;
74		case 3:
75			nmr->nr_rx_rings = v;
76			break;
77		default:
78			D("ignored config: %s", tok);
79			break;
80		}
81	}
82	D("txr %d txd %d rxr %d rxd %d",
83			nmr->nr_tx_rings, nmr->nr_tx_slots,
84			nmr->nr_rx_rings, nmr->nr_rx_slots);
85	free(w);
86}
87
88static int
89bdg_ctl(const char *name, int nr_cmd, int nr_arg, char *nmr_config)
90{
91	struct nmreq nmr;
92	int error = 0;
93	int fd = open("/dev/netmap", O_RDWR);
94
95	if (fd == -1) {
96		D("Unable to open /dev/netmap");
97		return -1;
98	}
99
100	bzero(&nmr, sizeof(nmr));
101	nmr.nr_version = NETMAP_API;
102	if (name != NULL) /* might be NULL */
103		strncpy(nmr.nr_name, name, sizeof(nmr.nr_name));
104	nmr.nr_cmd = nr_cmd;
105	parse_nmr_config(nmr_config, &nmr);
106
107	switch (nr_cmd) {
108	case NETMAP_BDG_DELIF:
109	case NETMAP_BDG_NEWIF:
110		error = ioctl(fd, NIOCREGIF, &nmr);
111		if (error == -1) {
112			ND("Unable to %s %s", nr_cmd == NETMAP_BDG_DELIF ? "delete":"create", name);
113			perror(name);
114		} else {
115			ND("Success to %s %s", nr_cmd == NETMAP_BDG_DELIF ? "delete":"create", name);
116		}
117		break;
118	case NETMAP_BDG_ATTACH:
119	case NETMAP_BDG_DETACH:
120		if (nr_arg && nr_arg != NETMAP_BDG_HOST)
121			nr_arg = 0;
122		nmr.nr_arg1 = nr_arg;
123		error = ioctl(fd, NIOCREGIF, &nmr);
124		if (error == -1) {
125			ND("Unable to %s %s to the bridge", nr_cmd ==
126			    NETMAP_BDG_DETACH?"detach":"attach", name);
127			perror(name);
128		} else
129			ND("Success to %s %s to the bridge", nr_cmd ==
130			    NETMAP_BDG_DETACH?"detach":"attach", name);
131		break;
132
133	case NETMAP_BDG_LIST:
134		if (strlen(nmr.nr_name)) { /* name to bridge/port info */
135			error = ioctl(fd, NIOCGINFO, &nmr);
136			if (error) {
137				ND("Unable to obtain info for %s", name);
138				perror(name);
139			} else
140				D("%s at bridge:%d port:%d", name, nmr.nr_arg1,
141				    nmr.nr_arg2);
142			break;
143		}
144
145		/* scan all the bridges and ports */
146		nmr.nr_arg1 = nmr.nr_arg2 = 0;
147		for (; !ioctl(fd, NIOCGINFO, &nmr); nmr.nr_arg2++) {
148			D("bridge:%d port:%d %s", nmr.nr_arg1, nmr.nr_arg2,
149			    nmr.nr_name);
150			nmr.nr_name[0] = '\0';
151		}
152
153		break;
154
155	default: /* GINFO */
156		nmr.nr_cmd = nmr.nr_arg1 = nmr.nr_arg2 = 0;
157		error = ioctl(fd, NIOCGINFO, &nmr);
158		if (error) {
159			ND("Unable to get if info for %s", name);
160			perror(name);
161		} else
162			D("%s: %d queues.", name, nmr.nr_rx_rings);
163		break;
164	}
165	close(fd);
166	return error;
167}
168
169int
170main(int argc, char *argv[])
171{
172	int ch, nr_cmd = 0, nr_arg = 0;
173	const char *command = basename(argv[0]);
174	char *name = NULL, *nmr_config = NULL;
175
176	if (argc > 3) {
177usage:
178		fprintf(stderr,
179			"Usage:\n"
180			"%s arguments\n"
181			"\t-g interface	interface name to get info\n"
182			"\t-d interface	interface name to be detached\n"
183			"\t-a interface	interface name to be attached\n"
184			"\t-h interface	interface name to be attached with the host stack\n"
185			"\t-n interface	interface name to be created\n"
186			"\t-r interface	interface name to be deleted\n"
187			"\t-l list all or specified bridge's interfaces (default)\n"
188			"\t-C string ring/slot setting of an interface creating by -n\n"
189			"", command);
190		return 0;
191	}
192
193	while ((ch = getopt(argc, argv, "d:a:h:g:l:n:r:C:")) != -1) {
194		name = optarg; /* default */
195		switch (ch) {
196		default:
197			fprintf(stderr, "bad option %c %s", ch, optarg);
198			goto usage;
199		case 'd':
200			nr_cmd = NETMAP_BDG_DETACH;
201			break;
202		case 'a':
203			nr_cmd = NETMAP_BDG_ATTACH;
204			break;
205		case 'h':
206			nr_cmd = NETMAP_BDG_ATTACH;
207			nr_arg = NETMAP_BDG_HOST;
208			break;
209		case 'n':
210			nr_cmd = NETMAP_BDG_NEWIF;
211			break;
212		case 'r':
213			nr_cmd = NETMAP_BDG_DELIF;
214			break;
215		case 'g':
216			nr_cmd = 0;
217			break;
218		case 'l':
219			nr_cmd = NETMAP_BDG_LIST;
220			if (optind < argc && argv[optind][0] == '-')
221				name = NULL;
222			break;
223		case 'C':
224			nmr_config = strdup(optarg);
225			break;
226		}
227		if (optind != argc) {
228			// fprintf(stderr, "optind %d argc %d\n", optind, argc);
229			goto usage;
230		}
231	}
232	if (argc == 1)
233		nr_cmd = NETMAP_BDG_LIST;
234	return bdg_ctl(name, nr_cmd, nr_arg, nmr_config) ? 1 : 0;
235}
236