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 [-r timeout[:retries]] [-C] [-b local_bridge] [-B remote_bridge] local remote
38 * vknet -S [-b local_bridge] local (server mode)
40 * Connect a SOCK_SEQPACKET socket or TUN device on the local host with
41 * a SOCK_SEQPACKET socket or TUN device on the remote host through a SSH
42 * connection. When a TUN device is specified it may be optionally bridged.
44 * This program expects packetized reads and writes on the local and remote
45 * sides and will re-block them over the SSH stream.
50 static void vknet_blastaway(ioinfo_t ios
, ioinfo_t iod
);
51 static void *vknet_stream(void *arg
);
52 static void vknet_connect(ioinfo_t ios
,
53 const char *localSide
, const char *localBridge
);
54 static void vknet_execssh(int fdin
, int fdout
, int compressOpt
,
55 const char *remoteSide
, const char *remoteBridge
);
56 static void usage(void);
58 pthread_mutex_t MasterLock
;
61 main(int ac
, char **av
)
65 const char *localBridge
= NULL
;
66 const char *remoteBridge
= NULL
;
67 const char *localSide
;
68 const char *remoteSide
;
76 while ((c
= getopt(ac
, av
, "b:B:r:CS")) != -1) {
82 remoteBridge
= optarg
;
85 timeoutOpt
= strtol(optarg
, &ptr
, 0);
86 if (ptr
&& *ptr
== ':')
87 retriesOpt
= strtol(ptr
+ 1, NULL
, 0);
103 * Local and remote arguments.
117 pthread_mutex_init(&MasterLock
, NULL
);
123 vknet_connect(&ios
, localSide
, localBridge
);
134 vknet_execssh(fds
[1], fds
[1], compressOpt
,
135 remoteSide
, remoteBridge
);
142 * Blast away, timeout/retry on failure
144 vknet_blastaway(&ios
, &iod
);
147 * Handle timeout/retries
149 if (timeoutOpt
>= 0 && retriesOpt
!= 0) {
150 printf("timeout %d retries %d\n", timeoutOpt
, retriesOpt
);
161 vknet_blastaway(ioinfo_t ios
, ioinfo_t iod
)
163 struct streaminfo stream1
;
164 struct streaminfo stream2
;
166 pthread_mutex_lock(&MasterLock
);
167 stream1
.fdin
= ios
->fdin
;
168 stream1
.fdout
= iod
->fdout
;
169 stream1
.flags
= REBLOCK_OUT
;
170 stream1
.other
= &stream2
;
171 stream2
.fdin
= iod
->fdin
;
172 stream2
.fdout
= ios
->fdout
;
173 stream2
.flags
= REBLOCK_IN
;
174 stream2
.other
= &stream1
;
175 pthread_create(&stream1
.thread
, NULL
, vknet_stream
, &stream1
);
176 pthread_create(&stream2
.thread
, NULL
, vknet_stream
, &stream2
);
177 pthread_mutex_unlock(&MasterLock
);
178 pthread_join(stream1
.thread
, NULL
);
179 pthread_join(stream2
.thread
, NULL
);
183 * Transfer packets between two descriptors
187 vknet_stream(void *arg
)
189 streaminfo_t stream
= arg
;
197 * Synchronize with master thread, then loop
199 pthread_mutex_lock(&MasterLock
);
200 pthread_mutex_unlock(&MasterLock
);
202 pkt
= malloc(MAXPKT
);
208 if (stream
->flags
& REBLOCK_IN
) {
209 bytes
= sizeof(head
);
210 for (n
= 0; n
< bytes
; n
+= r
) {
211 r
= read(stream
->fdin
, (char *)&head
+ n
,
218 if (le32toh(head
.magic
) != MAGIC
)
220 bytes
= le32toh(head
.bytes
);
221 if (bytes
<= 0 || bytes
> MAXPKT
)
223 for (n
= 0; n
< bytes
; n
+= r
) {
224 r
= read(stream
->fdin
, pkt
+ n
, bytes
- n
);
231 bytes
= read(stream
->fdin
, pkt
, MAXPKT
);
239 if (stream
->flags
& REBLOCK_OUT
) {
240 head
.magic
= htole32(MAGIC
);
241 head
.bytes
= htole32(bytes
);
242 if (write(stream
->fdout
, &head
, sizeof(head
)) != sizeof(head
))
244 if (write(stream
->fdout
, pkt
, bytes
) != bytes
)
247 if (write(stream
->fdout
, pkt
, bytes
) != bytes
)
253 close(stream
->fdout
);
254 pthread_cancel(stream
->other
->thread
);
259 * vknet_connect() - Connect to local side, optinally find or bridge the tap
263 vknet_connect(ioinfo_t io
, const char *localSide
, const char *localBridge
)
266 struct ifaliasreq ifra
;
277 if (strcmp(localSide
, "auto") == 0) {
279 asprintf(&buf
, "/dev/tap%d", i
);
280 tap_fd
= open(buf
, O_RDWR
| O_NONBLOCK
);
282 if (tap_fd
>= 0 || errno
== ENOENT
) {
287 } else if (strncmp(localSide
, "tap", 3) == 0) {
288 asprintf(&buf
, "/dev/%s", localSide
);
289 tap_fd
= open(buf
, O_RDWR
| O_NONBLOCK
);
290 tap_unit
= strtol(localSide
+ 3, NULL
, 10);
292 } else if ((tap_fd
= open(localSide
, O_RDWR
| O_NONBLOCK
)) >= 0) {
293 const char *ptr
= localSide
+ strlen(localSide
);
294 while (ptr
> localSide
&& ptr
[-1] >= '0' && ptr
[-1] <= '9')
296 tap_unit
= strtol(ptr
, NULL
, 10);
298 struct sockaddr_un sunx
;
301 snprintf(sunx
.sun_path
, sizeof(sunx
.sun_path
), "%s", localSide
);
302 len
= offsetof(struct sockaddr_un
,
303 sun_path
[strlen(sunx
.sun_path
)]);
304 ++len
; /* include nul */
305 sunx
.sun_family
= AF_UNIX
;
308 tap_fd
= socket(AF_UNIX
, SOCK_SEQPACKET
, 0);
310 if (connect(tap_fd
, (void *)&sunx
, len
) < 0) {
318 err(1, "Unable to connect to %s", localSide
);
322 fcntl(tap_fd
, F_SETFL
, 0);
327 * If this isn't a TAP device we are done.
333 * Bring up the TAP interface
335 bzero(&ifr
, sizeof(ifr
));
336 bzero(&ifra
, sizeof(ifra
));
337 snprintf(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), "tap%d", tap_unit
);
338 snprintf(ifra
.ifra_name
, sizeof(ifra
.ifra_name
), "tap%d", tap_unit
);
340 s
= socket(AF_INET
, SOCK_DGRAM
, 0);
344 * Set the interface address if in Secure mode.
347 struct sockaddr_in
*in
;
349 in
= (void *)&ifra
.ifra_addr
;
350 in
->sin_family
= AF_INET
;
351 in
->sin_len
= sizeof(ifra
.ifra_addr
);
352 in
->sin_addr
= NetAddress
;
353 in
= (void *)&ifra
.ifra_mask
;
354 in
->sin_family
= AF_INET
;
355 in
->sin_len
= sizeof(ifra
.ifra_mask
);
356 in
->sin_addr
= NetMask
;
357 if (ioctl(s
, SIOCAIFADDR
, &ifra
) < 0) {
358 perror("Unable to set address on tap interface");
365 * Turn up the interface
368 if (ioctl(s
, SIOCGIFFLAGS
, &ifr
) >= 0) {
369 bzero(&ifr
, sizeof(ifr
));
370 snprintf(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), "tap%d", tap_unit
);
371 ifr
.ifr_flags
|= flags
& 0xFFFF;
372 ifr
.ifr_flagshigh
|= flags
>> 16;
373 if (ioctl(s
, SIOCSIFFLAGS
, &ifr
) < 0) {
374 perror("Unable to set IFF_UP on tap interface");
380 * If a bridge was specified associate the tap interface with the
388 * Create the bridge if necessary.
390 bzero(&ifr
, sizeof(ifr
));
391 snprintf(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), "%s", localBridge
);
392 if (ioctl(s
, SIOCIFCREATE
, &ifr
) < 0) {
393 if (errno
!= EEXIST
) {
394 perror("Unable to create bridge interface");
401 * Add the tap interface to the bridge
403 bzero(&ifbr
, sizeof(ifbr
));
404 snprintf(ifbr
.ifbr_ifsname
, sizeof(ifbr
.ifbr_ifsname
),
407 bzero(&ifd
, sizeof(ifd
));
408 snprintf(ifd
.ifd_name
, sizeof(ifd
.ifd_name
), "%s", localBridge
);
409 ifd
.ifd_cmd
= BRDGADD
;
410 ifd
.ifd_len
= sizeof(ifbr
);
411 ifd
.ifd_data
= &ifbr
;
413 if (ioctl(s
, SIOCSDRVSPEC
, &ifd
) < 0) {
414 if (errno
!= EEXIST
) {
415 perror("Unable to add tap ifc to bridge!");
424 * Connect to the remote machine with ssh and set up a stream
427 vknet_execssh(int fdin
, int fdout
, int compressOpt
,
428 const char *remoteSide
, const char *remoteBridge
)
437 * Fork / parent returns.
439 if ((pid
= fork()) > 0)
447 * Setup stdin, stdout
459 remoteHost
= strdup(remoteSide
);
460 if ((remotePath
= strchr(remoteHost
, ':')) != NULL
) {
463 remotePath
= strdup("/dev/vknet");
473 av
[ac
++] = remoteHost
;
479 av
[ac
++] = remoteBridge
;
481 av
[ac
++] = remotePath
;
483 execv("/usr/bin/ssh", (void *)av
);
494 "vknet [-C] [-b local_bridge] [-B remote_bridge] local remote\n"
495 "vknet -S [-b local_bridge] local\n"