Documented, cleaned and indented gnunet-helper-vpn
[oweals/gnunet.git] / src / vpn / gnunet-helper-vpn.c
1 /*
2      This file is part of GNUnet.
3      (C) 2010 Christian Grothoff
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file vpn/gnunet-daemon-vpn.c
23  * @brief the helper for various vpn-daemons. Opens a virtual network-interface,
24  * sends data received on the if to stdout, sends data received on stdin to the
25  * interface
26  * @author Philipp Tölke
27  */
28 #include <platform.h>
29 #include <linux/if_tun.h>
30
31 /**
32  * Need 'struct GNUNET_MessageHeader'.
33  */
34 #include "gnunet_common.h"
35
36 /**
37  * Need VPN message types.
38  */
39 #include "gnunet_protocols.h"
40
41 /**
42  * Maximum size of a GNUnet message (GNUNET_SERVER_MAX_MESSAGE_SIZE)
43  */
44 #define MAX_SIZE 65536
45
46 #ifndef _LINUX_IN6_H
47 /**
48  * This is in linux/include/net/ipv6.h, but not always exported...
49  */
50 struct in6_ifreq
51 {
52   struct in6_addr ifr6_addr;
53   uint32_t ifr6_prefixlen;
54   unsigned int ifr6_ifindex;
55 };
56 #endif
57
58 /**
59  * Creates a tun-interface called dev;
60  * @param dev is asumed to point to a char[IFNAMSIZ]
61  *        if *dev == '\\0', uses the name supplied by the kernel
62  * @return the fd to the tun or -1 on error
63  */
64 static int
65 init_tun (char *dev)
66 {
67   struct ifreq ifr;
68   int fd;
69
70   if (NULL == dev)
71     {
72       errno = EINVAL;
73       return -1;
74     }
75
76   if (-1 == (fd = open ("/dev/net/tun", O_RDWR)))
77     {
78       fprintf (stderr,
79                "Error opening `%s': %s\n", "/dev/net/tun", strerror (errno));
80       return -1;
81     }
82
83   if (fd >= FD_SETSIZE)
84     {
85       fprintf (stderr, "Filedescriptor to large: %d", fd);
86       return -1;
87     }
88
89   memset (&ifr, 0, sizeof (ifr));
90   ifr.ifr_flags = IFF_TUN;
91
92   if ('\0' == *dev)
93     strncpy (ifr.ifr_name, dev, IFNAMSIZ);
94
95   if (-1 == ioctl (fd, TUNSETIFF, (void *) &ifr))
96     {
97       fprintf (stderr,
98                "Error with ioctl on `%s': %s\n",
99                "/dev/net/tun", strerror (errno));
100       close (fd);
101       return -1;
102     }
103   strcpy (dev, ifr.ifr_name);
104   return fd;
105 }
106
107
108 /**
109  * @brief Sets the IPv6-Address given in address on the interface dev
110  *
111  * @param dev the interface to configure
112  * @param address the IPv6-Address
113  * @param prefix_len the length of the network-prefix
114  */
115 static void
116 set_address6 (const char *dev, const char *address, unsigned long prefix_len)
117 {
118   struct ifreq ifr;
119   struct in6_ifreq ifr6;
120   struct sockaddr_in6 sa6;
121   int fd;
122
123   if (-1 == (fd = socket (PF_INET6, SOCK_DGRAM, 0)))
124     {
125       fprintf (stderr, "Error creating socket: %s\n", strerror (errno));
126       exit (1);
127     }
128   memset (&sa6, 0, sizeof (struct sockaddr_in6));
129   sa6.sin6_family = AF_INET6;
130
131   /*
132    * parse the new address
133    */
134   if (1 != inet_pton (AF_INET6, address, sa6.sin6_addr.s6_addr))
135     {
136       fprintf (stderr,
137                "Failed to parse address `%s': %s\n",
138                address, strerror (errno));
139       exit (1);
140     }
141   memcpy (&ifr6.ifr6_addr, &sa6.sin6_addr, sizeof (struct in6_addr));
142
143
144   /*
145    * Get the index of the if
146    */
147   strncpy (ifr.ifr_name, dev, IFNAMSIZ);
148   if (-1 == ioctl (fd, SIOGIFINDEX, &ifr))
149     {
150       fprintf (stderr,
151                "ioctl failed at %d: %s\n", __LINE__, strerror (errno));
152       exit (1);
153     }
154   ifr6.ifr6_ifindex = ifr.ifr_ifindex;
155
156   ifr6.ifr6_prefixlen = prefix_len;
157
158   /*
159    * Set the address
160    */
161   if (-1 == ioctl (fd, SIOCSIFADDR, &ifr6))
162     {
163       fprintf (stderr,
164                "ioctl failed at line %d: %s\n", __LINE__, strerror (errno));
165       exit (1);
166     }
167
168   /*
169    * Get the flags
170    */
171   if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr))
172     {
173       fprintf (stderr,
174                "ioctl failed at line %d: %s\n", __LINE__, strerror (errno));
175       exit (1);
176     }
177
178   /*
179    * Add the UP and RUNNING flags
180    */
181   ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
182   if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr))
183     {
184       fprintf (stderr,
185                "ioctl failed at line %d: %s\n", __LINE__, strerror (errno));
186       exit (1);
187     }
188
189   if (0 != close (fd))
190     {
191       fprintf (stderr, "close failed: %s\n", strerror (errno));
192       exit (1);
193     }
194 }
195
196
197 /**
198  * @brief Sets the IPv4-Address given in address on the interface dev
199  *
200  * @param dev the interface to configure
201  * @param address the IPv4-Address
202  * @param mask the netmask
203  */
204 static void
205 set_address4 (char *dev, char *address, char *mask)
206 {
207   int fd = 0;
208   struct sockaddr_in *addr;
209   struct ifreq ifr;
210
211   memset (&ifr, 0, sizeof (struct ifreq));
212   addr = (struct sockaddr_in *) &(ifr.ifr_addr);
213   memset (addr, 0, sizeof (struct sockaddr_in));
214   addr->sin_family = AF_INET;
215   addr->sin_addr.s_addr = inet_addr (address);
216
217   /*
218    * Parse the address
219    */
220   int r = inet_pton (AF_INET, address, &addr->sin_addr.s_addr);
221   if (r < 0)
222     {
223       fprintf (stderr, "error at inet_pton: %m\n");
224       exit (1);
225     }
226
227   fd = socket (PF_INET, SOCK_DGRAM, 0);
228   if (fd < 0)
229     {
230       perror ("socket()");
231       return;
232     }
233
234   strncpy (ifr.ifr_name, dev, IFNAMSIZ);
235
236   /*
237    * Set the address
238    */
239   if (ioctl (fd, SIOCSIFADDR, &ifr) != 0)
240     {
241       perror ("SIOCSIFADDR");
242       close (fd);
243       return;
244     }
245
246   /*
247    * Parse the netmask
248    */
249   addr = (struct sockaddr_in *) &(ifr.ifr_netmask);
250   r = inet_pton (AF_INET, mask, &addr->sin_addr.s_addr);
251   if (r < 0)
252     {
253       fprintf (stderr, "error at inet_pton: %m\n");
254       exit (1);
255     }
256
257   /*
258    * Set the netmask
259    */
260   if (ioctl (fd, SIOCSIFNETMASK, &ifr) != 0)
261     {
262       perror ("SIOCSIFNETMASK");
263       close (fd);
264       return;
265     }
266
267   /*
268    * Get the flags
269    */
270   if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr))
271     {
272       fprintf (stderr,
273                "ioctl failed at line %d: %s\n", __LINE__, strerror (errno));
274       exit (1);
275     }
276
277   /*
278    * Add the UP and RUNNING flags
279    */
280   ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
281   if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr))
282     {
283       fprintf (stderr,
284                "ioctl failed at line %d: %s\n", __LINE__, strerror (errno));
285       exit (1);
286     }
287
288   if (0 != close (fd))
289     {
290       fprintf (stderr, "close failed: %s\n", strerror (errno));
291       exit (1);
292     }
293 }
294
295
296 static void
297 run (int fd_tun)
298 {
299   /*
300    * The buffer filled by reading from fd_tun
301    */
302   unsigned char buftun[MAX_SIZE];
303   ssize_t buftun_size = 0;
304   unsigned char *buftun_read;
305
306   /*
307    * The buffer filled by reading from stdin
308    */
309   unsigned char bufin[MAX_SIZE];
310   ssize_t bufin_size = 0;
311   unsigned char *bufin_write;
312
313   fd_set fds_w;
314   fd_set fds_r;
315
316   int rea = 1;
317   int wri = 1;
318
319   while ((1 == rea) || (1 == wri))
320     {
321       FD_ZERO (&fds_w);
322       FD_ZERO (&fds_r);
323
324       /*
325        * We are supposed to read and the buffer is empty
326        * -> select on read from tun
327        */
328       if (rea && (0 == buftun_size))
329         {
330           FD_SET (fd_tun, &fds_r);
331         }
332
333       /*
334        * We are supposed to read and the buffer is not empty
335        * -> select on write to stdout
336        */
337       if (rea && (0 != buftun_size))
338         {
339           FD_SET (1, &fds_w);
340         }
341
342       /*
343        * We are supposed to write and the buffer is empty
344        * -> select on read from stdin
345        */
346       if (wri && (0 == bufin_size))
347         {
348           FD_SET (0, &fds_r);
349         }
350
351       /*
352        * We are supposed to write and the buffer is not empty
353        * -> select on write to tun
354        */
355       if (wri && (0 != bufin_size))
356         {
357           FD_SET (fd_tun, &fds_w);
358         }
359
360       int r = select (fd_tun + 1, &fds_r, &fds_w, NULL, NULL);
361       if (-1 == r)
362         {
363           fprintf (stderr, "select failed: %s\n", strerror (errno));
364           exit (1);
365         }
366
367       if (r > 0)
368         {
369           if (FD_ISSET (fd_tun, &fds_r))
370             {
371               buftun_size =
372                 read (fd_tun, buftun + sizeof (struct GNUNET_MessageHeader),
373                       MAX_SIZE - sizeof (struct GNUNET_MessageHeader)) +
374                 sizeof (struct GNUNET_MessageHeader);
375               if (-1 == buftun_size)
376                 {
377                   fprintf (stderr, "read-error: %s\n", strerror (errno));
378                   shutdown (fd_tun, SHUT_RD);
379                   shutdown (1, SHUT_WR);
380                   rea = 0;
381                   buftun_size = 0;
382                 }
383               else if (0 == buftun_size)
384                 {
385                   fprintf (stderr, "eof on tun\n");
386                   shutdown (fd_tun, SHUT_RD);
387                   shutdown (1, SHUT_WR);
388                   rea = 0;
389                   buftun_size = 0;
390                 }
391               else
392                 {
393                   buftun_read = buftun;
394                   struct GNUNET_MessageHeader *hdr =
395                     (struct GNUNET_MessageHeader *) buftun;
396                   hdr->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
397                   hdr->size = htons (buftun_size);
398                 }
399             }
400           else if (FD_ISSET (1, &fds_w))
401             {
402               ssize_t written = write (1, buftun_read, buftun_size);
403               if (-1 == written)
404                 {
405                   fprintf (stderr, "write-error to stdout: %s\n",
406                            strerror (errno));
407                   shutdown (fd_tun, SHUT_RD);
408                   shutdown (1, SHUT_WR);
409                   rea = 0;
410                   buftun_size = 0;
411                 }
412               buftun_size -= written;
413               buftun_read += written;
414             }
415
416           if (FD_ISSET (0, &fds_r))
417             {
418               bufin_size = read (0, bufin, MAX_SIZE);
419               if (-1 == bufin_size)
420                 {
421                   fprintf (stderr, "read-error: %s\n", strerror (errno));
422                   shutdown (0, SHUT_RD);
423                   shutdown (fd_tun, SHUT_WR);
424                   wri = 0;
425                   bufin_size = 0;
426                 }
427               else if (0 == bufin_size)
428                 {
429                   fprintf (stderr, "eof on stdin\n");
430                   shutdown (0, SHUT_RD);
431                   shutdown (fd_tun, SHUT_WR);
432                   wri = 0;
433                   bufin_size = 0;
434                 }
435               else
436                 {
437                   struct GNUNET_MessageHeader *hdr =
438                     (struct GNUNET_MessageHeader *) bufin;
439                   if ((bufin_size < sizeof (struct GNUNET_MessageHeader))
440                       || (ntohs (hdr->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER)
441                       || (ntohs (hdr->size) != bufin_size))
442                     {
443                       fprintf (stderr, "protocol violation!\n");
444                       exit (1);
445                     }
446                   bufin_write = bufin + sizeof (struct GNUNET_MessageHeader);
447                   bufin_size -= sizeof (struct GNUNET_MessageHeader);
448                 }
449             }
450           else if (FD_ISSET (fd_tun, &fds_w))
451             {
452               ssize_t written = write (fd_tun, bufin_write, bufin_size);
453               if (-1 == written)
454                 {
455                   fprintf (stderr, "write-error to tun: %s\n",
456                            strerror (errno));
457                   shutdown (0, SHUT_RD);
458                   shutdown (fd_tun, SHUT_WR);
459                   wri = 0;
460                   bufin_size = 0;
461                 }
462               bufin_size -= written;
463               bufin_write += written;
464             }
465         }
466     }
467 }
468
469
470 int
471 main (int argc, char **argv)
472 {
473   char dev[IFNAMSIZ];
474   int fd_tun;
475
476   memset (dev, 0, IFNAMSIZ);
477   if (-1 == (fd_tun = init_tun (dev)))
478     {
479       fprintf (stderr, "Fatal: could not initialize tun-interface\n");
480       return 1;
481     }
482
483   {
484     // TODO: get this out of argv
485     char address[] = "1234::1";
486     unsigned long prefix_len = 16;
487
488     set_address6 (dev, address, prefix_len);
489   }
490
491   {
492     char address[] = "10.10.10.1";
493     char mask[] = "255.255.255.252";
494
495     set_address4 (dev, address, mask);
496   }
497
498   uid_t uid = getuid ();
499   if (0 != setresuid (uid, uid, uid))
500     fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno));
501   run (fd_tun);
502   close (fd_tun);
503   return 0;
504 }