summaryrefslogtreecommitdiff
path: root/ipif/service.c
diff options
context:
space:
mode:
authorian <ian>1999-09-18 20:12:47 +0000
committerian <ian>1999-09-18 20:12:47 +0000
commit1c1a9fa137045a9ac8cca121cd7ed89be5fe43e0 (patch)
treeb5d0a0cb14e231c7b87e01753886608450aa35af /ipif/service.c
parent3ef5750459cf85928dfb275b2c72001e3553cf3c (diff)
downloaduserv-utils-1c1a9fa137045a9ac8cca121cd7ed89be5fe43e0.tar.gz
Initial checkin. Compiles but doesn't run yet.
Diffstat (limited to 'ipif/service.c')
-rw-r--r--ipif/service.c293
1 files changed, 293 insertions, 0 deletions
diff --git a/ipif/service.c b/ipif/service.c
new file mode 100644
index 0000000..c1c1f71
--- /dev/null
+++ b/ipif/service.c
@@ -0,0 +1,293 @@
+/*
+ * userv service (or standalone program)
+ * for per-user IP subranges.
+ *
+ * This is invoked as root, directly from userv.
+ * Its arguments are supposed to be, in order:
+ * <base-prefix>/<base-prefix-len>
+ * Specifies the base address and prefix to restrict the
+ * addresses used to.
+ * <gid-min>-<gid-max>:<gid-add>/<gid-mask>[,...]
+ * The ranges specified by <gid-min> are checked <gid-max> until
+ * one is found which matches at least one gid in USERV_GID.
+ * Then the gid will have <gid-add> added to it and will then be
+ * masked so that it is <gid-mask> long (higher set bits are
+ * discarded). The result is added to the base prefix. It is an
+ * error for no gid to match. Alternatively, if this argument
+ * is `*' then USERV_GID is not checked.
+ * -- Indicates that the remaining arguments are user-supplied
+ * and therefore untrusted.
+ * <local-addr>,<peer-addr>,<mtu>,<proto>
+ * As for slattach. Supported protocols are slip, cslip, and
+ * adaptive. Alternatively, set to `debug' to print debugging
+ * info. <local-addr> is address of the interface on chiark;
+ * <peer-addr> is the address of the point-to-point peer.
+ * <prefix>/<mask>,<prefix>/<mask>,...
+ * List of additional routes to add for this interface.
+ * May be the empty argument.
+ *
+ * Should be run from userv with no-disconnect-hup.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+
+#define NARGS 5
+#define MAXEXROUTES 5
+#define ATXTLEN 12
+
+static const char *proto;
+static unsigned long baseprefix, basemask;
+static unsigned long localaddr, peeraddr, mtu;
+static int nexroutes;
+static struct exroute {
+ unsigned long prefix, mask;
+ char prefixtxt[ATXTLEN], masktxt[ATXTLEN];
+} exroutes[MAXEXROUTES];
+
+char localtxt[ATXTLEN];
+char peertxt[ATXTLEN];
+
+static void fatal(const char *msg) {
+ fprintf(stderr,"userv-ipif: service: fatal error: %s\n",msg);
+ exit(8);
+}
+
+static void sysfatal(const char *msg) {
+ fprintf(stderr,"userv-ipif: service: fatal system error: %s: %s\n",
+ msg, strerror(errno));
+ exit(12);
+}
+
+static void badusage(void) {
+ fputs("userv-ipif: service: bad usage or permission denied\n",stderr);
+ exit(16);
+}
+
+static char *ip2txt(unsigned long addr, char *buf) {
+ sprintf(buf, "%lu.%lu.%lu.%lu",
+ (addr>>24) & 0x0ff,
+ (addr>>16) & 0x0ff,
+ (addr>>8) & 0x0ff,
+ (addr) & 0x0ff);
+ return buf;
+}
+
+static unsigned long eat_number(const char **argp, const char *what,
+ unsigned long min, unsigned long max,
+ const char *endchars, int *endchar_r) {
+ /* If !endchars then the endchar must be a nul, otherwise it may be
+ * a nul (resulting in *argp set to 0) or something else (*argp set
+ * to point to after delim, *endchar_r set to delim).
+ * *endchar_r may be 0.
+ */
+ unsigned long rv;
+ char *ep;
+ int endchar;
+
+ if (!*argp) { fprintf(stderr,"missing number %s\n",what); badusage(); }
+ rv= strtoul(*argp,&ep,0);
+ if ((endchar= *ep)) {
+ if (!endchars) { fprintf(stderr,"junk after number %s\n",what); badusage(); }
+ if (!strchr(endchars,endchar)) {
+ fprintf(stderr,"invalid delimiter %c after number %s: expected %s (or none?)\n",
+ endchar,what,endchars);
+ badusage();
+ }
+ *argp= ep+1;
+ } else {
+ *argp= 0;
+ }
+ if (endchar_r) *endchar_r= endchar;
+ if (rv < min || rv > max) {
+ fprintf(stderr,"number %s value %lu out of range %lu..%lu",
+ what, rv, min, max);
+ badusage();
+ }
+ return rv;
+}
+
+static void addrnet_mustbein(const char *what,
+ unsigned long prefix, unsigned long mask,
+ unsigned long mprefix, unsigned long mmask) {
+ if (!(~mask & mmask) && (prefix & mmask) == mprefix) return;
+ fprintf(stderr, "%s %08lx/%08lx not in required subspace %08lx/%08lx\n",
+ what, prefix, mask, mprefix, mmask);
+ badusage();
+}
+
+static void addrnet_mustdiffer(const char *w1, unsigned long p1, unsigned long m1,
+ const char *w2, unsigned long p2, unsigned long m2) {
+ unsigned long mask;
+
+ mask= m1&m2;
+ if ((p1 & mask) != (p2 & mask)) return;
+ fprintf(stderr, "%s %08lx/%08lx overlaps/clashes with %s %08lx/%08lx",
+ w1,p1,m1, w2,p2,m2);
+ badusage();
+}
+
+static unsigned long eat_addr(const char **argp, const char *what,
+ unsigned long mprefix, unsigned long mmask,
+ const char *endchars, int *endchar_r) {
+ char whatbuf[100];
+ unsigned long rv;
+ int i;
+
+ assert(!(~mmask & mprefix));
+
+ for (rv=0, i=0;
+ i<4;
+ i++) {
+ rv <<= 8;
+ sprintf(whatbuf,"%s byte #%d",what,i);
+ rv |= eat_number(argp,whatbuf, 0,255, i<3 ? "." : endchars, endchar_r);
+ }
+
+ addrnet_mustbein(what,rv,~0UL, mprefix,mmask);
+ return rv;
+}
+
+static void eat_prefixmask(const char **argp, const char *what,
+ unsigned long mprefix, unsigned long mmask,
+ const char *endchars, int *endchar_r,
+ unsigned long *prefix_r, unsigned long *mask_r, int *len_r) {
+ /* mask_r and len_r may be 0 */
+ char whatbuf[100];
+ int len;
+ unsigned long prefix, mask;
+
+ prefix= eat_addr(argp,what, 0,0, "/",0);
+ sprintf(whatbuf,"%s length",what);
+ len= eat_number(argp,whatbuf, 0,32, endchars,endchar_r);
+
+ mask= (~0UL << (32-len));
+ if (prefix & ~mask) {
+ fprintf(stderr,"%s prefix %08lx not fully contained in mask %08lx\n",
+ what,prefix,mask);
+ badusage();
+ }
+ addrnet_mustbein(what,prefix,mask, mprefix,mmask);
+ *prefix_r= prefix;
+ if (mask_r) *mask_r= mask;
+ if (len_r) *len_r= len;
+}
+
+int main(int argc, const char *const *argv) {
+ static unsigned long gidmaxval= (unsigned long)((gid_t)-2);
+ static const char *const protos_ok[]= { "slip", "cslip", "adaptive", 0 };
+
+ unsigned long gidmin, gidmax, gidadd;
+ int baselen;
+ unsigned long routeaddr, routemask, tgid;
+ const char *carg, *gidlist;
+ const char *const *cprotop;
+ int gidlen, i;
+ char erwhatbuf[100], erwhatbuf2[100];
+
+ if (argc < NARGS+1) { fputs("too few arguments\n",stderr); badusage(); }
+ if (argc > NARGS+1) { fputs("too many arguments\n",stderr); badusage(); }
+
+ carg= *++argv;
+ eat_prefixmask(&carg,"base", 0UL,0UL, 0,0, &baseprefix, &basemask, &baselen);
+
+ carg= *++argv;
+ if (!strcmp(carg,"*")) {
+ for (;;) {
+ if (!*carg) fatal("no gid authorised");
+ gidmin= eat_number(&carg,"gid-min", 0,gidmaxval, "-",0);
+ gidmax= eat_number(&carg,"gid-max", gidmin,gidmaxval, ":",0);
+ gidadd= eat_number(&carg,"gid-add", 0,gidmaxval, "/",0);
+ gidlen= eat_number(&carg,"gid-len", 0,32-baselen, 0,0);
+
+ gidlist= getenv("USERV_GID");
+ if (!gidlist) fatal("USERV_GID not set");
+ while (gidlist) {
+ tgid= eat_number(&gidlist,"userv_gid", 0,gidmaxval, " ",0);
+ if (tgid >= gidmin && tgid <= gidmax) goto gid_found;
+ }
+ }
+ gid_found:
+ tgid += gidadd;
+ tgid &= ((1UL << gidlen) - 1);
+ baselen += gidlen;
+ baseprefix |= (tgid << (32-baselen));
+ basemask = (~0UL << (32-baselen));
+ } else {
+ tgid= 0;
+ }
+
+ carg= *++argv;
+
+ localaddr= eat_addr(&carg,"local-addr", baseprefix,basemask, ",",0);
+ peeraddr= eat_addr(&carg,"peer-addr", baseprefix,basemask, ",",0);
+ mtu= eat_number(&carg,"mtu", 576,65536, ",",0);
+
+ if (!strcmp(carg,"debug")) {
+ proto= 0;
+ } else {
+ for (cprotop= protos_ok;
+ (proto= *cprotop) && strcmp(proto,carg);
+ cprotop++);
+ if (!proto) fatal("invalid protocol");
+ }
+
+ addrnet_mustdiffer("local-addr",localaddr,~0UL, "peer-addr",peeraddr,~0UL);
+
+ carg= *++argv;
+ for (nexroutes=0;
+ *carg;
+ carg++, nexroutes++) {
+ if (nexroutes == MAXEXROUTES) {
+ fprintf(stderr,"only %d extra routes allowed\n",MAXEXROUTES);
+ fatal("too many extra routes");
+ }
+ sprintf(erwhatbuf,"route %d",nexroutes+1);
+
+ eat_prefixmask(&carg,erwhatbuf, baseprefix,basemask, ",",0, &routeaddr,&routemask,0);
+ addrnet_mustdiffer(erwhatbuf,routeaddr,routemask, "local-addr",localaddr,~0UL);
+ addrnet_mustdiffer(erwhatbuf,routeaddr,routemask, "peer-addr",peeraddr,~0UL);
+ for (i=0; i<nexroutes; i++) {
+ sprintf(erwhatbuf2,"route %d",i+1);
+ addrnet_mustdiffer(erwhatbuf,routeaddr,routemask,
+ erwhatbuf2,exroutes[i].prefix,exroutes[i].mask);
+ }
+ exroutes[nexroutes].prefix= routeaddr;
+ exroutes[nexroutes].mask= routemask;
+ ip2txt(routeaddr,exroutes[nexroutes].prefixtxt);
+ ip2txt(routemask,exroutes[nexroutes].masktxt);
+ }
+ ip2txt(localaddr,localtxt);
+ ip2txt(peeraddr,peertxt);
+
+ if (!proto) {
+ char basetxt[ATXTLEN];
+
+ printf("protocol: debug\n"
+ "base: %08lx/%-2ld == %s/%ld\n"
+ "auth gid: %ld"
+ "local: %08lx == %s\n"
+ "peer: %08lx == %s\n"
+ "mtu: %ld"
+ "routes: %d",
+ baseprefix, basemask, ip2txt(baseprefix,basetxt), basemask,
+ tgid,
+ localaddr, localtxt,
+ peeraddr, peertxt,
+ mtu,
+ nexroutes);
+ for (i=0; i<nexroutes; i++) {
+ sprintf(erwhatbuf2, "route %d:", i+1);
+ printf("%-9s %08lx/%-2ld == %s/%s\n",
+ erwhatbuf,
+ exroutes[i].prefix, exroutes[i].mask,
+ exroutes[i].prefixtxt, exroutes[i].masktxt);
+ }
+ if (ferror(stdout) || fclose(stdout)) sysfatal("flush stdout");
+ exit(0);
+ }
+ abort();
+}