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