byebye
[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 /**
60  * Creates a tun-interface called dev;
61  * @param dev is asumed to point to a char[IFNAMSIZ]
62  *        if *dev == '\0', uses the name supplied by the kernel
63  * @return the fd to the tun or -1 on error
64  */
65 static int 
66 init_tun (char *dev) 
67 {
68   struct ifreq ifr;
69   int fd;
70
71   if (NULL == dev) 
72     {
73       errno = EINVAL;
74       return -1;
75     }
76
77   if (-1 == (fd = open("/dev/net/tun", O_RDWR))) 
78     {
79       fprintf (stderr, 
80                "Error opening `%s': %s\n", 
81                "/dev/net/tun",
82                strerror(errno));
83       return -1;
84     }
85
86   memset(&ifr, 0, sizeof(ifr));
87   ifr.ifr_flags = IFF_TUN;
88
89   if ('\0' == *dev)
90     strncpy(ifr.ifr_name, dev, IFNAMSIZ);
91
92   if (-1 == ioctl(fd, TUNSETIFF, (void *) &ifr))
93     {
94       fprintf (stderr, 
95                "Error with ioctl on `%s': %s\n", 
96                "/dev/net/tun",
97                strerror(errno));
98       close (fd);
99       return -1;
100     }
101   strcpy(dev, ifr.ifr_name);
102   return fd;
103 }
104
105
106 /**
107  * @brief Sets the IPv6-Address given in address on the interface dev
108  *
109  * @param dev the interface to configure
110  * @param address the IPv6-Address
111  * @param prefix_len the length of the network-prefix
112  */
113 static void
114 set_address6 (const char *dev, 
115               const char *address, 
116               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, 
126                "Error creating socket: %s\n",
127                strerror (errno));
128       exit (1);
129     }
130   memset (&sa6, 0, sizeof (struct sockaddr_in6));
131   sa6.sin6_family = AF_INET6;
132
133   if (1 != inet_pton (AF_INET6, address, sa6.sin6_addr.s6_addr))
134     {
135       fprintf (stderr, 
136                "Failed to parse address `%s': %s\n",
137                address,
138                strerror (errno));
139       exit (1);
140     }
141
142   memcpy (&ifr6.ifr6_addr, 
143           &sa6.sin6_addr,
144           sizeof (struct in6_addr));
145   strncpy (ifr.ifr_name, dev, IFNAMSIZ);
146   if (-1 == ioctl (fd, SIOGIFINDEX, &ifr))
147     {
148       fprintf (stderr, 
149                "ioctl failed at %d: %s\n",
150                __LINE__,
151                strerror (errno));
152       exit (1);
153     }
154
155   ifr6.ifr6_ifindex = ifr.ifr_ifindex;
156   ifr6.ifr6_prefixlen = prefix_len;
157   if (-1 == ioctl (fd, SIOCSIFADDR, &ifr6))
158     {
159       fprintf (stderr, 
160                "ioctl failed at line %d: %s\n",
161                __LINE__,
162                strerror (errno));
163       exit (1);
164     }
165
166   if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr))
167     {
168       fprintf (stderr, 
169                "ioctl failed at line %d: %s\n",
170                __LINE__,
171                strerror (errno));
172       exit (1);
173     }
174   ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
175   if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr))
176     {
177       fprintf (stderr, 
178                "ioctl failed at line %d: %s\n",
179                __LINE__,
180                strerror (errno));
181       exit (1);
182     }
183
184   if (0 != close (fd))
185     {
186       fprintf (stderr, 
187                "close failed: %s\n",
188                strerror (errno));
189       exit (1);
190     }
191 }
192
193
194 /**
195  * @brief Sets the IPv4-Address given in address on the interface dev
196  *
197  * @param dev the interface to configure
198  * @param address the IPv4-Address
199  * @param mask the netmask
200  */
201 static void
202 set_address4 (char *dev, char *address, char *mask)
203 {
204   int fd = 0;
205   struct sockaddr_in *addr;
206   struct ifreq ifr;
207
208   memset (&ifr, 0, sizeof (struct ifreq));
209   addr = (struct sockaddr_in *) &(ifr.ifr_addr);
210   memset (addr, 0, sizeof (struct sockaddr_in));
211   addr->sin_family = AF_INET;
212   addr->sin_addr.s_addr = inet_addr (address);
213
214   int r = inet_pton (AF_INET, address, &addr->sin_addr.s_addr);
215   if (r < 0)
216     {
217       fprintf (stderr, "error at inet_pton: %m\n");
218       exit (1);
219     }
220
221   fd = socket (PF_INET, SOCK_DGRAM, 0);
222   if (fd < 0)
223     {
224       perror ("socket()");
225       return;
226     }
227
228   strncpy (ifr.ifr_name, dev, IFNAMSIZ);
229
230   if (ioctl (fd, SIOCSIFADDR, &ifr) != 0)
231     {
232       perror ("SIOCSIFADDR");
233       close (fd);
234       return;
235     }
236
237   addr = (struct sockaddr_in *) &(ifr.ifr_netmask);
238   r = inet_pton (AF_INET, mask, &addr->sin_addr.s_addr);
239   if (r < 0)
240     {
241       fprintf (stderr, "error at inet_pton: %m\n");
242       exit (1);
243     }
244
245   if (ioctl (fd, SIOCSIFNETMASK, &ifr) != 0)
246     {
247       perror ("SIOCSIFNETMASK");
248       close (fd);
249       return;
250     }
251
252   (void) ioctl (fd, SIOCGIFFLAGS, &ifr);
253   ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
254   (void) ioctl (fd, SIOCSIFFLAGS, &ifr);
255   close (fd);
256 }
257
258
259 static void
260 run (int fd_tun)
261 {
262   unsigned char buf[MAX_SIZE];
263   fd_set fds_w;
264   fd_set fds_r;
265   int rea = 1;
266   int wri = 1;
267   int write_fd_possible = 0;
268   int write_stdout_possible = 0;
269   ssize_t tin;
270 outer:
271   while ((1 == rea) || (1 == wri))
272     {
273       FD_ZERO (&fds_w);
274       FD_ZERO (&fds_r);
275
276       if (rea)
277         {
278           FD_SET (fd_tun, &fds_r);
279           if (!write_stdout_possible)
280             FD_SET (1, &fds_w);
281         }
282
283       if (wri)
284         {
285           FD_SET (0, &fds_r);
286           if (!write_fd_possible)
287             FD_SET (fd_tun, &fds_w);
288         }
289
290       int r = select (fd_tun + 1, &fds_r, &fds_w, NULL, NULL);
291       if (r > 0)
292         {
293           if (FD_ISSET (fd_tun, &fds_w))
294             write_fd_possible = 1;
295           if (FD_ISSET (1, &fds_w))
296             write_stdout_possible = 1;
297
298           if (FD_ISSET (0, &fds_r) && write_fd_possible)
299             {
300               write_fd_possible = 0;
301               struct GNUNET_MessageHeader *pkt = ( struct GNUNET_MessageHeader *) buf;
302               tin = read (0, buf, sizeof (struct GNUNET_MessageHeader));
303               if (tin <= 0)
304                 {
305                   fprintf (stderr, "read-error: %s\n", strerror (errno));
306                   shutdown (fd_tun, SHUT_WR);
307                   shutdown (0, SHUT_RD);
308                   wri = 0;
309                   goto outer;
310                 }
311               if (pkt->type != ntohs (GNUNET_MESSAGE_TYPE_VPN_HELPER))
312                 abort ();
313               while (tin < ntohs (pkt->size))
314                 {
315                   ssize_t t = read (0, buf + tin, ntohs (pkt->size) - tin);
316                   if (t <= 0)
317                     {
318                       fprintf (stderr, "read-error: %s\n", strerror (errno));
319                       shutdown (fd_tun, SHUT_WR);
320                       shutdown (0, SHUT_RD);
321                       wri = 0;
322                       goto outer;
323                     }
324                   tin += t;
325                 }
326               tin = 0;
327               while (tin <
328                      ntohs (pkt->size) -
329                      sizeof (struct GNUNET_MessageHeader))
330                 {
331                   ssize_t t = write (fd_tun, &pkt[1],
332                                  ntohs (pkt->size) -
333                                  sizeof (struct GNUNET_MessageHeader) - tin);
334                   if (t <= 0)
335                     {
336                       fprintf (stderr, "write-error 3: %s\n",
337                                strerror (errno));
338                       shutdown (fd_tun, SHUT_WR);
339                       shutdown (0, SHUT_RD);
340                       wri = 0;
341                       goto outer;
342                     }
343                   tin += t;
344                 }
345             }
346           else if (write_stdout_possible && FD_ISSET (fd_tun, &fds_r))
347             {
348               write_stdout_possible = 0;
349               tin = read (fd_tun, buf, MAX_SIZE);
350               if (tin <= 0)
351                 {
352                   fprintf (stderr, "read-error: %s\n", strerror (errno));
353                   shutdown (fd_tun, SHUT_RD);
354                   shutdown (1, SHUT_WR);
355                   rea = 0;
356                   goto outer;
357                 }
358               struct GNUNET_MessageHeader hdr = {.size =
359                   htons (r + sizeof (struct GNUNET_MessageHeader)),.type =
360                   htons (GNUNET_MESSAGE_TYPE_VPN_HELPER)
361               };
362               tin = 0;
363               while (tin < sizeof (struct GNUNET_MessageHeader))
364                 {
365                   ssize_t t =
366                     write (1, &hdr, sizeof (struct GNUNET_MessageHeader) - tin);
367                   if (t < 0)
368                     {
369                       fprintf (stderr, "write-error 2: %s\n",
370                                strerror (errno));
371                       shutdown (fd_tun, SHUT_RD);
372                       shutdown (1, SHUT_WR);
373                       rea = 0;
374                       goto outer;
375                     }
376                   tin += t;
377                 }
378               while (tin < ntohs (hdr.size))
379                 {
380                   size_t t = write (1, buf, ntohs (hdr.size) - tin);
381                   if (t < 0)
382                     {
383                       fprintf (stderr, "write-error 1: %s, written %d/%d\n",
384                                strerror (errno), r, ntohs (hdr.size));
385                       shutdown (fd_tun, SHUT_RD);
386                       shutdown (1, SHUT_WR);
387                       rea = 0;
388                       goto outer;
389                     }
390                   tin += t;
391                 }
392             }
393         }
394     }
395 }
396
397
398
399 /**
400  * @brief sets the socket to nonblocking
401  *
402  * @param fd the socket
403  */
404 static void
405 setnonblocking (int fd)
406 {
407   int opts;
408
409   if (-1 == (opts = fcntl (fd, F_GETFL)))
410     {
411       fprintf (stderr, 
412                "Error in fcntl at line %d: %s\n",
413                __LINE__,
414                strerror (errno));
415       exit (1);
416     }
417   opts |= O_NONBLOCK;
418   if (-1 == fcntl (fd, F_SETFL, opts)) 
419     {
420       fprintf (stderr, 
421                "Error in fcntl at line %d: %s\n",
422                __LINE__,
423                strerror (errno));
424       exit (1);
425     }
426 }
427
428
429 int 
430 main (int argc, 
431       char** argv) 
432 {
433   char dev[IFNAMSIZ];
434   int fd_tun;
435
436   memset (dev, 0, IFNAMSIZ);
437   if (-1 == (fd_tun = init_tun (dev)))
438     {
439       fprintf (stderr, 
440                "Fatal: could not initialize tun-interface\n");
441       return 1;
442     }
443
444   {
445     // TODO: get this out of argv
446     char address[] = "1234::1";
447     unsigned long prefix_len = 16;
448
449     set_address6 (dev, address, prefix_len);
450   }
451
452   {
453     char address[] = "10.10.10.1";
454     char mask[] = "255.255.255.252";
455
456     set_address4 (dev, address, mask);
457   }
458
459   uid_t uid = getuid ();
460   if (0 != setresuid (uid, uid, uid))
461     fprintf (stderr, 
462              "Failed to setresuid: %s\n", 
463              strerror (errno));
464   run (fd_tun);
465   close (fd_tun);
466   return 0;
467 }