From 98fdb08d96bd62384e5f4f18d63bd2045c302e1f Mon Sep 17 00:00:00 2001 Message-Id: <98fdb08d96bd62384e5f4f18d63bd2045c302e1f.1714613778.git.mdw@distorted.org.uk> From: Mark Wooding Date: Mon, 12 Sep 2005 01:25:47 +0000 Subject: [PATCH 1/1] Improve the SLIP driver: allow dynamic creation of SLIP interfaces. Organization: Straylight/Edgeware From: mdw --- doc/tripe.8 | 47 +++++++++++++++---- doc/tripectl.1 | 2 +- tripe.h | 2 + tun-slip.c | 119 +++++++++++++++++++++++++++++++++++++++++++------ 4 files changed, 146 insertions(+), 24 deletions(-) diff --git a/doc/tripe.8 b/doc/tripe.8 index 8ccdc575..7ff497c3 100644 --- a/doc/tripe.8 +++ b/doc/tripe.8 @@ -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 diff --git a/doc/tripectl.1 b/doc/tripectl.1 index e24a018c..c0f57a46 100644 --- a/doc/tripectl.1 +++ b/doc/tripectl.1 @@ -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, +Mark Wooding, diff --git a/tripe.h b/tripe.h index c8ad9ae1..6dbfbad3 100644 --- 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 diff --git a/tun-slip.c b/tun-slip.c index 1d1bd14b..b9914e43 100644 --- a/tun-slip.c +++ b/tun-slip.c @@ -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); } } -- [mdw]