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 [-r timeout[:retries]] [-C] [-b local_bridge] [-B remote_bridge] local remote 38 * vknet -S [-b local_bridge] local (server mode) 39 * 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. 43 * 44 * This program expects packetized reads and writes on the local and remote 45 * sides and will re-block them over the SSH stream. 46 */ 47 48 #include "vknet.h" 49 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); 57 58 pthread_mutex_t MasterLock; 59 60 int 61 main(int ac, char **av) 62 { 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; 75 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(); 97 } 98 } 99 av += optind; 100 ac -= optind; 101 102 /* 103 * Local and remote arguments. 104 */ 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]; 115 } 116 117 pthread_mutex_init(&MasterLock, NULL); 118 119 retry: 120 /* 121 * Setup connections 122 */ 123 vknet_connect(&ios, localSide, localBridge); 124 if (remoteOpt) { 125 iod.fdin = 0; 126 iod.fdout = 1; 127 } else { 128 int fds[2]; 129 130 if (pipe(fds) < 0) { 131 perror("pipe"); 132 exit(1); 133 } 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]; 139 } 140 141 /* 142 * Blast away, timeout/retry on failure 143 */ 144 vknet_blastaway(&ios, &iod); 145 146 /* 147 * Handle timeout/retries 148 */ 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; 156 } 157 exit(0); 158 } 159 160 static void 161 vknet_blastaway(ioinfo_t ios, ioinfo_t iod) 162 { 163 struct streaminfo stream1; 164 struct streaminfo stream2; 165 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); 180 } 181 182 /* 183 * Transfer packets between two descriptors 184 */ 185 static 186 void * 187 vknet_stream(void *arg) 188 { 189 streaminfo_t stream = arg; 190 struct blkhead head; 191 u_int8_t *pkt; 192 int bytes; 193 int n; 194 int r; 195 196 /* 197 * Synchronize with master thread, then loop 198 */ 199 pthread_mutex_lock(&MasterLock); 200 pthread_mutex_unlock(&MasterLock); 201 202 pkt = malloc(MAXPKT); 203 204 for (;;) { 205 /* 206 * Input side 207 */ 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; 215 } 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; 227 } 228 if (n != bytes) 229 break; 230 } else { 231 bytes = read(stream->fdin, pkt, MAXPKT); 232 if (bytes <= 0) 233 break; 234 } 235 236 /* 237 * Output side 238 */ 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; 249 } 250 } 251 free(pkt); 252 close(stream->fdin); 253 close(stream->fdout); 254 pthread_cancel(stream->other->thread); 255 pthread_exit(NULL); 256 } 257 258 /* 259 * vknet_connect() - Connect to local side, optinally find or bridge the tap 260 * interface. 261 */ 262 static void 263 vknet_connect(ioinfo_t io, const char *localSide, const char *localBridge) 264 { 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; 273 274 tap_unit = -1; 275 tap_fd = -1; 276 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; 285 } 286 } 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; 300 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; 307 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; 313 } 314 } 315 } 316 317 if (tap_fd < 0) { 318 err(1, "Unable to connect to %s", localSide); 319 /* NOT REACHED */ 320 } 321 322 fcntl(tap_fd, F_SETFL, 0); 323 io->fdin = tap_fd; 324 io->fdout = tap_fd; 325 326 /* 327 * If this isn't a TAP device we are done. 328 */ 329 if (tap_unit < 0) 330 return; 331 332 /* 333 * Bring up the TAP interface 334 */ 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); 339 340 s = socket(AF_INET, SOCK_DGRAM, 0); 341 342 #if 0 343 /* 344 * Set the interface address if in Secure mode. 345 */ 346 if (SecureOpt) { 347 struct sockaddr_in *in; 348 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); 360 } 361 } 362 #endif 363 364 /* 365 * Turn up the interface 366 */ 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); 376 } 377 } 378 379 /* 380 * If a bridge was specified associate the tap interface with the 381 * bridge. 382 */ 383 if (localBridge) { 384 struct ifbreq ifbr; 385 struct ifdrv ifd; 386 387 /* 388 * Create the bridge if necessary. 389 */ 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); 396 } 397 } 398 399 400 /* 401 * Add the tap interface to the bridge 402 */ 403 bzero(&ifbr, sizeof(ifbr)); 404 snprintf(ifbr.ifbr_ifsname, sizeof(ifbr.ifbr_ifsname), 405 "tap%d", tap_unit); 406 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; 412 413 if (ioctl(s, SIOCSDRVSPEC, &ifd) < 0) { 414 if (errno != EEXIST) { 415 perror("Unable to add tap ifc to bridge!"); 416 exit(1); 417 } 418 } 419 } 420 close(s); 421 } 422 423 /* 424 * Connect to the remote machine with ssh and set up a stream 425 */ 426 static void 427 vknet_execssh(int fdin, int fdout, int compressOpt, 428 const char *remoteSide, const char *remoteBridge) 429 { 430 char *remoteHost; 431 char *remotePath; 432 const char *av[24]; 433 int ac; 434 pid_t pid; 435 436 /* 437 * Fork / parent returns. 438 */ 439 if ((pid = fork()) > 0) 440 return; 441 if (pid < 0) { 442 perror("fork"); 443 exit(1); 444 } 445 446 /* 447 * Setup stdin, stdout 448 */ 449 assert(fdin > 2); 450 assert(fdout > 2); 451 dup2(fdin, 0); 452 dup2(fdout, 1); 453 close(fdin); 454 close(fdout); 455 456 /* 457 * Set up arguments 458 */ 459 remoteHost = strdup(remoteSide); 460 if ((remotePath = strchr(remoteHost, ':')) != NULL) { 461 *remotePath++ = 0; 462 } else { 463 remotePath = strdup("/dev/vknet"); 464 } 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; 480 } 481 av[ac++] = remotePath; 482 av[ac++] = NULL; 483 execv("/usr/bin/ssh", (void *)av); 484 } 485 486 /* 487 * Misc 488 */ 489 static 490 void 491 usage(void) 492 { 493 fprintf(stderr, 494 "vknet [-C] [-b local_bridge] [-B remote_bridge] local remote\n" 495 "vknet -S [-b local_bridge] local\n" 496 ); 497 exit(1); 498 } 499 500