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 8ccdc575f749f85042ec6d72759bbce2fd1eaa27..7ff497c3b30611acda70694613abaa432f3e5030 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
 .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
 .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
 .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 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
 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
 The output file descriptor should not block unless it really needs to:
 the
 .B tripe
index e24a018c2c9e574a3758a7680f2c03f84885c76d..c0f57a46ebe385b658eb8f24accbc3adf5a5fac3 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"
 .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 c8ad9ae1a40a42c2a278046f23eac11cf319cc0c..6dbfbad3c4ffa5814162c45c9c4c442dfbe76d65 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 */
     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 */
     unsigned f;                                /* Various flags */
 #     define SLIPIFF_INUSE 1u          /*   Interface is in use */
+#     define SLIPIFF_DYNAMIC 2u                /*   Interface found dynamically */
   } slipif;
 #endif
 
   } slipif;
 #endif
 
index 1d1bd14b1ae69a691bb0506d5113b5a2bace3db4..b9914e43f902580270ef2d5de282b99f93a91684 100644 (file)
@@ -33,6 +33,7 @@
 /*----- Static variables --------------------------------------------------*/
 
 static slipif *slipifs;                        /* List of available interfaces */
 /*----- Static variables --------------------------------------------------*/
 
 static slipif *slipifs;                        /* List of available interfaces */
+static const char *slipcmd;            /* Script to make new interfaces */
 
 /*----- Main code ---------------------------------------------------------*/
 
 
 /*----- 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 ||
   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)
        errno == EWOULDBLOCK ||
 #endif
        errno == EAGAIN)
@@ -173,14 +174,20 @@ void tun_init(void)
   unsigned long uli, ulo;
   size_t n;
 
   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");
   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 (;;) {
   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;
     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;
     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->ofd = ulo;
     sl->name = xmalloc(n + 1);
+    sl->kid = -1;
     sl->f = 0;
     memcpy(sl->name, q + 1, n);
     sl->name[n] = 0;
     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)
 {
 
 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 };
 
   static const char end[] = { SL_END, SL_END };
 
+  /* --- Try to find a spare static interface --- */
+
   for (sl = slipifs; sl; sl = sl->next) {
   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;
       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;
 
 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));
   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);
   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@ --- *
 }
 
 /* --- @tun_ifname@ --- *
@@ -260,10 +344,7 @@ found:
  * Returns:    A pointer to the tunnel's interface name.
  */
 
  * 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@ --- *
  *
 
 /* --- @tun_inject@ --- *
  *
@@ -314,11 +395,21 @@ void tun_inject(tunnel *t, buf *b)
 
 void tun_destroy(tunnel *t)
 {
 
 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);
   /* --- 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);
   }
 }
 
   }
 }