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