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: 10 * 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. 20 * 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. 33 * 34 * $DragonFly: src/usr.bin/vknet/vknet.c,v 1.1 2008/05/27 23:26:38 dillon Exp $ 35 */ 36 /* 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) 40 * 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. 44 * 45 * This program expects packetized reads and writes on the local and remote 46 * sides and will re-block them over the SSH stream. 47 */ 48 49 #include "vknet.h" 50 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 void vknet_execssh(int fdin, int fdout, int compressOpt, 56 const char *remoteSide, const char *remoteBridge); 57 static void usage(void); 58 59 pthread_mutex_t MasterLock; 60 61 int 62 main(int ac, char **av) 63 { 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 struct ioinfo ios; 75 struct ioinfo iod; 76 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(); 98 } 99 } 100 av += optind; 101 ac -= optind; 102 103 /* 104 * Local and remote arguments. 105 */ 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]; 116 } 117 118 pthread_mutex_init(&MasterLock, NULL); 119 120 retry: 121 /* 122 * Setup connections 123 */ 124 vknet_connect(&ios, localSide, localBridge); 125 if (remoteOpt) { 126 iod.fdin = 0; 127 iod.fdout = 1; 128 } else { 129 int fds[2]; 130 131 if (pipe(fds) < 0) { 132 perror("pipe"); 133 exit(1); 134 } 135 vknet_execssh(fds[1], fds[1], compressOpt, 136 remoteSide, remoteBridge); 137 close(fds[1]); 138 iod.fdin = fds[0]; 139 iod.fdout = fds[0]; 140 } 141 142 /* 143 * Blast away, timeout/retry on failure 144 */ 145 vknet_blastaway(&ios, &iod); 146 147 /* 148 * Handle timeout/retries 149 */ 150 if (timeoutOpt >= 0 && retriesOpt != 0) { 151 printf("timeout %d retries %d\n", timeoutOpt, retriesOpt); 152 if (timeoutOpt > 0) 153 sleep(timeoutOpt); 154 if (retriesOpt > 0) 155 --retriesOpt; 156 goto retry; 157 } 158 exit(0); 159 } 160 161 static void 162 vknet_blastaway(ioinfo_t ios, ioinfo_t iod) 163 { 164 struct streaminfo stream1; 165 struct streaminfo stream2; 166 167 pthread_mutex_lock(&MasterLock); 168 stream1.fdin = ios->fdin; 169 stream1.fdout = iod->fdout; 170 stream1.flags = REBLOCK_OUT; 171 stream1.other = &stream2; 172 stream2.fdin = iod->fdin; 173 stream2.fdout = ios->fdout; 174 stream2.flags = REBLOCK_IN; 175 stream2.other = &stream1; 176 pthread_create(&stream1.thread, NULL, vknet_stream, &stream1); 177 pthread_create(&stream2.thread, NULL, vknet_stream, &stream2); 178 pthread_mutex_unlock(&MasterLock); 179 pthread_join(stream1.thread, NULL); 180 pthread_join(stream2.thread, NULL); 181 } 182 183 /* 184 * Transfer packets between two descriptors 185 */ 186 static 187 void * 188 vknet_stream(void *arg) 189 { 190 streaminfo_t stream = arg; 191 struct blkhead head; 192 u_int8_t *pkt; 193 int bytes; 194 int n; 195 int r; 196 197 /* 198 * Synchronize with master thread, then loop 199 */ 200 pthread_mutex_lock(&MasterLock); 201 pthread_mutex_unlock(&MasterLock); 202 203 pkt = malloc(MAXPKT); 204 205 for (;;) { 206 /* 207 * Input side 208 */ 209 if (stream->flags & REBLOCK_IN) { 210 bytes = sizeof(head); 211 for (n = 0; n < bytes; n += r) { 212 r = read(stream->fdin, (char *)&head + n, 213 bytes - n); 214 if (r <= 0) 215 break; 216 } 217 if (n != bytes) 218 break; 219 if (le32toh(head.magic) != MAGIC) 220 break; 221 bytes = le32toh(head.bytes); 222 if (bytes <= 0 || bytes > MAXPKT) 223 break; 224 for (n = 0; n < bytes; n += r) { 225 r = read(stream->fdin, pkt + n, bytes - n); 226 if (r <= 0) 227 break; 228 } 229 if (n != bytes) 230 break; 231 } else { 232 bytes = read(stream->fdin, pkt, MAXPKT); 233 if (bytes <= 0) 234 break; 235 } 236 237 /* 238 * Output side 239 */ 240 if (stream->flags & REBLOCK_OUT) { 241 head.magic = htole32(MAGIC); 242 head.bytes = htole32(bytes); 243 if (write(stream->fdout, &head, sizeof(head)) != sizeof(head)) 244 break; 245 if (write(stream->fdout, pkt, bytes) != bytes) 246 break; 247 } else { 248 if (write(stream->fdout, pkt, bytes) != bytes) 249 break; 250 } 251 } 252 free(pkt); 253 close(stream->fdin); 254 close(stream->fdout); 255 pthread_cancel(stream->other->thread); 256 pthread_exit(NULL); 257 } 258 259 /* 260 * vknet_connect() - Connect to local side, optionally find or bridge the tap 261 * interface. 262 */ 263 static void 264 vknet_connect(ioinfo_t io, const char *localSide, const char *localBridge) 265 { 266 struct ifreq ifr; 267 struct ifaliasreq ifra; 268 char *buf = NULL; 269 int tap_fd; 270 int tap_unit; 271 int i; 272 int s; 273 int flags; 274 275 tap_unit = -1; 276 tap_fd = -1; 277 278 if (strcmp(localSide, "auto") == 0) { 279 for (i = 0; ; ++i) { 280 asprintf(&buf, "/dev/tap%d", i); 281 tap_fd = open(buf, O_RDWR | O_NONBLOCK); 282 free(buf); 283 if (tap_fd >= 0 || errno == ENOENT) { 284 tap_unit = i; 285 break; 286 } 287 } 288 } else if (strncmp(localSide, "tap", 3) == 0) { 289 asprintf(&buf, "/dev/%s", localSide); 290 tap_fd = open(buf, O_RDWR | O_NONBLOCK); 291 tap_unit = strtol(localSide + 3, NULL, 10); 292 free(buf); 293 } else if ((tap_fd = open(localSide, O_RDWR | O_NONBLOCK)) >= 0) { 294 const char *ptr = localSide + strlen(localSide); 295 while (ptr > localSide && ptr[-1] >= '0' && ptr[-1] <= '9') 296 --ptr; 297 tap_unit = strtol(ptr, NULL, 10); 298 } else { 299 struct sockaddr_un sunx; 300 int len; 301 302 snprintf(sunx.sun_path, sizeof(sunx.sun_path), "%s", localSide); 303 len = offsetof(struct sockaddr_un, 304 sun_path[strlen(sunx.sun_path)]); 305 ++len; /* include nul */ 306 sunx.sun_family = AF_UNIX; 307 sunx.sun_len = len; 308 309 tap_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0); 310 if (tap_fd >= 0) { 311 if (connect(tap_fd, (void *)&sunx, len) < 0) { 312 close(tap_fd); 313 tap_fd = -1; 314 } 315 } 316 } 317 318 if (tap_fd < 0) { 319 err(1, "Unable to connect to %s", localSide); 320 /* NOT REACHED */ 321 } 322 323 fcntl(tap_fd, F_SETFL, 0); 324 io->fdin = tap_fd; 325 io->fdout = tap_fd; 326 327 /* 328 * If this isn't a TAP device we are done. 329 */ 330 if (tap_unit < 0) 331 return; 332 333 /* 334 * Bring up the TAP interface 335 */ 336 bzero(&ifr, sizeof(ifr)); 337 bzero(&ifra, sizeof(ifra)); 338 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "tap%d", tap_unit); 339 snprintf(ifra.ifra_name, sizeof(ifra.ifra_name), "tap%d", tap_unit); 340 341 s = socket(AF_INET, SOCK_DGRAM, 0); 342 343 #if 0 344 /* 345 * Set the interface address if in Secure mode. 346 */ 347 if (SecureOpt) { 348 struct sockaddr_in *in; 349 350 in = (void *)&ifra.ifra_addr; 351 in->sin_family = AF_INET; 352 in->sin_len = sizeof(ifra.ifra_addr); 353 in->sin_addr = NetAddress; 354 in = (void *)&ifra.ifra_mask; 355 in->sin_family = AF_INET; 356 in->sin_len = sizeof(ifra.ifra_mask); 357 in->sin_addr = NetMask; 358 if (ioctl(s, SIOCAIFADDR, &ifra) < 0) { 359 perror("Unable to set address on tap interface"); 360 exit(1); 361 } 362 } 363 #endif 364 365 /* 366 * Turn up the interface 367 */ 368 flags = IFF_UP; 369 if (ioctl(s, SIOCGIFFLAGS, &ifr) >= 0) { 370 bzero(&ifr, sizeof(ifr)); 371 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "tap%d", tap_unit); 372 ifr.ifr_flags |= flags & 0xFFFF; 373 ifr.ifr_flagshigh |= flags >> 16; 374 if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) { 375 perror("Unable to set IFF_UP on tap interface"); 376 exit(1); 377 } 378 } 379 380 /* 381 * If a bridge was specified associate the tap interface with the 382 * bridge. 383 */ 384 if (localBridge) { 385 struct ifbreq ifbr; 386 struct ifdrv ifd; 387 388 /* 389 * Create the bridge if necessary. 390 */ 391 bzero(&ifr, sizeof(ifr)); 392 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", localBridge); 393 if (ioctl(s, SIOCIFCREATE, &ifr) < 0) { 394 if (errno != EEXIST) { 395 perror("Unable to create bridge interface"); 396 exit(1); 397 } 398 } 399 400 401 /* 402 * Add the tap interface to the bridge 403 */ 404 bzero(&ifbr, sizeof(ifbr)); 405 snprintf(ifbr.ifbr_ifsname, sizeof(ifbr.ifbr_ifsname), 406 "tap%d", tap_unit); 407 408 bzero(&ifd, sizeof(ifd)); 409 snprintf(ifd.ifd_name, sizeof(ifd.ifd_name), "%s", localBridge); 410 ifd.ifd_cmd = BRDGADD; 411 ifd.ifd_len = sizeof(ifbr); 412 ifd.ifd_data = &ifbr; 413 414 if (ioctl(s, SIOCSDRVSPEC, &ifd) < 0) { 415 if (errno != EEXIST) { 416 perror("Unable to add tap ifc to bridge!"); 417 exit(1); 418 } 419 } 420 } 421 close(s); 422 } 423 424 /* 425 * Connect to the remote machine with ssh and set up a stream 426 */ 427 static void 428 vknet_execssh(int fdin, int fdout, int compressOpt, 429 const char *remoteSide, const char *remoteBridge) 430 { 431 char *remoteHost; 432 char *remotePath; 433 const char *av[24]; 434 int ac; 435 pid_t pid; 436 437 /* 438 * Fork / parent returns. 439 */ 440 if ((pid = fork()) > 0) 441 return; 442 if (pid < 0) { 443 perror("fork"); 444 exit(1); 445 } 446 447 /* 448 * Setup stdin, stdout 449 */ 450 assert(fdin > 2); 451 assert(fdout > 2); 452 dup2(fdin, 0); 453 dup2(fdout, 1); 454 close(fdin); 455 close(fdout); 456 457 /* 458 * Set up arguments 459 */ 460 remoteHost = strdup(remoteSide); 461 if ((remotePath = strchr(remoteHost, ':')) != NULL) { 462 *remotePath++ = 0; 463 } else { 464 remotePath = strdup("/var/run/vknet"); 465 } 466 ac = 0; 467 av[ac++] = "ssh"; 468 if (compressOpt) 469 av[ac++] = "-C"; 470 av[ac++] = "-x"; 471 av[ac++] = "-T"; 472 av[ac++] = "-e"; 473 av[ac++] = "none"; 474 av[ac++] = remoteHost; 475 av[ac++] = "exec"; 476 av[ac++] = "vknet"; 477 av[ac++] = "-S"; 478 if (remoteBridge) { 479 av[ac++] = "-b"; 480 av[ac++] = remoteBridge; 481 } 482 av[ac++] = remotePath; 483 av[ac++] = NULL; 484 execv("/usr/bin/ssh", (void *)av); 485 } 486 487 /* 488 * Misc 489 */ 490 static 491 void 492 usage(void) 493 { 494 fprintf(stderr, 495 "vknet [-C] [-b local-bridge] [-B remote-bridge] [-r delay[:retries]]\n" 496 " local-spec [user@]remote[:remote-spec]\n" 497 "vknet -S [-b local-bridge] local-spec\n" 498 ); 499 exit(1); 500 } 501 502