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
35 * vknet [-C] [-b local-bridge] [-B remote-bridge] [-r delay[:retries]]
36 * local-spec [user@]remote[:remote-spec]
37 * vknet -S [-b local-bridge] local-spec (server mode)
39 * Connect a SOCK_SEQPACKET socket or TUN device on the local host with
40 * a SOCK_SEQPACKET socket or TUN device on the remote host through a SSH
41 * connection. When a TUN device is specified it may be optionally bridged.
43 * This program expects packetized reads and writes on the local and remote
44 * sides and will re-block them over the SSH stream.
49 static void vknet_blastaway(ioinfo_t ios
, ioinfo_t iod
);
50 static void *vknet_stream(void *arg
);
51 static void vknet_connect(ioinfo_t ios
,
52 const char *localSide
, const char *localBridge
);
53 static pid_t
vknet_execssh(int fdin
, int fdout
, int compressOpt
,
54 const char *remoteSide
, const char *remoteBridge
);
55 static void usage(void);
57 static pthread_mutex_t MasterLock
;
60 main(int ac
, char **av
)
64 const char *localBridge
= NULL
;
65 const char *remoteBridge
= NULL
;
66 const char *localSide
;
67 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 sshpid
= vknet_execssh(fds
[1], fds
[1], compressOpt
,
136 remoteSide
, remoteBridge
);
143 * Blast away, timeout/retry on failure
145 vknet_blastaway(&ios
, &iod
);
148 * Terminate child process
151 if (kill(sshpid
, SIGTERM
) != 0)
153 while ((p
= waitpid(sshpid
, NULL
, 0)) != sshpid
) {
154 if (p
< 0 && errno
!= EINTR
)
161 * Handle timeout/retries
163 if (timeoutOpt
>= 0 && retriesOpt
!= 0) {
164 printf("timeout %d retries %d\n", timeoutOpt
, retriesOpt
);
175 vknet_blastaway(ioinfo_t ios
, ioinfo_t iod
)
177 struct streaminfo stream1
;
178 struct streaminfo stream2
;
180 pthread_mutex_lock(&MasterLock
);
181 stream1
.fdin
= ios
->fdin
;
182 stream1
.fdout
= iod
->fdout
;
183 stream1
.flags
= REBLOCK_OUT
;
184 stream1
.other
= &stream2
;
185 stream2
.fdin
= iod
->fdin
;
186 stream2
.fdout
= ios
->fdout
;
187 stream2
.flags
= REBLOCK_IN
;
188 stream2
.other
= &stream1
;
189 pthread_create(&stream1
.thread
, NULL
, vknet_stream
, &stream1
);
190 pthread_create(&stream2
.thread
, NULL
, vknet_stream
, &stream2
);
191 pthread_mutex_unlock(&MasterLock
);
192 pthread_join(stream1
.thread
, NULL
);
193 pthread_join(stream2
.thread
, NULL
);
197 * Transfer packets between two descriptors
201 vknet_stream(void *arg
)
203 streaminfo_t stream
= arg
;
211 * Synchronize with master thread, then loop
213 pthread_mutex_lock(&MasterLock
);
214 pthread_mutex_unlock(&MasterLock
);
216 pkt
= malloc(MAXPKT
);
222 if (stream
->flags
& REBLOCK_IN
) {
223 bytes
= sizeof(head
);
224 for (n
= 0; n
< bytes
; n
+= r
) {
225 r
= read(stream
->fdin
, (char *)&head
+ n
,
232 if (le32toh(head
.magic
) != MAGIC
)
234 bytes
= le32toh(head
.bytes
);
235 if (bytes
<= 0 || bytes
> MAXPKT
)
237 for (n
= 0; n
< bytes
; n
+= r
) {
238 r
= read(stream
->fdin
, pkt
+ n
, bytes
- n
);
245 bytes
= read(stream
->fdin
, pkt
, MAXPKT
);
253 if (stream
->flags
& REBLOCK_OUT
) {
254 head
.magic
= htole32(MAGIC
);
255 head
.bytes
= htole32(bytes
);
256 if (write(stream
->fdout
, &head
, sizeof(head
)) != sizeof(head
))
258 if (write(stream
->fdout
, pkt
, bytes
) != bytes
)
261 if (write(stream
->fdout
, pkt
, bytes
) != bytes
)
267 close(stream
->fdout
);
268 pthread_cancel(stream
->other
->thread
);
273 * vknet_connect() - Connect to local side, optionally find or bridge the tap
277 vknet_connect(ioinfo_t io
, const char *localSide
, const char *localBridge
)
280 struct ifaliasreq ifra
;
291 if (strcmp(localSide
, "auto") == 0) {
293 asprintf(&buf
, "/dev/tap%d", i
);
294 tap_fd
= open(buf
, O_RDWR
| O_NONBLOCK
);
296 if (tap_fd
>= 0 || errno
== ENOENT
) {
301 } else if (strncmp(localSide
, "tap", 3) == 0) {
302 asprintf(&buf
, "/dev/%s", localSide
);
303 tap_fd
= open(buf
, O_RDWR
| O_NONBLOCK
);
304 tap_unit
= strtol(localSide
+ 3, NULL
, 10);
306 } else if ((tap_fd
= open(localSide
, O_RDWR
| O_NONBLOCK
)) >= 0) {
307 const char *ptr
= localSide
+ strlen(localSide
);
308 while (ptr
> localSide
&& ptr
[-1] >= '0' && ptr
[-1] <= '9')
310 tap_unit
= strtol(ptr
, NULL
, 10);
312 struct sockaddr_un sunx
;
315 snprintf(sunx
.sun_path
, sizeof(sunx
.sun_path
), "%s", localSide
);
316 len
= offsetof(struct sockaddr_un
,
317 sun_path
[strlen(sunx
.sun_path
)]);
318 ++len
; /* include nul */
319 sunx
.sun_family
= AF_UNIX
;
322 tap_fd
= socket(AF_UNIX
, SOCK_SEQPACKET
, 0);
324 if (connect(tap_fd
, (void *)&sunx
, len
) < 0) {
332 err(1, "Unable to connect to %s", localSide
);
336 fcntl(tap_fd
, F_SETFL
, 0);
341 * If this isn't a TAP device we are done.
347 * Bring up the TAP interface
349 bzero(&ifr
, sizeof(ifr
));
350 bzero(&ifra
, sizeof(ifra
));
351 snprintf(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), "tap%d", tap_unit
);
352 snprintf(ifra
.ifra_name
, sizeof(ifra
.ifra_name
), "tap%d", tap_unit
);
354 s
= socket(AF_INET
, SOCK_DGRAM
, 0);
358 * Set the interface address if in Secure mode.
361 struct sockaddr_in
*in
;
363 in
= (void *)&ifra
.ifra_addr
;
364 in
->sin_family
= AF_INET
;
365 in
->sin_len
= sizeof(ifra
.ifra_addr
);
366 in
->sin_addr
= NetAddress
;
367 in
= (void *)&ifra
.ifra_mask
;
368 in
->sin_family
= AF_INET
;
369 in
->sin_len
= sizeof(ifra
.ifra_mask
);
370 in
->sin_addr
= NetMask
;
371 if (ioctl(s
, SIOCAIFADDR
, &ifra
) < 0) {
372 perror("Unable to set address on tap interface");
379 * Turn up the interface
382 if (ioctl(s
, SIOCGIFFLAGS
, &ifr
) >= 0) {
383 bzero(&ifr
, sizeof(ifr
));
384 snprintf(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), "tap%d", tap_unit
);
385 ifr
.ifr_flags
|= flags
& 0xFFFF;
386 ifr
.ifr_flagshigh
|= flags
>> 16;
387 if (ioctl(s
, SIOCSIFFLAGS
, &ifr
) < 0) {
388 perror("Unable to set IFF_UP on tap interface");
394 * If a bridge was specified associate the tap interface with the
402 * Create the bridge if necessary.
404 bzero(&ifr
, sizeof(ifr
));
405 snprintf(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), "%s", localBridge
);
406 if (ioctl(s
, SIOCIFCREATE
, &ifr
) < 0) {
407 if (errno
!= EEXIST
) {
408 perror("Unable to create bridge interface");
415 * Add the tap interface to the bridge
417 bzero(&ifbr
, sizeof(ifbr
));
418 snprintf(ifbr
.ifbr_ifsname
, sizeof(ifbr
.ifbr_ifsname
),
421 bzero(&ifd
, sizeof(ifd
));
422 snprintf(ifd
.ifd_name
, sizeof(ifd
.ifd_name
), "%s", localBridge
);
423 ifd
.ifd_cmd
= BRDGADD
;
424 ifd
.ifd_len
= sizeof(ifbr
);
425 ifd
.ifd_data
= &ifbr
;
427 if (ioctl(s
, SIOCSDRVSPEC
, &ifd
) < 0) {
428 if (errno
!= EEXIST
) {
429 perror("Unable to add tap ifc to bridge!");
438 * Connect to the remote machine with ssh and set up a stream
441 vknet_execssh(int fdin
, int fdout
, int compressOpt
,
442 const char *remoteSide
, const char *remoteBridge
)
451 * Fork / parent returns.
453 if ((pid
= fork()) > 0)
461 * Setup stdin, stdout
473 remoteHost
= strdup(remoteSide
);
474 if ((remotePath
= strchr(remoteHost
, ':')) != NULL
) {
477 remotePath
= strdup("/var/run/vknet");
487 av
[ac
++] = remoteHost
;
493 av
[ac
++] = remoteBridge
;
495 av
[ac
++] = remotePath
;
497 execv("/usr/bin/ssh", (void *)av
);
509 "vknet [-C] [-b local-bridge] [-B remote-bridge] [-r delay[:retries]]\n"
510 " local-spec [user@]remote[:remote-spec]\n"
511 "vknet -S [-b local-bridge] local-spec\n"