usr.sbin/fstyp: Use NULL instead of 0 for pointers. (FreeBSD@GitHub 27f4c84a)
[dragonfly.git] / usr.bin / vknet / vknet.c
blob348d16dba71cc31ee9fcf4173656c489a66adda8
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 [-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.
49 #include "vknet.h"
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;
61 int
62 main(int ac, char **av)
64 int compressOpt = 0;
65 int remoteOpt = 0;
66 const char *localBridge = NULL;
67 const char *remoteBridge = NULL;
68 const char *localSide;
69 const char *remoteSide;
70 char *ptr;
71 int c;
72 int retriesOpt = -1;
73 int timeoutOpt = -1;
74 pid_t sshpid = -1;
75 pid_t p;
76 struct ioinfo ios;
77 struct ioinfo iod;
79 while ((c = getopt(ac, av, "b:B:r:CS")) != -1) {
80 switch (c) {
81 case 'b':
82 localBridge = optarg;
83 break;
84 case 'B':
85 remoteBridge = optarg;
86 break;
87 case 'r':
88 timeoutOpt = strtol(optarg, &ptr, 0);
89 if (ptr && *ptr == ':')
90 retriesOpt = strtol(ptr + 1, NULL, 0);
91 break;
92 case 'S':
93 remoteOpt = 1;
94 break;
95 case 'C':
96 compressOpt = 1;
97 break;
98 default:
99 usage();
102 av += optind;
103 ac -= optind;
106 * Local and remote arguments.
108 if (remoteOpt) {
109 if (ac != 1)
110 usage();
111 localSide = av[0];
112 remoteSide = NULL;
113 } else {
114 if (ac != 2)
115 usage();
116 localSide = av[0];
117 remoteSide = av[1];
120 pthread_mutex_init(&MasterLock, NULL);
122 retry:
124 * Setup connections
126 vknet_connect(&ios, localSide, localBridge);
127 if (remoteOpt) {
128 iod.fdin = 0;
129 iod.fdout = 1;
130 } else {
131 int fds[2];
133 if (pipe(fds) < 0) {
134 perror("pipe");
135 exit(1);
137 sshpid = vknet_execssh(fds[1], fds[1], compressOpt,
138 remoteSide, remoteBridge);
139 close(fds[1]);
140 iod.fdin = fds[0];
141 iod.fdout = fds[0];
145 * Blast away, timeout/retry on failure
147 vknet_blastaway(&ios, &iod);
150 * Terminate child process
152 if (sshpid > 0) {
153 if (kill(sshpid, SIGTERM) != 0)
154 perror("kill");
155 while ((p = waitpid(sshpid, NULL, 0)) != sshpid) {
156 if (p < 0 && errno != EINTR)
157 break;
159 sshpid = -1;
163 * Handle timeout/retries
165 if (timeoutOpt >= 0 && retriesOpt != 0) {
166 printf("timeout %d retries %d\n", timeoutOpt, retriesOpt);
167 if (timeoutOpt > 0)
168 sleep(timeoutOpt);
169 if (retriesOpt > 0)
170 --retriesOpt;
171 goto retry;
173 exit(0);
176 static void
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
201 static
202 void *
203 vknet_stream(void *arg)
205 streaminfo_t stream = arg;
206 struct blkhead head;
207 u_int8_t *pkt;
208 int bytes;
209 int n;
210 int r;
213 * Synchronize with master thread, then loop
215 pthread_mutex_lock(&MasterLock);
216 pthread_mutex_unlock(&MasterLock);
218 pkt = malloc(MAXPKT);
220 for (;;) {
222 * Input side
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,
228 bytes - n);
229 if (r <= 0)
230 break;
232 if (n != bytes)
233 break;
234 if (le32toh(head.magic) != MAGIC)
235 break;
236 bytes = le32toh(head.bytes);
237 if (bytes <= 0 || bytes > MAXPKT)
238 break;
239 for (n = 0; n < bytes; n += r) {
240 r = read(stream->fdin, pkt + n, bytes - n);
241 if (r <= 0)
242 break;
244 if (n != bytes)
245 break;
246 } else {
247 bytes = read(stream->fdin, pkt, MAXPKT);
248 if (bytes <= 0)
249 break;
253 * Output side
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))
259 break;
260 if (write(stream->fdout, pkt, bytes) != bytes)
261 break;
262 } else {
263 if (write(stream->fdout, pkt, bytes) != bytes)
264 break;
267 free(pkt);
268 close(stream->fdin);
269 close(stream->fdout);
270 pthread_cancel(stream->other->thread);
271 pthread_exit(NULL);
275 * vknet_connect() - Connect to local side, optionally find or bridge the tap
276 * interface.
278 static void
279 vknet_connect(ioinfo_t io, const char *localSide, const char *localBridge)
281 struct ifreq ifr;
282 struct ifaliasreq ifra;
283 char *buf = NULL;
284 int tap_fd;
285 int tap_unit;
286 int i;
287 int s;
288 int flags;
290 tap_unit = -1;
291 tap_fd = -1;
293 if (strcmp(localSide, "auto") == 0) {
294 for (i = 0; ; ++i) {
295 asprintf(&buf, "/dev/tap%d", i);
296 tap_fd = open(buf, O_RDWR | O_NONBLOCK);
297 free(buf);
298 if (tap_fd >= 0 || errno == ENOENT) {
299 tap_unit = i;
300 break;
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);
307 free(buf);
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')
311 --ptr;
312 tap_unit = strtol(ptr, NULL, 10);
313 } else {
314 struct sockaddr_un sunx;
315 int len;
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;
322 sunx.sun_len = len;
324 tap_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
325 if (tap_fd >= 0) {
326 if (connect(tap_fd, (void *)&sunx, len) < 0) {
327 close(tap_fd);
328 tap_fd = -1;
333 if (tap_fd < 0) {
334 err(1, "Unable to connect to %s", localSide);
335 /* NOT REACHED */
338 fcntl(tap_fd, F_SETFL, 0);
339 io->fdin = tap_fd;
340 io->fdout = tap_fd;
343 * If this isn't a TAP device we are done.
345 if (tap_unit < 0)
346 return;
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);
358 #if 0
360 * Set the interface address if in Secure mode.
362 if (SecureOpt) {
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");
375 exit(1);
378 #endif
381 * Turn up the interface
383 flags = IFF_UP;
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");
391 exit(1);
396 * If a bridge was specified associate the tap interface with the
397 * bridge.
399 if (localBridge) {
400 struct ifbreq ifbr;
401 struct ifdrv ifd;
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");
411 exit(1);
417 * Add the tap interface to the bridge
419 bzero(&ifbr, sizeof(ifbr));
420 snprintf(ifbr.ifbr_ifsname, sizeof(ifbr.ifbr_ifsname),
421 "tap%d", tap_unit);
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!");
432 exit(1);
436 close(s);
440 * Connect to the remote machine with ssh and set up a stream
442 static pid_t
443 vknet_execssh(int fdin, int fdout, int compressOpt,
444 const char *remoteSide, const char *remoteBridge)
446 char *remoteHost;
447 char *remotePath;
448 const char *av[24];
449 int ac;
450 pid_t pid;
453 * Fork / parent returns.
455 if ((pid = fork()) > 0)
456 return pid;
457 if (pid < 0) {
458 perror("fork");
459 exit(1);
463 * Setup stdin, stdout
465 assert(fdin > 2);
466 assert(fdout > 2);
467 dup2(fdin, 0);
468 dup2(fdout, 1);
469 close(fdin);
470 close(fdout);
473 * Set up arguments
475 remoteHost = strdup(remoteSide);
476 if ((remotePath = strchr(remoteHost, ':')) != NULL) {
477 *remotePath++ = 0;
478 } else {
479 remotePath = strdup("/var/run/vknet");
481 ac = 0;
482 av[ac++] = "ssh";
483 if (compressOpt)
484 av[ac++] = "-C";
485 av[ac++] = "-x";
486 av[ac++] = "-T";
487 av[ac++] = "-e";
488 av[ac++] = "none";
489 av[ac++] = remoteHost;
490 av[ac++] = "exec";
491 av[ac++] = "vknet";
492 av[ac++] = "-S";
493 if (remoteBridge) {
494 av[ac++] = "-b";
495 av[ac++] = remoteBridge;
497 av[ac++] = remotePath;
498 av[ac++] = NULL;
499 execv("/usr/bin/ssh", (void *)av);
500 exit(1);
504 * Misc
506 static
507 void
508 usage(void)
510 fprintf(stderr,
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"
515 exit(1);