chiark / gitweb /
Improve the SLIP driver: allow dynamic creation of SLIP interfaces.
[tripe] / tun-slip.c
index 1d1bd14b1ae69a691bb0506d5113b5a2bace3db4..b9914e43f902580270ef2d5de282b99f93a91684 100644 (file)
@@ -33,6 +33,7 @@
 /*----- Static variables --------------------------------------------------*/
 
 static slipif *slipifs;                        /* List of available interfaces */
+static const char *slipcmd;            /* Script to make new interfaces */
 
 /*----- Main code ---------------------------------------------------------*/
 
@@ -71,7 +72,7 @@ static void t_read(int fd, unsigned mode, void *v)
   n = read(fd, buf_t, sizeof(buf_t));
   if (n < 0) {
     if (errno == EINTR ||
-#ifdef EWOULDBLOCK
+#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
        errno == EWOULDBLOCK ||
 #endif
        errno == EAGAIN)
@@ -173,14 +174,20 @@ void tun_init(void)
   unsigned long uli, ulo;
   size_t n;
 
-  /* --- Build the list of available interfaces --- */
-
   if ((p = getenv("TRIPE_SLIPIF")) == 0)
     die(1, "no slip interfaces listed: set TRIPE_SLIPIF");
+
+  /* --- Build the list of available interfaces --- */
+
   dstr_puts(&d, p);
 
   p = d.buf;
   for (;;) {
+    if (*p == '/' || *p == '.') {
+      slipcmd = p;
+      T( trace(T_TUNNEL, "tunnel: declared slip command `%s'", slipcmd); )
+      break;
+    }
     uli = strtoul(p, &q, 0);
     if (uli > INT_MAX || q == p)
       goto whine;
@@ -197,8 +204,11 @@ void tun_init(void)
     sl = CREATE(slipif);
     sl->next = 0;
     sl->ifd = uli;
+    fdflags(sl->ifd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
+    fdflags(sl->ofd, O_NONBLOCK, 0, FD_CLOEXEC, FD_CLOEXEC);
     sl->ofd = ulo;
     sl->name = xmalloc(n + 1);
+    sl->kid = -1;
     sl->f = 0;
     memcpy(sl->name, q + 1, n);
     sl->name[n] = 0;
@@ -229,15 +239,78 @@ whine:
 
 int tun_create(tunnel *t, peer *p)
 {
-  slipif *sl;
+  slipif *sl = 0;
+  int pin[2] = { -1, -1 }, pout[2] = { -1, -1 };
+  pid_t kid = -1;
+  dstr d = DSTR_INIT;
+  unsigned char ch;
   static const char end[] = { SL_END, SL_END };
 
+  /* --- Try to find a spare static interface --- */
+
   for (sl = slipifs; sl; sl = sl->next) {
-    if (!(sl->f & SLIPIFF_INUSE))
+    if (!(sl->f & SLIPIFF_INUSE)) {
+      T( trace(T_TUNNEL, "tunnel: %s using static slipif %s",
+              p_name(p), sl->name); )
       goto found;
+    }
   }
-  a_warn("TUN - slip no-slip-interfaces");
-  return (-1);
+
+  /* --- If no dynamic interfaces are available, give up --- */
+
+  if (!slipcmd) {
+    a_warn("TUN %s slip no-slip-interfaces", p_name(p));
+    goto fail;
+  }
+
+  /* --- Fork off a child process to create a dynamic SLIP interface --- */
+
+  if (pipe(pin) || pipe(pout)) {
+    a_warn("TUN %s slip pipe-error -- %s", p_name(p), strerror(errno));
+    goto fail;
+  }
+  if ((kid = fork()) < 0) {
+    a_warn("TUN %s slip fork-error -- %s", p_name(p), strerror(errno));
+    goto fail;
+  }
+  if (!kid) {
+    close(pin[1]);
+    close(pout[0]);
+    dup2(pin[0], STDIN_FILENO);
+    dup2(pout[1], STDOUT_FILENO);
+    execlp(slipcmd, slipcmd, p_name(p), (char *)0);
+    _exit(127);
+  }
+
+  /* --- Read the interface name --- */
+
+  sl = CREATE(slipif);
+  close(pin[0]); pin[0] = -1;
+  close(pout[1]); pout[1] = -1;
+  for (;;) {
+    errno = EIO;
+    if (read(pout[0], &ch, 1) != 1 || ch == SL_END) {
+      a_warn("TUN %s slip read-ifname-failed -- %s",
+            p_name(p), strerror(errno));
+      goto fail;
+    }
+    if (ch == '\n')
+      break;
+    DPUTC(&d, (char)ch);
+  }
+  DPUTZ(&d);
+  sl->name = xstrdup(d.buf);
+  sl->ifd = pout[0];
+  sl->ofd = pin[1];
+  sl->kid = kid;
+  sl->next = 0;
+  sl->f = SLIPIFF_DYNAMIC;
+  T( trace(T_TUNNEL, "tunnel: %s using dynamic slipif %s",
+          p_name(p), sl->name); )
+  fdflags(pout[0], O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
+  fdflags(pin[1], O_NONBLOCK, 0, FD_CLOEXEC, FD_CLOEXEC);
+
+  /* --- Set up the new tunnel --- */
 
 found:
   t->p = p;
@@ -248,9 +321,20 @@ found:
   sel_initfile(&sel, &t->f, sl->ifd, SEL_READ, t_read, t);
   sel_addfile(&t->f);
   write(sl->ofd, end, sizeof(end));
-  T( trace(T_TUNNEL, "tunnel: attached interface %s to peer `%s'",
-          sl->name, p_name(p)); )
+  dstr_destroy(&d);
   return (0);
+
+  /* --- Tidy up after a failure --- */
+
+fail:
+#define CLOSE(fd) do if (fd != -1) close(fd); while (0)
+  CLOSE(pin[0]); CLOSE(pout[0]);
+  CLOSE(pin[1]); CLOSE(pout[1]);
+#undef CLOSE
+  if (kid != -1) kill(kid, SIGTERM);
+  if (sl && (sl->f & SLIPIFF_DYNAMIC)) DESTROY(sl);
+  dstr_destroy(&d);
+  return (-1);
 }
 
 /* --- @tun_ifname@ --- *
@@ -260,10 +344,7 @@ found:
  * Returns:    A pointer to the tunnel's interface name.
  */
 
-const char *tun_ifname(tunnel *t)
-{
-  return (t->sl->name);
-}
+const char *tun_ifname(tunnel *t) { return (t->sl->name); }
 
 /* --- @tun_inject@ --- *
  *
@@ -314,11 +395,21 @@ void tun_inject(tunnel *t, buf *b)
 
 void tun_destroy(tunnel *t)
 {
+  slipif *sl = t->sl;
+
   /* --- If it reported EOF, leave it out-of-action --- */
 
   if (!(t->st & SLIPST_EOF)) {
     sel_rmfile(&t->f);
-    t->sl->f &= ~SLIPIFF_INUSE;
+    sl->f &= ~SLIPIFF_INUSE;
+  }
+  if (sl && (sl->f & SLIPIFF_DYNAMIC)) {
+    T( trace(T_TUNNEL, "tunnel: releasing dynamic slipif %s", sl->name); )
+    close(sl->ofd);
+    close(sl->ifd);
+    kill(sl->kid, SIGTERM);
+    xfree(sl->name);
+    DESTROY(sl);
   }
 }