2 * userv service (or standalone program)
3 * for per-user IP subranges.
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
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.
29 * Should be run from userv with no-disconnect-hup.
42 static const char *proto;
43 static unsigned long baseprefix, basemask;
44 static unsigned long localaddr, peeraddr, mtu;
46 static struct exroute {
47 unsigned long prefix, mask;
48 char prefixtxt[ATXTLEN], masktxt[ATXTLEN];
49 } exroutes[MAXEXROUTES];
51 char localtxt[ATXTLEN];
52 char peertxt[ATXTLEN];
54 static void fatal(const char *msg) {
55 fprintf(stderr,"userv-ipif: service: fatal error: %s\n",msg);
59 static void sysfatal(const char *msg) {
60 fprintf(stderr,"userv-ipif: service: fatal system error: %s: %s\n",
61 msg, strerror(errno));
65 static void badusage(void) {
66 fputs("userv-ipif: service: bad usage or permission denied\n",stderr);
70 static char *ip2txt(unsigned long addr, char *buf) {
71 sprintf(buf, "%lu.%lu.%lu.%lu",
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.
91 if (!*argp) { fprintf(stderr,"missing number %s\n",what); badusage(); }
92 rv= strtoul(*argp,&ep,0);
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);
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",
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);
122 static void addrnet_mustdiffer(const char *w1, unsigned long p1, unsigned long m1,
123 const char *w2, unsigned long p2, unsigned long m2) {
127 if ((p1 & mask) != (p2 & mask)) return;
128 fprintf(stderr, "%s %08lx/%08lx overlaps/clashes with %s %08lx/%08lx",
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) {
140 assert(!(~mmask & mprefix));
146 sprintf(whatbuf,"%s byte #%d",what,i);
147 rv |= eat_number(argp,whatbuf, 0,255, i<3 ? "." : endchars, endchar_r);
150 addrnet_mustbein(what,rv,~0UL, mprefix,mmask);
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 */
161 unsigned long prefix, mask;
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);
167 mask= (~0UL << (32-len));
168 if (prefix & ~mask) {
169 fprintf(stderr,"%s prefix %08lx not fully contained in mask %08lx\n",
173 addrnet_mustbein(what,prefix,mask, mprefix,mmask);
175 if (mask_r) *mask_r= mask;
176 if (len_r) *len_r= len;
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 };
183 unsigned long gidmin, gidmax, gidadd;
185 unsigned long routeaddr, routemask, tgid;
186 const char *carg, *gidlist;
187 const char *const *cprotop;
189 char erwhatbuf[100], erwhatbuf2[100];
191 if (argc < NARGS+1) { fputs("too few arguments\n",stderr); badusage(); }
192 if (argc > NARGS+1) { fputs("too many arguments\n",stderr); badusage(); }
195 eat_prefixmask(&carg,"base", 0UL,0UL, 0,0, &baseprefix, &basemask, &baselen);
198 if (!strcmp(carg,"*")) {
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);
206 gidlist= getenv("USERV_GID");
207 if (!gidlist) fatal("USERV_GID not set");
209 tgid= eat_number(&gidlist,"userv_gid", 0,gidmaxval, " ",0);
210 if (tgid >= gidmin && tgid <= gidmax) goto gid_found;
215 tgid &= ((1UL << gidlen) - 1);
217 baseprefix |= (tgid << (32-baselen));
218 basemask = (~0UL << (32-baselen));
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);
229 if (!strcmp(carg,"debug")) {
232 for (cprotop= protos_ok;
233 (proto= *cprotop) && strcmp(proto,carg);
235 if (!proto) fatal("invalid protocol");
238 addrnet_mustdiffer("local-addr",localaddr,~0UL, "peer-addr",peeraddr,~0UL);
243 carg++, nexroutes++) {
244 if (nexroutes == MAXEXROUTES) {
245 fprintf(stderr,"only %d extra routes allowed\n",MAXEXROUTES);
246 fatal("too many extra routes");
248 sprintf(erwhatbuf,"route %d",nexroutes+1);
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);
258 exroutes[nexroutes].prefix= routeaddr;
259 exroutes[nexroutes].mask= routemask;
260 ip2txt(routeaddr,exroutes[nexroutes].prefixtxt);
261 ip2txt(routemask,exroutes[nexroutes].masktxt);
263 ip2txt(localaddr,localtxt);
264 ip2txt(peeraddr,peertxt);
267 char basetxt[ATXTLEN];
269 printf("protocol: debug\n"
270 "base: %08lx/%-2ld == %s/%ld\n"
272 "local: %08lx == %s\n"
273 "peer: %08lx == %s\n"
276 baseprefix, basemask, ip2txt(baseprefix,basetxt), basemask,
282 for (i=0; i<nexroutes; i++) {
283 sprintf(erwhatbuf2, "route %d:", i+1);
284 printf("%-9s %08lx/%-2ld == %s/%s\n",
286 exroutes[i].prefix, exroutes[i].mask,
287 exroutes[i].prefixtxt, exroutes[i].masktxt);
289 if (ferror(stdout) || fclose(stdout)) sysfatal("flush stdout");