xref: /dragonfly/usr.sbin/vknetd/vknetd.c (revision fcf53d9b)
1 /*
2  * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 /*
35  * vknet [-cdU] [-b bridgeN] [-p socket_path] [-t tapN] [address/cidrbits]
36  *
37  * Create a named unix-domain socket which userland vkernels can open
38  * to gain access to a local network.  All connections to the socket
39  * are bridged together and the local network can also be bridged onto
40  * a TAP interface by specifying the -t option.
41  */
42 #include "vknetd.h"
43 
44 static ioinfo_t vknet_tap(const char *tapName, const char *bridgeName);
45 static int vknet_listener(const char *pathName);
46 static void vknet_acceptor(int net_fd);
47 static void *vknet_io(void *arg);
48 static int vknet_connect(const char *pathName);
49 static void vknet_monitor(int net_fd);
50 static void usage(void);
51 
52 pthread_mutex_t BridgeMutex;
53 
54 int SecureOpt = 1;
55 int DebugOpt = 0;
56 int SetAddrOpt = 0;
57 struct in_addr NetAddress;
58 struct in_addr NetMask;
59 
60 int
61 main(int ac, char **av)
62 {
63 	const char *pathName = "/var/run/vknet";
64 	const char *tapName = "auto";
65 	const char *bridgeName = NULL;
66 	int net_fd;
67 	int connectOpt = 0;
68 	int c;
69 	ioinfo_t tap_info;
70 	pthread_t dummy_td;
71 
72 	while ((c = getopt(ac, av, "b:cdp:t:U")) != -1) {
73 		switch (c) {
74 		case 'U':
75 			SecureOpt = 0;
76 			break;
77 		case 'b':
78 			bridgeName = optarg;
79 			break;
80 		case 'd':
81 			DebugOpt = 1;
82 			break;
83 		case 'p':
84 			pathName = optarg;
85 			break;
86 		case 't':
87 			tapName = optarg;
88 			break;
89 		case 'c':
90 			connectOpt = 1;
91 			break;
92 		default:
93 			usage();
94 		}
95 	}
96 	av += optind;
97 	ac -= optind;
98 	if (ac)
99 		SetAddrOpt = 1;
100 
101 	/*
102 	 * Special connect/debug mode
103 	 */
104 	if (connectOpt) {
105 		net_fd = vknet_connect(pathName);
106 		if (net_fd < 0) {
107 			perror("connect");
108 			exit(1);
109 		}
110 		vknet_monitor(net_fd);
111 		exit(0);
112 	}
113 
114 	/*
115 	 * In secure mode (the default), a network address/mask must be
116 	 * specified.  e.g. 10.1.0.0/16.  Any traffic going out the TAP
117 	 * interface will be filtered.
118 	 *
119 	 * If non-secure mode the network address/mask is optional.
120 	 */
121 	if (SecureOpt || SetAddrOpt) {
122 		char *str;
123 		int masklen;
124 		u_int32_t mask;
125 
126 		if (ac == 0 || strchr(av[0], '/') == NULL)
127 			usage();
128 		str = strdup(av[0]);
129 		if (inet_pton(AF_INET, strtok(str, "/"), &NetAddress) <= 0)
130 			usage();
131 		masklen = strtoul(strtok(NULL, "/"), NULL, 10);
132 		mask = (1 << (32 - masklen)) - 1;
133 		NetMask.s_addr = htonl(~mask);
134 	}
135 
136 	/*
137 	 * Normal operation, create the tap/bridge and listener.  This
138 	 * part is not threaded.
139 	 */
140 	mac_init();
141 
142 	if ((tap_info = vknet_tap(tapName, bridgeName)) == NULL) {
143 		perror("tap: ");
144 		exit(1);
145 	}
146 	if ((net_fd = vknet_listener(pathName)) < 0) {
147 		perror("listener: ");
148 		exit(1);
149 	}
150 
151 	/*
152 	 * Now make us a demon and start the threads going.
153 	 */
154 	if (DebugOpt == 0)
155 		daemon(1, 0);
156 	pthread_mutex_init(&BridgeMutex, NULL);
157 	pthread_create(&dummy_td, NULL, vknet_io, tap_info);
158 	vknet_acceptor(net_fd);
159 
160 	exit(0);
161 }
162 
163 #define TAPDEV_MINOR(x) ((int)((x) & 0xffff00ff))
164 
165 static ioinfo_t
166 vknet_tap(const char *tapName, const char *bridgeName)
167 {
168 	struct ifreq ifr;
169 	struct ifaliasreq ifra;
170 	struct stat st;
171 	char *buf = NULL;
172 	int tap_fd;
173 	int tap_unit;
174 	int i;
175 	int s;
176 	int flags;
177 	ioinfo_t info;
178 
179 	if (strcmp(tapName, "auto") == 0) {
180 		for (i = 0; ; ++i) {
181 			asprintf(&buf, "/dev/tap%d", i);
182 			tap_fd = open(buf, O_RDWR | O_NONBLOCK);
183 			free(buf);
184 			if (tap_fd >= 0 || errno == ENOENT)
185 				break;
186 		}
187 	} else if (strncmp(tapName, "tap", 3) == 0) {
188 		asprintf(&buf, "/dev/%s", tapName);
189 		tap_fd = open(buf, O_RDWR | O_NONBLOCK);
190 		free(buf);
191 	} else {
192 		tap_fd = open(tapName, O_RDWR | O_NONBLOCK);
193 	}
194 	if (tap_fd < 0)
195 		return(NULL);
196 
197 	/*
198 	 * Figure out the tap unit number
199 	 */
200 	if (fstat(tap_fd, &st) < 0) {
201 		close(tap_fd);
202 		return(NULL);
203 	}
204 	tap_unit = TAPDEV_MINOR(st.st_rdev);
205 
206 	/*
207 	 * Setup for ioctls
208 	 */
209 	fcntl(tap_fd, F_SETFL, 0);
210 	bzero(&ifr, sizeof(ifr));
211 	bzero(&ifra, sizeof(ifra));
212 	snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "tap%d", tap_unit);
213 	snprintf(ifra.ifra_name, sizeof(ifra.ifra_name), "tap%d", tap_unit);
214 
215 	s = socket(AF_INET, SOCK_DGRAM, 0);
216 
217 	/*
218 	 * Set the interface address if in Secure mode.
219 	 */
220 	if (SetAddrOpt) {
221 		struct sockaddr_in *in;
222 
223 		in = (void *)&ifra.ifra_addr;
224 		in->sin_family = AF_INET;
225 		in->sin_len = sizeof(ifra.ifra_addr);
226 		in->sin_addr = NetAddress;
227 		in = (void *)&ifra.ifra_mask;
228 		in->sin_family = AF_INET;
229 		in->sin_len = sizeof(ifra.ifra_mask);
230 		in->sin_addr = NetMask;
231 		if (ioctl(s, SIOCAIFADDR, &ifra) < 0) {
232 			perror("Unable to set address on tap interface");
233 			exit(1);
234 		}
235 	}
236 
237 	/*
238 	 * Turn up the interface
239 	 */
240 	flags = IFF_UP;
241 	if (ioctl(s, SIOCGIFFLAGS, &ifr) >= 0) {
242 		bzero(&ifr, sizeof(ifr));
243 		snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "tap%d", tap_unit);
244 		ifr.ifr_flags |= flags & 0xFFFF;
245 		ifr.ifr_flagshigh |= flags >> 16;
246 		if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) {
247 			perror("Unable to set IFF_UP on tap interface");
248 			exit(1);
249 		}
250 	}
251 
252 	if (bridgeName) {
253 		struct ifbreq ifbr;
254 		struct ifdrv ifd;
255 
256 		/*
257 		 * Create the bridge if necessary.
258 		 */
259 		bzero(&ifr, sizeof(ifr));
260 		snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", bridgeName);
261 		if (ioctl(s, SIOCIFCREATE, &ifr) < 0) {
262 			if (errno != EEXIST) {
263 				perror("Unable to create bridge interface");
264 				exit(1);
265 			}
266 		}
267 
268 
269 		/*
270 		 * Add the tap interface to the bridge
271 		 */
272 		bzero(&ifbr, sizeof(ifbr));
273 		snprintf(ifbr.ifbr_ifsname, sizeof(ifbr.ifbr_ifsname),
274 			 "tap%d", tap_unit);
275 
276 		bzero(&ifd, sizeof(ifd));
277 		snprintf(ifd.ifd_name, sizeof(ifd.ifd_name), "%s", bridgeName);
278 		ifd.ifd_cmd = BRDGADD;
279 		ifd.ifd_len = sizeof(ifbr);
280 		ifd.ifd_data = &ifbr;
281 
282 		if (ioctl(s, SIOCSDRVSPEC, &ifd) < 0) {
283 			if (errno != EEXIST) {
284 				perror("Unable to add tap ifc to bridge!");
285 				exit(1);
286 			}
287 		}
288 	}
289 
290 	close(s);
291 	info = malloc(sizeof(*info));
292 	bzero(info, sizeof(*info));
293 	info->fd = tap_fd;
294 	info->istap = 1;
295 	return(info);
296 }
297 
298 #undef TAPDEV_MINOR
299 
300 static int
301 vknet_listener(const char *pathName)
302 {
303 	struct sockaddr_un sunx;
304 	int net_fd;
305 	int len;
306 	gid_t gid;
307 	struct group *grp;
308 
309 	/*
310 	 * Group access to our named unix domain socket.
311 	 */
312 	if ((grp = getgrnam("vknet")) == NULL) {
313 		fprintf(stderr, "The 'vknet' group must exist\n");
314 		exit(1);
315 	}
316 	gid = grp->gr_gid;
317 	endgrent();
318 
319 	/*
320 	 * Socket setup
321 	 */
322 	snprintf(sunx.sun_path, sizeof(sunx.sun_path), "%s", pathName);
323 	len = offsetof(struct sockaddr_un, sun_path[strlen(sunx.sun_path)]);
324 	++len;	/* include nul */
325 	sunx.sun_family = AF_UNIX;
326 	sunx.sun_len = len;
327 
328 	net_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
329 	if (net_fd < 0)
330 		return(-1);
331 	remove(pathName);
332 	if (bind(net_fd, (void *)&sunx, len) < 0) {
333 		close(net_fd);
334 		return(-1);
335 	}
336 	if (listen(net_fd, 1024) < 0) {
337 		close(net_fd);
338 		return(-1);
339 	}
340 	if (chown(pathName, (uid_t)-1, gid) < 0) {
341 		close(net_fd);
342 		return(-1);
343 	}
344 	if (chmod(pathName, 0660) < 0) {
345 		close(net_fd);
346 		return(-1);
347 	}
348 	return(net_fd);
349 }
350 
351 static
352 void
353 vknet_acceptor(int net_fd)
354 {
355 	struct sockaddr_un sunx;
356 	pthread_t dummy_td;
357 	int sunx_len;
358 	int rfd;
359 	ioinfo_t info;
360 
361 	for (;;) {
362 		sunx_len = sizeof(sunx);
363 		rfd = accept(net_fd, (void *)&sunx, &sunx_len);
364 		if (rfd < 0)
365 			break;
366 		info = malloc(sizeof(*info));
367 		bzero(info, sizeof(*info));
368 		info->fd = rfd;
369 		info->istap = 0;
370 		pthread_create(&dummy_td, NULL, vknet_io, info);
371 	}
372 }
373 
374 /*
375  * This I/O thread implements the core of the bridging code.
376  */
377 static
378 void *
379 vknet_io(void *arg)
380 {
381 	ioinfo_t info = arg;
382 	bridge_t bridge;
383 	u_int8_t *pkt;
384 	int bytes;
385 
386 	pthread_detach(pthread_self());
387 
388 	/*
389 	 * Assign as a bridge slot using our thread id.
390 	 */
391 	pthread_mutex_lock(&BridgeMutex);
392 	bridge = bridge_add(info);
393 	pthread_mutex_unlock(&BridgeMutex);
394 
395 	/*
396 	 * Read packet loop.  Writing is handled by the bridge code.
397 	 */
398 	pkt = malloc(MAXPKT);
399 	while ((bytes = read(info->fd, pkt, MAXPKT)) > 0) {
400 		pthread_mutex_lock(&BridgeMutex);
401 		bridge_packet(bridge, pkt, bytes);
402 		pthread_mutex_unlock(&BridgeMutex);
403 	}
404 
405 	/*
406 	 * Cleanup
407 	 */
408 	pthread_mutex_lock(&BridgeMutex);
409 	bridge_del(bridge);
410 	pthread_mutex_unlock(&BridgeMutex);
411 
412 	close(info->fd);
413 	free(pkt);
414 	pthread_exit(NULL);
415 }
416 
417 /*
418  * Debugging
419  */
420 static int
421 vknet_connect(const char *pathName)
422 {
423 	struct sockaddr_un sunx;
424 	int len;
425 	int net_fd;
426 
427 	snprintf(sunx.sun_path, sizeof(sunx.sun_path), "%s", pathName);
428 	len = offsetof(struct sockaddr_un, sun_path[strlen(sunx.sun_path)]);
429 	++len;	/* include nul */
430 	sunx.sun_family = AF_UNIX;
431 	sunx.sun_len = len;
432 
433 	net_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
434 	if (net_fd < 0)
435 		return(-1);
436 	if (connect(net_fd, (void *)&sunx, len) < 0) {
437 		close(net_fd);
438 		return(-1);
439 	}
440 	return(net_fd);
441 }
442 
443 static void
444 vknet_monitor(int net_fd)
445 {
446 	u_int8_t *pkt;
447 	int bytes;
448 	int i;
449 
450 	pkt = malloc(MAXPKT);
451 	while ((bytes = read(net_fd, pkt, MAXPKT)) > 0) {
452 		printf("%02x:%02x:%02x:%02x:%02x:%02x <- "
453 		       "%02x:%02x:%02x:%02x:%02x:%02x",
454 		       pkt[0], pkt[1], pkt[2], pkt[3], pkt[4], pkt[5],
455 		       pkt[6], pkt[7], pkt[8], pkt[9], pkt[10], pkt[11]);
456 		for (i = 12; i < bytes; ++i) {
457 			if (((i - 12) & 15) == 0) {
458 				printf("\n\t");
459 			}
460 			printf(" %02x", pkt[i]);
461 		}
462 		printf("\n");
463 	}
464 	free(pkt);
465 }
466 
467 /*
468  * Misc
469  */
470 static
471 void
472 usage(void)
473 {
474 	fprintf(stderr, "usage: vknet [-cdU] [-b bridgeN] [-p socket_path] [-t tapN] [address/cidrbits]\n");
475 	fprintf(stderr, "address/cidrbits must be specified in default secure mode.\n");
476 	exit(1);
477 }
478 
479