chiark / gitweb /
Improve the SLIP driver: allow dynamic creation of SLIP interfaces.
authormdw <mdw>
Mon, 12 Sep 2005 01:25:47 +0000 (01:25 +0000)
committermdw <mdw>
Mon, 12 Sep 2005 01:25:47 +0000 (01:25 +0000)
doc/tripe.8
doc/tripectl.1
tripe.h
tun-slip.c

index 8ccdc57..7ff497c 100644 (file)
@@ -482,25 +482,54 @@ Though not for the faint of heart, it is possible to get
 .B tripe
 to read and write network packets to a pair of file descriptors using
 SLIP encapsulation.  No fancy header compression of any kind is
-supported.  The intended use is that you allocate a pty pair, set the
-slave's line discipline to SLIP, and hand the
+supported.
+.PP
+Two usage modes are supported: a preallocation system, whereby SLIP
+interfaces are created and passed to the
+.B tripe
+server at startup; and a dynamic system, where the server runs a script
+to allocate a new SLIP interface when it needs one.  It is possible to
+use a mixture of these two modes, starting
 .B tripe
-server the master end, though in fact any old file descriptors will do.
+with a few preallocated interfaces and having it allocate more
+dynamically as it needs them.
+.PP
+The behaviour of
+.BR tripe 's
+SLIP driver is controlled by the
+.B TRIPE_SLIPIF
+environment variable.  The server will fail to start if this variable is
+not defined.  The variable's value is a colon-delimited list of
+preallocated interfaces, followed optionally by the filename of a script
+to run to dynamically allocate more interfaces.
 .PP
-The SLIP descriptors must be set up and running before the daemon is
-started, and you must inform it about the SLIP descriptors it's meant to
-use in an environment variable
-.BR TRIPE_SLIPIF ,
-whose value must be a colon-separated list of items of the form
+A static allocation entry has the form
 .IR infd [ \c
 .BI , outfd \c
 .RB ] \c
 .BI = \c
-.IR ifname .
+.IR ifname ,
 If the
 .I outfd
 is omitted, the same file descriptor is used for input and output.
 .PP
+The dynamic allocation script must be named by an absolute or relative
+pathname, beginning with 
+.RB ` / '
+or
+.RB ` . '.
+The server will pass the script an argument, which is the name of the
+peer for which the interface is being created.  The script should
+allocate a new SLIP interface (presumably by creating a pty pair),
+configure it appropriately, and write the interface's name to its
+standard output, followed by a newline.  It should then read and write
+SLIP packets on its stdin and stdout.  The script's stdin will be closed
+when the interface is no longer needed, and the server will attempt to
+send it a
+.B SIGTERM
+signal (though this may fail if the script runs with higher privileges
+than the server).
+.PP
 The output file descriptor should not block unless it really needs to:
 the
 .B tripe
index e24a018..c0f57a4 100644 (file)
@@ -243,4 +243,4 @@ or to give it a command and quit, but this is seldom useful.
 .IR "The Trivial IP Encryption Protocol" ,
 .IR "The Wrestlers Protocol" .
 .SH "AUTHOR"
-Mark Wooding, <mdw@nsict.org>
+Mark Wooding, <mdw@distorted.org.uk>
diff --git a/tripe.h b/tripe.h
index c8ad9ae..6dbfbad 100644 (file)
--- a/tripe.h
+++ b/tripe.h
@@ -270,8 +270,10 @@ enum {
     struct slipif *next;               /* Next one in the list */
     int ifd, ofd;                      /* File descriptors to talk on */
     char *name;                                /* Interface name */
+    pid_t kid;                         /* Child process id */
     unsigned f;                                /* Various flags */
 #     define SLIPIFF_INUSE 1u          /*   Interface is in use */
+#     define SLIPIFF_DYNAMIC 2u                /*   Interface found dynamically */
   } slipif;
 #endif
 
index 1d1bd14..b9914e4 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);
   }
 }