poll - Fix events == 0 handling for TAP and TUN, fix console spam
[dragonfly.git] / usr.sbin / vknetd / vknetd.c
blob5fd04cf3e60513790e4499777db4653733509987
1 /*
2 * Copyright (c) 2008 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
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.
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.
36 * vknet [-cdU] [-b bridgeN] [-p socket_path] [-t tapN] [address/cidrbits]
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.
44 #include "vknetd.h"
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);
56 pthread_mutex_t BridgeMutex;
58 static int DebugOpt = 0;
59 static int SetAddrOpt = 0;
60 static const char *pidfile = "/var/run/vknetd.pid";
62 int SecureOpt = 1;
63 struct in_addr NetAddress;
64 struct in_addr NetMask;
66 int
67 main(int ac, char **av)
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;
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();
105 av += optind;
106 ac -= optind;
107 if (ac)
108 SetAddrOpt = 1;
111 * Ignore SIGPIPE to prevent write() races against disconnecting
112 * clients from killing vknetd. Should be inherited by all I/O
113 * threads.
115 signal(SIGPIPE, SIG_IGN);
118 * Special connect/debug mode
120 if (connectOpt) {
121 net_fd = vknet_connect(pathName);
122 if (net_fd < 0) {
123 perror("connect");
124 exit(1);
126 vknet_monitor(net_fd);
127 exit(0);
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.
135 * If non-secure mode the network address/mask is optional.
137 if (SecureOpt || SetAddrOpt) {
138 char *str;
139 int masklen;
140 u_int32_t mask;
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);
153 * Normal operation, create the tap/bridge and listener. This
154 * part is not threaded.
156 mac_init();
158 if ((tap_info = vknet_tap(tapName, bridgeName)) == NULL) {
159 perror("tap: ");
160 exit(1);
162 if ((net_fd = vknet_listener(pathName)) < 0) {
163 perror("listener: ");
164 exit(1);
168 * Now make us a demon and start the threads going.
170 if (DebugOpt == 0)
171 daemon(1, 0);
173 writepid();
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);
183 pthread_mutex_init(&BridgeMutex, NULL);
184 pthread_create(&dummy_td, NULL, vknet_io, tap_info);
185 vknet_acceptor(net_fd);
187 exit(0);
190 #define TAPDEV_MINOR(x) ((int)((x) & 0xffff00ff))
192 static ioinfo_t
193 vknet_tap(const char *tapName, const char *bridgeName)
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;
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);
214 if (tap_fd < 0)
215 return(NULL);
218 * Figure out the tap unit number
220 if (fstat(tap_fd, &st) < 0) {
221 close(tap_fd);
222 return(NULL);
224 tap_unit = TAPDEV_MINOR(st.st_rdev);
227 * Output the tap interface before detaching for any script
228 * that might be using vknetd.
230 printf("/dev/tap%d\n", tap_unit);
231 fflush(stdout);
234 * Setup for ioctls
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);
242 s = socket(AF_INET, SOCK_DGRAM, 0);
245 * Set the interface address if in Secure mode.
247 if (SetAddrOpt) {
248 struct sockaddr_in *in;
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);
265 * Turn up the interface
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);
279 if (bridgeName) {
280 struct ifbreq ifbr;
281 struct ifdrv ifd;
284 * Create the bridge if necessary.
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);
296 * Add the tap interface to the bridge
298 bzero(&ifbr, sizeof(ifbr));
299 snprintf(ifbr.ifbr_ifsname, sizeof(ifbr.ifbr_ifsname),
300 "tap%d", tap_unit);
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;
308 if (ioctl(s, SIOCSDRVSPEC, &ifd) < 0) {
309 if (errno != EEXIST) {
310 perror("Unable to add tap ifc to bridge!");
311 exit(1);
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);
324 #undef TAPDEV_MINOR
326 static int
327 vknet_listener(const char *pathName)
329 struct sockaddr_un sunx;
330 int net_fd;
331 int len;
332 gid_t gid;
333 struct group *grp;
336 * Group access to our named unix domain socket.
338 if ((grp = getgrnam("vknet")) == NULL) {
339 fprintf(stderr, "The 'vknet' group must exist\n");
340 exit(1);
342 gid = grp->gr_gid;
343 endgrent();
346 * Socket setup
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;
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);
362 if (listen(net_fd, 1024) < 0) {
363 close(net_fd);
364 return(-1);
366 if (chown(pathName, (uid_t)-1, gid) < 0) {
367 close(net_fd);
368 return(-1);
370 if (chmod(pathName, 0660) < 0) {
371 close(net_fd);
372 return(-1);
374 return(net_fd);
377 static void
378 vknet_acceptor(int net_fd)
380 struct sockaddr_un sunx;
381 pthread_t dummy_td;
382 int sunx_len;
383 int rfd;
384 ioinfo_t info;
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);
400 * This I/O thread implements the core of the bridging code.
402 static void *
403 vknet_io(void *arg)
405 ioinfo_t info = arg;
406 bridge_t bridge;
407 u_int8_t *pkt;
408 int bytes;
410 pthread_detach(pthread_self());
413 * Assign as a bridge slot using our thread id.
415 pthread_mutex_lock(&BridgeMutex);
416 bridge = bridge_add(info);
417 pthread_mutex_unlock(&BridgeMutex);
420 * Read packet loop. Writing is handled by the bridge code.
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);
430 * Cleanup
432 pthread_mutex_lock(&BridgeMutex);
433 bridge_del(bridge);
434 pthread_mutex_unlock(&BridgeMutex);
436 close(info->fd);
437 free(pkt);
438 pthread_exit(NULL);
442 * Debugging
444 static int
445 vknet_connect(const char *pathName)
447 struct sockaddr_un sunx;
448 int len;
449 int net_fd;
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;
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);
464 return(net_fd);
467 static void
468 vknet_monitor(int net_fd)
470 u_int8_t *pkt;
471 int bytes;
472 int i;
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");
484 printf(" %02x", pkt[i]);
486 printf("\n");
488 free(pkt);
492 * Misc
494 static void
495 writepid(void)
497 FILE *pf;
499 if ((pf = fopen(pidfile, "w+")) == NULL)
500 errx(1, "Failed to create pidfile %s", pidfile);
502 if ((fprintf(pf, "%d\n", getpid())) < 1)
503 err(1, "fprintf");
505 fclose(pf);
508 static void
509 cleanup(int __unused sig)
511 if (pidfile)
512 unlink(pidfile);
515 static void
516 usage(void)
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);