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