+ allallow= 1;
+ checkallow(localallow,"local-addr", localtxt,"32");
+ checkallow(peerallow,"peer-addr", peertxt,"32");
+ for (i=0; i<nexroutes; i++) {
+ sprintf(erwhatbuf, "route#%d", i);
+ checkallow(exroutes[i].allow, erwhatbuf, exroutes[i].prefixtxt, exroutes[i].masktxt);
+ }
+ if (!allallow) fatal("access denied");
+}
+
+static void dumpdebug(void) __attribute__((noreturn));
+static void dumpdebug(void) {
+ int i;
+ char erwhatbuf[100];
+
+ printf("protocol: debug\n"
+ "local: %08lx == %s\n"
+ "peer: %08lx == %s\n"
+ "mtu: %ld\n"
+ "routes: %d\n",
+ localaddr, localtxt,
+ peeraddr, peertxt,
+ mtu,
+ nexroutes);
+ for (i=0; i<nexroutes; i++) {
+ sprintf(erwhatbuf, "route#%d:", i);
+ printf("%-9s %08lx/%08lx == %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);
+}
+
+
+static int task(const char *desc) {
+ pid_t pid, pidr;
+ int status;
+
+ pid= fork();
+ if (pid == (pid_t)-1) sysfatal("fork for task");
+ if (!pid) return 1;
+
+ for (;;) {
+ pidr= waitpid(pid,&status,0);
+ if (pidr!=(pid_t)-1) break;
+ if (errno==EINTR) continue;
+ sysfatal("waitpid for task");
+ }
+ assert(pidr==pid);
+
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status))
+ fatal("userv-ipif service: %s exited with error exit status %d\n",
+ desc, WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ fatal("userv-ipif service: %s died due to signal %s%s\n",
+ desc, strsignal(WTERMSIG(status)),
+ WCOREDUMP(status) ? " (core dumped)" : "");
+ } else {
+ fatal("userv-ipif service: %s unexpectedly terminated"
+ " with unknown status code %d\n", desc, status);
+ }
+
+ return 0;
+}
+
+static void createif(void) {
+ static const char ifnamepat[]= "userv%d";
+ struct ifreq ifr;
+ int r;
+
+ memset(&ifr,0,sizeof(ifr));
+ ifr.ifr_flags= IFF_TUN | IFF_NO_PI;
+
+ assert(sizeof(ifr.ifr_name) >= sizeof(ifnamepat));
+ strcpy(ifr.ifr_name, ifnamepat);
+
+ tunfd= open("/dev/net/tun", O_RDWR);
+ if (!tunfd) sysfatal("open /dev/net/tun");
+
+ r= fcntl(tunfd, F_GETFD);
+ if (r==-1) sysfatal("fcntl(tunfd,F_GETFD)");
+ r= fcntl(tunfd, F_SETFD, r|FD_CLOEXEC);
+ if (r==-1) sysfatal("fcntl(tunfd,F_SETFD,|FD_CLOEXEC)");
+
+ r= ioctl(tunfd, TUNSETIFF, (void*)&ifr);
+ if (r) sysfatal("ioctl TUNSETIFF");
+
+ /* ifr.ifr_name might not be null-terminated. crazy abi. */
+ ifname= malloc(sizeof(ifr.ifr_name)+1);
+ if (!ifname) sysfatal("malloc for interface name");
+ memcpy(ifname, ifr.ifr_name, sizeof(ifr.ifr_name));
+ ifname[sizeof(ifr.ifr_name)]= 0;
+}
+
+static void netconfigure(void) {
+ char mtutxt[100];
+ int i;
+
+ if (task("ifconfig")) {
+ sprintf(mtutxt,"%lu",mtu);
+
+ execlp("ifconfig", "ifconfig", ifname, localtxt,
+ "netmask","255.255.255.255", "pointopoint",peertxt, "-broadcast",
+ "mtu",mtutxt, "up", (char*)0);
+ sysfatal("cannot exec ifconfig");
+ }
+
+ for (i=0; i<nexroutes; i++) {
+ if (task("route")) {
+ 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 setnonblock(int fd) {
+ int r;
+ r= fcntl(fd,F_GETFL);
+ if (r==-1) sysfatal("fcntl F_GETFL");
+ r= fcntl(fd,F_SETFL, r|O_NONBLOCK);
+ if (r==-1) sysfatal("fcntl F_SETFL O_NONBLOCK");
+}
+
+static void rx_packet(const uint8_t *packet, int len) {
+ if (!len)
+ return;
+ for (;;) {
+ int r= write(tunfd, packet, len);
+ if (r<0) {
+ if (errno==EINTR) continue;
+ if (errno==EAGAIN || errno==ENOMEM) return; /* oh well */
+ sysfatal("error writing packet to tun (transmitting)");