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