* <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.
+ * May be the empty argument, or `-' if this is problematic.
*
* <config> is either
* <gid>,<prefix>/<len>[,<junk>]
#include <stdarg.h>
#include <ctype.h>
#include <limits.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
#define NARGS 4
#define MAXEXROUTES 5
-#define ATXTLEN 12
+#define ATXTLEN 16
static const unsigned long gidmaxval= (unsigned long)((gid_t)-2);
static const char *const protos_ok[]= { "slip", "cslip", "adaptive", 0 };
+static const int signals[]= { SIGHUP, SIGINT, SIGTERM, 0 };
static const char *configstr, *proto;
static unsigned long localaddr, peeraddr, mtu;
int lineno;
} *cpplace;
+
+static int slpipe[2], ptmaster, undoslattach;
+static const char *ifname;
+static const char *ptyname;
+
+#define NPIDS 4
+
+static union {
+ struct { pid_t sl, cout, cin, task; } byname;
+ pid_t bynumber[NPIDS];
+} pids;
+sigset_t emptyset, fullset;
+
+
+static int cleantask(void) {
+ pid_t pid;
+
+ pid= fork();
+ if (!pid) return 1;
+ if (pid == (pid_t)-1)
+ perror("userv-ipif: fork for undo slattach failed - cannot clean up properly");
+ return 0;
+}
+
+static void terminate(int estatus) {
+ int i, status;
+ pid_t pid;
+
+ for (i=0; i<NPIDS; i++)
+ if (pids.bynumber[i]) kill(pids.bynumber[i], SIGTERM);
+
+ if (undoslattach) {
+ if (cleantask()) {
+ execlp("slattach", "slattach", "-p", "tty", ptyname, (char*)0);
+ perror("userv-ipif: exec slattach for undo slattach failed");
+ exit(-1);
+ }
+ if (ifname && cleantask()) {
+ execlp("ifconfig", "ifconfig", ifname, "down", (char*)0);
+ perror("userv-ipif: exec ifconfig for undo ifconfig failed");
+ exit(-1);
+ }
+ }
+
+ for (;;) {
+ pid= waitpid(-1,&status,0);
+ if (pid == (pid_t)-1) break;
+ }
+ exit(estatus);
+}
+
+
static void fatal(const char *fmt, ...)
__attribute__((format(printf,1,2)));
static void fatal(const char *fmt, ...) {
fputs("userv-ipif service: fatal error: ",stderr);
vfprintf(stderr, fmt, al);
putc('\n',stderr);
- exit(8);
+ terminate(8);
}
static void sysfatal(const char *fmt, ...)
e= errno;
va_start(al,fmt);
- fputs("userv-ipif service: fatal system error",stderr);
+ fputs("userv-ipif service: fatal system error: ",stderr);
vfprintf(stderr, fmt, al);
- fprintf(stderr,"%s\n", strerror(e));
- exit(12);
+ fprintf(stderr,": %s\n", strerror(e));
+ terminate(12);
}
cpp->filename, cpp->lineno);
}
}
- exit(16);
+ terminate(16);
}
static char *ip2txt(unsigned long addr, char *buf) {
char *ep;
int endchar;
- if (!*argp) { badusage("missing number %s\n",what); }
+ if (!*argp) { badusage("missing number %s",what); }
rv= strtoul(*argp,&ep,0);
if ((endchar= *ep)) {
- if (!endchars) badusage("junk after number %s\n",what);
+ if (!endchars) badusage("junk after number %s",what);
if (!strchr(endchars,endchar))
badusage("invalid character or delimiter `%c' in or after number, %s:"
- " expected %s (or none?)\n", endchar,what,endchars);
+ " expected %s (or none?)", endchar,what,endchars);
*argp= ep+1;
} else {
*argp= 0;
len= eat_number(argp,whatbuf, 0,32, endchars,endchar_r);
mask= (~0UL << (32-len));
- if (prefix & ~mask) badusage("%s prefix %08lx not fully contained in mask %08lx\n",
+ if (prefix & ~mask) badusage("%s prefix %08lx not fully contained in mask %08lx",
what,prefix,mask);
*prefix_r= prefix;
if (mask_r) *mask_r= mask;
int i, any;
assert(!(pprefix & ~pmask));
+ any= 0;
if (!proto) fputs("permits",stdout);
if (addrnet_isin(localaddr,~0UL, pprefix,pmask)) {
}
}
if (!proto) {
- if (!any) fputs(" nothing!",stderr);
+ if (!any) fputs(" nothing",stdout);
putchar('\n');
}
}
addrnet_mustdiffer("local-addr",localaddr,~0UL, "peer-addr",peeraddr,~0UL);
carg= *++argv;
- for (nexroutes=0;
- carg;
- nexroutes++) {
- if (nexroutes == MAXEXROUTES)
- fatal("too many extra routes (only %d allowed)",MAXEXROUTES);
- sprintf(erwhatbuf,"route#%d",nexroutes);
+ if (strcmp(carg,"-")) {
+ for (nexroutes=0;
+ carg && *carg;
+ nexroutes++) {
+ if (nexroutes == MAXEXROUTES)
+ fatal("too many extra routes (only %d allowed)",MAXEXROUTES);
+ sprintf(erwhatbuf,"route#%d",nexroutes);
- eat_prefixmask(&carg,erwhatbuf, ",",0, &routeaddr,&routemask,0);
- if (routemask == ~0UL) {
- 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);
- addrnet_mustdiffer(erwhatbuf,routeaddr,routemask,
- erwhatbuf2,exroutes[i].prefix,exroutes[i].mask);
+ eat_prefixmask(&carg,erwhatbuf, ",",0, &routeaddr,&routemask,0);
+ if (routemask == ~0UL) {
+ 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);
+ addrnet_mustdiffer(erwhatbuf,routeaddr,routemask,
+ erwhatbuf2,exroutes[i].prefix,exroutes[i].mask);
+ }
+ exroutes[nexroutes].prefix= routeaddr;
+ exroutes[nexroutes].mask= routemask;
+ exroutes[nexroutes].allow= 0;
+ ip2txt(routeaddr,exroutes[nexroutes].prefixtxt);
+ ip2txt(routemask,exroutes[nexroutes].masktxt);
}
- exroutes[nexroutes].prefix= routeaddr;
- exroutes[nexroutes].mask= routemask;
- exroutes[nexroutes].allow= 0;
- ip2txt(routeaddr,exroutes[nexroutes].prefixtxt);
- ip2txt(routemask,exroutes[nexroutes].masktxt);
}
+
ip2txt(localaddr,localtxt);
ip2txt(peeraddr,peertxt);
}
exit(0);
}
+
+static void setsigmask(const sigset_t *ss) {
+ int r;
+
+ r= sigprocmask(SIG_SETMASK, ss, 0);
+ if (r) sysfatal("[un]block signals");
+}
+
+static void setsignals(void (*handler)(int), struct sigaction *sa, int chldflags) {
+ const int *signalp;
+ int r, sig;
+
+ sa->sa_handler= handler;
+ sa->sa_flags= 0;
+ for (signalp=signals; (sig=*signalp); signalp++) {
+ r= sigaction(sig, sa, 0); if (r) sysfatal("uncatch signal");
+ }
+ sa->sa_flags= chldflags;
+ r= sigaction(SIGCHLD, sa, 0); if (r) sysfatal("uncatch children");
+}
+
+static void infork(void) {
+ struct sigaction sa;
+
+ memset(&pids,0,sizeof(pids));
+ sigemptyset(&sa.sa_mask);
+ setsignals(SIG_DFL,&sa,0);
+ setsigmask(&emptyset);
+ undoslattach= 0;
+}
+
+static pid_t makesubproc(void (*entry)(void)) {
+ pid_t pid;
+
+ pid= fork(); if (pid == (pid_t)-1) sysfatal("fork for subprocess");
+ if (pid) return pid;
+
+ infork();
+ entry();
+ abort();
+}
+
+static int task(void) {
+ pid_t pid;
+
+ pid= fork();
+ if (pid == (pid_t)-1) sysfatal("fork for task");
+ if (!pid) { infork(); return 1; }
+
+ pids.byname.task= pid;
+ while (pids.byname.task) sigsuspend(&emptyset);
+ return 0;
+}
+
+static void mdup2(int fd1, int fd2, const char *what) {
+ int r;
+
+ for (;;) {
+ r= dup2(fd1,fd2); if (r==fd2) return;
+ if (r!=-1) fatal("dup2 in %s gave wrong answer %d instead of %d",what,r,fd2);
+ if (errno != EINTR) sysfatal("dup2 failed in %s",what);
+ }
+}
+
+static void sl_entry(void) {
+ mdup2(slpipe[1],1,"slattach child");
+ execlp("slattach", "slattach", "-v", "-L", "-p",proto, ptyname, (char*)0);
+ sysfatal("cannot exec slattach");
+}
+
+static void cin_entry(void) {
+ mdup2(ptmaster,1,"cat input child");
+ execlp("cat", "cat", (char*)0);
+ sysfatal("cannot exec cat input");
+}
+
+static void cout_entry(void) {
+ mdup2(ptmaster,0,"cat output child");
+ execlp("cat", "cat", (char*)0);
+ sysfatal("cannot exec cat output");
+}
+
+static void sighandler(int signum) {
+ pid_t pid;
+ int estatus, status;
+ const char *taskfail;
+
+ estatus= 4;
+
+ if (signum == SIGCHLD) {
+ for (;;) {
+ pid= waitpid(-1,&status,WNOHANG);
+ if (!pid || pid == (pid_t)-1) return;
+
+ if (pid == pids.byname.task) {
+ pids.byname.task= 0;
+ if (!status) return;
+ taskfail= "task";
+ } else if (pid == pids.byname.cin) {
+ pids.byname.cin= 0;
+ if (status) {
+ taskfail= "input cat";
+ } else {
+ taskfail= 0;
+ estatus= 0;
+ }
+ } else if (pid == pids.byname.cout) {
+ pids.byname.cout= 0;
+ taskfail= "output cat";
+ } else if (pid == pids.byname.sl) {
+ pids.byname.sl= 0;
+ taskfail= "slattach";
+ } else {
+ continue;
+ }
+ break;
+ }
+ if (taskfail) {
+ if (WIFEXITED(status)) {
+ fprintf(stderr,
+ "userv-ipif service: %s unexpectedly exited with exit status %d\n",
+ taskfail, WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ fprintf(stderr,
+ "userv-ipif service: %s unexpectedly killed by signal %s%s\n",
+ taskfail, strsignal(WTERMSIG(status)),
+ WCOREDUMP(status) ? " (core dumped)" : "");
+ } else {
+ fprintf(stderr, "userv-ipif service: %s unexpectedly terminated"
+ " with unknown status code %d\n", taskfail, status);
+ }
+ }
+ } else {
+ fprintf(stderr,
+ "userv-ipif service: received signal %d, terminating\n",
+ signum);
+ }
+
+ terminate(estatus);
+}
+
+static void startup(void) {
+ int r;
+ struct sigaction sa;
+
+ sigfillset(&fullset);
+ sigemptyset(&emptyset);
+
+ ptmaster= getpt(); if (ptmaster==-1) sysfatal("allocate pty master");
+ r= grantpt(ptmaster); if (r) sysfatal("grab/grant pty slave");
+ ptyname= ptsname(ptmaster); if (!ptyname) sysfatal("get pty slave name");
+ r= chmod(ptyname,0600); if (r) sysfatal("chmod pty slave");
+ r= unlockpt(ptmaster); if (r) sysfatal("unlock pty");
+
+ sigfillset(&sa.sa_mask);
+ setsignals(sighandler,&sa,SA_NOCLDSTOP);
+ setsigmask(&fullset);
+}
+
+static void startslattach(void) {
+ static char ifnbuf[200];
+
+ FILE *piper;
+ int r, l, k;
+
+ r= pipe(slpipe); if (r) sysfatal("create pipe");
+ piper= fdopen(slpipe[0],"r"); if (!piper) sysfatal("fdopen pipe");
+
+ undoslattach= 1;
+ pids.byname.sl= makesubproc(sl_entry);
+
+ close(slpipe[1]);
+ setsigmask(&emptyset);
+ if (!fgets(ifnbuf,sizeof(ifnbuf),piper)) {
+ if (ferror(piper)) sysfatal("cannot read ifname from slattach");
+ else fatal("cannot read ifname from slattach");
+ }
+ setsigmask(&fullset);
+ l= strlen(ifnbuf);
+ if (l<=0 || ifnbuf[l-1] != '\n') fatal("slattach gave strange output `%s'",ifnbuf);
+ ifnbuf[l-1]= 0;
+ for (k=l; k>0 && ifnbuf[k-1]!=' '; k--);
+ ifname= ifnbuf+k;
+}
+
+static void netconfigure(void) {
+ char mtutxt[100];
+ int i;
+
+ if (task()) {
+ sprintf(mtutxt,"%lu",mtu);
+
+ execlp("ifconfig", "ifconfig", ifname, localtxt,
+ "netmask","255.255.255.255", "-broadcast", "pointopoint",peertxt,
+ "mtu",mtutxt, "up", (char*)0);
+ sysfatal("cannot exec ifconfig");
+ }
+
+ for (i=0; i<nexroutes; i++) {
+ if (task()) {
+ execlp("route","route", "add", "-net",exroutes[i].prefixtxt,
+ "netmask",exroutes[i].masktxt,
+ "gw",peertxt, "dev",ifname, (char*)0);
+ sysfatal("cannot exec route (for route)");
+ }
+ }
+}
+
+static void copydata(void) __attribute__((noreturn));
+static void copydata(void) {
+ int r;
+
+ pids.byname.cin= makesubproc(cin_entry);
+ for (;;) {
+ r= write(1, "\300", 1); if (r==1) break;
+ assert(r==-1); if (errno != EINTR) sysfatal("send initial delim to confirm");
+ }
+ pids.byname.cout= makesubproc(cout_entry);
+
+ for (;;) sigsuspend(&emptyset);
+}
+
int main(int argc, const char *const *argv) {
parseargs(argc,argv);
pconfig(configstr,0);
checkpermit();
if (!proto) dumpdebug();
- abort();
+ startup();
+ startslattach();
+ netconfigure();
+ copydata();
}