Add PCICAP_{ID,NEXTPTR} to avoid using magic number
[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.
33 *
1b893691 34 * $DragonFly: src/usr.sbin/vknetd/vknetd.c,v 1.2 2008/06/02 20:03:22 dillon Exp $
dbfd168b
MD
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
46static ioinfo_t vknet_tap(const char *tapName, const char *bridgeName);
47static int vknet_listener(const char *pathName);
48static void vknet_acceptor(int net_fd);
49static void *vknet_io(void *arg);
50static int vknet_connect(const char *pathName);
51static void vknet_monitor(int net_fd);
52static void usage(void);
53
54pthread_mutex_t BridgeMutex;
55
56int SecureOpt = 1;
57int DebugOpt = 0;
58struct in_addr NetAddress;
59struct in_addr NetMask;
60
61int
62main(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
162static ioinfo_t
163vknet_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
297static int
298vknet_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
348static
349void
350vknet_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 */
374static
375void *
376vknet_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 */
417static int
418vknet_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
440static void
441vknet_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 */
467static
468void
469usage(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");
1b893691 473 exit(1);
dbfd168b
MD
474}
475