chiark / gitweb /
server/admin.c: Remove spurious `ping' in usage message.
[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 it under
13  * the terms of the GNU General Public License as published by the Free
14  * Software Foundation; either version 3 of the License, or (at your
15  * option) any later version.
16  *
17  * TrIPE is distributed in the hope that it will be useful, but WITHOUT
18  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20  * for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with TrIPE.  If not, see <https://www.gnu.org/licenses/>.
24  */
25
26 /*----- Header files ------------------------------------------------------*/
27
28 #define TUN_INTERNALS
29
30 #include "tripe.h"
31
32 /*----- Data structures ---------------------------------------------------*/
33
34 typedef struct slipif {
35   struct slipif *next;                  /* Next one in the list */
36   int ifd, ofd;                         /* File descriptors to talk on */
37   char *name;                           /* Interface name */
38   pid_t kid;                            /* Child process id */
39   unsigned f;                           /* Various flags */
40 #   define F_INUSE 1u                   /*   Interface is in use */
41 #   define F_DYNAMIC 2u                 /*   Interface found dynamically */
42 } slipif;
43
44 struct tunnel {
45   const tunnel_ops *ops;                /* Pointer to operations */
46   slipif *sl;                           /* My interface record */
47   sel_file f;                           /* Selector for SLIP tty */
48   struct peer *p;                       /* Pointer to my peer */
49   unsigned st;                          /* Current parser state */
50 #   define ST_ESC 1u                    /*   Last saw an escape character */
51 #   define ST_BAD 2u                    /*   This packet is malformed */
52 #   define ST_MASK 3u                   /*   Mask for the above bits */
53 #   define ST_EOF 4u                    /*   File descriptor reported EOF */
54 #   define ST_BROKEN 8u                 /*   Sending side is broken */
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           st |= ST_BAD;
126         } else if (q == t->buf) {
127           T( trace(T_TUNNEL, "tun-slip: empty packet"); )
128         } else {
129           IF_TRACING(T_TUNNEL, {
130             trace(T_TUNNEL, "tun-slip: packet arrived");
131             trace_block(T_PACKET, "tun-slip: packet contents",
132                         t->buf, q - t->buf);
133           })
134           buf_init(&b, t->buf, q - t->buf);
135           p_tun(t->p, &b);
136         }
137         q = t->buf;
138         st &= ~ST_MASK;
139         break;
140       case SL_ESC:
141         if ((st & ST_ESC) && !(st & ST_BAD)) {
142           a_warn("TUN", "%s", p_ifname(t->p), "slip", "bad-escape", A_END);
143           st |= ST_BAD;
144         } else
145           st |= ST_ESC;
146         break;
147       case SL_ESCEND:
148         if (st & ST_ESC)
149           o = SL_END;
150         goto emit;
151       case SL_ESCESC:
152         if (st & ST_ESC)
153           o = SL_ESC;
154         goto emit;
155       default:
156         if ((st & ST_ESC) && !(st & ST_BAD)) {
157           a_warn("TUN", "%s", p_ifname(t->p), "slip", "bad-escape", A_END);
158           st |= ST_BAD;
159         }
160       emit:
161         if (!(st & ST_BAD)) {
162           if (q < ll)
163             *q++ = o;
164           else {
165             a_warn("TUN", "%s", p_ifname(t->p), "slip", "overflow", A_END);
166             st |= ST_BAD;
167           }
168         }
169         st &= ~ST_ESC;
170         break;
171     }
172   }
173
174   t->n = q - t->buf;
175   t->st = st;
176 }
177
178 /* --- @t_init@ --- *
179  *
180  * Arguments:   ---
181  *
182  * Returns:     Zero on success, @-1@ on failure.
183  *
184  * Use:         Initializes the tunneling system.  Maybe this will require
185  *              opening file descriptors or something.
186  */
187
188 static int t_init(void)
189 {
190   char *p, *q;
191   dstr d = DSTR_INIT;
192   slipif *sl, **tail = &slipifs;
193   unsigned long uli, ulo;
194   size_t n;
195
196   if ((p = getenv("TRIPE_SLIPIF")) == 0)
197     return (0);
198
199   /* --- Build the list of available interfaces --- */
200
201   dstr_puts(&d, p);
202
203   p = d.buf;
204   for (;;) {
205     if (*p == '/' || *p == '.') {
206       slipcmd = p;
207       T( trace(T_TUNNEL, "tun-slip: declared slip command `%s'", slipcmd); )
208       break;
209     }
210     uli = strtoul(p, &q, 0);
211     if (uli > INT_MAX || q == p)
212       goto whine;
213     if (*q != ',')
214       ulo = uli;
215     else {
216       p = q + 1;
217       ulo = strtoul(p, &q, 0);
218       if (ulo > INT_MAX || q == p)
219         goto whine;
220     }
221     if (*q != '=' || (n = strcspn(q + 1, ":")) == 0)
222       goto whine;
223     sl = CREATE(slipif);
224     sl->next = 0;
225     sl->ifd = uli;
226     fdflags(sl->ifd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
227     fdflags(sl->ofd, O_NONBLOCK, 0, FD_CLOEXEC, FD_CLOEXEC);
228     sl->ofd = ulo;
229     sl->name = xmalloc(n + 1);
230     sl->kid = -1;
231     sl->f = 0;
232     memcpy(sl->name, q + 1, n);
233     sl->name[n] = 0;
234     *tail = sl;
235     tail = &sl->next;
236     T( trace(T_TUNNEL, "tun-slip: declared slipif %d,%d=%s",
237              sl->ifd, sl->ofd, sl->name); )
238     p = q + n + 1;
239     if (!*p)
240       break;
241     p++;
242   }
243   return (0);
244
245 whine:
246   a_warn("TUN", "-", "slip", "bad-interface-list", A_END);
247   return (-1);
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 -------------------------------------------------*/