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