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