Initial checkin. Compiles but doesn't run yet.
[userv-utils] / ipif / service.c
CommitLineData
1c1a9fa1 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
42static const char *proto;
43static unsigned long baseprefix, basemask;
44static unsigned long localaddr, peeraddr, mtu;
45static int nexroutes;
46static struct exroute {
47 unsigned long prefix, mask;
48 char prefixtxt[ATXTLEN], masktxt[ATXTLEN];
49} exroutes[MAXEXROUTES];
50
51char localtxt[ATXTLEN];
52char peertxt[ATXTLEN];
53
54static void fatal(const char *msg) {
55 fprintf(stderr,"userv-ipif: service: fatal error: %s\n",msg);
56 exit(8);
57}
58
59static 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
65static void badusage(void) {
66 fputs("userv-ipif: service: bad usage or permission denied\n",stderr);
67 exit(16);
68}
69
70static 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
79static 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
113static 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
122static 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
133static 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
154static 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
179int 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}