chiark / gitweb /
Improve the SLIP driver: allow dynamic creation of SLIP interfaces.
[tripe] / tun-slip.c
1 /* -*-c-*-
2  *
3  * $Id$
4  *
5  * Tunnel packets via SLIP
6  *
7  * (c) 2005 Straylight/Edgeware
8  */
9
10 /*----- Licensing notice --------------------------------------------------* 
11  *
12  * This file is part of Trivial IP Encryption (TrIPE).
13  *
14  * TrIPE is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  * 
19  * TrIPE is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  * 
24  * You should have received a copy of the GNU General Public License
25  * along with TrIPE; if not, write to the Free Software Foundation,
26  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27  */
28
29 /*----- Header files ------------------------------------------------------*/
30
31 #include "tripe.h"
32
33 /*----- Static variables --------------------------------------------------*/
34
35 static slipif *slipifs;                 /* List of available interfaces */
36 static const char *slipcmd;             /* Script to make new interfaces */
37
38 /*----- Main code ---------------------------------------------------------*/
39
40 #if TUN_TYPE != TUN_SLIP
41 #  error "Tunnel type mismatch: fix the Makefile"
42 #endif
43
44 #define SL_END 0xc0
45 #define SL_ESC 0xdb
46 #define SL_ESCEND 0xdc
47 #define SL_ESCESC 0xdd
48
49 /* --- @t_read@ --- *
50  *
51  * Arguments:   @int fd@ = file descriptor to read
52  *              @unsigned mode@ = what's happened
53  *              @void *v@ = pointer to tunnel block
54  *
55  * Returns:     ---
56  *
57  * Use:         Reads data from the tunnel.
58  */
59
60 static void t_read(int fd, unsigned mode, void *v)
61 {
62   tunnel *t = v;
63   ssize_t n;
64   const octet *p, *l, *ll;
65   octet *q;
66   unsigned st;
67   octet o;
68   buf b;
69
70   /* --- Read the input data --- */
71
72   n = read(fd, buf_t, sizeof(buf_t));
73   if (n < 0) {
74     if (errno == EINTR ||
75 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
76         errno == EWOULDBLOCK ||
77 #endif
78         errno == EAGAIN)
79       return;
80     a_warn("TUN %s read-error -- %s", t->sl->name, strerror(errno));
81     return;
82   }
83   if (!n) {
84     a_warn("TUN %s slip eof", t->sl->name);
85     t->st = SLIPST_EOF;
86     sel_rmfile(&t->f);
87     return;
88   }
89   IF_TRACING(T_TUNNEL, {
90     trace_block(T_PACKET, "tunnel: SLIP-encapsulated data",
91                 buf_t, n);
92   })
93
94   /* --- Decapsulate the packet --- */
95
96   for (p = buf_t, l = p + n, st = t->st,
97        q = t->buf + t->n, ll = t->buf + sizeof(t->buf);
98        p < l;
99        p++) {
100     o = *p;
101     switch (o) {
102       case SL_END:
103         if (st & SLIPST_BAD)
104           ;
105         else if (st & SLIPST_ESC)
106           a_warn("TUN %s slip escape-end", t->sl->name);
107         else if (q == t->buf) {
108           T( trace(T_TUNNEL, "tunnel: empty packet"); )
109         } else {
110           IF_TRACING(T_TUNNEL, {
111             trace(T_TUNNEL, "tunnel: packet arrived");
112             trace_block(T_PACKET, "tunnel: packet contents",
113                         t->buf, q - t->buf);
114           })
115           buf_init(&b, t->buf, q - t->buf);
116           p_tun(t->p, &b);
117         }
118         q = t->buf;
119         st &= ~(SLIPST_ESC | SLIPST_BAD);
120         break;
121       case SL_ESC:
122         if ((st & SLIPST_ESC) && !(st & SLIPST_BAD)) {
123           a_warn("TUN %s slip bad-escape", t->sl->name);
124           st |= SLIPST_BAD;
125         } else
126           st |= SLIPST_ESC;
127         break;
128       case SL_ESCEND:
129         if (st & SLIPST_ESC)
130           o = SL_END;
131         goto emit;
132       case SL_ESCESC:
133         if (st & SLIPST_ESC)
134           o = SL_ESC;
135         goto emit;
136       default:
137         if ((st & SLIPST_ESC) && !(st & SLIPST_BAD)) {
138           a_warn("TUN %s slip bad-escape", t->sl->name);
139           st |= SLIPST_BAD;
140         }
141       emit:
142         if (!(st & SLIPST_BAD)) {
143           if (q < ll)
144             *q++ = o;
145           else {
146             a_warn("TUN %s slip overflow", t->sl->name);
147             st |= SLIPST_BAD;
148           }
149         }
150         st &= ~SLIPST_ESC;
151         break;
152     }
153   }
154
155   t->n = q - t->buf;
156   t->st = st;
157 }
158
159 /* --- @tun_init@ --- *
160  *
161  * Arguments:   ---
162  *
163  * Returns:     ---
164  *
165  * Use:         Initializes the tunneling system.  Maybe this will require
166  *              opening file descriptors or something.
167  */
168
169 void tun_init(void)
170 {
171   char *p, *q;
172   dstr d = DSTR_INIT;
173   slipif *sl, **tail = &slipifs;
174   unsigned long uli, ulo;
175   size_t n;
176
177   if ((p = getenv("TRIPE_SLIPIF")) == 0)
178     die(1, "no slip interfaces listed: set TRIPE_SLIPIF");
179
180   /* --- Build the list of available interfaces --- */
181
182   dstr_puts(&d, p);
183
184   p = d.buf;
185   for (;;) {
186     if (*p == '/' || *p == '.') {
187       slipcmd = p;
188       T( trace(T_TUNNEL, "tunnel: declared slip command `%s'", slipcmd); )
189       break;
190     }
191     uli = strtoul(p, &q, 0);
192     if (uli > INT_MAX || q == p)
193       goto whine;
194     if (*q != ',')
195       ulo = uli;
196     else {
197       p = q + 1;
198       ulo = strtoul(p, &q, 0);
199       if (ulo > INT_MAX || q == p)
200         goto whine;
201     }
202     if (*q != '=' || (n = strcspn(q + 1, ":")) == 0)
203       goto whine;
204     sl = CREATE(slipif);
205     sl->next = 0;
206     sl->ifd = uli;
207     fdflags(sl->ifd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
208     fdflags(sl->ofd, O_NONBLOCK, 0, FD_CLOEXEC, FD_CLOEXEC);
209     sl->ofd = ulo;
210     sl->name = xmalloc(n + 1);
211     sl->kid = -1;
212     sl->f = 0;
213     memcpy(sl->name, q + 1, n);
214     sl->name[n] = 0;
215     *tail = sl;
216     tail = &sl->next;
217     T( trace(T_TUNNEL, "tunnel: declared slipif %d,%d=%s",
218              sl->ifd, sl->ofd, sl->name); )
219     p = q + n + 1;
220     if (!*p)
221       break;
222     p++;
223   }
224   return;
225
226 whine:
227   die(1, "bad slip interface list");
228 }
229
230 /* --- @tun_create@ --- *
231  *
232  * Arguments:   @tunnel *t@ = pointer to tunnel block
233  *              @peer *p@ = pointer to peer block
234  *
235  * Returns:     Zero if it worked, nonzero on failure.
236  *
237  * Use:         Initializes a new tunnel.
238  */
239
240 int tun_create(tunnel *t, peer *p)
241 {
242   slipif *sl = 0;
243   int pin[2] = { -1, -1 }, pout[2] = { -1, -1 };
244   pid_t kid = -1;
245   dstr d = DSTR_INIT;
246   unsigned char ch;
247   static const char end[] = { SL_END, SL_END };
248
249   /* --- Try to find a spare static interface --- */
250
251   for (sl = slipifs; sl; sl = sl->next) {
252     if (!(sl->f & SLIPIFF_INUSE)) {
253       T( trace(T_TUNNEL, "tunnel: %s using static slipif %s",
254                p_name(p), sl->name); )
255       goto found;
256     }
257   }
258
259   /* --- If no dynamic interfaces are available, give up --- */
260
261   if (!slipcmd) {
262     a_warn("TUN %s slip no-slip-interfaces", p_name(p));
263     goto fail;
264   }
265
266   /* --- Fork off a child process to create a dynamic SLIP interface --- */
267
268   if (pipe(pin) || pipe(pout)) {
269     a_warn("TUN %s slip pipe-error -- %s", p_name(p), strerror(errno));
270     goto fail;
271   }
272   if ((kid = fork()) < 0) {
273     a_warn("TUN %s slip fork-error -- %s", p_name(p), strerror(errno));
274     goto fail;
275   }
276   if (!kid) {
277     close(pin[1]);
278     close(pout[0]);
279     dup2(pin[0], STDIN_FILENO);
280     dup2(pout[1], STDOUT_FILENO);
281     execlp(slipcmd, slipcmd, p_name(p), (char *)0);
282     _exit(127);
283   }
284
285   /* --- Read the interface name --- */
286
287   sl = CREATE(slipif);
288   close(pin[0]); pin[0] = -1;
289   close(pout[1]); pout[1] = -1;
290   for (;;) {
291     errno = EIO;
292     if (read(pout[0], &ch, 1) != 1 || ch == SL_END) {
293       a_warn("TUN %s slip read-ifname-failed -- %s",
294              p_name(p), strerror(errno));
295       goto fail;
296     }
297     if (ch == '\n')
298       break;
299     DPUTC(&d, (char)ch);
300   }
301   DPUTZ(&d);
302   sl->name = xstrdup(d.buf);
303   sl->ifd = pout[0];
304   sl->ofd = pin[1];
305   sl->kid = kid;
306   sl->next = 0;
307   sl->f = SLIPIFF_DYNAMIC;
308   T( trace(T_TUNNEL, "tunnel: %s using dynamic slipif %s",
309            p_name(p), sl->name); )
310   fdflags(pout[0], O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
311   fdflags(pin[1], O_NONBLOCK, 0, FD_CLOEXEC, FD_CLOEXEC);
312
313   /* --- Set up the new tunnel --- */
314
315 found:
316   t->p = p;
317   t->sl = sl;
318   t->st = 0;
319   t->n = 0;
320   sl->f |= SLIPIFF_INUSE;
321   sel_initfile(&sel, &t->f, sl->ifd, SEL_READ, t_read, t);
322   sel_addfile(&t->f);
323   write(sl->ofd, end, sizeof(end));
324   dstr_destroy(&d);
325   return (0);
326
327   /* --- Tidy up after a failure --- */
328
329 fail:
330 #define CLOSE(fd) do if (fd != -1) close(fd); while (0)
331   CLOSE(pin[0]); CLOSE(pout[0]);
332   CLOSE(pin[1]); CLOSE(pout[1]);
333 #undef CLOSE
334   if (kid != -1) kill(kid, SIGTERM);
335   if (sl && (sl->f & SLIPIFF_DYNAMIC)) DESTROY(sl);
336   dstr_destroy(&d);
337   return (-1);
338 }
339
340 /* --- @tun_ifname@ --- *
341  *
342  * Arguments:   @tunnel *t@ = pointer to tunnel block
343  *
344  * Returns:     A pointer to the tunnel's interface name.
345  */
346
347 const char *tun_ifname(tunnel *t) { return (t->sl->name); }
348
349 /* --- @tun_inject@ --- *
350  *
351  * Arguments:   @tunnel *t@ = pointer to tunnel block
352  *              @buf *b@ = buffer to send
353  *
354  * Returns:     ---
355  *
356  * Use:         Injects a packet into the local network stack.
357  */
358
359 void tun_inject(tunnel *t, buf *b)
360 {
361   octet buf[PKBUFSZ * 2 + 2];
362   const octet *p, *l;
363   octet *q;
364
365   IF_TRACING(T_TUNNEL, {
366     trace(T_TUNNEL, "tunnel: inject decrypted packet");
367     trace_block(T_PACKET, "tunnel: packet contents", BBASE(b), BLEN(b));
368   })
369
370   q = buf;
371   *q++ = SL_END;
372   for (p = BBASE(b), l = BCUR(b); p < l; p++) {
373     switch (*p) {
374       case SL_END: *q++ = SL_ESC; *q++ = SL_ESCEND; break;
375       case SL_ESC: *q++ = SL_ESC; *q++ = SL_ESCESC; break;
376       default: *q++ = *p; break;
377     }
378   }
379   *q++ = SL_END;
380   IF_TRACING(T_TUNNEL, {
381     trace_block(T_PACKET, "tunnel: SLIP-encapsulated contents",
382                 buf, q - buf);
383   })
384   write(t->sl->ofd, buf, q - buf);
385 }
386
387 /* --- @tun_destroy@ --- *
388  *
389  * Arguments:   @tunnel *t@ = pointer to tunnel block
390  *
391  * Returns:     ---
392  *
393  * Use:         Destroys a tunnel.
394  */
395
396 void tun_destroy(tunnel *t)
397 {
398   slipif *sl = t->sl;
399
400   /* --- If it reported EOF, leave it out-of-action --- */
401
402   if (!(t->st & SLIPST_EOF)) {
403     sel_rmfile(&t->f);
404     sl->f &= ~SLIPIFF_INUSE;
405   }
406   if (sl && (sl->f & SLIPIFF_DYNAMIC)) {
407     T( trace(T_TUNNEL, "tunnel: releasing dynamic slipif %s", sl->name); )
408     close(sl->ofd);
409     close(sl->ifd);
410     kill(sl->kid, SIGTERM);
411     xfree(sl->name);
412     DESTROY(sl);
413   }
414 }
415
416 /*----- That's all, folks -------------------------------------------------*/