xref: /freebsd/usr.sbin/valectl/valectl.c (revision e17f5b1d)
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 NETMAP_WITH_LIBS
29 #include <net/netmap_user.h>
30 #include <net/netmap.h>
31 
32 #include <errno.h>
33 #include <stdio.h>
34 #include <inttypes.h>	/* PRI* macros */
35 #include <string.h>	/* strcmp */
36 #include <fcntl.h>	/* open */
37 #include <unistd.h>	/* close */
38 #include <sys/ioctl.h>	/* ioctl */
39 #include <sys/param.h>
40 #include <sys/socket.h>	/* apple needs sockaddr */
41 #include <net/if.h>	/* ifreq */
42 #include <libgen.h>	/* basename */
43 #include <stdlib.h>	/* atoi, free */
44 
45 static void
46 parse_nmr_config(const char* conf, struct nmreq *nmr)
47 {
48 	char *w, *tok;
49 	int i, v;
50 
51 	nmr->nr_tx_rings = nmr->nr_rx_rings = 0;
52 	nmr->nr_tx_slots = nmr->nr_rx_slots = 0;
53 	if (conf == NULL || ! *conf)
54 		return;
55 	w = strdup(conf);
56 	for (i = 0, tok = strtok(w, ","); tok; i++, tok = strtok(NULL, ",")) {
57 		v = atoi(tok);
58 		switch (i) {
59 		case 0:
60 			nmr->nr_tx_slots = nmr->nr_rx_slots = v;
61 			break;
62 		case 1:
63 			nmr->nr_rx_slots = v;
64 			break;
65 		case 2:
66 			nmr->nr_tx_rings = nmr->nr_rx_rings = v;
67 			break;
68 		case 3:
69 			nmr->nr_rx_rings = v;
70 			break;
71 		default:
72 			D("ignored config: %s", tok);
73 			break;
74 		}
75 	}
76 	D("txr %d txd %d rxr %d rxd %d",
77 			nmr->nr_tx_rings, nmr->nr_tx_slots,
78 			nmr->nr_rx_rings, nmr->nr_rx_slots);
79 	free(w);
80 }
81 
82 static int
83 bdg_ctl(const char *name, int nr_cmd, int nr_arg, char *nmr_config, int nr_arg2)
84 {
85 	struct nmreq nmr;
86 	int error = 0;
87 	int fd = open("/dev/netmap", O_RDWR);
88 
89 	if (fd == -1) {
90 		D("Unable to open /dev/netmap");
91 		return -1;
92 	}
93 
94 	bzero(&nmr, sizeof(nmr));
95 	nmr.nr_version = NETMAP_API;
96 	if (name != NULL) /* might be NULL */
97 		strncpy(nmr.nr_name, name, sizeof(nmr.nr_name)-1);
98 	nmr.nr_cmd = nr_cmd;
99 	parse_nmr_config(nmr_config, &nmr);
100 	nmr.nr_arg2 = nr_arg2;
101 
102 	switch (nr_cmd) {
103 	case NETMAP_BDG_DELIF:
104 	case NETMAP_BDG_NEWIF:
105 		error = ioctl(fd, NIOCREGIF, &nmr);
106 		if (error == -1) {
107 			ND("Unable to %s %s", nr_cmd == NETMAP_BDG_DELIF ? "delete":"create", name);
108 			perror(name);
109 		} else {
110 			ND("Success to %s %s", nr_cmd == NETMAP_BDG_DELIF ? "delete":"create", name);
111 		}
112 		break;
113 	case NETMAP_BDG_ATTACH:
114 	case NETMAP_BDG_DETACH:
115 		nmr.nr_flags = NR_REG_ALL_NIC;
116 		if (nr_arg && nr_arg != NETMAP_BDG_HOST) {
117 			nmr.nr_flags = NR_REG_NIC_SW;
118 			nr_arg = 0;
119 		}
120 		nmr.nr_arg1 = nr_arg;
121 		error = ioctl(fd, NIOCREGIF, &nmr);
122 		if (error == -1) {
123 			ND("Unable to %s %s to the bridge", nr_cmd ==
124 			    NETMAP_BDG_DETACH?"detach":"attach", name);
125 			perror(name);
126 		} else
127 			ND("Success to %s %s to the bridge", nr_cmd ==
128 			    NETMAP_BDG_DETACH?"detach":"attach", name);
129 		break;
130 
131 	case NETMAP_BDG_LIST:
132 		if (strlen(nmr.nr_name)) { /* name to bridge/port info */
133 			error = ioctl(fd, NIOCGINFO, &nmr);
134 			if (error) {
135 				ND("Unable to obtain info for %s", name);
136 				perror(name);
137 			} else
138 				D("%s at bridge:%d port:%d", name, nmr.nr_arg1,
139 				    nmr.nr_arg2);
140 			break;
141 		}
142 
143 		/* scan all the bridges and ports */
144 		nmr.nr_arg1 = nmr.nr_arg2 = 0;
145 		for (; !ioctl(fd, NIOCGINFO, &nmr); nmr.nr_arg2++) {
146 			D("bridge:%d port:%d %s", nmr.nr_arg1, nmr.nr_arg2,
147 			    nmr.nr_name);
148 			nmr.nr_name[0] = '\0';
149 		}
150 
151 		break;
152 
153 	case NETMAP_BDG_POLLING_ON:
154 	case NETMAP_BDG_POLLING_OFF:
155 		/* We reuse nmreq fields as follows:
156 		 *   nr_tx_slots: 0 and non-zero indicate REG_ALL_NIC
157 		 *                REG_ONE_NIC, respectively.
158 		 *   nr_rx_slots: CPU core index. This also indicates the
159 		 *                first queue in the case of REG_ONE_NIC
160 		 *   nr_tx_rings: (REG_ONE_NIC only) indicates the
161 		 *                number of CPU cores or the last queue
162 		 */
163 		nmr.nr_flags |= nmr.nr_tx_slots ?
164 			NR_REG_ONE_NIC : NR_REG_ALL_NIC;
165 		nmr.nr_ringid = nmr.nr_rx_slots;
166 		/* number of cores/rings */
167 		if (nmr.nr_flags == NR_REG_ALL_NIC)
168 			nmr.nr_arg1 = 1;
169 		else
170 			nmr.nr_arg1 = nmr.nr_tx_rings;
171 
172 		error = ioctl(fd, NIOCREGIF, &nmr);
173 		if (!error)
174 			D("polling on %s %s", nmr.nr_name,
175 				nr_cmd == NETMAP_BDG_POLLING_ON ?
176 				"started" : "stopped");
177 		else
178 			D("polling on %s %s (err %d)", nmr.nr_name,
179 				nr_cmd == NETMAP_BDG_POLLING_ON ?
180 				"couldn't start" : "couldn't stop", error);
181 		break;
182 
183 	default: /* GINFO */
184 		nmr.nr_cmd = nmr.nr_arg1 = nmr.nr_arg2 = 0;
185 		error = ioctl(fd, NIOCGINFO, &nmr);
186 		if (error) {
187 			ND("Unable to get if info for %s", name);
188 			perror(name);
189 		} else
190 			D("%s: %d queues.", name, nmr.nr_rx_rings);
191 		break;
192 	}
193 	close(fd);
194 	return error;
195 }
196 
197 static void
198 usage(int errcode)
199 {
200 	fprintf(stderr,
201 	    "Usage:\n"
202 	    "valectl arguments\n"
203 	    "\t-g interface	interface name to get info\n"
204 	    "\t-d interface	interface name to be detached\n"
205 	    "\t-a interface	interface name to be attached\n"
206 	    "\t-h interface	interface name to be attached with the host stack\n"
207 	    "\t-n interface	interface name to be created\n"
208 	    "\t-r interface	interface name to be deleted\n"
209 	    "\t-l list all or specified bridge's interfaces (default)\n"
210 	    "\t-C string ring/slot setting of an interface creating by -n\n"
211 	    "\t-p interface start polling. Additional -C x,y,z configures\n"
212 	    "\t\t x: 0 (REG_ALL_NIC) or 1 (REG_ONE_NIC),\n"
213 	    "\t\t y: CPU core id for ALL_NIC and core/ring for ONE_NIC\n"
214 	    "\t\t z: (ONE_NIC only) num of total cores/rings\n"
215 	    "\t-P interface stop polling\n"
216 	    "\t-m memid to use when creating a new interface\n");
217 	exit(errcode);
218 }
219 
220 int
221 main(int argc, char *argv[])
222 {
223 	int ch, nr_cmd = 0, nr_arg = 0;
224 	char *name = NULL, *nmr_config = NULL;
225 	int nr_arg2 = 0;
226 
227 	while ((ch = getopt(argc, argv, "d:a:h:g:l:n:r:C:p:P:m:")) != -1) {
228 		if (ch != 'C' && ch != 'm')
229 			name = optarg; /* default */
230 		switch (ch) {
231 		default:
232 			fprintf(stderr, "bad option %c %s", ch, optarg);
233 			usage(-1);
234 			break;
235 		case 'd':
236 			nr_cmd = NETMAP_BDG_DETACH;
237 			break;
238 		case 'a':
239 			nr_cmd = NETMAP_BDG_ATTACH;
240 			break;
241 		case 'h':
242 			nr_cmd = NETMAP_BDG_ATTACH;
243 			nr_arg = NETMAP_BDG_HOST;
244 			break;
245 		case 'n':
246 			nr_cmd = NETMAP_BDG_NEWIF;
247 			break;
248 		case 'r':
249 			nr_cmd = NETMAP_BDG_DELIF;
250 			break;
251 		case 'g':
252 			nr_cmd = 0;
253 			break;
254 		case 'l':
255 			nr_cmd = NETMAP_BDG_LIST;
256 			break;
257 		case 'C':
258 			nmr_config = strdup(optarg);
259 			break;
260 		case 'p':
261 			nr_cmd = NETMAP_BDG_POLLING_ON;
262 			break;
263 		case 'P':
264 			nr_cmd = NETMAP_BDG_POLLING_OFF;
265 			break;
266 		case 'm':
267 			nr_arg2 = atoi(optarg);
268 			break;
269 		}
270 	}
271 	if (optind != argc) {
272 		// fprintf(stderr, "optind %d argc %d\n", optind, argc);
273 		usage(-1);
274 	}
275 	if (argc == 1) {
276 		nr_cmd = NETMAP_BDG_LIST;
277 		name = NULL;
278 	}
279 	return bdg_ctl(name, nr_cmd, nr_arg, nmr_config, nr_arg2) ? 1 : 0;
280 }
281