global reindent, now with uncrustify hook enabled
[oweals/gnunet.git] / src / exit / gnunet-helper-exit.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2010, 2011, 2012 Christian Grothoff
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      Affero General Public License for more details.
14
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
19  */
20
21 /**
22  * @file exit/gnunet-helper-exit.c
23  *
24  * @brief the helper for exit nodes. Opens a virtual
25  * network-interface, sends data received on the if to stdout, sends
26  * data received on stdin to the interface.  The code also enables
27  * IPv4/IPv6 forwarding and NAT on the current system (the latter on
28  * an interface specified on the command-line); these changes to the
29  * network configuration are NOT automatically undone when the program
30  * is stopped (this is because we cannot be sure that some other
31  * application didn't enable them before or after us; also, these
32  * changes should be mostly harmless as it simply turns the system
33  * into a router).
34  *
35  * @author Philipp Tölke
36  * @author Christian Grothoff
37  *
38  * The following list of people have reviewed this code and considered
39  * it safe since the last modification (if you reviewed it, please
40  * have your name added to the list):
41  *
42  * - Philipp Tölke
43  */
44 #include "platform.h"
45 #include <linux/if_tun.h>
46
47 /**
48  * Need 'struct GNUNET_MessageHeader'.
49  */
50 #include "gnunet_crypto_lib.h"
51 #include "gnunet_common.h"
52
53 /**
54  * Need VPN message types.
55  */
56 #include "gnunet_protocols.h"
57
58 /**
59  * Should we print (interesting|debug) messages that can happen during
60  * normal operation?
61  */
62 #define DEBUG GNUNET_NO
63
64 /**
65  * Maximum size of a GNUnet message (GNUNET_MAX_MESSAGE_SIZE)
66  */
67 #define MAX_SIZE 65536
68
69 /**
70  * Path to 'sysctl' binary.
71  */
72 static const char *sbin_sysctl;
73
74 /**
75  * Path to 'iptables' binary.
76  */
77 static const char *sbin_iptables;
78
79
80 #ifndef _LINUX_IN6_H
81 /**
82  * This is in linux/include/net/ipv6.h, but not always exported...
83  */
84 struct in6_ifreq
85 {
86   struct in6_addr ifr6_addr;
87   uint32_t ifr6_prefixlen; /* __u32 in the original */
88   int ifr6_ifindex;
89 };
90 #endif
91
92
93 /**
94  * Open '/dev/null' and make the result the given
95  * file descriptor.
96  *
97  * @param target_fd desired FD to point to /dev/null
98  * @param flags open flags (O_RDONLY, O_WRONLY)
99  */
100 static void
101 open_dev_null (int target_fd,
102                int flags)
103 {
104   int fd;
105
106   fd = open ("/dev/null", flags);
107   if (-1 == fd)
108     abort ();
109   if (fd == target_fd)
110     return;
111   if (-1 == dup2 (fd, target_fd))
112   {
113     (void) close (fd);
114     abort ();
115   }
116   (void) close (fd);
117 }
118
119
120 /**
121  * Run the given command and wait for it to complete.
122  *
123  * @param file name of the binary to run
124  * @param cmd command line arguments (as given to 'execv')
125  * @return 0 on success, 1 on any error
126  */
127 static int
128 fork_and_exec (const char *file,
129                char *const cmd[])
130 {
131   int status;
132   pid_t pid;
133   pid_t ret;
134
135   pid = fork ();
136   if (-1 == pid)
137   {
138     fprintf (stderr,
139              "fork failed: %s\n",
140              strerror (errno));
141     return 1;
142   }
143   if (0 == pid)
144   {
145     /* we are the child process */
146     /* close stdin/stdout to not cause interference
147        with the helper's main protocol! */
148     (void) close (0);
149     open_dev_null (0, O_RDONLY);
150     (void) close (1);
151     open_dev_null (1, O_WRONLY);
152     (void) execv (file, cmd);
153     /* can only get here on error */
154     fprintf (stderr,
155              "exec `%s' failed: %s\n",
156              file,
157              strerror (errno));
158     _exit (1);
159   }
160   /* keep running waitpid as long as the only error we get is 'EINTR' */
161   while ((-1 == (ret = waitpid (pid, &status, 0))) &&
162          (errno == EINTR))
163     ;
164   if (-1 == ret)
165   {
166     fprintf (stderr,
167              "waitpid failed: %s\n",
168              strerror (errno));
169     return 1;
170   }
171   if (! (WIFEXITED (status) && (0 == WEXITSTATUS (status))))
172     return 1;
173   /* child process completed and returned success, we're happy */
174   return 0;
175 }
176
177
178 /**
179  * Creates a tun-interface called dev;
180  *
181  * @param dev is asumed to point to a char[IFNAMSIZ]
182  *        if *dev == '\\0', uses the name supplied by the kernel;
183  * @return the fd to the tun or -1 on error
184  */
185 static int
186 init_tun (char *dev)
187 {
188   struct ifreq ifr;
189   int fd;
190
191   if (NULL == dev)
192   {
193     errno = EINVAL;
194     return -1;
195   }
196
197   if (-1 == (fd = open ("/dev/net/tun", O_RDWR)))
198   {
199     fprintf (stderr, "Error opening `%s': %s\n", "/dev/net/tun",
200              strerror (errno));
201     return -1;
202   }
203
204   if (fd >= FD_SETSIZE)
205   {
206     fprintf (stderr, "File descriptor to large: %d", fd);
207     (void) close (fd);
208     return -1;
209   }
210
211   memset (&ifr, 0, sizeof(ifr));
212   ifr.ifr_flags = IFF_TUN;
213
214   if ('\0' != *dev)
215     strncpy (ifr.ifr_name, dev, IFNAMSIZ);
216
217   if (-1 == ioctl (fd, TUNSETIFF, (void *) &ifr))
218   {
219     fprintf (stderr,
220              "Error with ioctl on `%s': %s\n", "/dev/net/tun",
221              strerror (errno));
222     (void) close (fd);
223     return -1;
224   }
225   strcpy (dev, ifr.ifr_name);
226   return fd;
227 }
228
229
230 /**
231  * @brief Sets the IPv6-Address given in address on the interface dev
232  *
233  * @param dev the interface to configure
234  * @param address the IPv6-Address
235  * @param prefix_len the length of the network-prefix
236  */
237 static void
238 set_address6 (const char *dev, const char *address, unsigned long prefix_len)
239 {
240   struct ifreq ifr;
241   struct sockaddr_in6 sa6;
242   int fd;
243   struct in6_ifreq ifr6;
244
245   /*
246    * parse the new address
247    */
248   memset (&sa6, 0, sizeof(struct sockaddr_in6));
249   sa6.sin6_family = AF_INET6;
250   if (1 != inet_pton (AF_INET6, address, &sa6.sin6_addr))
251   {
252     fprintf (stderr, "Failed to parse address `%s': %s\n", address,
253              strerror (errno));
254     exit (1);
255   }
256
257   if (-1 == (fd = socket (PF_INET6, SOCK_DGRAM, 0)))
258   {
259     fprintf (stderr, "Error creating socket: %s\n", strerror (errno));
260     exit (1);
261   }
262
263   memset (&ifr, 0, sizeof(struct ifreq));
264   /*
265    * Get the index of the if
266    */
267   strncpy (ifr.ifr_name, dev, IFNAMSIZ);
268   if (-1 == ioctl (fd, SIOGIFINDEX, &ifr))
269   {
270     fprintf (stderr, "ioctl failed at %d: %s\n", __LINE__, strerror (errno));
271     (void) close (fd);
272     exit (1);
273   }
274
275   memset (&ifr6, 0, sizeof(struct in6_ifreq));
276   ifr6.ifr6_addr = sa6.sin6_addr;
277   ifr6.ifr6_ifindex = ifr.ifr_ifindex;
278   ifr6.ifr6_prefixlen = prefix_len;
279
280   /*
281    * Set the address
282    */
283   if (-1 == ioctl (fd, SIOCSIFADDR, &ifr6))
284   {
285     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
286              strerror (errno));
287     (void) close (fd);
288     exit (1);
289   }
290
291   /*
292    * Get the flags
293    */
294   if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr))
295   {
296     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
297              strerror (errno));
298     (void) close (fd);
299     exit (1);
300   }
301
302   /*
303    * Add the UP and RUNNING flags
304    */
305   ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
306   if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr))
307   {
308     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
309              strerror (errno));
310     (void) close (fd);
311     exit (1);
312   }
313
314   if (0 != close (fd))
315   {
316     fprintf (stderr, "close failed: %s\n", strerror (errno));
317     exit (1);
318   }
319 }
320
321
322 /**
323  * @brief Sets the IPv4-Address given in address on the interface dev
324  *
325  * @param dev the interface to configure
326  * @param address the IPv4-Address
327  * @param mask the netmask
328  */
329 static void
330 set_address4 (const char *dev, const char *address, const char *mask)
331 {
332   int fd;
333   struct sockaddr_in *addr;
334   struct ifreq ifr;
335
336   memset (&ifr, 0, sizeof(struct ifreq));
337   addr = (struct sockaddr_in *) &(ifr.ifr_addr);
338   addr->sin_family = AF_INET;
339
340   /*
341    * Parse the address
342    */
343   if (1 != inet_pton (AF_INET, address, &addr->sin_addr.s_addr))
344   {
345     fprintf (stderr, "Failed to parse address `%s': %s\n", address,
346              strerror (errno));
347     exit (1);
348   }
349
350   if (-1 == (fd = socket (PF_INET, SOCK_DGRAM, 0)))
351   {
352     fprintf (stderr, "Error creating socket: %s\n", strerror (errno));
353     exit (1);
354   }
355
356   strncpy (ifr.ifr_name, dev, IFNAMSIZ);
357
358   /*
359    * Set the address
360    */
361   if (-1 == ioctl (fd, SIOCSIFADDR, &ifr))
362   {
363     fprintf (stderr, "ioctl failed at %d: %s\n", __LINE__, strerror (errno));
364     (void) close (fd);
365     exit (1);
366   }
367
368   /*
369    * Parse the netmask
370    */
371   addr = (struct sockaddr_in *) &(ifr.ifr_netmask);
372   if (1 != inet_pton (AF_INET, mask, &addr->sin_addr.s_addr))
373   {
374     fprintf (stderr, "Failed to parse address `%s': %s\n", mask,
375              strerror (errno));
376     (void) close (fd);
377     exit (1);
378   }
379
380   /*
381    * Set the netmask
382    */
383   if (-1 == ioctl (fd, SIOCSIFNETMASK, &ifr))
384   {
385     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
386              strerror (errno));
387     (void) close (fd);
388     exit (1);
389   }
390
391   /*
392    * Get the flags
393    */
394   if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr))
395   {
396     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
397              strerror (errno));
398     (void) close (fd);
399     exit (1);
400   }
401
402   /*
403    * Add the UP and RUNNING flags
404    */
405   ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
406   if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr))
407   {
408     fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
409              strerror (errno));
410     (void) close (fd);
411     exit (1);
412   }
413
414   if (0 != close (fd))
415   {
416     fprintf (stderr, "close failed: %s\n", strerror (errno));
417     (void) close (fd);
418     exit (1);
419   }
420 }
421
422
423 /**
424  * Start forwarding to and from the tunnel.
425  *
426  * @param fd_tun tunnel FD
427  */
428 static void
429 run (int fd_tun)
430 {
431   /*
432    * The buffer filled by reading from fd_tun
433    */
434   unsigned char buftun[MAX_SIZE];
435   ssize_t buftun_size = 0;
436   unsigned char *buftun_read = NULL;
437
438   /*
439    * The buffer filled by reading from stdin
440    */
441   unsigned char bufin[MAX_SIZE];
442   ssize_t bufin_size = 0;
443   size_t bufin_rpos = 0;
444   unsigned char *bufin_read = NULL;
445
446   fd_set fds_w;
447   fd_set fds_r;
448
449   /* read refers to reading from fd_tun, writing to stdout */
450   int read_open = 1;
451
452   /* write refers to reading from stdin, writing to fd_tun */
453   int write_open = 1;
454
455   while ((1 == read_open) && (1 == write_open))
456   {
457     FD_ZERO (&fds_w);
458     FD_ZERO (&fds_r);
459
460     /*
461      * We are supposed to read and the buffer is empty
462      * -> select on read from tun
463      */
464     if (read_open && (0 == buftun_size))
465       FD_SET (fd_tun, &fds_r);
466
467     /*
468      * We are supposed to read and the buffer is not empty
469      * -> select on write to stdout
470      */
471     if (read_open && (0 != buftun_size))
472       FD_SET (1, &fds_w);
473
474     /*
475      * We are supposed to write and the buffer is empty
476      * -> select on read from stdin
477      */
478     if (write_open && (NULL == bufin_read))
479       FD_SET (0, &fds_r);
480
481     /*
482      * We are supposed to write and the buffer is not empty
483      * -> select on write to tun
484      */
485     if (write_open && (NULL != bufin_read))
486       FD_SET (fd_tun, &fds_w);
487
488     int r = select (fd_tun + 1, &fds_r, &fds_w, NULL, NULL);
489
490     if (-1 == r)
491     {
492       if (EINTR == errno)
493         continue;
494       fprintf (stderr, "select failed: %s\n", strerror (errno));
495       exit (1);
496     }
497
498     if (r > 0)
499     {
500       if (FD_ISSET (fd_tun, &fds_r))
501       {
502         buftun_size =
503           read (fd_tun, buftun + sizeof(struct GNUNET_MessageHeader),
504                 MAX_SIZE - sizeof(struct GNUNET_MessageHeader));
505         if (-1 == buftun_size)
506         {
507           fprintf (stderr,
508                    "read-error: %s\n",
509                    strerror (errno));
510           shutdown (fd_tun, SHUT_RD);
511           shutdown (1, SHUT_WR);
512           read_open = 0;
513           buftun_size = 0;
514         }
515         else if (0 == buftun_size)
516         {
517 #if DEBUG
518           fprintf (stderr, "EOF on tun\n");
519 #endif
520           shutdown (fd_tun, SHUT_RD);
521           shutdown (1, SHUT_WR);
522           read_open = 0;
523           buftun_size = 0;
524         }
525         else
526         {
527           buftun_read = buftun;
528           struct GNUNET_MessageHeader *hdr =
529             (struct GNUNET_MessageHeader *) buftun;
530           buftun_size += sizeof(struct GNUNET_MessageHeader);
531           hdr->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
532           hdr->size = htons (buftun_size);
533         }
534       }
535       else if (FD_ISSET (1, &fds_w))
536       {
537         ssize_t written = write (1, buftun_read, buftun_size);
538
539         if (-1 == written)
540         {
541 #if ! DEBUG
542           if (errno != EPIPE)
543 #endif
544           fprintf (stderr,
545                    "write-error to stdout: %s\n",
546                    strerror (errno));
547           shutdown (fd_tun, SHUT_RD);
548           shutdown (1, SHUT_WR);
549           read_open = 0;
550           buftun_size = 0;
551         }
552         else if (0 == written)
553         {
554           fprintf (stderr, "write returned 0!?\n");
555           exit (1);
556         }
557         else
558         {
559           buftun_size -= written;
560           buftun_read += written;
561         }
562       }
563
564       if (FD_ISSET (0, &fds_r))
565       {
566         bufin_size = read (0, bufin + bufin_rpos, MAX_SIZE - bufin_rpos);
567         if (-1 == bufin_size)
568         {
569           fprintf (stderr, "read-error: %s\n", strerror (errno));
570           shutdown (0, SHUT_RD);
571           shutdown (fd_tun, SHUT_WR);
572           write_open = 0;
573           bufin_size = 0;
574         }
575         else if (0 == bufin_size)
576         {
577 #if DEBUG
578           fprintf (stderr, "EOF on stdin\n");
579 #endif
580           shutdown (0, SHUT_RD);
581           shutdown (fd_tun, SHUT_WR);
582           write_open = 0;
583           bufin_size = 0;
584         }
585         else
586         {
587           struct GNUNET_MessageHeader *hdr;
588
589 PROCESS_BUFFER:
590           bufin_rpos += bufin_size;
591           if (bufin_rpos < sizeof(struct GNUNET_MessageHeader))
592             continue;
593           hdr = (struct GNUNET_MessageHeader *) bufin;
594           if (ntohs (hdr->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER)
595           {
596             fprintf (stderr, "protocol violation!\n");
597             exit (1);
598           }
599           if (ntohs (hdr->size) > bufin_rpos)
600             continue;
601           bufin_read = bufin + sizeof(struct GNUNET_MessageHeader);
602           bufin_size = ntohs (hdr->size) - sizeof(struct GNUNET_MessageHeader);
603           bufin_rpos -= bufin_size + sizeof(struct GNUNET_MessageHeader);
604         }
605       }
606       else if (FD_ISSET (fd_tun, &fds_w))
607       {
608         ssize_t written = write (fd_tun, bufin_read, bufin_size);
609
610         if (-1 == written)
611         {
612           fprintf (stderr, "write-error to tun: %s\n", strerror (errno));
613           shutdown (0, SHUT_RD);
614           shutdown (fd_tun, SHUT_WR);
615           write_open = 0;
616           bufin_size = 0;
617         }
618         else if (0 == written)
619         {
620           fprintf (stderr, "write returned 0!?\n");
621           exit (1);
622         }
623         else
624         {
625           bufin_size -= written;
626           bufin_read += written;
627           if (0 == bufin_size)
628           {
629             memmove (bufin, bufin_read, bufin_rpos);
630             bufin_read = NULL;           /* start reading again */
631             bufin_size = 0;
632             goto PROCESS_BUFFER;
633           }
634         }
635       }
636     }
637   }
638 }
639
640
641 /**
642  * Open VPN tunnel interface.
643  *
644  * @param argc must be 6
645  * @param argv 0: binary name ("gnunet-helper-exit")
646  *             1: tunnel interface name ("gnunet-exit")
647  *             2: "physical" interface name ("eth0"), or "-" to not setup NAT
648  *                and routing
649  *             3: IPv6 address ("::1"), or "-" to skip IPv6
650  *             4: IPv6 netmask length in bits ("64") [ignored if #4 is "-"]
651  *             5: IPv4 address ("1.2.3.4"), or "-" to skip IPv4
652  *             6: IPv4 netmask ("255.255.0.0") [ignored if #4 is "-"]
653  */
654 int
655 main (int argc, char **argv)
656 {
657   char dev[IFNAMSIZ];
658   int fd_tun;
659   int global_ret;
660
661   if (7 != argc)
662   {
663     fprintf (stderr, "Fatal: must supply 6 arguments!\n");
664     return 1;
665   }
666   if ((0 == strcmp (argv[3], "-")) &&
667       (0 == strcmp (argv[5], "-")))
668   {
669     fprintf (stderr, "Fatal: disabling both IPv4 and IPv6 makes no sense.\n");
670     return 1;
671   }
672   if (0 != strcmp (argv[2], "-"))
673   {
674 #ifdef IPTABLES
675     if (0 == access (IPTABLES, X_OK))
676       sbin_iptables = IPTABLES;
677     else
678 #endif
679     if (0 == access ("/sbin/iptables", X_OK))
680       sbin_iptables = "/sbin/iptables";
681     else if (0 == access ("/usr/sbin/iptables", X_OK))
682       sbin_iptables = "/usr/sbin/iptables";
683     else
684     {
685       fprintf (stderr,
686                "Fatal: executable iptables not found in approved directories: %s\n",
687                strerror (errno));
688       return 1;
689     }
690 #ifdef SYSCTL
691     if (0 == access (SYSCTL, X_OK))
692       sbin_sysctl = SYSCTL;
693     else
694 #endif
695     if (0 == access ("/sbin/sysctl", X_OK))
696       sbin_sysctl = "/sbin/sysctl";
697     else if (0 == access ("/usr/sbin/sysctl", X_OK))
698       sbin_sysctl = "/usr/sbin/sysctl";
699     else
700     {
701       fprintf (stderr,
702                "Fatal: executable sysctl not found in approved directories: %s\n",
703                strerror (errno));
704       return 1;
705     }
706   }
707
708   strncpy (dev, argv[1], IFNAMSIZ);
709   dev[IFNAMSIZ - 1] = '\0';
710
711   if (-1 == (fd_tun = init_tun (dev)))
712   {
713     fprintf (stderr,
714              "Fatal: could not initialize tun-interface `%s' with IPv6 %s/%s and IPv4 %s/%s\n",
715              dev,
716              argv[3],
717              argv[4],
718              argv[5],
719              argv[6]);
720     return 1;
721   }
722
723   if (0 != strcmp (argv[3], "-"))
724   {
725     {
726       const char *address = argv[3];
727       long prefix_len = atol (argv[4]);
728
729       if ((prefix_len < 1) || (prefix_len > 127))
730       {
731         fprintf (stderr, "Fatal: prefix_len out of range\n");
732         return 1;
733       }
734       set_address6 (dev, address, prefix_len);
735     }
736     if (0 != strcmp (argv[2], "-"))
737     {
738       char *const sysctl_args[] = {
739         "sysctl", "-w", "net.ipv6.conf.all.forwarding=1", NULL
740       };
741       if (0 != fork_and_exec (sbin_sysctl,
742                               sysctl_args))
743       {
744         fprintf (stderr,
745                  "Failed to enable IPv6 forwarding.  Will continue anyway.\n");
746       }
747     }
748   }
749
750   if (0 != strcmp (argv[5], "-"))
751   {
752     {
753       const char *address = argv[5];
754       const char *mask = argv[6];
755
756       set_address4 (dev, address, mask);
757     }
758     if (0 != strcmp (argv[2], "-"))
759     {
760       {
761         char *const sysctl_args[] = {
762           "sysctl", "-w", "net.ipv4.ip_forward=1", NULL
763         };
764         if (0 != fork_and_exec (sbin_sysctl,
765                                 sysctl_args))
766         {
767           fprintf (stderr,
768                    "Failed to enable IPv4 forwarding.  Will continue anyway.\n");
769         }
770       }
771       {
772         char *const iptables_args[] = {
773           "iptables", "-t", "nat", "-A", "POSTROUTING", "-o", argv[2], "-j",
774           "MASQUERADE", NULL
775         };
776         if (0 != fork_and_exec (sbin_iptables,
777                                 iptables_args))
778         {
779           fprintf (stderr,
780                    "Failed to enable IPv4 masquerading (NAT).  Will continue anyway.\n");
781         }
782       }
783     }
784   }
785
786   uid_t uid = getuid ();
787 #ifdef HAVE_SETRESUID
788   if (0 != setresuid (uid, uid, uid))
789   {
790     fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno));
791     global_ret = 2;
792     goto cleanup;
793   }
794 #else
795   if (0 != (setuid (uid) | seteuid (uid)))
796   {
797     fprintf (stderr, "Failed to setuid: %s\n", strerror (errno));
798     global_ret = 2;
799     goto cleanup;
800   }
801 #endif
802
803   if (SIG_ERR == signal (SIGPIPE, SIG_IGN))
804   {
805     fprintf (stderr, "Failed to protect against SIGPIPE: %s\n",
806              strerror (errno));
807     /* no exit, we might as well die with SIGPIPE should it ever happen */
808   }
809   run (fd_tun);
810   global_ret = 0;
811 cleanup:
812   (void) close (fd_tun);
813   return global_ret;
814 }
815
816 /* end of gnunet-helper-exit.c */