usr.sbin/makefs: Sync with sys/vfs/hammer2
[dragonfly.git] / usr.bin / vknet / vknet.c
blob4b9089084072bfe408b39b04edea2dd3b9a4e69c
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.
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.
47 #include "vknet.h"
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;
59 int
60 main(int ac, char **av)
62 int compressOpt = 0;
63 int remoteOpt = 0;
64 const char *localBridge = NULL;
65 const char *remoteBridge = NULL;
66 const char *localSide;
67 const char *remoteSide;
68 char *ptr;
69 int c;
70 int retriesOpt = -1;
71 int timeoutOpt = -1;
72 pid_t sshpid = -1;
73 pid_t p;
74 struct ioinfo ios;
75 struct ioinfo iod;
77 while ((c = getopt(ac, av, "b:B:r:CS")) != -1) {
78 switch (c) {
79 case 'b':
80 localBridge = optarg;
81 break;
82 case 'B':
83 remoteBridge = optarg;
84 break;
85 case 'r':
86 timeoutOpt = strtol(optarg, &ptr, 0);
87 if (ptr && *ptr == ':')
88 retriesOpt = strtol(ptr + 1, NULL, 0);
89 break;
90 case 'S':
91 remoteOpt = 1;
92 break;
93 case 'C':
94 compressOpt = 1;
95 break;
96 default:
97 usage();
100 av += optind;
101 ac -= optind;
104 * Local and remote arguments.
106 if (remoteOpt) {
107 if (ac != 1)
108 usage();
109 localSide = av[0];
110 remoteSide = NULL;
111 } else {
112 if (ac != 2)
113 usage();
114 localSide = av[0];
115 remoteSide = av[1];
118 pthread_mutex_init(&MasterLock, NULL);
120 retry:
122 * Setup connections
124 vknet_connect(&ios, localSide, localBridge);
125 if (remoteOpt) {
126 iod.fdin = 0;
127 iod.fdout = 1;
128 } else {
129 int fds[2];
131 if (pipe(fds) < 0) {
132 perror("pipe");
133 exit(1);
135 sshpid = vknet_execssh(fds[1], fds[1], compressOpt,
136 remoteSide, remoteBridge);
137 close(fds[1]);
138 iod.fdin = fds[0];
139 iod.fdout = fds[0];
143 * Blast away, timeout/retry on failure
145 vknet_blastaway(&ios, &iod);
148 * Terminate child process
150 if (sshpid > 0) {
151 if (kill(sshpid, SIGTERM) != 0)
152 perror("kill");
153 while ((p = waitpid(sshpid, NULL, 0)) != sshpid) {
154 if (p < 0 && errno != EINTR)
155 break;
157 sshpid = -1;
161 * Handle timeout/retries
163 if (timeoutOpt >= 0 && retriesOpt != 0) {
164 printf("timeout %d retries %d\n", timeoutOpt, retriesOpt);
165 if (timeoutOpt > 0)
166 sleep(timeoutOpt);
167 if (retriesOpt > 0)
168 --retriesOpt;
169 goto retry;
171 exit(0);
174 static void
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
199 static
200 void *
201 vknet_stream(void *arg)
203 streaminfo_t stream = arg;
204 struct blkhead head;
205 u_int8_t *pkt;
206 int bytes;
207 int n;
208 int r;
211 * Synchronize with master thread, then loop
213 pthread_mutex_lock(&MasterLock);
214 pthread_mutex_unlock(&MasterLock);
216 pkt = malloc(MAXPKT);
218 for (;;) {
220 * Input side
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,
226 bytes - n);
227 if (r <= 0)
228 break;
230 if (n != bytes)
231 break;
232 if (le32toh(head.magic) != MAGIC)
233 break;
234 bytes = le32toh(head.bytes);
235 if (bytes <= 0 || bytes > MAXPKT)
236 break;
237 for (n = 0; n < bytes; n += r) {
238 r = read(stream->fdin, pkt + n, bytes - n);
239 if (r <= 0)
240 break;
242 if (n != bytes)
243 break;
244 } else {
245 bytes = read(stream->fdin, pkt, MAXPKT);
246 if (bytes <= 0)
247 break;
251 * Output side
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))
257 break;
258 if (write(stream->fdout, pkt, bytes) != bytes)
259 break;
260 } else {
261 if (write(stream->fdout, pkt, bytes) != bytes)
262 break;
265 free(pkt);
266 close(stream->fdin);
267 close(stream->fdout);
268 pthread_cancel(stream->other->thread);
269 pthread_exit(NULL);
273 * vknet_connect() - Connect to local side, optionally find or bridge the tap
274 * interface.
276 static void
277 vknet_connect(ioinfo_t io, const char *localSide, const char *localBridge)
279 struct ifreq ifr;
280 struct ifaliasreq ifra;
281 char *buf = NULL;
282 int tap_fd;
283 int tap_unit;
284 int i;
285 int s;
286 int flags;
288 tap_unit = -1;
289 tap_fd = -1;
291 if (strcmp(localSide, "auto") == 0) {
292 for (i = 0; ; ++i) {
293 asprintf(&buf, "/dev/tap%d", i);
294 tap_fd = open(buf, O_RDWR | O_NONBLOCK);
295 free(buf);
296 if (tap_fd >= 0 || errno == ENOENT) {
297 tap_unit = i;
298 break;
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);
305 free(buf);
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')
309 --ptr;
310 tap_unit = strtol(ptr, NULL, 10);
311 } else {
312 struct sockaddr_un sunx;
313 int len;
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;
320 sunx.sun_len = len;
322 tap_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
323 if (tap_fd >= 0) {
324 if (connect(tap_fd, (void *)&sunx, len) < 0) {
325 close(tap_fd);
326 tap_fd = -1;
331 if (tap_fd < 0) {
332 err(1, "Unable to connect to %s", localSide);
333 /* NOT REACHED */
336 fcntl(tap_fd, F_SETFL, 0);
337 io->fdin = tap_fd;
338 io->fdout = tap_fd;
341 * If this isn't a TAP device we are done.
343 if (tap_unit < 0)
344 return;
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);
356 #if 0
358 * Set the interface address if in Secure mode.
360 if (SecureOpt) {
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");
373 exit(1);
376 #endif
379 * Turn up the interface
381 flags = IFF_UP;
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");
389 exit(1);
394 * If a bridge was specified associate the tap interface with the
395 * bridge.
397 if (localBridge) {
398 struct ifbreq ifbr;
399 struct ifdrv ifd;
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");
409 exit(1);
415 * Add the tap interface to the bridge
417 bzero(&ifbr, sizeof(ifbr));
418 snprintf(ifbr.ifbr_ifsname, sizeof(ifbr.ifbr_ifsname),
419 "tap%d", tap_unit);
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!");
430 exit(1);
434 close(s);
438 * Connect to the remote machine with ssh and set up a stream
440 static pid_t
441 vknet_execssh(int fdin, int fdout, int compressOpt,
442 const char *remoteSide, const char *remoteBridge)
444 char *remoteHost;
445 char *remotePath;
446 const char *av[24];
447 int ac;
448 pid_t pid;
451 * Fork / parent returns.
453 if ((pid = fork()) > 0)
454 return pid;
455 if (pid < 0) {
456 perror("fork");
457 exit(1);
461 * Setup stdin, stdout
463 assert(fdin > 2);
464 assert(fdout > 2);
465 dup2(fdin, 0);
466 dup2(fdout, 1);
467 close(fdin);
468 close(fdout);
471 * Set up arguments
473 remoteHost = strdup(remoteSide);
474 if ((remotePath = strchr(remoteHost, ':')) != NULL) {
475 *remotePath++ = 0;
476 } else {
477 remotePath = strdup("/var/run/vknet");
479 ac = 0;
480 av[ac++] = "ssh";
481 if (compressOpt)
482 av[ac++] = "-C";
483 av[ac++] = "-x";
484 av[ac++] = "-T";
485 av[ac++] = "-e";
486 av[ac++] = "none";
487 av[ac++] = remoteHost;
488 av[ac++] = "exec";
489 av[ac++] = "vknet";
490 av[ac++] = "-S";
491 if (remoteBridge) {
492 av[ac++] = "-b";
493 av[ac++] = remoteBridge;
495 av[ac++] = remotePath;
496 av[ac++] = NULL;
497 execv("/usr/bin/ssh", (void *)av);
498 exit(1);
502 * Misc
504 static
505 void
506 usage(void)
508 fprintf(stderr,
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"
513 exit(1);