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
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
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
34 * $DragonFly: src/usr.bin/vknet/vknet.c,v 1.1 2008/05/27 23:26:38 dillon Exp $
37 * vknet [-C] [-b local-bridge] [-B remote-bridge] [-r delay[:retries]]
38 * local-spec [user@]remote[:remote-spec]
39 * vknet -S [-b local-bridge] local-spec (server mode)
41 * Connect a SOCK_SEQPACKET socket or TUN device on the local host with
42 * a SOCK_SEQPACKET socket or TUN device on the remote host through a SSH
43 * connection. When a TUN device is specified it may be optionally bridged.
45 * This program expects packetized reads and writes on the local and remote
46 * sides and will re-block them over the SSH stream.
51 static void vknet_blastaway(ioinfo_t ios, ioinfo_t iod);
52 static void *vknet_stream(void *arg);
53 static void vknet_connect(ioinfo_t ios,
54 const char *localSide, const char *localBridge);
55 static void vknet_execssh(int fdin, int fdout, int compressOpt,
56 const char *remoteSide, const char *remoteBridge);
57 static void usage(void);
59 pthread_mutex_t MasterLock;
62 main(int ac, char **av)
66 const char *localBridge = NULL;
67 const char *remoteBridge = NULL;
68 const char *localSide;
69 const char *remoteSide;
77 while ((c = getopt(ac, av, "b:B:r:CS")) != -1) {
83 remoteBridge = optarg;
86 timeoutOpt = strtol(optarg, &ptr, 0);
87 if (ptr && *ptr == ':')
88 retriesOpt = strtol(ptr + 1, NULL, 0);
104 * Local and remote arguments.
118 pthread_mutex_init(&MasterLock, NULL);
124 vknet_connect(&ios, localSide, localBridge);
135 vknet_execssh(fds[1], fds[1], compressOpt,
136 remoteSide, remoteBridge);
143 * Blast away, timeout/retry on failure
145 vknet_blastaway(&ios, &iod);
148 * Handle timeout/retries
150 if (timeoutOpt >= 0 && retriesOpt != 0) {
151 printf("timeout %d retries %d\n", timeoutOpt, retriesOpt);
162 vknet_blastaway(ioinfo_t ios, ioinfo_t iod)
164 struct streaminfo stream1;
165 struct streaminfo stream2;
167 pthread_mutex_lock(&MasterLock);
168 stream1.fdin = ios->fdin;
169 stream1.fdout = iod->fdout;
170 stream1.flags = REBLOCK_OUT;
171 stream1.other = &stream2;
172 stream2.fdin = iod->fdin;
173 stream2.fdout = ios->fdout;
174 stream2.flags = REBLOCK_IN;
175 stream2.other = &stream1;
176 pthread_create(&stream1.thread, NULL, vknet_stream, &stream1);
177 pthread_create(&stream2.thread, NULL, vknet_stream, &stream2);
178 pthread_mutex_unlock(&MasterLock);
179 pthread_join(stream1.thread, NULL);
180 pthread_join(stream2.thread, NULL);
184 * Transfer packets between two descriptors
188 vknet_stream(void *arg)
190 streaminfo_t stream = arg;
198 * Synchronize with master thread, then loop
200 pthread_mutex_lock(&MasterLock);
201 pthread_mutex_unlock(&MasterLock);
203 pkt = malloc(MAXPKT);
209 if (stream->flags & REBLOCK_IN) {
210 bytes = sizeof(head);
211 for (n = 0; n < bytes; n += r) {
212 r = read(stream->fdin, (char *)&head + n,
219 if (le32toh(head.magic) != MAGIC)
221 bytes = le32toh(head.bytes);
222 if (bytes <= 0 || bytes > MAXPKT)
224 for (n = 0; n < bytes; n += r) {
225 r = read(stream->fdin, pkt + n, bytes - n);
232 bytes = read(stream->fdin, pkt, MAXPKT);
240 if (stream->flags & REBLOCK_OUT) {
241 head.magic = htole32(MAGIC);
242 head.bytes = htole32(bytes);
243 if (write(stream->fdout, &head, sizeof(head)) != sizeof(head))
245 if (write(stream->fdout, pkt, bytes) != bytes)
248 if (write(stream->fdout, pkt, bytes) != bytes)
254 close(stream->fdout);
255 pthread_cancel(stream->other->thread);
260 * vknet_connect() - Connect to local side, optionally find or bridge the tap
264 vknet_connect(ioinfo_t io, const char *localSide, const char *localBridge)
267 struct ifaliasreq ifra;
278 if (strcmp(localSide, "auto") == 0) {
280 asprintf(&buf, "/dev/tap%d", i);
281 tap_fd = open(buf, O_RDWR | O_NONBLOCK);
283 if (tap_fd >= 0 || errno == ENOENT) {
288 } else if (strncmp(localSide, "tap", 3) == 0) {
289 asprintf(&buf, "/dev/%s", localSide);
290 tap_fd = open(buf, O_RDWR | O_NONBLOCK);
291 tap_unit = strtol(localSide + 3, NULL, 10);
293 } else if ((tap_fd = open(localSide, O_RDWR | O_NONBLOCK)) >= 0) {
294 const char *ptr = localSide + strlen(localSide);
295 while (ptr > localSide && ptr[-1] >= '0' && ptr[-1] <= '9')
297 tap_unit = strtol(ptr, NULL, 10);
299 struct sockaddr_un sunx;
302 snprintf(sunx.sun_path, sizeof(sunx.sun_path), "%s", localSide);
303 len = offsetof(struct sockaddr_un,
304 sun_path[strlen(sunx.sun_path)]);
305 ++len; /* include nul */
306 sunx.sun_family = AF_UNIX;
309 tap_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
311 if (connect(tap_fd, (void *)&sunx, len) < 0) {
319 err(1, "Unable to connect to %s", localSide);
323 fcntl(tap_fd, F_SETFL, 0);
328 * If this isn't a TAP device we are done.
334 * Bring up the TAP interface
336 bzero(&ifr, sizeof(ifr));
337 bzero(&ifra, sizeof(ifra));
338 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "tap%d", tap_unit);
339 snprintf(ifra.ifra_name, sizeof(ifra.ifra_name), "tap%d", tap_unit);
341 s = socket(AF_INET, SOCK_DGRAM, 0);
345 * Set the interface address if in Secure mode.
348 struct sockaddr_in *in;
350 in = (void *)&ifra.ifra_addr;
351 in->sin_family = AF_INET;
352 in->sin_len = sizeof(ifra.ifra_addr);
353 in->sin_addr = NetAddress;
354 in = (void *)&ifra.ifra_mask;
355 in->sin_family = AF_INET;
356 in->sin_len = sizeof(ifra.ifra_mask);
357 in->sin_addr = NetMask;
358 if (ioctl(s, SIOCAIFADDR, &ifra) < 0) {
359 perror("Unable to set address on tap interface");
366 * Turn up the interface
369 if (ioctl(s, SIOCGIFFLAGS, &ifr) >= 0) {
370 bzero(&ifr, sizeof(ifr));
371 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "tap%d", tap_unit);
372 ifr.ifr_flags |= flags & 0xFFFF;
373 ifr.ifr_flagshigh |= flags >> 16;
374 if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) {
375 perror("Unable to set IFF_UP on tap interface");
381 * If a bridge was specified associate the tap interface with the
389 * Create the bridge if necessary.
391 bzero(&ifr, sizeof(ifr));
392 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", localBridge);
393 if (ioctl(s, SIOCIFCREATE, &ifr) < 0) {
394 if (errno != EEXIST) {
395 perror("Unable to create bridge interface");
402 * Add the tap interface to the bridge
404 bzero(&ifbr, sizeof(ifbr));
405 snprintf(ifbr.ifbr_ifsname, sizeof(ifbr.ifbr_ifsname),
408 bzero(&ifd, sizeof(ifd));
409 snprintf(ifd.ifd_name, sizeof(ifd.ifd_name), "%s", localBridge);
410 ifd.ifd_cmd = BRDGADD;
411 ifd.ifd_len = sizeof(ifbr);
412 ifd.ifd_data = &ifbr;
414 if (ioctl(s, SIOCSDRVSPEC, &ifd) < 0) {
415 if (errno != EEXIST) {
416 perror("Unable to add tap ifc to bridge!");
425 * Connect to the remote machine with ssh and set up a stream
428 vknet_execssh(int fdin, int fdout, int compressOpt,
429 const char *remoteSide, const char *remoteBridge)
438 * Fork / parent returns.
440 if ((pid = fork()) > 0)
448 * Setup stdin, stdout
460 remoteHost = strdup(remoteSide);
461 if ((remotePath = strchr(remoteHost, ':')) != NULL) {
464 remotePath = strdup("/var/run/vknet");
474 av[ac++] = remoteHost;
480 av[ac++] = remoteBridge;
482 av[ac++] = remotePath;
484 execv("/usr/bin/ssh", (void *)av);
495 "vknet [-C] [-b local-bridge] [-B remote-bridge] [-r delay[:retries]]\n"
496 " local-spec [user@]remote[:remote-spec]\n"
497 "vknet -S [-b local-bridge] local-spec\n"