+/* -*-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 ------------------------------------------------------*/
+
+#include "tripe.h"
+
+/*----- Static variables --------------------------------------------------*/
+
+static slipif *slipifs; /* List of available interfaces */
+
+/*----- Main code ---------------------------------------------------------*/
+
+#if TUN_TYPE != TUN_SLIP
+# error "Tunnel type mismatch: fix the Makefile"
+#endif
+
+#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 ||
+#ifdef EWOULDBLOCK
+ errno == EWOULDBLOCK ||
+#endif
+ errno == EAGAIN)
+ return;
+ a_warn("TUN %s read-error -- %s", t->sl->name, strerror(errno));
+ return;
+ }
+ if (!n) {
+ a_warn("TUN %s slip eof", t->sl->name);
+ t->st = SLIPST_EOF;
+ sel_rmfile(&t->f);
+ return;
+ }
+ IF_TRACING(T_TUNNEL, {
+ trace_block(T_PACKET, "tunnel: 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 & SLIPST_BAD)
+ ;
+ else if (st & SLIPST_ESC)
+ a_warn("TUN %s slip escape-end", t->sl->name);
+ else if (q == t->buf) {
+ T( trace(T_TUNNEL, "tunnel: empty packet"); )
+ } else {
+ IF_TRACING(T_TUNNEL, {
+ trace(T_TUNNEL, "tunnel: packet arrived");
+ trace_block(T_PACKET, "tunnel: packet contents",
+ t->buf, q - t->buf);
+ })
+ buf_init(&b, t->buf, q - t->buf);
+ p_tun(t->p, &b);
+ }
+ q = t->buf;
+ st &= ~(SLIPST_ESC | SLIPST_BAD);
+ break;
+ case SL_ESC:
+ if ((st & SLIPST_ESC) && !(st & SLIPST_BAD)) {
+ a_warn("TUN %s slip bad-escape", t->sl->name);
+ st |= SLIPST_BAD;
+ } else
+ st |= SLIPST_ESC;
+ break;
+ case SL_ESCEND:
+ if (st & SLIPST_ESC)
+ o = SL_END;
+ goto emit;
+ case SL_ESCESC:
+ if (st & SLIPST_ESC)
+ o = SL_ESC;
+ goto emit;
+ default:
+ if ((st & SLIPST_ESC) && !(st & SLIPST_BAD)) {
+ a_warn("TUN %s slip bad-escape", t->sl->name);
+ st |= SLIPST_BAD;
+ }
+ emit:
+ if (!(st & SLIPST_BAD)) {
+ if (q < ll)
+ *q++ = o;
+ else {
+ a_warn("TUN %s slip overflow", t->sl->name);
+ st |= SLIPST_BAD;
+ }
+ }
+ st &= ~SLIPST_ESC;
+ break;
+ }
+ }
+
+ t->n = q - t->buf;
+ t->st = st;
+}
+
+/* --- @tun_init@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: ---
+ *
+ * Use: Initializes the tunneling system. Maybe this will require
+ * opening file descriptors or something.
+ */
+
+void tun_init(void)
+{
+ char *p, *q;
+ dstr d = DSTR_INIT;
+ slipif *sl, **tail = &slipifs;
+ 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");
+ dstr_puts(&d, p);
+
+ p = d.buf;
+ for (;;) {
+ 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;
+ sl->ofd = ulo;
+ sl->name = xmalloc(n + 1);
+ sl->f = 0;
+ memcpy(sl->name, q + 1, n);
+ sl->name[n] = 0;
+ *tail = sl;
+ tail = &sl->next;
+ T( trace(T_TUNNEL, "tunnel: declared slipif %d,%d=%s",
+ sl->ifd, sl->ofd, sl->name); )
+ p = q + n + 1;
+ if (!*p)
+ break;
+ p++;
+ }
+ return;
+
+whine:
+ die(1, "bad slip interface list");
+}
+
+/* --- @tun_create@ --- *
+ *
+ * Arguments: @tunnel *t@ = pointer to tunnel block
+ * @peer *p@ = pointer to peer block
+ *
+ * Returns: Zero if it worked, nonzero on failure.
+ *
+ * Use: Initializes a new tunnel.
+ */
+
+int tun_create(tunnel *t, peer *p)
+{
+ slipif *sl;
+ static const char end[] = { SL_END, SL_END };
+
+ for (sl = slipifs; sl; sl = sl->next) {
+ if (!(sl->f & SLIPIFF_INUSE))
+ goto found;
+ }
+ a_warn("TUN - slip no-slip-interfaces");
+ return (-1);
+
+found:
+ t->p = p;
+ t->sl = sl;
+ t->st = 0;
+ t->n = 0;
+ sl->f |= SLIPIFF_INUSE;
+ 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)); )
+ return (0);
+}
+
+/* --- @tun_ifname@ --- *
+ *
+ * Arguments: @tunnel *t@ = pointer to tunnel block
+ *
+ * Returns: A pointer to the tunnel's interface name.
+ */
+
+const char *tun_ifname(tunnel *t)
+{
+ return (t->sl->name);
+}
+
+/* --- @tun_inject@ --- *
+ *
+ * Arguments: @tunnel *t@ = pointer to tunnel block
+ * @buf *b@ = buffer to send
+ *
+ * Returns: ---
+ *
+ * Use: Injects a packet into the local network stack.
+ */
+
+void tun_inject(tunnel *t, buf *b)
+{
+ octet buf[PKBUFSZ * 2 + 2];
+ const octet *p, *l;
+ octet *q;
+
+ IF_TRACING(T_TUNNEL, {
+ trace(T_TUNNEL, "tunnel: inject decrypted packet");
+ trace_block(T_PACKET, "tunnel: 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, "tunnel: SLIP-encapsulated contents",
+ buf, q - buf);
+ })
+ write(t->sl->ofd, buf, q - buf);
+}
+
+/* --- @tun_destroy@ --- *
+ *
+ * Arguments: @tunnel *t@ = pointer to tunnel block
+ *
+ * Returns: ---
+ *
+ * Use: Destroys a tunnel.
+ */
+
+void tun_destroy(tunnel *t)
+{
+ /* --- If it reported EOF, leave it out-of-action --- */
+
+ if (!(t->st & SLIPST_EOF)) {
+ sel_rmfile(&t->f);
+ t->sl->f &= ~SLIPIFF_INUSE;
+ }
+}
+
+/*----- That's all, folks -------------------------------------------------*/