chiark / gitweb /
85e9cd84da4ef6ab407579343d5bda7d41781505
[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 #   define ST_BROKEN 8u                 /*   Sending side is broken */
56   size_t n;                             /* Number of bytes used in buffer */
57   octet buf[PKBUFSZ];                   /* Buffer for incoming data */
58 };
59
60 /*----- Static variables --------------------------------------------------*/
61
62 static slipif *slipifs;                 /* List of available interfaces */
63 static const char *slipcmd;             /* Script to make new interfaces */
64
65 /*----- Main code ---------------------------------------------------------*/
66
67 /* --- @t_read@ --- *
68  *
69  * Arguments:   @int fd@ = file descriptor to read
70  *              @unsigned mode@ = what's happened
71  *              @void *v@ = pointer to tunnel block
72  *
73  * Returns:     ---
74  *
75  * Use:         Reads data from the tunnel.
76  */
77
78 static void t_read(int fd, unsigned mode, void *v)
79 {
80   tunnel *t = v;
81   ssize_t n;
82   const octet *p, *l, *ll;
83   octet *q;
84   unsigned st;
85   octet o;
86   buf b;
87
88   /* --- Read the input data --- */
89
90   n = read(fd, buf_t, sizeof(buf_t));
91   if (n < 0) {
92     if (errno == EINTR ||
93 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
94         errno == EWOULDBLOCK ||
95 #endif
96         errno == EAGAIN)
97       return;
98     a_warn("TUN", "%s", p_ifname(t->p), "slip",
99            "read-error", "?ERRNO", A_END);
100     return;
101   }
102   if (!n) {
103     a_warn("TUN", "%s", p_ifname(t->p), "slip", "eof", A_END);
104     t->st = (t->st & ~ST_MASK) | ST_EOF;
105     sel_rmfile(&t->f);
106     return;
107   }
108   IF_TRACING(T_TUNNEL, {
109     trace_block(T_PACKET, "tun-slip: SLIP-encapsulated data",
110                 buf_t, n);
111   })
112
113   /* --- Decapsulate the packet --- */
114
115   for (p = buf_t, l = p + n, st = t->st,
116        q = t->buf + t->n, ll = t->buf + sizeof(t->buf);
117        p < l;
118        p++) {
119     o = *p;
120     switch (o) {
121       case SL_END:
122         if (st & ST_BAD)
123           ;
124         else if (st & ST_ESC) {
125           a_warn("TUN", "%s", p_ifname(t->p), "slip", "escape-end", A_END);
126           st |= ST_BAD;
127         } else if (q == t->buf) {
128           T( trace(T_TUNNEL, "tun-slip: empty packet"); )
129         } else {
130           IF_TRACING(T_TUNNEL, {
131             trace(T_TUNNEL, "tun-slip: packet arrived");
132             trace_block(T_PACKET, "tun-slip: packet contents",
133                         t->buf, q - t->buf);
134           })
135           buf_init(&b, t->buf, q - t->buf);
136           p_tun(t->p, &b);
137         }
138         q = t->buf;
139         st &= ~ST_MASK;
140         break;
141       case SL_ESC:
142         if ((st & ST_ESC) && !(st & ST_BAD)) {
143           a_warn("TUN", "%s", p_ifname(t->p), "slip", "bad-escape", A_END);
144           st |= ST_BAD;
145         } else
146           st |= ST_ESC;
147         break;
148       case SL_ESCEND:
149         if (st & ST_ESC)
150           o = SL_END;
151         goto emit;
152       case SL_ESCESC:
153         if (st & ST_ESC)
154           o = SL_ESC;
155         goto emit;
156       default:
157         if ((st & ST_ESC) && !(st & ST_BAD)) {
158           a_warn("TUN", "%s", p_ifname(t->p), "slip", "bad-escape", A_END);
159           st |= ST_BAD;
160         }
161       emit:
162         if (!(st & ST_BAD)) {
163           if (q < ll)
164             *q++ = o;
165           else {
166             a_warn("TUN", "%s", p_ifname(t->p), "slip", "overflow", A_END);
167             st |= ST_BAD;
168           }
169         }
170         st &= ~ST_ESC;
171         break;
172     }
173   }
174
175   t->n = q - t->buf;
176   t->st = st;
177 }
178
179 /* --- @t_init@ --- *
180  *
181  * Arguments:   ---
182  *
183  * Returns:     ---
184  *
185  * Use:         Initializes the tunneling system.  Maybe this will require
186  *              opening file descriptors or something.
187  */
188
189 static void t_init(void)
190 {
191   char *p, *q;
192   dstr d = DSTR_INIT;
193   slipif *sl, **tail = &slipifs;
194   unsigned long uli, ulo;
195   size_t n;
196
197   if ((p = getenv("TRIPE_SLIPIF")) == 0)
198     return;
199
200   /* --- Build the list of available interfaces --- */
201
202   dstr_puts(&d, p);
203
204   p = d.buf;
205   for (;;) {
206     if (*p == '/' || *p == '.') {
207       slipcmd = p;
208       T( trace(T_TUNNEL, "tun-slip: declared slip command `%s'", slipcmd); )
209       break;
210     }
211     uli = strtoul(p, &q, 0);
212     if (uli > INT_MAX || q == p)
213       goto whine;
214     if (*q != ',')
215       ulo = uli;
216     else {
217       p = q + 1;
218       ulo = strtoul(p, &q, 0);
219       if (ulo > INT_MAX || q == p)
220         goto whine;
221     }
222     if (*q != '=' || (n = strcspn(q + 1, ":")) == 0)
223       goto whine;
224     sl = CREATE(slipif);
225     sl->next = 0;
226     sl->ifd = uli;
227     fdflags(sl->ifd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
228     fdflags(sl->ofd, O_NONBLOCK, 0, FD_CLOEXEC, FD_CLOEXEC);
229     sl->ofd = ulo;
230     sl->name = xmalloc(n + 1);
231     sl->kid = -1;
232     sl->f = 0;
233     memcpy(sl->name, q + 1, n);
234     sl->name[n] = 0;
235     *tail = sl;
236     tail = &sl->next;
237     T( trace(T_TUNNEL, "tun-slip: declared slipif %d,%d=%s",
238              sl->ifd, sl->ofd, sl->name); )
239     p = q + n + 1;
240     if (!*p)
241       break;
242     p++;
243   }
244   return;
245
246 whine:
247   moan("bad slip interface list");
248 }
249
250 /* --- @t_broken@ --- *
251  *
252  * Arguments:   @tunnel *t@ = pointer to the tunnel
253  *
254  * Returns:     ---
255  *
256  * Use:         Marks the tunnel as broken and reports an error.
257  */
258
259 static void t_broken(tunnel *t)
260 {
261   if (errno == EINTR ||
262 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
263       errno == EWOULDBLOCK ||
264 #endif
265       errno == EAGAIN)
266     return;
267   a_warn("TUN", "%s", p_ifname(t->p), "slip",
268          "write-error", "?ERRNO", A_END);
269   T( trace(T_TUNNEL, "tun-slip: marking tunnel broken"); )
270   t->st |= ST_BROKEN;
271 }
272
273 /* --- @t_create@ --- *
274  *
275  * Arguments:   @peer *p@ = pointer to peer block
276  *              @int fd@ = file descriptor of tunnel device (unused)
277  *              @char **ifn@ = where to put the interface name
278  *
279  * Returns:     A tunnel block if it worked, or null on failure.
280  *
281  * Use:         Initializes a new tunnel.
282  */
283
284 static tunnel *t_create(peer *p, int fd, char **ifn)
285 {
286   slipif *sl = 0;
287   int pin[2] = { -1, -1 }, pout[2] = { -1, -1 };
288   mdup_fd md[2];
289   pid_t kid = -1;
290   dstr d = DSTR_INIT;
291   unsigned char ch;
292   tunnel *t;
293   static const octet end[] = { SL_END, SL_END };
294
295   /* --- Try to find a spare static interface --- */
296
297   for (sl = slipifs; sl; sl = sl->next) {
298     if (!(sl->f & F_INUSE)) {
299       T( trace(T_TUNNEL, "tun-slip: %s using static slipif %s",
300                p_name(p), sl->name); )
301       goto found;
302     }
303   }
304
305   /* --- If no dynamic interfaces are available, give up --- */
306
307   if (!slipcmd) {
308     a_warn("TUN", "-", "slip", "no-slip-interfaces", A_END);
309     goto fail;
310   }
311
312   /* --- Fork off a child process to create a dynamic SLIP interface --- */
313
314   if (pipe(pin) || pipe(pout)) {
315     a_warn("TUN", "-", "slip", "pipe-error", "?ERRNO", A_END);
316     goto fail;
317   }
318   if ((kid = fork()) < 0) {
319     a_warn("TUN", "-", "slip", "fork-error", "?ERRNO", A_END);
320     goto fail;
321   }
322   if (!kid) {
323     close(pin[1]); close(pout[0]);
324     md[0].cur = pin[0];  md[0].want = STDIN_FILENO;
325     md[1].cur = pout[1]; md[1].want = STDOUT_FILENO;
326     mdup(md, 2);
327     execlp(slipcmd, slipcmd, p_name(p), (char *)0);
328     _exit(127);
329   }
330
331   /* --- Read the interface name --- */
332
333   sl = CREATE(slipif);
334   close(pin[0]); pin[0] = -1;
335   close(pout[1]); pout[1] = -1;
336   for (;;) {
337     errno = EIO;
338     if (read(pout[0], &ch, 1) != 1 || ch == SL_END) {
339       a_warn("TUN", "-", "slip", "read-ifname-failed", "?ERRNO", A_END);
340       goto fail;
341     }
342     if (ch == '\n')
343       break;
344     DPUTC(&d, (char)ch);
345   }
346   DPUTZ(&d);
347   sl->name = xstrdup(d.buf);
348   sl->ifd = pout[0];
349   sl->ofd = pin[1];
350   sl->kid = kid;
351   sl->next = 0;
352   sl->f = F_DYNAMIC;
353   T( trace(T_TUNNEL, "tun-slip: %s using dynamic slipif %s",
354            p_name(p), sl->name); )
355   fdflags(pout[0], O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
356   fdflags(pin[1], O_NONBLOCK, 0, FD_CLOEXEC, FD_CLOEXEC);
357
358   /* --- Set up the new tunnel --- */
359
360 found:
361   t = CREATE(tunnel);
362   t->ops = &tun_slip;
363   t->p = p;
364   t->sl = sl;
365   t->st = 0;
366   t->n = 0;
367   sl->f |= F_INUSE;
368   sel_initfile(&sel, &t->f, sl->ifd, SEL_READ, t_read, t);
369   sel_addfile(&t->f);
370   if (write(sl->ofd, end, sizeof(end)) < 0)
371     t_broken(t);
372   *ifn = xstrdup(sl->name);
373   dstr_destroy(&d);
374   return (t);
375
376   /* --- Tidy up after a failure --- */
377
378 fail:
379 #define CLOSE(fd) do if (fd != -1) close(fd); while (0)
380   CLOSE(pin[0]); CLOSE(pout[0]);
381   CLOSE(pin[1]); CLOSE(pout[1]);
382 #undef CLOSE
383   if (kid != -1) kill(kid, SIGTERM);
384   if (sl && (sl->f & F_DYNAMIC)) DESTROY(sl);
385   dstr_destroy(&d);
386   return (0);
387 }
388
389 /* --- @t_setifname@ --- *
390  *
391  * Arguments:   @tunnel *t@ = pointer to tunnel block
392  *              @const char *ifn@ = new interface name
393  *
394  * Returns:     ---
395  *
396  * Use:         Updates the interface name of a slip interface.
397  */
398
399 static void t_setifname(tunnel *t, const char *ifn)
400   { xfree(t->sl->name); t->sl->name = xstrdup(ifn); }
401
402 /* --- @t_inject@ --- *
403  *
404  * Arguments:   @tunnel *t@ = pointer to tunnel block
405  *              @buf *b@ = buffer to send
406  *
407  * Returns:     ---
408  *
409  * Use:         Injects a packet into the local network stack.
410  */
411
412 static void t_inject(tunnel *t, buf *b)
413 {
414   octet buf[PKBUFSZ * 2 + 2];
415   const octet *p, *l;
416   octet *q;
417
418   IF_TRACING(T_TUNNEL, {
419     trace(T_TUNNEL, "tun-slip: inject decrypted packet");
420     trace_block(T_PACKET, "tun-slip: packet contents", BBASE(b), BLEN(b));
421   })
422
423   if (t-> st & ST_BROKEN) {
424     T( trace(T_TUNNEL, "tun-slip: tunnel broken; discarding"); )
425     return;
426   }
427   q = buf;
428   *q++ = SL_END;
429   for (p = BBASE(b), l = BCUR(b); p < l; p++) {
430     switch (*p) {
431       case SL_END: *q++ = SL_ESC; *q++ = SL_ESCEND; break;
432       case SL_ESC: *q++ = SL_ESC; *q++ = SL_ESCESC; break;
433       default: *q++ = *p; break;
434     }
435   }
436   *q++ = SL_END;
437   IF_TRACING(T_TUNNEL, {
438     trace_block(T_PACKET, "tun-slip: SLIP-encapsulated contents",
439                 buf, q - buf);
440   })
441   if (write(t->sl->ofd, buf, q - buf) < 0)
442     t_broken(t);
443 }
444
445 /* --- @t_destroy@ --- *
446  *
447  * Arguments:   @tunnel *t@ = pointer to tunnel block
448  *
449  * Returns:     ---
450  *
451  * Use:         Destroys a tunnel.
452  */
453
454 static void t_destroy(tunnel *t)
455 {
456   slipif *sl = t->sl;
457
458   /* --- If it reported EOF, leave it out-of-action --- */
459
460   if (!(t->st & ST_EOF)) {
461     sel_rmfile(&t->f);
462     sl->f &= ~F_INUSE;
463   }
464   if (sl && (sl->f & F_DYNAMIC)) {
465     T( trace(T_TUNNEL, "tun-slip: releasing dynamic slipif %s", sl->name); )
466     close(sl->ofd);
467     close(sl->ifd);
468     kill(sl->kid, SIGTERM);
469     xfree(sl->name);
470     DESTROY(sl);
471   }
472   DESTROY(t);
473 }
474
475 const tunnel_ops tun_slip = {
476   "slip",
477   0,
478   t_init,
479   t_create,
480   t_setifname,
481   t_inject,
482   t_destroy
483 };
484
485 /*----- That's all, folks -------------------------------------------------*/