chiark / gitweb /
Initial checkin. Compiles but doesn't run yet.
[userv-utils.git] / ipif / service.c
1 /*
2  * userv service (or standalone program)
3  * for per-user IP subranges.
4  *
5  * This is invoked as root, directly from userv.
6  * Its arguments are supposed to be, in order:
7  *  <base-prefix>/<base-prefix-len>
8  *      Specifies the base address and prefix to restrict the
9  *      addresses used to.
10  *  <gid-min>-<gid-max>:<gid-add>/<gid-mask>[,...]
11  *      The ranges specified by <gid-min> are checked <gid-max> until
12  *      one is found which matches at least one gid in USERV_GID.
13  *      Then the gid will have <gid-add> added to it and will then be
14  *      masked so that it is <gid-mask> long (higher set bits are
15  *      discarded).  The result is added to the base prefix.  It is an
16  *      error for no gid to match.  Alternatively, if this argument
17  *      is `*' then USERV_GID is not checked.
18  *  --  Indicates that the remaining arguments are user-supplied
19  *      and therefore untrusted.
20  *  <local-addr>,<peer-addr>,<mtu>,<proto>
21  *      As for slattach.  Supported protocols are slip, cslip, and
22  *      adaptive.  Alternatively, set to `debug' to print debugging
23  *      info.  <local-addr> is address of the interface on chiark;
24  *      <peer-addr> is the address of the point-to-point peer.
25  *  <prefix>/<mask>,<prefix>/<mask>,...
26  *      List of additional routes to add for this interface.
27  *      May be the empty argument.
28  *
29  * Should be run from userv with no-disconnect-hup.
30  */
31
32 #include <stdio.h>
33 #include <string.h>
34 #include <stdlib.h>
35 #include <assert.h>
36 #include <errno.h>
37
38 #define NARGS 5
39 #define MAXEXROUTES 5
40 #define ATXTLEN 12
41
42 static const char *proto;
43 static unsigned long baseprefix, basemask;
44 static unsigned long localaddr, peeraddr, mtu;
45 static int nexroutes;
46 static struct exroute {
47   unsigned long prefix, mask;
48   char prefixtxt[ATXTLEN], masktxt[ATXTLEN];
49 } exroutes[MAXEXROUTES];
50
51 char localtxt[ATXTLEN];
52 char peertxt[ATXTLEN];
53
54 static void fatal(const char *msg) {
55   fprintf(stderr,"userv-ipif: service: fatal error: %s\n",msg);
56   exit(8);
57 }
58   
59 static void sysfatal(const char *msg) {
60   fprintf(stderr,"userv-ipif: service: fatal system error: %s: %s\n",
61           msg, strerror(errno));
62   exit(12);
63 }
64   
65 static void badusage(void) {
66   fputs("userv-ipif: service: bad usage or permission denied\n",stderr);
67   exit(16);
68 }
69
70 static char *ip2txt(unsigned long addr, char *buf) {
71   sprintf(buf, "%lu.%lu.%lu.%lu",
72           (addr>>24) & 0x0ff,
73           (addr>>16) & 0x0ff,
74           (addr>>8) & 0x0ff,
75           (addr) & 0x0ff);
76   return buf;
77 }
78
79 static unsigned long eat_number(const char **argp, const char *what,
80                                 unsigned long min, unsigned long max,
81                                 const char *endchars, int *endchar_r) {
82   /* If !endchars then the endchar must be a nul, otherwise it may be
83    * a nul (resulting in *argp set to 0) or something else (*argp set
84    * to point to after delim, *endchar_r set to delim).
85    * *endchar_r may be 0.
86    */
87   unsigned long rv;
88   char *ep;
89   int endchar;
90
91   if (!*argp) { fprintf(stderr,"missing number %s\n",what); badusage(); }
92   rv= strtoul(*argp,&ep,0);
93   if ((endchar= *ep)) {
94     if (!endchars) { fprintf(stderr,"junk after number %s\n",what); badusage(); }
95     if (!strchr(endchars,endchar)) {
96       fprintf(stderr,"invalid delimiter %c after number %s: expected %s (or none?)\n",
97               endchar,what,endchars);
98       badusage();
99     }
100     *argp= ep+1;
101   } else {
102     *argp= 0;
103   }
104   if (endchar_r) *endchar_r= endchar;
105   if (rv < min || rv > max) {
106     fprintf(stderr,"number %s value %lu out of range %lu..%lu",
107             what, rv, min, max);
108     badusage();
109   }
110   return rv;
111 }
112
113 static void addrnet_mustbein(const char *what,
114                              unsigned long prefix, unsigned long mask,
115                              unsigned long mprefix, unsigned long mmask) {
116   if (!(~mask & mmask) && (prefix & mmask) == mprefix) return;
117   fprintf(stderr, "%s %08lx/%08lx not in required subspace %08lx/%08lx\n",
118           what, prefix, mask, mprefix, mmask);
119   badusage();
120 }
121
122 static void addrnet_mustdiffer(const char *w1, unsigned long p1, unsigned long m1,
123                                const char *w2, unsigned long p2, unsigned long m2) {
124   unsigned long mask;
125
126   mask= m1&m2;
127   if ((p1 & mask) != (p2 & mask)) return;
128   fprintf(stderr, "%s %08lx/%08lx overlaps/clashes with %s %08lx/%08lx",
129           w1,p1,m1, w2,p2,m2);
130   badusage();
131 }
132   
133 static unsigned long eat_addr(const char **argp, const char *what,
134                               unsigned long mprefix, unsigned long mmask,
135                               const char *endchars, int *endchar_r) {
136   char whatbuf[100];
137   unsigned long rv;
138   int i;
139
140   assert(!(~mmask & mprefix));
141
142   for (rv=0, i=0;
143        i<4;
144        i++) {
145     rv <<= 8;
146     sprintf(whatbuf,"%s byte #%d",what,i);
147     rv |= eat_number(argp,whatbuf, 0,255, i<3 ? "." : endchars, endchar_r);
148   }
149
150   addrnet_mustbein(what,rv,~0UL, mprefix,mmask);
151   return rv;
152 }
153
154 static void eat_prefixmask(const char **argp, const char *what,
155                            unsigned long mprefix, unsigned long mmask,
156                            const char *endchars, int *endchar_r,
157                            unsigned long *prefix_r, unsigned long *mask_r, int *len_r) {
158   /* mask_r and len_r may be 0 */
159   char whatbuf[100];
160   int len;
161   unsigned long prefix, mask;
162
163   prefix= eat_addr(argp,what, 0,0, "/",0);
164   sprintf(whatbuf,"%s length",what);
165   len= eat_number(argp,whatbuf, 0,32, endchars,endchar_r);
166
167   mask= (~0UL << (32-len));
168   if (prefix & ~mask) {
169     fprintf(stderr,"%s prefix %08lx not fully contained in mask %08lx\n",
170             what,prefix,mask);
171     badusage();
172   }
173   addrnet_mustbein(what,prefix,mask, mprefix,mmask);
174   *prefix_r= prefix;
175   if (mask_r) *mask_r= mask;
176   if (len_r) *len_r= len;
177 }
178   
179 int main(int argc, const char *const *argv) {
180   static unsigned long gidmaxval= (unsigned long)((gid_t)-2);
181   static const char *const protos_ok[]= { "slip", "cslip", "adaptive", 0 };
182
183   unsigned long gidmin, gidmax, gidadd;
184   int baselen;
185   unsigned long routeaddr, routemask, tgid;
186   const char *carg, *gidlist;
187   const char *const *cprotop;
188   int gidlen, i;
189   char erwhatbuf[100], erwhatbuf2[100];
190   
191   if (argc < NARGS+1) { fputs("too few arguments\n",stderr); badusage(); }
192   if (argc > NARGS+1) { fputs("too many arguments\n",stderr); badusage(); }
193
194   carg= *++argv;
195   eat_prefixmask(&carg,"base", 0UL,0UL, 0,0, &baseprefix, &basemask, &baselen);
196
197   carg= *++argv;
198   if (!strcmp(carg,"*")) {
199     for (;;) {
200       if (!*carg) fatal("no gid authorised");
201       gidmin= eat_number(&carg,"gid-min", 0,gidmaxval, "-",0);
202       gidmax= eat_number(&carg,"gid-max", gidmin,gidmaxval, ":",0);
203       gidadd= eat_number(&carg,"gid-add", 0,gidmaxval, "/",0);
204       gidlen= eat_number(&carg,"gid-len", 0,32-baselen, 0,0);
205
206       gidlist= getenv("USERV_GID");
207       if (!gidlist) fatal("USERV_GID not set");
208       while (gidlist) {
209         tgid= eat_number(&gidlist,"userv_gid", 0,gidmaxval, " ",0);
210         if (tgid >= gidmin && tgid <= gidmax) goto gid_found;
211       }
212     }
213   gid_found:
214     tgid += gidadd;
215     tgid &= ((1UL << gidlen) - 1);
216     baselen += gidlen;
217     baseprefix |= (tgid << (32-baselen));
218     basemask = (~0UL << (32-baselen));
219   } else {
220     tgid= 0;
221   }
222   
223   carg= *++argv;
224
225   localaddr= eat_addr(&carg,"local-addr", baseprefix,basemask, ",",0);
226   peeraddr= eat_addr(&carg,"peer-addr", baseprefix,basemask, ",",0);
227   mtu= eat_number(&carg,"mtu", 576,65536, ",",0);
228   
229   if (!strcmp(carg,"debug")) {
230     proto= 0;
231   } else {
232     for (cprotop= protos_ok;
233          (proto= *cprotop) && strcmp(proto,carg);
234          cprotop++);
235     if (!proto) fatal("invalid protocol");
236   }
237   
238   addrnet_mustdiffer("local-addr",localaddr,~0UL, "peer-addr",peeraddr,~0UL);
239   
240   carg= *++argv;
241   for (nexroutes=0;
242        *carg;
243        carg++, nexroutes++) {
244     if (nexroutes == MAXEXROUTES) {
245       fprintf(stderr,"only %d extra routes allowed\n",MAXEXROUTES);
246       fatal("too many extra routes");
247     }
248     sprintf(erwhatbuf,"route %d",nexroutes+1);
249     
250     eat_prefixmask(&carg,erwhatbuf, baseprefix,basemask, ",",0, &routeaddr,&routemask,0);
251     addrnet_mustdiffer(erwhatbuf,routeaddr,routemask, "local-addr",localaddr,~0UL);
252     addrnet_mustdiffer(erwhatbuf,routeaddr,routemask, "peer-addr",peeraddr,~0UL);
253     for (i=0; i<nexroutes; i++) {
254       sprintf(erwhatbuf2,"route %d",i+1);
255       addrnet_mustdiffer(erwhatbuf,routeaddr,routemask,
256                          erwhatbuf2,exroutes[i].prefix,exroutes[i].mask);
257     }
258     exroutes[nexroutes].prefix= routeaddr;
259     exroutes[nexroutes].mask= routemask;
260     ip2txt(routeaddr,exroutes[nexroutes].prefixtxt);
261     ip2txt(routemask,exroutes[nexroutes].masktxt);
262   }
263   ip2txt(localaddr,localtxt);
264   ip2txt(peeraddr,peertxt);
265   
266   if (!proto) {
267     char basetxt[ATXTLEN];
268     
269     printf("protocol: debug\n"
270            "base:     %08lx/%-2ld == %s/%ld\n"
271            "auth gid: %ld"
272            "local:    %08lx    == %s\n"
273            "peer:     %08lx    == %s\n"
274            "mtu:      %ld"
275            "routes:   %d",
276            baseprefix, basemask, ip2txt(baseprefix,basetxt), basemask,
277            tgid,
278            localaddr, localtxt,
279            peeraddr, peertxt,
280            mtu,
281            nexroutes);
282     for (i=0; i<nexroutes; i++) {
283       sprintf(erwhatbuf2, "route %d:", i+1);
284       printf("%-9s %08lx/%-2ld == %s/%s\n",
285              erwhatbuf,
286              exroutes[i].prefix, exroutes[i].mask,
287              exroutes[i].prefixtxt, exroutes[i].masktxt);
288     }
289     if (ferror(stdout) || fclose(stdout)) sysfatal("flush stdout");
290     exit(0);
291   }
292   abort();
293 }