Pre-2.0 release: Sync with HAMMER 64 - NFS and cross-device link fixes.
[dragonfly.git] / usr.bin / vknet / vknet.c
blob5ea9670bf3b25bc646568cfe6a4ef6e1b9961738
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:
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.
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.
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.
48 #include "vknet.h"
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;
60 int
61 main(int ac, char **av)
63 int compressOpt = 0;
64 int remoteOpt = 0;
65 const char *localBridge = NULL;
66 const char *remoteBridge = NULL;
67 const char *localSide;
68 const char *remoteSide;
69 char *ptr;
70 int c;
71 int retriesOpt = -1;
72 int timeoutOpt = -1;
73 struct ioinfo ios;
74 struct ioinfo iod;
76 while ((c = getopt(ac, av, "b:B:r:CS")) != -1) {
77 switch (c) {
78 case 'b':
79 localBridge = optarg;
80 break;
81 case 'B':
82 remoteBridge = optarg;
83 break;
84 case 'r':
85 timeoutOpt = strtol(optarg, &ptr, 0);
86 if (ptr && *ptr == ':')
87 retriesOpt = strtol(ptr + 1, NULL, 0);
88 break;
89 case 'S':
90 remoteOpt = 1;
91 break;
92 case 'C':
93 compressOpt = 1;
94 break;
95 default:
96 usage();
99 av += optind;
100 ac -= optind;
103 * Local and remote arguments.
105 if (remoteOpt) {
106 if (ac != 1)
107 usage();
108 localSide = av[0];
109 remoteSide = NULL;
110 } else {
111 if (ac != 2)
112 usage();
113 localSide = av[0];
114 remoteSide = av[1];
117 pthread_mutex_init(&MasterLock, NULL);
119 retry:
121 * Setup connections
123 vknet_connect(&ios, localSide, localBridge);
124 if (remoteOpt) {
125 iod.fdin = 0;
126 iod.fdout = 1;
127 } else {
128 int fds[2];
130 if (pipe(fds) < 0) {
131 perror("pipe");
132 exit(1);
134 vknet_execssh(fds[1], fds[1], compressOpt,
135 remoteSide, remoteBridge);
136 close(fds[1]);
137 iod.fdin = fds[0];
138 iod.fdout = fds[0];
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);
151 if (timeoutOpt > 0)
152 sleep(timeoutOpt);
153 if (retriesOpt > 0)
154 --retriesOpt;
155 goto retry;
157 exit(0);
160 static void
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
185 static
186 void *
187 vknet_stream(void *arg)
189 streaminfo_t stream = arg;
190 struct blkhead head;
191 u_int8_t *pkt;
192 int bytes;
193 int n;
194 int r;
197 * Synchronize with master thread, then loop
199 pthread_mutex_lock(&MasterLock);
200 pthread_mutex_unlock(&MasterLock);
202 pkt = malloc(MAXPKT);
204 for (;;) {
206 * Input side
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,
212 bytes - n);
213 if (r <= 0)
214 break;
216 if (n != bytes)
217 break;
218 if (le32toh(head.magic) != MAGIC)
219 break;
220 bytes = le32toh(head.bytes);
221 if (bytes <= 0 || bytes > MAXPKT)
222 break;
223 for (n = 0; n < bytes; n += r) {
224 r = read(stream->fdin, pkt + n, bytes - n);
225 if (r <= 0)
226 break;
228 if (n != bytes)
229 break;
230 } else {
231 bytes = read(stream->fdin, pkt, MAXPKT);
232 if (bytes <= 0)
233 break;
237 * Output side
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))
243 break;
244 if (write(stream->fdout, pkt, bytes) != bytes)
245 break;
246 } else {
247 if (write(stream->fdout, pkt, bytes) != bytes)
248 break;
251 free(pkt);
252 close(stream->fdin);
253 close(stream->fdout);
254 pthread_cancel(stream->other->thread);
255 pthread_exit(NULL);
259 * vknet_connect() - Connect to local side, optinally find or bridge the tap
260 * interface.
262 static void
263 vknet_connect(ioinfo_t io, const char *localSide, const char *localBridge)
265 struct ifreq ifr;
266 struct ifaliasreq ifra;
267 char *buf = NULL;
268 int tap_fd;
269 int tap_unit;
270 int i;
271 int s;
272 int flags;
274 tap_unit = -1;
275 tap_fd = -1;
277 if (strcmp(localSide, "auto") == 0) {
278 for (i = 0; ; ++i) {
279 asprintf(&buf, "/dev/tap%d", i);
280 tap_fd = open(buf, O_RDWR | O_NONBLOCK);
281 free(buf);
282 if (tap_fd >= 0 || errno == ENOENT) {
283 tap_unit = i;
284 break;
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);
291 free(buf);
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')
295 --ptr;
296 tap_unit = strtol(ptr, NULL, 10);
297 } else {
298 struct sockaddr_un sunx;
299 int len;
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;
306 sunx.sun_len = len;
308 tap_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
309 if (tap_fd >= 0) {
310 if (connect(tap_fd, (void *)&sunx, len) < 0) {
311 close(tap_fd);
312 tap_fd = -1;
317 if (tap_fd < 0) {
318 err(1, "Unable to connect to %s", localSide);
319 /* NOT REACHED */
322 fcntl(tap_fd, F_SETFL, 0);
323 io->fdin = tap_fd;
324 io->fdout = tap_fd;
327 * If this isn't a TAP device we are done.
329 if (tap_unit < 0)
330 return;
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);
342 #if 0
344 * Set the interface address if in Secure mode.
346 if (SecureOpt) {
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");
359 exit(1);
362 #endif
365 * Turn up the interface
367 flags = IFF_UP;
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");
375 exit(1);
380 * If a bridge was specified associate the tap interface with the
381 * bridge.
383 if (localBridge) {
384 struct ifbreq ifbr;
385 struct ifdrv ifd;
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");
395 exit(1);
401 * Add the tap interface to the bridge
403 bzero(&ifbr, sizeof(ifbr));
404 snprintf(ifbr.ifbr_ifsname, sizeof(ifbr.ifbr_ifsname),
405 "tap%d", tap_unit);
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!");
416 exit(1);
420 close(s);
424 * Connect to the remote machine with ssh and set up a stream
426 static void
427 vknet_execssh(int fdin, int fdout, int compressOpt,
428 const char *remoteSide, const char *remoteBridge)
430 char *remoteHost;
431 char *remotePath;
432 const char *av[24];
433 int ac;
434 pid_t pid;
437 * Fork / parent returns.
439 if ((pid = fork()) > 0)
440 return;
441 if (pid < 0) {
442 perror("fork");
443 exit(1);
447 * Setup stdin, stdout
449 assert(fdin > 2);
450 assert(fdout > 2);
451 dup2(fdin, 0);
452 dup2(fdout, 1);
453 close(fdin);
454 close(fdout);
457 * Set up arguments
459 remoteHost = strdup(remoteSide);
460 if ((remotePath = strchr(remoteHost, ':')) != NULL) {
461 *remotePath++ = 0;
462 } else {
463 remotePath = strdup("/dev/vknet");
465 ac = 0;
466 av[ac++] = "ssh";
467 if (compressOpt)
468 av[ac++] = "-C";
469 av[ac++] = "-x";
470 av[ac++] = "-T";
471 av[ac++] = "-e";
472 av[ac++] = "none";
473 av[ac++] = remoteHost;
474 av[ac++] = "exec";
475 av[ac++] = "vknet";
476 av[ac++] = "-S";
477 if (remoteBridge) {
478 av[ac++] = "-b";
479 av[ac++] = remoteBridge;
481 av[ac++] = remotePath;
482 av[ac++] = NULL;
483 execv("/usr/bin/ssh", (void *)av);
487 * Misc
489 static
490 void
491 usage(void)
493 fprintf(stderr,
494 "vknet [-C] [-b local_bridge] [-B remote_bridge] local remote\n"
495 "vknet -S [-b local_bridge] local\n"
497 exit(1);