Initial import of binutils 2.22 on the new vendor branch
[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 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