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 pid_t
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
;
79 while ((c
= getopt(ac
, av
, "b:B:r:CS")) != -1) {
85 remoteBridge
= optarg
;
88 timeoutOpt
= strtol(optarg
, &ptr
, 0);
89 if (ptr
&& *ptr
== ':')
90 retriesOpt
= strtol(ptr
+ 1, NULL
, 0);
106 * Local and remote arguments.
120 pthread_mutex_init(&MasterLock
, NULL
);
126 vknet_connect(&ios
, localSide
, localBridge
);
137 sshpid
= vknet_execssh(fds
[1], fds
[1], compressOpt
,
138 remoteSide
, remoteBridge
);
145 * Blast away, timeout/retry on failure
147 vknet_blastaway(&ios
, &iod
);
150 * Terminate child process
153 if (kill(sshpid
, SIGTERM
) != 0)
155 while ((p
= waitpid(sshpid
, NULL
, 0)) != sshpid
) {
156 if (p
< 0 && errno
!= EINTR
)
163 * Handle timeout/retries
165 if (timeoutOpt
>= 0 && retriesOpt
!= 0) {
166 printf("timeout %d retries %d\n", timeoutOpt
, retriesOpt
);
177 vknet_blastaway(ioinfo_t ios
, ioinfo_t iod
)
179 struct streaminfo stream1
;
180 struct streaminfo stream2
;
182 pthread_mutex_lock(&MasterLock
);
183 stream1
.fdin
= ios
->fdin
;
184 stream1
.fdout
= iod
->fdout
;
185 stream1
.flags
= REBLOCK_OUT
;
186 stream1
.other
= &stream2
;
187 stream2
.fdin
= iod
->fdin
;
188 stream2
.fdout
= ios
->fdout
;
189 stream2
.flags
= REBLOCK_IN
;
190 stream2
.other
= &stream1
;
191 pthread_create(&stream1
.thread
, NULL
, vknet_stream
, &stream1
);
192 pthread_create(&stream2
.thread
, NULL
, vknet_stream
, &stream2
);
193 pthread_mutex_unlock(&MasterLock
);
194 pthread_join(stream1
.thread
, NULL
);
195 pthread_join(stream2
.thread
, NULL
);
199 * Transfer packets between two descriptors
203 vknet_stream(void *arg
)
205 streaminfo_t stream
= arg
;
213 * Synchronize with master thread, then loop
215 pthread_mutex_lock(&MasterLock
);
216 pthread_mutex_unlock(&MasterLock
);
218 pkt
= malloc(MAXPKT
);
224 if (stream
->flags
& REBLOCK_IN
) {
225 bytes
= sizeof(head
);
226 for (n
= 0; n
< bytes
; n
+= r
) {
227 r
= read(stream
->fdin
, (char *)&head
+ n
,
234 if (le32toh(head
.magic
) != MAGIC
)
236 bytes
= le32toh(head
.bytes
);
237 if (bytes
<= 0 || bytes
> MAXPKT
)
239 for (n
= 0; n
< bytes
; n
+= r
) {
240 r
= read(stream
->fdin
, pkt
+ n
, bytes
- n
);
247 bytes
= read(stream
->fdin
, pkt
, MAXPKT
);
255 if (stream
->flags
& REBLOCK_OUT
) {
256 head
.magic
= htole32(MAGIC
);
257 head
.bytes
= htole32(bytes
);
258 if (write(stream
->fdout
, &head
, sizeof(head
)) != sizeof(head
))
260 if (write(stream
->fdout
, pkt
, bytes
) != bytes
)
263 if (write(stream
->fdout
, pkt
, bytes
) != bytes
)
269 close(stream
->fdout
);
270 pthread_cancel(stream
->other
->thread
);
275 * vknet_connect() - Connect to local side, optionally find or bridge the tap
279 vknet_connect(ioinfo_t io
, const char *localSide
, const char *localBridge
)
282 struct ifaliasreq ifra
;
293 if (strcmp(localSide
, "auto") == 0) {
295 asprintf(&buf
, "/dev/tap%d", i
);
296 tap_fd
= open(buf
, O_RDWR
| O_NONBLOCK
);
298 if (tap_fd
>= 0 || errno
== ENOENT
) {
303 } else if (strncmp(localSide
, "tap", 3) == 0) {
304 asprintf(&buf
, "/dev/%s", localSide
);
305 tap_fd
= open(buf
, O_RDWR
| O_NONBLOCK
);
306 tap_unit
= strtol(localSide
+ 3, NULL
, 10);
308 } else if ((tap_fd
= open(localSide
, O_RDWR
| O_NONBLOCK
)) >= 0) {
309 const char *ptr
= localSide
+ strlen(localSide
);
310 while (ptr
> localSide
&& ptr
[-1] >= '0' && ptr
[-1] <= '9')
312 tap_unit
= strtol(ptr
, NULL
, 10);
314 struct sockaddr_un sunx
;
317 snprintf(sunx
.sun_path
, sizeof(sunx
.sun_path
), "%s", localSide
);
318 len
= offsetof(struct sockaddr_un
,
319 sun_path
[strlen(sunx
.sun_path
)]);
320 ++len
; /* include nul */
321 sunx
.sun_family
= AF_UNIX
;
324 tap_fd
= socket(AF_UNIX
, SOCK_SEQPACKET
, 0);
326 if (connect(tap_fd
, (void *)&sunx
, len
) < 0) {
334 err(1, "Unable to connect to %s", localSide
);
338 fcntl(tap_fd
, F_SETFL
, 0);
343 * If this isn't a TAP device we are done.
349 * Bring up the TAP interface
351 bzero(&ifr
, sizeof(ifr
));
352 bzero(&ifra
, sizeof(ifra
));
353 snprintf(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), "tap%d", tap_unit
);
354 snprintf(ifra
.ifra_name
, sizeof(ifra
.ifra_name
), "tap%d", tap_unit
);
356 s
= socket(AF_INET
, SOCK_DGRAM
, 0);
360 * Set the interface address if in Secure mode.
363 struct sockaddr_in
*in
;
365 in
= (void *)&ifra
.ifra_addr
;
366 in
->sin_family
= AF_INET
;
367 in
->sin_len
= sizeof(ifra
.ifra_addr
);
368 in
->sin_addr
= NetAddress
;
369 in
= (void *)&ifra
.ifra_mask
;
370 in
->sin_family
= AF_INET
;
371 in
->sin_len
= sizeof(ifra
.ifra_mask
);
372 in
->sin_addr
= NetMask
;
373 if (ioctl(s
, SIOCAIFADDR
, &ifra
) < 0) {
374 perror("Unable to set address on tap interface");
381 * Turn up the interface
384 if (ioctl(s
, SIOCGIFFLAGS
, &ifr
) >= 0) {
385 bzero(&ifr
, sizeof(ifr
));
386 snprintf(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), "tap%d", tap_unit
);
387 ifr
.ifr_flags
|= flags
& 0xFFFF;
388 ifr
.ifr_flagshigh
|= flags
>> 16;
389 if (ioctl(s
, SIOCSIFFLAGS
, &ifr
) < 0) {
390 perror("Unable to set IFF_UP on tap interface");
396 * If a bridge was specified associate the tap interface with the
404 * Create the bridge if necessary.
406 bzero(&ifr
, sizeof(ifr
));
407 snprintf(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), "%s", localBridge
);
408 if (ioctl(s
, SIOCIFCREATE
, &ifr
) < 0) {
409 if (errno
!= EEXIST
) {
410 perror("Unable to create bridge interface");
417 * Add the tap interface to the bridge
419 bzero(&ifbr
, sizeof(ifbr
));
420 snprintf(ifbr
.ifbr_ifsname
, sizeof(ifbr
.ifbr_ifsname
),
423 bzero(&ifd
, sizeof(ifd
));
424 snprintf(ifd
.ifd_name
, sizeof(ifd
.ifd_name
), "%s", localBridge
);
425 ifd
.ifd_cmd
= BRDGADD
;
426 ifd
.ifd_len
= sizeof(ifbr
);
427 ifd
.ifd_data
= &ifbr
;
429 if (ioctl(s
, SIOCSDRVSPEC
, &ifd
) < 0) {
430 if (errno
!= EEXIST
) {
431 perror("Unable to add tap ifc to bridge!");
440 * Connect to the remote machine with ssh and set up a stream
443 vknet_execssh(int fdin
, int fdout
, int compressOpt
,
444 const char *remoteSide
, const char *remoteBridge
)
453 * Fork / parent returns.
455 if ((pid
= fork()) > 0)
463 * Setup stdin, stdout
475 remoteHost
= strdup(remoteSide
);
476 if ((remotePath
= strchr(remoteHost
, ':')) != NULL
) {
479 remotePath
= strdup("/var/run/vknet");
489 av
[ac
++] = remoteHost
;
495 av
[ac
++] = remoteBridge
;
497 av
[ac
++] = remotePath
;
499 execv("/usr/bin/ssh", (void *)av
);
511 "vknet [-C] [-b local-bridge] [-B remote-bridge] [-r delay[:retries]]\n"
512 " local-spec [user@]remote[:remote-spec]\n"
513 "vknet -S [-b local-bridge] local-spec\n"