xref: /dragonfly/usr.sbin/vknetd/vknetd.c (revision 479ab7f0)
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 /*
36  * vknet [-cdU] [-b bridgeN] [-p socket_path] [-t tapN] [address/cidrbits]
37  *
38  * Create a named unix-domain socket which userland vkernels can open
39  * to gain access to a local network.  All connections to the socket
40  * are bridged together and the local network can also be bridged onto
41  * a TAP interface by specifying the -t option.
42  */
43 
44 #include "vknetd.h"
45 
46 static ioinfo_t vknet_tap(const char *tapName, const char *bridgeName);
47 static int vknet_listener(const char *pathName);
48 static void vknet_acceptor(int net_fd);
49 static void *vknet_io(void *arg);
50 static int vknet_connect(const char *pathName);
51 static void vknet_monitor(int net_fd);
52 static void usage(void) __dead2;
53 static void writepid(void);
54 static void cleanup(int);
55 
56 pthread_mutex_t BridgeMutex;
57 
58 static int DebugOpt = 0;
59 static int SetAddrOpt = 0;
60 static const char *pidfile = "/var/run/vknetd.pid";
61 
62 int SecureOpt = 1;
63 struct in_addr NetAddress;
64 struct in_addr NetMask;
65 
66 int
67 main(int ac, char **av)
68 {
69 	const char *pathName = "/var/run/vknet";
70 	const char *tapName = "auto";
71 	const char *bridgeName = NULL;
72 	int net_fd;
73 	int connectOpt = 0;
74 	int c;
75 	ioinfo_t tap_info;
76 	pthread_t dummy_td;
77 
78 	while ((c = getopt(ac, av, "b:cdp:i:t:U")) != -1) {
79 		switch (c) {
80 		case 'U':
81 			SecureOpt = 0;
82 			break;
83 		case 'b':
84 			bridgeName = optarg;
85 			break;
86 		case 'd':
87 			DebugOpt = 1;
88 			break;
89 		case 'p':
90 			pathName = optarg;
91 			break;
92 		case 'i':
93 			pidfile = optarg;
94 			break;
95 		case 't':
96 			tapName = optarg;
97 			break;
98 		case 'c':
99 			connectOpt = 1;
100 			break;
101 		default:
102 			usage();
103 		}
104 	}
105 	av += optind;
106 	ac -= optind;
107 	if (ac)
108 		SetAddrOpt = 1;
109 
110 	/*
111 	 * Ignore SIGPIPE to prevent write() races against disconnecting
112 	 * clients from killing vknetd.  Should be inherited by all I/O
113 	 * threads.
114 	 */
115 	signal(SIGPIPE, SIG_IGN);
116 
117 	/*
118 	 * Special connect/debug mode
119 	 */
120 	if (connectOpt) {
121 		net_fd = vknet_connect(pathName);
122 		if (net_fd < 0) {
123 			perror("connect");
124 			exit(1);
125 		}
126 		vknet_monitor(net_fd);
127 		exit(0);
128 	}
129 
130 	/*
131 	 * In secure mode (the default), a network address/mask must be
132 	 * specified.  e.g. 10.1.0.0/16.  Any traffic going out the TAP
133 	 * interface will be filtered.
134 	 *
135 	 * If non-secure mode the network address/mask is optional.
136 	 */
137 	if (SecureOpt || SetAddrOpt) {
138 		char *str;
139 		int masklen;
140 		u_int32_t mask;
141 
142 		if (ac == 0 || strchr(av[0], '/') == NULL)
143 			usage();
144 		str = strdup(av[0]);
145 		if (inet_pton(AF_INET, strtok(str, "/"), &NetAddress) <= 0)
146 			usage();
147 		masklen = strtoul(strtok(NULL, "/"), NULL, 10);
148 		mask = (1 << (32 - masklen)) - 1;
149 		NetMask.s_addr = htonl(~mask);
150 	}
151 
152 	/*
153 	 * Normal operation, create the tap/bridge and listener.  This
154 	 * part is not threaded.
155 	 */
156 	mac_init();
157 
158 	if ((tap_info = vknet_tap(tapName, bridgeName)) == NULL) {
159 		perror("tap: ");
160 		exit(1);
161 	}
162 	if ((net_fd = vknet_listener(pathName)) < 0) {
163 		perror("listener: ");
164 		exit(1);
165 	}
166 
167 	/*
168 	 * Now make us a demon and start the threads going.
169 	 */
170 	if (DebugOpt == 0)
171 		daemon(1, 0);
172 
173 	writepid();
174 
175 	signal(SIGINT, cleanup);
176 	signal(SIGHUP, cleanup);
177 	signal(SIGTERM, cleanup);
178 	if (tap_info && tap_info->fd >= 0) {
179 		int pid = getpid();
180 		ioctl(tap_info->fd, FIOSETOWN, &pid);
181 	}
182 
183 	pthread_mutex_init(&BridgeMutex, NULL);
184 	pthread_create(&dummy_td, NULL, vknet_io, tap_info);
185 	vknet_acceptor(net_fd);
186 
187 	exit(0);
188 }
189 
190 #define TAPDEV_MINOR(x) ((int)((x) & 0xffff00ff))
191 
192 static ioinfo_t
193 vknet_tap(const char *tapName, const char *bridgeName)
194 {
195 	struct ifreq ifr;
196 	struct ifaliasreq ifra;
197 	struct stat st;
198 	char *buf = NULL;
199 	int tap_fd;
200 	int tap_unit;
201 	int s;
202 	int flags;
203 	ioinfo_t info;
204 
205 	if (strcmp(tapName, "auto") == 0) {
206 		tap_fd = open("/dev/tap", O_RDWR);
207 	} else if (strncmp(tapName, "tap", 3) == 0) {
208 		asprintf(&buf, "/dev/%s", tapName);
209 		tap_fd = open(buf, O_RDWR | O_NONBLOCK);
210 		free(buf);
211 	} else {
212 		tap_fd = open(tapName, O_RDWR | O_NONBLOCK);
213 	}
214 	if (tap_fd < 0)
215 		return(NULL);
216 
217 	/*
218 	 * Figure out the tap unit number
219 	 */
220 	if (fstat(tap_fd, &st) < 0) {
221 		close(tap_fd);
222 		return(NULL);
223 	}
224 	tap_unit = TAPDEV_MINOR(st.st_rdev);
225 
226 	/*
227 	 * Output the tap interface before detaching for any script
228 	 * that might be using vknetd.
229 	 */
230 	printf("/dev/tap%d\n", tap_unit);
231 	fflush(stdout);
232 
233 	/*
234 	 * Setup for ioctls
235 	 */
236 	fcntl(tap_fd, F_SETFL, 0);
237 	bzero(&ifr, sizeof(ifr));
238 	bzero(&ifra, sizeof(ifra));
239 	snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "tap%d", tap_unit);
240 	snprintf(ifra.ifra_name, sizeof(ifra.ifra_name), "tap%d", tap_unit);
241 
242 	s = socket(AF_INET, SOCK_DGRAM, 0);
243 
244 	/*
245 	 * Set the interface address if in Secure mode.
246 	 */
247 	if (SetAddrOpt) {
248 		struct sockaddr_in *in;
249 
250 		in = (void *)&ifra.ifra_addr;
251 		in->sin_family = AF_INET;
252 		in->sin_len = sizeof(ifra.ifra_addr);
253 		in->sin_addr = NetAddress;
254 		in = (void *)&ifra.ifra_mask;
255 		in->sin_family = AF_INET;
256 		in->sin_len = sizeof(ifra.ifra_mask);
257 		in->sin_addr = NetMask;
258 		if (ioctl(s, SIOCAIFADDR, &ifra) < 0) {
259 			perror("Unable to set address on tap interface");
260 			exit(1);
261 		}
262 	}
263 
264 	/*
265 	 * Turn up the interface
266 	 */
267 	flags = IFF_UP;
268 	if (ioctl(s, SIOCGIFFLAGS, &ifr) >= 0) {
269 		bzero(&ifr, sizeof(ifr));
270 		snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "tap%d", tap_unit);
271 		ifr.ifr_flags |= flags & 0xFFFF;
272 		ifr.ifr_flagshigh |= flags >> 16;
273 		if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) {
274 			perror("Unable to set IFF_UP on tap interface");
275 			exit(1);
276 		}
277 	}
278 
279 	if (bridgeName) {
280 		struct ifbreq ifbr;
281 		struct ifdrv ifd;
282 
283 		/*
284 		 * Create the bridge if necessary.
285 		 */
286 		bzero(&ifr, sizeof(ifr));
287 		snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", bridgeName);
288 		if (ioctl(s, SIOCIFCREATE, &ifr) < 0) {
289 			if (errno != EEXIST) {
290 				perror("Unable to create bridge interface");
291 				exit(1);
292 			}
293 		}
294 
295 		/*
296 		 * Add the tap interface to the bridge
297 		 */
298 		bzero(&ifbr, sizeof(ifbr));
299 		snprintf(ifbr.ifbr_ifsname, sizeof(ifbr.ifbr_ifsname),
300 			 "tap%d", tap_unit);
301 
302 		bzero(&ifd, sizeof(ifd));
303 		snprintf(ifd.ifd_name, sizeof(ifd.ifd_name), "%s", bridgeName);
304 		ifd.ifd_cmd = BRDGADD;
305 		ifd.ifd_len = sizeof(ifbr);
306 		ifd.ifd_data = &ifbr;
307 
308 		if (ioctl(s, SIOCSDRVSPEC, &ifd) < 0) {
309 			if (errno != EEXIST) {
310 				perror("Unable to add tap ifc to bridge!");
311 				exit(1);
312 			}
313 		}
314 	}
315 
316 	close(s);
317 	info = malloc(sizeof(*info));
318 	bzero(info, sizeof(*info));
319 	info->fd = tap_fd;
320 	info->istap = 1;
321 	return(info);
322 }
323 
324 #undef TAPDEV_MINOR
325 
326 static int
327 vknet_listener(const char *pathName)
328 {
329 	struct sockaddr_un sunx;
330 	int net_fd;
331 	int len;
332 	gid_t gid;
333 	struct group *grp;
334 
335 	/*
336 	 * Group access to our named unix domain socket.
337 	 */
338 	if ((grp = getgrnam("vknet")) == NULL) {
339 		fprintf(stderr, "The 'vknet' group must exist\n");
340 		exit(1);
341 	}
342 	gid = grp->gr_gid;
343 	endgrent();
344 
345 	/*
346 	 * Socket setup
347 	 */
348 	snprintf(sunx.sun_path, sizeof(sunx.sun_path), "%s", pathName);
349 	len = offsetof(struct sockaddr_un, sun_path[strlen(sunx.sun_path)]);
350 	++len;	/* include nul */
351 	sunx.sun_family = AF_UNIX;
352 	sunx.sun_len = len;
353 
354 	net_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
355 	if (net_fd < 0)
356 		return(-1);
357 	remove(pathName);
358 	if (bind(net_fd, (void *)&sunx, len) < 0) {
359 		close(net_fd);
360 		return(-1);
361 	}
362 	if (listen(net_fd, 1024) < 0) {
363 		close(net_fd);
364 		return(-1);
365 	}
366 	if (chown(pathName, (uid_t)-1, gid) < 0) {
367 		close(net_fd);
368 		return(-1);
369 	}
370 	if (chmod(pathName, 0660) < 0) {
371 		close(net_fd);
372 		return(-1);
373 	}
374 	return(net_fd);
375 }
376 
377 static void
378 vknet_acceptor(int net_fd)
379 {
380 	struct sockaddr_un sunx;
381 	pthread_t dummy_td;
382 	int sunx_len;
383 	int rfd;
384 	ioinfo_t info;
385 
386 	for (;;) {
387 		sunx_len = sizeof(sunx);
388 		rfd = accept(net_fd, (void *)&sunx, &sunx_len);
389 		if (rfd < 0)
390 			break;
391 		info = malloc(sizeof(*info));
392 		bzero(info, sizeof(*info));
393 		info->fd = rfd;
394 		info->istap = 0;
395 		pthread_create(&dummy_td, NULL, vknet_io, info);
396 	}
397 }
398 
399 /*
400  * This I/O thread implements the core of the bridging code.
401  */
402 static void *
403 vknet_io(void *arg)
404 {
405 	ioinfo_t info = arg;
406 	bridge_t bridge;
407 	u_int8_t *pkt;
408 	int bytes;
409 
410 	pthread_detach(pthread_self());
411 
412 	/*
413 	 * Assign as a bridge slot using our thread id.
414 	 */
415 	pthread_mutex_lock(&BridgeMutex);
416 	bridge = bridge_add(info);
417 	pthread_mutex_unlock(&BridgeMutex);
418 
419 	/*
420 	 * Read packet loop.  Writing is handled by the bridge code.
421 	 */
422 	pkt = malloc(MAXPKT);
423 	while ((bytes = read(info->fd, pkt, MAXPKT)) > 0) {
424 		pthread_mutex_lock(&BridgeMutex);
425 		bridge_packet(bridge, pkt, bytes);
426 		pthread_mutex_unlock(&BridgeMutex);
427 	}
428 
429 	/*
430 	 * Cleanup
431 	 */
432 	pthread_mutex_lock(&BridgeMutex);
433 	bridge_del(bridge);
434 	pthread_mutex_unlock(&BridgeMutex);
435 
436 	close(info->fd);
437 	free(pkt);
438 	pthread_exit(NULL);
439 }
440 
441 /*
442  * Debugging
443  */
444 static int
445 vknet_connect(const char *pathName)
446 {
447 	struct sockaddr_un sunx;
448 	int len;
449 	int net_fd;
450 
451 	snprintf(sunx.sun_path, sizeof(sunx.sun_path), "%s", pathName);
452 	len = offsetof(struct sockaddr_un, sun_path[strlen(sunx.sun_path)]);
453 	++len;	/* include nul */
454 	sunx.sun_family = AF_UNIX;
455 	sunx.sun_len = len;
456 
457 	net_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
458 	if (net_fd < 0)
459 		return(-1);
460 	if (connect(net_fd, (void *)&sunx, len) < 0) {
461 		close(net_fd);
462 		return(-1);
463 	}
464 	return(net_fd);
465 }
466 
467 static void
468 vknet_monitor(int net_fd)
469 {
470 	u_int8_t *pkt;
471 	int bytes;
472 	int i;
473 
474 	pkt = malloc(MAXPKT);
475 	while ((bytes = read(net_fd, pkt, MAXPKT)) > 0) {
476 		printf("%02x:%02x:%02x:%02x:%02x:%02x <- "
477 		       "%02x:%02x:%02x:%02x:%02x:%02x",
478 		       pkt[0], pkt[1], pkt[2], pkt[3], pkt[4], pkt[5],
479 		       pkt[6], pkt[7], pkt[8], pkt[9], pkt[10], pkt[11]);
480 		for (i = 12; i < bytes; ++i) {
481 			if (((i - 12) & 15) == 0) {
482 				printf("\n\t");
483 			}
484 			printf(" %02x", pkt[i]);
485 		}
486 		printf("\n");
487 	}
488 	free(pkt);
489 }
490 
491 /*
492  * Misc
493  */
494 static void
495 writepid(void)
496 {
497 	FILE *pf;
498 
499 	if ((pf = fopen(pidfile, "w+")) == NULL)
500 		errx(1, "Failed to create pidfile %s", pidfile);
501 
502 	if ((fprintf(pf, "%d\n", getpid())) < 1)
503 		err(1, "fprintf");
504 
505 	fclose(pf);
506 }
507 
508 static void
509 cleanup(int __unused sig)
510 {
511 	if (pidfile)
512 		unlink(pidfile);
513 }
514 
515 static void
516 usage(void)
517 {
518 	fprintf(stderr,
519 		"usage: vknet [-cdU] [-b bridgeN] [-p socket_path]\n"
520 		"             [-i pidfile] [-t tapN] [address/cidrbits]\n"
521 		"\n"
522 		"address/cidrbits must be specified in default secure mode.\n");
523 	exit(1);
524 }
525 
526