chiark / gitweb /
tripe-admin manpage: Generate a command and message summary.
[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 #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", t->sl->name, "read-error", "?ERRNO", A_END);
104     return;
105   }
106   if (!n) {
107     a_warn("TUN", "%s", t->sl->name, "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", t->sl->name, "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", t->sl->name, "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", t->sl->name, "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", t->sl->name, "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  *
257  * Returns:     A tunnel block if it worked, or null on failure.
258  *
259  * Use:         Initializes a new tunnel.
260  */
261
262 static tunnel *t_create(peer *p)
263 {
264   slipif *sl = 0;
265   int pin[2] = { -1, -1 }, pout[2] = { -1, -1 };
266   pid_t kid = -1;
267   dstr d = DSTR_INIT;
268   unsigned char ch;
269   tunnel *t;
270   static const char end[] = { SL_END, SL_END };
271
272   /* --- Try to find a spare static interface --- */
273
274   for (sl = slipifs; sl; sl = sl->next) {
275     if (!(sl->f & F_INUSE)) {
276       T( trace(T_TUNNEL, "tun-slip: %s using static slipif %s",
277                p_name(p), sl->name); )
278       goto found;
279     }
280   }
281
282   /* --- If no dynamic interfaces are available, give up --- */
283
284   if (!slipcmd) {
285     a_warn("TUN", "-", "slip", "no-slip-interfaces", A_END);
286     goto fail;
287   }
288
289   /* --- Fork off a child process to create a dynamic SLIP interface --- */
290
291   if (pipe(pin) || pipe(pout)) {
292     a_warn("TUN", "-", "slip", "pipe-error", "?ERRNO", A_END);
293     goto fail;
294   }
295   if ((kid = fork()) < 0) {
296     a_warn("TUN", "-", "slip", "fork-error", "?ERRNO", A_END);
297     goto fail;
298   }
299   if (!kid) {
300     close(pin[1]);
301     close(pout[0]);
302     dup2(pin[0], STDIN_FILENO);
303     dup2(pout[1], STDOUT_FILENO);
304     execlp(slipcmd, slipcmd, p_name(p), (char *)0);
305     _exit(127);
306   }
307
308   /* --- Read the interface name --- */
309
310   sl = CREATE(slipif);
311   close(pin[0]); pin[0] = -1;
312   close(pout[1]); pout[1] = -1;
313   for (;;) {
314     errno = EIO;
315     if (read(pout[0], &ch, 1) != 1 || ch == SL_END) {
316       a_warn("TUN", "-", "slip", "read-ifname-failed", "?ERRNO", A_END);
317       goto fail;
318     }
319     if (ch == '\n')
320       break;
321     DPUTC(&d, (char)ch);
322   }
323   DPUTZ(&d);
324   sl->name = xstrdup(d.buf);
325   sl->ifd = pout[0];
326   sl->ofd = pin[1];
327   sl->kid = kid;
328   sl->next = 0;
329   sl->f = F_DYNAMIC;
330   T( trace(T_TUNNEL, "tun-slip: %s using dynamic slipif %s",
331            p_name(p), sl->name); )
332   fdflags(pout[0], O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
333   fdflags(pin[1], O_NONBLOCK, 0, FD_CLOEXEC, FD_CLOEXEC);
334
335   /* --- Set up the new tunnel --- */
336
337 found:
338   t = CREATE(tunnel);
339   t->ops = &tun_slip;
340   t->p = p;
341   t->sl = sl;
342   t->st = 0;
343   t->n = 0;
344   sl->f |= F_INUSE;
345   sel_initfile(&sel, &t->f, sl->ifd, SEL_READ, t_read, t);
346   sel_addfile(&t->f);
347   write(sl->ofd, end, sizeof(end));
348   dstr_destroy(&d);
349   return (t);
350
351   /* --- Tidy up after a failure --- */
352
353 fail:
354 #define CLOSE(fd) do if (fd != -1) close(fd); while (0)
355   CLOSE(pin[0]); CLOSE(pout[0]);
356   CLOSE(pin[1]); CLOSE(pout[1]);
357 #undef CLOSE
358   if (kid != -1) kill(kid, SIGTERM);
359   if (sl && (sl->f & F_DYNAMIC)) DESTROY(sl);
360   dstr_destroy(&d);
361   return (0);
362 }
363
364 /* --- @t_ifname@ --- *
365  *
366  * Arguments:   @tunnel *t@ = pointer to tunnel block
367  *
368  * Returns:     A pointer to the tunnel's interface name.
369  */
370
371 static const char *t_ifname(tunnel *t) { return (t->sl->name); }
372
373 /* --- @t_inject@ --- *
374  *
375  * Arguments:   @tunnel *t@ = pointer to tunnel block
376  *              @buf *b@ = buffer to send
377  *
378  * Returns:     ---
379  *
380  * Use:         Injects a packet into the local network stack.
381  */
382
383 static void t_inject(tunnel *t, buf *b)
384 {
385   octet buf[PKBUFSZ * 2 + 2];
386   const octet *p, *l;
387   octet *q;
388
389   IF_TRACING(T_TUNNEL, {
390     trace(T_TUNNEL, "tun-slip: inject decrypted packet");
391     trace_block(T_PACKET, "tun-slip: packet contents", BBASE(b), BLEN(b));
392   })
393
394   q = buf;
395   *q++ = SL_END;
396   for (p = BBASE(b), l = BCUR(b); p < l; p++) {
397     switch (*p) {
398       case SL_END: *q++ = SL_ESC; *q++ = SL_ESCEND; break;
399       case SL_ESC: *q++ = SL_ESC; *q++ = SL_ESCESC; break;
400       default: *q++ = *p; break;
401     }
402   }
403   *q++ = SL_END;
404   IF_TRACING(T_TUNNEL, {
405     trace_block(T_PACKET, "tun-slip: SLIP-encapsulated contents",
406                 buf, q - buf);
407   })
408   write(t->sl->ofd, buf, q - buf);
409 }
410
411 /* --- @t_destroy@ --- *
412  *
413  * Arguments:   @tunnel *t@ = pointer to tunnel block
414  *
415  * Returns:     ---
416  *
417  * Use:         Destroys a tunnel.
418  */
419
420 static void t_destroy(tunnel *t)
421 {
422   slipif *sl = t->sl;
423
424   /* --- If it reported EOF, leave it out-of-action --- */
425
426   if (!(t->st & ST_EOF)) {
427     sel_rmfile(&t->f);
428     sl->f &= ~F_INUSE;
429   }
430   if (sl && (sl->f & F_DYNAMIC)) {
431     T( trace(T_TUNNEL, "tun-slip: releasing dynamic slipif %s", sl->name); )
432     close(sl->ofd);
433     close(sl->ifd);
434     kill(sl->kid, SIGTERM);
435     xfree(sl->name);
436     DESTROY(sl);
437   }
438   DESTROY(t);
439 }
440
441 const tunnel_ops tun_slip = {
442   "slip",
443   t_init,
444   t_create,
445   t_ifname,
446   t_inject,
447   t_destroy
448 };
449
450 /*----- That's all, folks -------------------------------------------------*/