chiark / gitweb /
found on zealot
[userv-utils.git] / ipif / service.c
1 /*
2  * userv service (or standalone program) for per-user IP subranges.
3  *
4  * When invoked appropriately, it creates a point-to-point network
5  * interface with specified parameters.  It arranges for packets sent out
6  * via that interface by the kernel to appear on its own stdout in SLIP or
7  * CSLIP encoding, and packets injected into its own stdin to be given to
8  * the kernel as if received on that interface.  Optionally, additional
9  * routes can be set up to arrange for traffic for other address ranges to
10  * be routed through the new interface.
11  *
12  * This is the service program, which is invoked as root from userv (or may
13  * be invoked firectly).
14  *
15  * Its arguments are supposed to be, in order, as follows:
16  *
17  *  The first two arguments are usually supplied by the userv
18  *  configuration.  See the file `ipif/ipif' in the source tree, which
19  *  is installed in /etc/userv/services.d/ipif by `make install':
20  *
21  *  <config>
22  *
23  *      Specifies address ranges and gids which own them.  The default
24  *      configuration supplies /etc/userv/ipif-networks, which is then read
25  *      for a list of entries, one per line.
26  *
27  *  --
28  *      Serves to separate the user-supplied and therefore untrusted
29  *      arguments from the trusted first argument.
30  *
31  *  The remaining arguments are supplied by the (untrusted) caller:
32  *
33  *  <local-addr>,<peer-addr>,<mtu>,<proto>
34  *
35  *      The supported protocols are:
36  *         slip     transfer IP packets using the SLIP protocol
37  *         debug    simply print debugging info and exit
38  *         counted  our own "counted packet" protocol, see below
39  *
40  *      <local-addr> is address of the interface to be created on the
41  *      local system; <peer-addr> is the address of the point-to-point
42  *      peer.  They must be actual addresses (not hostnames).
43  *
44  *  <prefix>/<mask>,<prefix>/<mask>,...
45  *
46  *      List of additional routes to add for this interface.  routes will
47  *      be set up on the local system arranging for packets for those
48  *      networks to be sent via the created interface.  <prefix> must be an
49  *      IPv4 address, and mask must be an integer (dotted-quad masks are
50  *      not supported).  If no additional routes are to be set up, use `-'
51  *      or supply an empty argument.
52  *
53  * Each <config> item - whether a line in a file such as
54  * /etc/userv/ipif-networks, or the single trusted argument supplied
55  * on the service program command line - is one of:
56  *
57  *   /<config-file-name>
58  *   ./<config-file-name>
59  *   ../<config-file-name>
60  *
61  *      Reads a file which contains lines which are each <config>
62  *      items.
63  *
64  *   <gid>,[=][-|+]<prefix>/<len>(-|+<prefix>/<len>...)[,<junk>]
65  *
66  *      Indicates that <gid> may allocate addresses in the relevant address
67  *      range (<junk> is ignored).  <gid> must be numeric.  To specify a
68  *      single host address, you must specify a mask of /32.  If `=' is
69  *      specified then the specific subrange is only allowed for the local
70  *      endpoint address, but not for remote addresses.
71  *
72  *      More than one range may be given, with each range prefixed
73  *      by + or -.  In this case each address range in the rule will
74  *      scanned in order, and the first range in the rule that matches
75  *      any desired rule will count: if that first matching range is
76  *      prefixed by `+' (or nothing) then the rule applies, if it
77  *      is prefixed by `-' (or nothing matches), the rule does not.
78  *
79  *   *
80  *      Means that anything is to be permitted.  This should not appear in
81  *      /etc/userv/ipif-networks, as that would permit any user on the
82  *      system to create any interfaces with any addresses and routes
83  *      attached.  It is provided so that root can usefully invoke the ipif
84  *      service program directly (not via userv), without needing to set up
85  *      permissions in /etc/userv/ipif-networks.
86  *
87  *   #...
88  *
89  *      Comment.  Blank lines are also ignored.
90  *
91  *   NB: Permission is granted if _any_ config entry matches the request.
92  *
93  * The service program should be run from userv with no-disconnect-hup.
94  */
95 /*
96  * Counted packet protocol:
97  *
98  *  In each direction a bytestream is sent, containing a series
99  *  of messages.
100  *
101  *  Messages are:
102  *     32 bits in network byte order        length L
103  *     8 bits                               message flags F
104  *     24 bits in network byte order        sub-opcode O
105  *     L bytes                              payload
106  *
107  *  Where the flags F are as follows:
108  *     0x01   first 4 bytes of payload are a command tag
109  *             allocated by caller and to be returned by service in response
110  *     0x02   response message indicates an error; opcode is an error code
111  *             and remainder of response depends on error code
112  *
113  *  And the flags and opcode (F | O) combinations are
114  *     0x00 | 0x000000  message is an IPv4 or IPv6 datagram
115  *     0x01 | 0x000001  add local address; payload is tag + V4 or V6 address
116  *     0x01 | 0x000002  remove local address
117  *                       command payload: tag + V4 or V6 address
118  *                       successful response payload: tag only
119  *     0x01 | 0x000003  add route
120  *                       command payload: tag + 1 byte prefix len + addr
121  */
122 /*
123  * Copyright (C) 1999-2000,2003 Ian Jackson
124  * This file is part of ipif, part of userv-utils
125  *
126  * This is free software; you can redistribute it and/or modify it
127  * under the terms of the GNU General Public License as published by
128  * the Free Software Foundation; either version 2 of the License, or
129  * (at your option) any later version.
130  *
131  * This program is distributed in the hope that it will be useful, but
132  * WITHOUT ANY WARRANTY; without even the implied warranty of
133  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
134  * General Public License for more details.
135  *
136  * You should have received a copy of the GNU General Public License
137  * along with userv-utils; if not, write to the Free Software
138  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
139  *
140  * $Id$
141  */
142
143 #include <stdio.h>
144 #include <string.h>
145 #include <stdlib.h>
146 #include <assert.h>
147 #include <errno.h>
148 #include <stdarg.h>
149 #include <ctype.h>
150 #include <limits.h>
151 #include <signal.h>
152 #include <unistd.h>
153 #include <stdint.h>
154 #include <poll.h>
155
156 #include <sys/types.h>
157 #include <sys/wait.h>
158 #include <sys/stat.h>
159
160 #include <sys/types.h>
161 #include <sys/ioctl.h>
162 #include <sys/socket.h>
163
164 #include <sys/stat.h>
165 #include <fcntl.h>
166
167 #include <linux/if.h>
168 #include <linux/if_tun.h>
169
170 #define NARGS 4
171 #define MAXEXROUTES 50
172 #define ATXTLEN 16
173
174 static const unsigned long gidmaxval= (unsigned long)((gid_t)-2);
175 static const char *const protos_ok[]= { "slip", 0 };
176 static const int signals[]= { SIGHUP, SIGINT, SIGTERM, 0 };
177
178 static const char *configstr, *proto;
179 static unsigned long localaddr, peeraddr, mtu;
180 static int localpming, peerpming;
181 static int localallow, peerallow, allallow;
182 static int nexroutes;
183 static struct exroute {
184   unsigned long prefix, mask;
185   int allow, pming;
186   char prefixtxt[ATXTLEN], masktxt[ATXTLEN];
187 } exroutes[MAXEXROUTES];
188
189 static char localtxt[ATXTLEN];
190 static char peertxt[ATXTLEN];
191
192 static struct pplace {
193   struct pplace *parent;
194   const char *filename;
195   int lineno;
196 } *cpplace;
197
198
199 static int tunfd;
200 static char *ifname;
201
202
203 static void terminate(int estatus) {
204   exit(estatus);
205 }
206
207
208 static void fatal(const char *fmt, ...)
209      __attribute__((format(printf,1,2)));
210 static void fatal(const char *fmt, ...) {
211   va_list al;
212   va_start(al,fmt);
213
214   fputs("userv-ipif service: fatal error: ",stderr);
215   vfprintf(stderr, fmt, al);
216   putc('\n',stderr);
217   terminate(8);
218 }
219   
220 static void sysfatal(const char *fmt, ...)
221      __attribute__((format(printf,1,2)));
222 static void sysfatal(const char *fmt, ...) {
223   va_list al;
224   int e;
225
226   e= errno;
227   va_start(al,fmt);
228
229   fputs("userv-ipif service: fatal system error: ",stderr);
230   vfprintf(stderr, fmt, al);
231   fprintf(stderr,": %s\n", strerror(e));
232   terminate(12);
233 }
234
235
236 static void badusage(const char *fmt, ...)
237      __attribute__((format(printf,1,2)));
238 static void badusage(const char *fmt, ...) {
239   va_list al;
240   struct pplace *cpp;
241   
242   if (cpplace) {
243     fprintf(stderr,
244             "userv-ipif service: %s:%d: ",
245             cpplace->filename, cpplace->lineno);
246   } else {
247     fputs("userv-ipif service: invalid usage: ",stderr);
248   }
249   va_start(al,fmt);
250   vfprintf(stderr, fmt, al);
251   putc('\n',stderr);
252
253   if (cpplace) {
254     for (cpp=cpplace->parent; cpp; cpp=cpp->parent) {
255       fprintf(stderr,
256               "userv-ipif service: %s:%d: ... in file included from here\n",
257               cpp->filename, cpp->lineno);
258     }
259   }
260   terminate(16);
261 }
262
263 static char *ip2txt(unsigned long addr, char *buf) {
264   sprintf(buf, "%lu.%lu.%lu.%lu",
265           (addr>>24) & 0x0ff,
266           (addr>>16) & 0x0ff,
267           (addr>>8) & 0x0ff,
268           (addr) & 0x0ff);
269   return buf;
270 }
271
272 static unsigned long eat_number(const char **argp, const char *what,
273                                 unsigned long min, unsigned long max,
274                                 const char *endchars, int *endchar_r) {
275   /* If !endchars then the endchar must be a nul, otherwise it may be
276    * a nul (resulting in *argp set to 0) or something else (*argp set
277    * to point to after delim, *endchar_r set to delim).
278    * *endchar_r may be 0.
279    */
280   unsigned long rv;
281   char *ep;
282   int endchar;
283
284   if (!*argp) { badusage("missing number %s",what); }
285   rv= strtoul(*argp,&ep,0);
286   if ((endchar= *ep)) {
287     if (!endchars) badusage("junk after number %s",what);
288     if (!strchr(endchars,endchar))
289       badusage("invalid character or delimiter `%c' in or after number, %s:"
290                " expected %s (or none?)", endchar,what,endchars);
291     *argp= ep+1;
292   } else {
293     *argp= 0;
294   }
295   if (endchar_r) *endchar_r= endchar;
296   if (rv < min || rv > max) badusage("number %s value %lu out of range %lu..%lu",
297                                      what, rv, min, max);
298   return rv;
299 }
300
301 static int addrnet_overlap(unsigned long p1, unsigned long m1,
302                            unsigned long p2, unsigned long m2) {
303   unsigned long mask;
304
305   mask= m1&m2;
306   return (p1 & mask) == (p2 & mask);
307 }
308
309 static void addrnet_mustdiffer(const char *w1, unsigned long p1, unsigned long m1,
310                                const char *w2, unsigned long p2, unsigned long m2) {
311   if (!addrnet_overlap(p1,m1,p2,m2)) return;
312   badusage("%s %08lx/%08lx overlaps/clashes with %s %08lx/%08lx",
313            w1,p1,m1, w2,p2,m2);
314 }
315   
316 static unsigned long eat_addr(const char **argp, const char *what,
317                               const char *endchars, int *endchar_r) {
318   char whatbuf[100];
319   unsigned long rv;
320   int i;
321
322   for (rv=0, i=0;
323        i<4;
324        i++) {
325     rv <<= 8;
326     sprintf(whatbuf,"%s byte #%d",what,i);
327     rv |= eat_number(argp,whatbuf, 0,255, i<3 ? "." : endchars, endchar_r);
328   }
329
330   return rv;
331 }
332
333 static void eat_prefixmask(const char **argp, const char *what,
334                            const char *endchars, int *endchar_r,
335                            unsigned long *prefix_r, unsigned long *mask_r, int *len_r) {
336   /* mask_r and len_r may be 0 */
337   char whatbuf[100];
338   int len;
339   unsigned long prefix, mask;
340
341   prefix= eat_addr(argp,what, "/",0);
342   sprintf(whatbuf,"%s length",what);
343   len= eat_number(argp,whatbuf, 0,32, endchars,endchar_r);
344
345   mask= len ? (~0UL << (32-len)) : 0UL;
346   if (prefix & ~mask) badusage("%s prefix %08lx not fully contained in mask %08lx",
347                                what,prefix,mask);
348   *prefix_r= prefix;
349   if (mask_r) *mask_r= mask;
350   if (len_r) *len_r= len;
351 }
352
353 static int addrnet_isin(unsigned long prefix, unsigned long mask,
354                         unsigned long mprefix, unsigned long mmask) {
355   return  !(~mask & mmask)  &&  (prefix & mmask) == mprefix;
356 }
357
358 /* Totally hideous algorithm for parsing the config file lines.
359  * For each config file line, we first see if its gid applies.  If not
360  * we skip it.  Otherwise, we do
361  *  permit_begin
362  *     which sets <foo>pming to 1
363  * for each range.  <foo>pming may be 0 if we've determined that
364  * this line does not apply to <foo>.
365  *  permit_range
366  *     which calls permit_range_thing for each <foo>
367  *        which checks to see if <foo> is inside the relevant
368  *        range (for +) or overlaps it (for -) and updates
369  *        <foo>allow and <foo>pming.
370  */
371
372 static void permit_begin(void) {
373   int i;
374   
375   localpming= peerpming= 1;
376   for (i=0; i<nexroutes; i++) exroutes[i].pming= 1;
377 }
378
379 static void permit_range_thing(unsigned long tprefix, unsigned long tmask,
380                                const char *what, int *tallow, int *tpming,
381                                unsigned long pprefix, unsigned long pmask,
382                                int plus, int *any) {
383   if (plus) {
384     if (!addrnet_isin(tprefix,tmask, pprefix,pmask)) return;
385     if (*tpming) *tallow= 1;
386   } else {
387     if (!addrnet_overlap(tprefix,tmask, pprefix,pmask)) return;
388     *tpming= 0;
389   }
390   if (!proto) printf(" %c%s", plus?'+':'-', what);
391   *any= 1;
392 }
393
394 static void permit_range(unsigned long prefix, unsigned long mask,
395                          int plus, int localonly) {
396   int i, any;
397   char idbuf[40];
398   
399   assert(!(prefix & ~mask));
400   any= 0;
401
402   permit_range_thing(localaddr,~0UL,"local", &localallow,&localpming,
403                      prefix,mask, plus,&any);
404
405   if (!localonly) {
406     permit_range_thing(peeraddr,~0UL, "peer-addr", &peerallow,&peerpming,
407                        prefix,mask, plus,&any);
408     for (i=0; i<nexroutes; i++) {
409       sprintf(idbuf,"route#%d",i);
410       permit_range_thing(exroutes[i].prefix,exroutes[i].mask, idbuf,
411                          &exroutes[i].allow,&exroutes[i].pming,
412                          prefix,mask, plus,&any);
413     }
414   }
415   if (!proto)
416     if (!any) fputs(" nothing",stdout);
417 }
418
419 static void pconfig(const char *configstr, int truncated);
420
421 static void pfile(const char *filename) {
422   FILE *file;
423   char buf[PATH_MAX];
424   int l, truncated, c;
425   struct pplace npp, *cpp;
426
427   for (cpp=cpplace; cpp; cpp=cpp->parent) {
428     if (!strcmp(cpp->filename,filename))
429       badusage("recursive configuration file `%s'",filename);
430   }
431
432   file= fopen(filename,"r");
433   if (!file)
434     badusage("cannot open configuration file `%s': %s", filename, strerror(errno));
435
436   if (!proto) printf("config file `%s':\n",filename);
437
438   npp.parent= cpplace;
439   npp.filename= filename;
440   npp.lineno= 0;
441   cpplace= &npp;
442
443   while (fgets(buf, sizeof(buf), file)) {
444     npp.lineno++;
445     l= strlen(buf);
446     if (!l) continue;
447
448     truncated= (buf[l-1] != '\n');
449     while (l>0 && isspace((unsigned char) buf[l-1])) l--;
450     if (!l) continue;
451     buf[l]= 0;
452
453     if (truncated) {
454       while ((c= getc(file)) != EOF && c != '\n');
455       if (c == EOF) break;
456     }
457
458     pconfig(buf,truncated);
459   }
460   if (ferror(file))
461     badusage("failed while reading configuration file: %s", strerror(errno));
462
463   cpplace= npp.parent;
464 }
465
466 static void pconfig(const char *configstr, int truncated) {
467   unsigned long fgid, tgid, pprefix, pmask;
468   int plen, localonly, plus, rangeix, delim;
469   char ptxt[ATXTLEN];
470   char whattxt[100];
471   const char *gidlist;
472   
473   switch (configstr[0]) {
474   case '*':
475     permit_begin();
476     permit_range(0UL,0UL,1,0);
477     return;
478     
479   case '#':
480     return;
481     
482   case '/': case '.':
483     if (truncated) badusage("filename too long (`%.100s...')",configstr);
484     pfile(configstr);
485     return;
486     
487   default:
488     if (!isdigit((unsigned char)configstr[0]))
489       badusage("unknown configuration directive");
490     
491     fgid= eat_number(&configstr,"gid", 0,gidmaxval, ",",0);
492
493     if (!proto) printf(" %5lu", fgid);
494
495     gidlist= getenv("USERV_GID");
496     if (!gidlist) fatal("USERV_GID not set");
497     for (;;) {
498       if (!gidlist) {
499         if (!proto) printf(" no matching gid\n");
500         return;
501       }
502       tgid= eat_number(&gidlist,"userv-gid", 0,gidmaxval, " ",0);
503       if (tgid == fgid) break;
504     }
505
506     if (configstr[0] == '=') {
507       localonly= 1;
508       configstr++;
509     } else {
510       localonly= 0;
511     }
512
513     permit_begin();
514
515     rangeix= 0;
516     plus= 1;
517     switch (configstr[0]) {
518     case '-': plus= 0;     /* fall through */
519     case '+': configstr++;
520     default:;
521     }
522
523     for (;;) {
524       sprintf(whattxt, "%s-prefix#%d",
525               plus ? "permitted" : "notpermitted",
526               rangeix);
527       eat_prefixmask(&configstr,whattxt, ",+-",&delim,
528                      &pprefix,&pmask,&plen);
529       if (!configstr && truncated)
530         badusage("gid,prefix/len,... spec too long");
531
532       if (!proto)
533         printf("  %c%s/%d:", plus?'+':'-',ip2txt(pprefix,ptxt), plen);
534
535       permit_range(pprefix,pmask,plus,localonly);
536       if (delim==',') break;
537
538       plus= delim=='-' ? 0 : 1;
539       rangeix++;
540     }
541
542     putchar('\n');
543     return;
544   }
545 }
546
547 static void checkallow(int allow, const char *what,
548                        const char *prefixtxt, const char *masktxt) {
549   if (allow) return;
550   fprintf(stderr,"userv-ipif service: access denied for %s, %s/%s\n",
551           what, prefixtxt, masktxt);
552   allallow= 0;
553 }
554
555 static void parseargs(int argc, const char *const *argv) {
556   unsigned long routeaddr, routemask;
557   const char *carg;
558   const char *const *cprotop;
559   int i;
560   char erwhatbuf[100], erwhatbuf2[100];
561   
562   if (argc < NARGS+1) { badusage("too few arguments"); }
563   if (argc > NARGS+1) { badusage("too many arguments"); }
564
565   configstr= *++argv;
566   
567   carg= *++argv;
568   if (strcmp(carg,"--")) badusage("separator argument `--' not found, got `%s'",carg);
569
570   carg= *++argv;
571   localaddr= eat_addr(&carg,"local-addr", ",",0);
572   peeraddr= eat_addr(&carg,"peer-addr", ",",0);
573   mtu= eat_number(&carg,"mtu", 576,65536, ",",0);
574   localallow= peerallow= 0;
575   
576   if (!strcmp(carg,"debug")) {
577     proto= 0;
578   } else {
579     for (cprotop= protos_ok;
580          (proto= *cprotop) && strcmp(proto,carg);
581          cprotop++);
582     if (!proto) fatal("invalid protocol");
583   }
584   
585   addrnet_mustdiffer("local-addr",localaddr,~0UL, "peer-addr",peeraddr,~0UL);
586   
587   carg= *++argv;
588   if (strcmp(carg,"-")) {
589     for (nexroutes=0;
590          carg && *carg;
591          nexroutes++) {
592       if (nexroutes == MAXEXROUTES)
593         fatal("too many extra routes (only %d allowed)",MAXEXROUTES);
594       sprintf(erwhatbuf,"route#%d",nexroutes);
595     
596       eat_prefixmask(&carg,erwhatbuf, ",",0, &routeaddr,&routemask,0);
597       if (routemask == ~0UL) {
598         addrnet_mustdiffer(erwhatbuf,routeaddr,routemask, "local-addr",localaddr,~0UL);
599         addrnet_mustdiffer(erwhatbuf,routeaddr,routemask, "peer-addr",peeraddr,~0UL);
600       }
601       for (i=0; i<nexroutes; i++) {
602         sprintf(erwhatbuf2,"route#%d",i);
603         addrnet_mustdiffer(erwhatbuf,routeaddr,routemask,
604                            erwhatbuf2,exroutes[i].prefix,exroutes[i].mask);
605       }
606       exroutes[nexroutes].prefix= routeaddr;
607       exroutes[nexroutes].mask= routemask;
608       exroutes[nexroutes].allow= 0;
609       ip2txt(routeaddr,exroutes[nexroutes].prefixtxt);
610       ip2txt(routemask,exroutes[nexroutes].masktxt);
611     }
612   }
613
614   ip2txt(localaddr,localtxt);
615   ip2txt(peeraddr,peertxt);
616 }
617
618 static void checkpermit(void) {
619   int i;
620   char erwhatbuf[100];
621   
622   allallow= 1;
623   checkallow(localallow,"local-addr", localtxt,"32");
624   checkallow(peerallow,"peer-addr", peertxt,"32");
625   for (i=0; i<nexroutes; i++) {
626     sprintf(erwhatbuf, "route#%d", i);
627     checkallow(exroutes[i].allow, erwhatbuf, exroutes[i].prefixtxt, exroutes[i].masktxt);
628   }
629   if (!allallow) fatal("access denied");
630 }
631
632 static void dumpdebug(void) __attribute__((noreturn));
633 static void dumpdebug(void) {
634   int i;
635   char erwhatbuf[100];
636   
637   printf("protocol: debug\n"
638          "local:    %08lx == %s\n"
639          "peer:     %08lx == %s\n"
640          "mtu:      %ld\n"
641          "routes:   %d\n",
642          localaddr, localtxt,
643          peeraddr, peertxt,
644          mtu,
645          nexroutes);
646   for (i=0; i<nexroutes; i++) {
647     sprintf(erwhatbuf, "route#%d:", i);
648     printf("%-9s %08lx/%08lx == %s/%s\n",
649            erwhatbuf,
650            exroutes[i].prefix, exroutes[i].mask,
651            exroutes[i].prefixtxt, exroutes[i].masktxt);
652   }
653   if (ferror(stdout) || fclose(stdout)) sysfatal("flush stdout");
654   exit(0);
655 }
656
657
658 static int task(const char *desc) {
659   pid_t pid, pidr;
660   int status;
661
662   pid= fork();
663   if (pid == (pid_t)-1) sysfatal("fork for task");
664   if (!pid) return 1;
665
666   for (;;) {
667     pidr= waitpid(pid,&status,0);
668     if (pidr!=(pid_t)-1) break;
669     if (errno==EINTR) continue;
670     sysfatal("waitpid for task");
671   }
672   assert(pidr==pid);
673
674   if (WIFEXITED(status)) {
675     if (WEXITSTATUS(status))
676       fatal("userv-ipif service: %s exited with error exit status %d\n",
677             desc, WEXITSTATUS(status));
678   } else if (WIFSIGNALED(status)) {
679     fatal("userv-ipif service: %s died due to signal %s%s\n",
680           desc, strsignal(WTERMSIG(status)),
681           WCOREDUMP(status) ? " (core dumped)" : "");
682   } else {
683     fatal("userv-ipif service: %s unexpectedly terminated"
684           " with unknown status code %d\n", desc, status);
685   }
686
687   return 0;
688 }
689
690 static void createif(void) {
691   static const char ifnamepat[]= "userv%d";
692   struct ifreq ifr;
693   int r;
694
695   memset(&ifr,0,sizeof(ifr));
696   ifr.ifr_flags= IFF_TUN | IFF_NO_PI;
697
698   assert(sizeof(ifr.ifr_name) >= sizeof(ifnamepat));
699   strcpy(ifr.ifr_name, ifnamepat);
700
701   tunfd= open("/dev/net/tun", O_RDWR);
702   if (!tunfd) sysfatal("open /dev/net/tun");
703
704   r= fcntl(tunfd, F_GETFD);
705   if (r==-1) sysfatal("fcntl(tunfd,F_GETFD)");
706   r= fcntl(tunfd, F_SETFD, r|FD_CLOEXEC);
707   if (r==-1) sysfatal("fcntl(tunfd,F_SETFD,|FD_CLOEXEC)");
708
709   r= ioctl(tunfd, TUNSETIFF, (void*)&ifr);
710   if (r) sysfatal("ioctl TUNSETIFF");
711
712   /* ifr.ifr_name might not be null-terminated.  crazy abi. */
713   ifname= malloc(sizeof(ifr.ifr_name)+1);
714   if (!ifname) sysfatal("malloc for interface name");
715   memcpy(ifname, ifr.ifr_name, sizeof(ifr.ifr_name));
716   ifname[sizeof(ifr.ifr_name)]= 0;
717 }
718
719 static void netconfigure(void) {
720   char mtutxt[100];
721   int i;
722
723   if (task("ifconfig")) {
724     sprintf(mtutxt,"%lu",mtu);
725   
726     execlp("ifconfig", "ifconfig", ifname, localtxt,
727            "netmask","255.255.255.255", "pointopoint",peertxt, "-broadcast",
728            "mtu",mtutxt, "up", (char*)0);
729     sysfatal("cannot exec ifconfig");
730   }
731
732   for (i=0; i<nexroutes; i++) {
733     if (task("route")) {
734       execlp("route","route", "add", "-net",exroutes[i].prefixtxt,
735              "netmask",exroutes[i].masktxt,
736              "gw",peertxt, "dev",ifname, (char*)0);
737       sysfatal("cannot exec route (for route)");
738     }
739   }
740 }
741
742 static void setnonblock(int fd) {
743   int r;
744   r= fcntl(fd,F_GETFL); 
745   if (r==-1) sysfatal("fcntl F_GETFL");
746   r= fcntl(fd,F_SETFL, r|O_NONBLOCK);
747   if (r==-1) sysfatal("fcntl F_SETFL O_NONBLOCK");
748 }
749
750 static void rx_packet(const uint8_t *packet, int len) {
751   for (;;) {
752     int r= write(tunfd, packet, len);
753     if (r<0) {
754       if (errno==EINTR) continue;
755       if (errno==EAGAIN) return; /* oh well */
756       sysfatal("error writing packet to tun (transmitting)");
757     }
758     assert(r==len);
759     return;
760   }
761 }
762
763 static int output_waiting, input_waiting;
764
765 #define SLIP_END     0300
766 #define SLIP_ESC     0333
767 #define SLIP_ESC_END 0334
768 #define SLIP_ESC_ESC 0335
769
770 static void more_rx_data(uint8_t *input_buf, uint8_t *output_buf) {
771   /* we make slip_data never contain continuation of a packet */
772   /* input_buf is passed as a parameter since it's in copydata's stack frame */
773   static int scanned;
774   static int output_len;
775
776   uint8_t *op= output_buf + output_len;
777   const uint8_t *ip= input_buf + scanned;
778   const uint8_t *ip_end= input_buf + input_waiting;
779   int eaten= 0;
780   
781   for (;;) {
782     if (ip>=ip_end) break;
783     uint8_t c= *ip++;
784     if (c==SLIP_END) {
785       rx_packet(output_buf, op-output_buf);
786       op= output_buf;
787       eaten= ip - input_buf;
788       continue;
789     }
790     if (c==SLIP_ESC) {
791       if (ip>=ip_end) { /* rescan this when there's more */ ip--; break; }
792       c= *ip++;
793       if (c==SLIP_ESC_END) c=SLIP_END;
794       else if (c==SLIP_ESC_ESC) c=SLIP_ESC;
795       else fatal("unexpected byte 0%o after SLIP_ESC",c);
796     }
797     if (op == output_buf+mtu)
798       fatal("SLIP packet exceeds mtu");
799     *op++= c;
800   }
801
802   output_len= op - output_buf;
803   scanned= ip - input_buf;
804
805   input_waiting -= eaten;
806   memmove(input_buf, input_buf+eaten, input_waiting);
807   scanned -= eaten;
808 }
809
810 static void tx_packet(uint8_t *output_buf, const uint8_t *ip, int inlen) {
811   /* output_buf is passed as a parameter since it's in copydata's stack frame */
812   assert(!output_waiting);
813   uint8_t *op= output_buf;
814
815   *op++= SLIP_END;
816   while (inlen-- >0) {
817     uint8_t c= *ip++;
818     if (c==SLIP_END) { *op++= SLIP_ESC; *op++= SLIP_ESC_END; }
819     else if (c==SLIP_ESC) { *op++= SLIP_ESC; *op++= SLIP_ESC_ESC; }
820     else *op++= c;
821   }
822   *op++= SLIP_END;
823   assert(op <= output_buf + mtu*2+2);
824
825   output_waiting= op - output_buf;
826 }
827
828 static void copydata(void) __attribute__((noreturn));
829 static void copydata(void) {
830   uint8_t output_buf[mtu*2+2];
831   uint8_t input_buf[mtu*2+2];
832   uint8_t rx_packet_buf[mtu];
833
834   int r, i;
835
836   struct pollfd polls[3];
837   memset(polls, 0, sizeof(polls));
838   
839   polls[0].fd= 0;      polls[0].events= POLLIN;
840   polls[1].fd= 1;
841   polls[2].fd= tunfd;
842
843   /* We don't do flow control on input packets; instead, we just throw
844    * away ones which the kernel doesn't accept.  So we always poll for
845    * those.
846    *
847    * Output packets we buffer, so we poll only as appropriate for those.
848    */
849
850   /* Start by transmitting one END byte to say we're ready. */
851   output_buf[0]= SLIP_END;
852   output_waiting= 1;
853
854   for (;;) {
855     if (output_waiting) {
856       r= write(1, output_buf, output_waiting);
857       if (r<0) {
858         if (errno==EINTR) continue;
859         if (errno!=EAGAIN)
860           sysfatal("error writing SLIP output (packets being received)");
861       } else {
862         assert(r>0);
863         output_waiting -= r;
864         memmove(output_buf, output_buf+r, output_waiting);
865       }
866     }
867     if (output_waiting) {
868       polls[1].events |= POLLOUT;
869       polls[2].events &= ~POLLIN;
870     } else {
871       polls[1].events &= ~POLLOUT;
872       polls[2].events |= POLLIN;
873     }
874     r= poll(polls,3,-1);
875
876     if (r<0) {
877       if (errno==EINTR) continue;
878       sysfatal("poll() failed");
879     }
880     assert(r>0); /* we used an infinite timeout */
881
882     for (i=0; i<sizeof(polls)/sizeof(polls[0]); i++)
883       if (polls[i].revents & ~polls[i].events)
884         fatal("unexpected revents 0x%x for fd=%d",
885               polls[i].revents, polls[i].fd);
886
887     if (polls[0].events & POLLIN) {
888       int want= sizeof(input_buf) - input_waiting;
889       if (want<0) fatal("incoming packet necessarily exceeds MTU");
890       r= read(0, input_buf + input_waiting, want);
891       if (r>0) {
892         input_waiting += r;
893         assert(r < sizeof(input_buf));
894         more_rx_data(input_buf, rx_packet_buf);
895       } else if (r==0) {
896         terminate(0);
897       } else {
898         if (!(errno==EINTR || errno==EAGAIN))
899           sysfatal("error reading input SLIP data (packets to transmit)");
900       }
901     }
902
903     /* We handle what would be (polls[1].events & POLLOUT) above,
904      * unconditionally.  That eliminates the need to poll in the usual case */
905       
906     if (polls[2].events & POLLIN) {
907       uint8_t packet_buf[mtu];
908       r= read(tunfd, packet_buf, mtu);
909       if (r>0) {
910         tx_packet(output_buf, packet_buf, r);
911       } else {
912         assert(r<0);
913         if (!(errno==EAGAIN || errno==EWOULDBLOCK))
914           sysfatal("error reading packet (being transmitted) from tun");
915       }
916     }
917   } 
918 }
919
920 int main(int argc, const char *const *argv) {
921   parseargs(argc,argv);
922   pconfig(configstr,0);
923   checkpermit();
924   if (!proto) dumpdebug();
925
926   createif();
927   netconfigure();
928   setnonblock(tunfd);
929   setnonblock(0);
930   setnonblock(1);
931   copydata();
932 }