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