Implement a new utility called vknet. This utility interconnects the
[dragonfly.git] / usr.sbin / vknetd / vknetd.c
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.1 2008/05/27 01:58:01 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 }
474