+/* -*-c-*-
+ *
+ * $Id$
+ *
+ * Tunnel packets via SLIP
+ *
+ * (c) 2005 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of Trivial IP Encryption (TrIPE).
+ *
+ * TrIPE is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * TrIPE is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TrIPE; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#define TUN_INTERNALS
+
+#include "tripe.h"
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef struct slipif {
+ 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 F_INUSE 1u /* Interface is in use */
+# define F_DYNAMIC 2u /* Interface found dynamically */
+} slipif;
+
+struct tunnel {
+ const tunnel_ops *ops; /* Pointer to operations */
+ slipif *sl; /* My interface record */
+ sel_file f; /* Selector for SLIP tty */
+ struct peer *p; /* Pointer to my peer */
+ unsigned st; /* Current parser state */
+# define ST_ESC 1u /* Last saw an escape character */
+# define ST_BAD 2u /* This packet is malformed */
+# define ST_EOF 4u /* File descriptor reported EOF */
+ size_t n; /* Number of bytes used in buffer */
+ octet buf[PKBUFSZ]; /* Buffer for incoming data */
+};
+
+/*----- Static variables --------------------------------------------------*/
+
+static slipif *slipifs; /* List of available interfaces */
+static const char *slipcmd; /* Script to make new interfaces */
+
+/*----- Main code ---------------------------------------------------------*/
+
+#define SL_END 0xc0
+#define SL_ESC 0xdb
+#define SL_ESCEND 0xdc
+#define SL_ESCESC 0xdd
+
+/* --- @t_read@ --- *
+ *
+ * Arguments: @int fd@ = file descriptor to read
+ * @unsigned mode@ = what's happened
+ * @void *v@ = pointer to tunnel block
+ *
+ * Returns: ---
+ *
+ * Use: Reads data from the tunnel.
+ */
+
+static void t_read(int fd, unsigned mode, void *v)
+{
+ tunnel *t = v;
+ ssize_t n;
+ const octet *p, *l, *ll;
+ octet *q;
+ unsigned st;
+ octet o;
+ buf b;
+
+ /* --- Read the input data --- */
+
+ n = read(fd, buf_t, sizeof(buf_t));
+ if (n < 0) {
+ if (errno == EINTR ||
+#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
+ errno == EWOULDBLOCK ||
+#endif
+ errno == EAGAIN)
+ return;
+ a_warn("TUN", "%s", t->sl->name, "read-error", "?ERRNO", A_END);
+ return;
+ }
+ if (!n) {
+ a_warn("TUN", "%s", t->sl->name, "slip", "eof", A_END);
+ t->st = ST_EOF;
+ sel_rmfile(&t->f);
+ return;
+ }
+ IF_TRACING(T_TUNNEL, {
+ trace_block(T_PACKET, "tun-slip: SLIP-encapsulated data",
+ buf_t, n);
+ })
+
+ /* --- Decapsulate the packet --- */
+
+ for (p = buf_t, l = p + n, st = t->st,
+ q = t->buf + t->n, ll = t->buf + sizeof(t->buf);
+ p < l;
+ p++) {
+ o = *p;
+ switch (o) {
+ case SL_END:
+ if (st & ST_BAD)
+ ;
+ else if (st & ST_ESC)
+ a_warn("TUN", "%s", t->sl->name, "slip", "escape-end", A_END);
+ else if (q == t->buf) {
+ T( trace(T_TUNNEL, "tun-slip: empty packet"); )
+ } else {
+ IF_TRACING(T_TUNNEL, {
+ trace(T_TUNNEL, "tun-slip: packet arrived");
+ trace_block(T_PACKET, "tun-slip: packet contents",
+ t->buf, q - t->buf);
+ })
+ buf_init(&b, t->buf, q - t->buf);
+ p_tun(t->p, &b);
+ }
+ q = t->buf;
+ st &= ~(ST_ESC | ST_BAD);
+ break;
+ case SL_ESC:
+ if ((st & ST_ESC) && !(st & ST_BAD)) {
+ a_warn("TUN", "%s", t->sl->name, "slip", "bad-escape", A_END);
+ st |= ST_BAD;
+ } else
+ st |= ST_ESC;
+ break;
+ case SL_ESCEND:
+ if (st & ST_ESC)
+ o = SL_END;
+ goto emit;
+ case SL_ESCESC:
+ if (st & ST_ESC)
+ o = SL_ESC;
+ goto emit;
+ default:
+ if ((st & ST_ESC) && !(st & ST_BAD)) {
+ a_warn("TUN", "%s", t->sl->name, "slip", "bad-escape", A_END);
+ st |= ST_BAD;
+ }
+ emit:
+ if (!(st & ST_BAD)) {
+ if (q < ll)
+ *q++ = o;
+ else {
+ a_warn("TUN", "%s", t->sl->name, "slip", "overflow", A_END);
+ st |= ST_BAD;
+ }
+ }
+ st &= ~ST_ESC;
+ break;
+ }
+ }
+
+ t->n = q - t->buf;
+ t->st = st;
+}
+
+/* --- @t_init@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: ---
+ *
+ * Use: Initializes the tunneling system. Maybe this will require
+ * opening file descriptors or something.
+ */
+
+static void t_init(void)
+{
+ char *p, *q;
+ dstr d = DSTR_INIT;
+ slipif *sl, **tail = &slipifs;
+ unsigned long uli, ulo;
+ size_t n;
+
+ if ((p = getenv("TRIPE_SLIPIF")) == 0)
+ return;
+
+ /* --- Build the list of available interfaces --- */
+
+ dstr_puts(&d, p);
+
+ p = d.buf;
+ for (;;) {
+ if (*p == '/' || *p == '.') {
+ slipcmd = p;
+ T( trace(T_TUNNEL, "tun-slip: declared slip command `%s'", slipcmd); )
+ break;
+ }
+ uli = strtoul(p, &q, 0);
+ if (uli > INT_MAX || q == p)
+ goto whine;
+ if (*q != ',')
+ ulo = uli;
+ else {
+ p = q + 1;
+ ulo = strtoul(p, &q, 0);
+ if (ulo > INT_MAX || q == p)
+ goto whine;
+ }
+ if (*q != '=' || (n = strcspn(q + 1, ":")) == 0)
+ goto whine;
+ 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;
+ *tail = sl;
+ tail = &sl->next;
+ T( trace(T_TUNNEL, "tun-slip: declared slipif %d,%d=%s",
+ sl->ifd, sl->ofd, sl->name); )
+ p = q + n + 1;
+ if (!*p)
+ break;
+ p++;
+ }
+ return;
+
+whine:
+ moan("bad slip interface list");
+}
+
+/* --- @t_create@ --- *
+ *
+ * Arguments: @peer *p@ = pointer to peer block
+ *
+ * Returns: A tunnel block if it worked, or null on failure.
+ *
+ * Use: Initializes a new tunnel.
+ */
+
+static tunnel *t_create(peer *p)
+{
+ slipif *sl = 0;
+ int pin[2] = { -1, -1 }, pout[2] = { -1, -1 };
+ pid_t kid = -1;
+ dstr d = DSTR_INIT;
+ unsigned char ch;
+ tunnel *t;
+ 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 & F_INUSE)) {
+ T( trace(T_TUNNEL, "tun-slip: %s using static slipif %s",
+ p_name(p), sl->name); )
+ goto found;
+ }
+ }
+
+ /* --- If no dynamic interfaces are available, give up --- */
+
+ if (!slipcmd) {
+ a_warn("TUN", "-", "slip", "no-slip-interfaces", A_END);
+ goto fail;
+ }
+
+ /* --- Fork off a child process to create a dynamic SLIP interface --- */
+
+ if (pipe(pin) || pipe(pout)) {
+ a_warn("TUN", "-", "slip", "pipe-error", "?ERRNO", A_END);
+ goto fail;
+ }
+ if ((kid = fork()) < 0) {
+ a_warn("TUN", "-", "slip", "fork-error", "?ERRNO", A_END);
+ 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", "-", "slip", "read-ifname-failed", "?ERRNO", A_END);
+ 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 = F_DYNAMIC;
+ T( trace(T_TUNNEL, "tun-slip: %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 = CREATE(tunnel);
+ t->ops = &tun_slip;
+ t->p = p;
+ t->sl = sl;
+ t->st = 0;
+ t->n = 0;
+ sl->f |= F_INUSE;
+ sel_initfile(&sel, &t->f, sl->ifd, SEL_READ, t_read, t);
+ sel_addfile(&t->f);
+ write(sl->ofd, end, sizeof(end));
+ dstr_destroy(&d);
+ return (t);
+
+ /* --- 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 & F_DYNAMIC)) DESTROY(sl);
+ dstr_destroy(&d);
+ return (0);
+}
+
+/* --- @t_ifname@ --- *
+ *
+ * Arguments: @tunnel *t@ = pointer to tunnel block
+ *
+ * Returns: A pointer to the tunnel's interface name.
+ */
+
+static const char *t_ifname(tunnel *t) { return (t->sl->name); }
+
+/* --- @t_inject@ --- *
+ *
+ * Arguments: @tunnel *t@ = pointer to tunnel block
+ * @buf *b@ = buffer to send
+ *
+ * Returns: ---
+ *
+ * Use: Injects a packet into the local network stack.
+ */
+
+static void t_inject(tunnel *t, buf *b)
+{
+ octet buf[PKBUFSZ * 2 + 2];
+ const octet *p, *l;
+ octet *q;
+
+ IF_TRACING(T_TUNNEL, {
+ trace(T_TUNNEL, "tun-slip: inject decrypted packet");
+ trace_block(T_PACKET, "tun-slip: packet contents", BBASE(b), BLEN(b));
+ })
+
+ q = buf;
+ *q++ = SL_END;
+ for (p = BBASE(b), l = BCUR(b); p < l; p++) {
+ switch (*p) {
+ case SL_END: *q++ = SL_ESC; *q++ = SL_ESCEND; break;
+ case SL_ESC: *q++ = SL_ESC; *q++ = SL_ESCESC; break;
+ default: *q++ = *p; break;
+ }
+ }
+ *q++ = SL_END;
+ IF_TRACING(T_TUNNEL, {
+ trace_block(T_PACKET, "tun-slip: SLIP-encapsulated contents",
+ buf, q - buf);
+ })
+ write(t->sl->ofd, buf, q - buf);
+}
+
+/* --- @t_destroy@ --- *
+ *
+ * Arguments: @tunnel *t@ = pointer to tunnel block
+ *
+ * Returns: ---
+ *
+ * Use: Destroys a tunnel.
+ */
+
+static void t_destroy(tunnel *t)
+{
+ slipif *sl = t->sl;
+
+ /* --- If it reported EOF, leave it out-of-action --- */
+
+ if (!(t->st & ST_EOF)) {
+ sel_rmfile(&t->f);
+ sl->f &= ~F_INUSE;
+ }
+ if (sl && (sl->f & F_DYNAMIC)) {
+ T( trace(T_TUNNEL, "tun-slip: releasing dynamic slipif %s", sl->name); )
+ close(sl->ofd);
+ close(sl->ifd);
+ kill(sl->kid, SIGTERM);
+ xfree(sl->name);
+ DESTROY(sl);
+ }
+ DESTROY(t);
+}
+
+const tunnel_ops tun_slip = {
+ "slip",
+ t_init,
+ t_create,
+ t_ifname,
+ t_inject,
+ t_destroy
+};
+
+/*----- That's all, folks -------------------------------------------------*/