sbin/fsck_msdosfs: Bring in FreeBSD/Git 41a4c010 (use calloc(3))
[dragonfly.git] / usr.bin / vknet / vknet.c
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 pid_t 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         pid_t sshpid = -1;
75         pid_t p;
76         struct ioinfo ios;
77         struct ioinfo iod;
78
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();
100                 }
101         }
102         av += optind;
103         ac -= optind;
104
105         /*
106          * Local and remote arguments.
107          */
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];
118         }
119
120         pthread_mutex_init(&MasterLock, NULL);
121
122 retry:
123         /*
124          * Setup connections
125          */
126         vknet_connect(&ios, localSide, localBridge);
127         if (remoteOpt) {
128                 iod.fdin = 0;
129                 iod.fdout = 1;
130         } else {
131                 int fds[2];
132
133                 if (pipe(fds) < 0) {
134                         perror("pipe");
135                         exit(1);
136                 }
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];
142         }
143
144         /*
145          * Blast away, timeout/retry on failure
146          */
147         vknet_blastaway(&ios, &iod);
148
149         /*
150          * Terminate child process
151          */
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;
158                 }
159                 sshpid = -1;
160         }
161
162         /*
163          * Handle timeout/retries
164          */
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;
172         }
173         exit(0);
174 }
175
176 static void
177 vknet_blastaway(ioinfo_t ios, ioinfo_t iod)
178 {
179         struct streaminfo stream1;
180         struct streaminfo stream2;
181
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);
196 }
197
198 /*
199  * Transfer packets between two descriptors
200  */
201 static
202 void *
203 vknet_stream(void *arg)
204 {
205         streaminfo_t stream = arg;
206         struct blkhead head;
207         u_int8_t *pkt;
208         int bytes;
209         int n;
210         int r;
211
212         /*
213          * Synchronize with master thread, then loop
214          */
215         pthread_mutex_lock(&MasterLock);
216         pthread_mutex_unlock(&MasterLock);
217
218         pkt = malloc(MAXPKT);
219
220         for (;;) {
221                 /*
222                  * Input side
223                  */
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;
231                         }
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;
243                         }
244                         if (n != bytes)
245                                 break;
246                 } else {
247                         bytes = read(stream->fdin, pkt, MAXPKT);
248                         if (bytes <= 0)
249                                 break;
250                 }
251
252                 /*
253                  * Output side
254                  */
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;
265                 }
266         }
267         free(pkt);
268         close(stream->fdin);
269         close(stream->fdout);
270         pthread_cancel(stream->other->thread);
271         pthread_exit(NULL);
272 }
273
274 /*
275  * vknet_connect() - Connect to local side, optionally find or bridge the tap
276  *                   interface.
277  */
278 static void
279 vknet_connect(ioinfo_t io, const char *localSide, const char *localBridge)
280 {
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;
289
290         tap_unit = -1;
291         tap_fd = -1;
292
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;
301                         }
302                 }
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;
316
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;
323
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;
329                         }
330                 }
331         }
332
333         if (tap_fd < 0) {
334                 err(1, "Unable to connect to %s", localSide);
335                 /* NOT REACHED */
336         }
337
338         fcntl(tap_fd, F_SETFL, 0);
339         io->fdin = tap_fd;
340         io->fdout = tap_fd;
341
342         /*
343          * If this isn't a TAP device we are done.
344          */
345         if (tap_unit < 0)
346                 return;
347
348         /*
349          * Bring up the TAP interface
350          */
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);
355
356         s = socket(AF_INET, SOCK_DGRAM, 0);
357
358 #if 0
359         /*
360          * Set the interface address if in Secure mode.
361          */
362         if (SecureOpt) {
363                 struct sockaddr_in *in;
364
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);
376                 }
377         }
378 #endif
379
380         /*
381          * Turn up the interface
382          */
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);
392                 }
393         }
394
395         /*
396          * If a bridge was specified associate the tap interface with the
397          * bridge.
398          */
399         if (localBridge) {
400                 struct ifbreq ifbr;
401                 struct ifdrv ifd;
402
403                 /*
404                  * Create the bridge if necessary.
405                  */
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);
412                         }
413                 }
414
415
416                 /*
417                  * Add the tap interface to the bridge
418                  */
419                 bzero(&ifbr, sizeof(ifbr));
420                 snprintf(ifbr.ifbr_ifsname, sizeof(ifbr.ifbr_ifsname),
421                          "tap%d", tap_unit);
422
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;
428
429                 if (ioctl(s, SIOCSDRVSPEC, &ifd) < 0) {
430                         if (errno != EEXIST) {
431                                 perror("Unable to add tap ifc to bridge!");
432                                 exit(1);
433                         }
434                 }
435         }
436         close(s);
437 }
438
439 /*
440  * Connect to the remote machine with ssh and set up a stream
441  */
442 static pid_t
443 vknet_execssh(int fdin, int fdout, int compressOpt, 
444               const char *remoteSide, const char *remoteBridge)
445 {
446         char *remoteHost;
447         char *remotePath;
448         const char *av[24];
449         int ac;
450         pid_t pid;
451
452         /*
453          * Fork / parent returns.
454          */
455         if ((pid = fork()) > 0)
456                 return pid;
457         if (pid < 0) {
458                 perror("fork");
459                 exit(1);
460         }
461
462         /*
463          * Setup stdin, stdout
464          */
465         assert(fdin > 2);
466         assert(fdout > 2);
467         dup2(fdin, 0);
468         dup2(fdout, 1);
469         close(fdin);
470         close(fdout);
471
472         /*
473          * Set up arguments
474          */
475         remoteHost = strdup(remoteSide);
476         if ((remotePath = strchr(remoteHost, ':')) != NULL) {
477                 *remotePath++ = 0;
478         } else {
479                 remotePath = strdup("/var/run/vknet");
480         }
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;
496         }
497         av[ac++] = remotePath;
498         av[ac++] = NULL;
499         execv("/usr/bin/ssh", (void *)av);
500         exit(1);
501 }
502
503 /*
504  * Misc
505  */
506 static
507 void
508 usage(void)
509 {
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"
514         );
515         exit(1);
516 }
517