adding configure code for --enable-benchmarks, --enable-expensive-tests, some clean up
[oweals/gnunet.git] / src / vpn / gnunet-daemon-vpn-helper.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-helper.c
23  * @brief
24  * @author Philipp Toelke
25  */
26 #include <platform.h>
27 #include <gnunet_common.h>
28 #include <gnunet_client_lib.h>
29 #include <gnunet_os_lib.h>
30 #include <gnunet_mesh_service.h>
31 #include <gnunet_protocols.h>
32 #include <gnunet_server_lib.h>
33 #include <gnunet_container_lib.h>
34 #include <block_dns.h>
35 #include <gnunet_configuration_lib.h>
36 #include <gnunet_applications.h>
37
38 #include "gnunet-daemon-vpn-dns.h"
39 #include "gnunet-daemon-vpn.h"
40 #include "gnunet-daemon-vpn-helper.h"
41 #include "gnunet-service-dns-p.h"
42 #include "gnunet-vpn-packet.h"
43 #include "gnunet-vpn-checksum.h"
44 #include "gnunet-helper-vpn-api.h"
45
46 struct GNUNET_VPN_HELPER_Handle *helper_handle;
47
48 struct remote_addr
49 {
50   char addrlen;
51   unsigned char addr[16];
52   char proto;
53 };
54
55 /**
56  * Start the helper-process
57  *
58  * If cls != NULL it is assumed that this function is called as a result of a dying
59  * helper. cls is then taken as handle to the old helper and is cleaned up.
60  * {{{
61  */
62 void
63 start_helper_and_schedule(void *cls,
64                           const struct GNUNET_SCHEDULER_TaskContext *tc) {
65     if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
66       return;
67
68     if (cls != NULL)
69       cleanup_helper(cls);
70     cls = NULL;
71
72     char* ifname;
73     char* ipv6addr;
74     char* ipv6prefix;
75     char* ipv4addr;
76     char* ipv4mask;
77
78     if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string(cfg, "vpn", "IFNAME", &ifname))
79       {
80         GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "No entry 'IFNAME' in configuration!\n");
81         exit(1);
82       }
83
84     if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string(cfg, "vpn", "IPV6ADDR", &ipv6addr))
85       {
86         GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "No entry 'IPV6ADDR' in configuration!\n");
87         exit(1);
88       }
89
90     if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string(cfg, "vpn", "IPV6PREFIX", &ipv6prefix))
91       {
92         GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "No entry 'IPV6PREFIX' in configuration!\n");
93         exit(1);
94       }
95
96     if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string(cfg, "vpn", "IPV4ADDR", &ipv4addr))
97       {
98         GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "No entry 'IPV4ADDR' in configuration!\n");
99         exit(1);
100       }
101
102     if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string(cfg, "vpn", "IPV4MASK", &ipv4mask))
103       {
104         GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "No entry 'IPV4MASK' in configuration!\n");
105         exit(1);
106       }
107
108     /* Start the helper
109      * Messages get passed to the function message_token
110      * When the helper dies, this function will be called again with the
111      * helper_handle as cls.
112      */
113     helper_handle = start_helper(ifname,
114                                  ipv6addr,
115                                  ipv6prefix,
116                                  ipv4addr,
117                                  ipv4mask,
118                                  "vpn-gnunet",
119                                  start_helper_and_schedule,
120                                  message_token,
121                                  NULL,
122                                  NULL);
123
124     GNUNET_free(ipv6addr);
125     GNUNET_free(ipv6prefix);
126     GNUNET_free(ipv4addr);
127     GNUNET_free(ipv4mask);
128     GNUNET_free(ifname);
129
130     /* Tell the dns-service to rehijack the dns-port
131      * The routing-table gets flushed if an interface disappears.
132      */
133     restart_hijack = 1;
134     if (NULL != dns_connection)
135       GNUNET_CLIENT_notify_transmit_ready(dns_connection, sizeof(struct GNUNET_MessageHeader), GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_YES, &send_query, NULL);
136 }
137 /*}}}*/
138
139 /**
140  * Send an dns-answer-packet to the helper
141  */
142 void
143 helper_write(void* cls, const struct GNUNET_SCHEDULER_TaskContext* tsdkctx) {
144     if (tsdkctx->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)
145       return;
146
147     struct answer_packet_list* ans = answer_proc_head;
148     size_t len = ntohs(ans->pkt.hdr.size);
149
150     GNUNET_assert(ans->pkt.subtype == GNUNET_DNS_ANSWER_TYPE_IP);
151
152     GNUNET_assert (20 == sizeof (struct ip_hdr));
153     GNUNET_assert (8 == sizeof (struct udp_pkt));
154     size_t data_len = len - sizeof(struct answer_packet) + 1;
155     size_t net_len = sizeof(struct ip_hdr) + sizeof(struct udp_dns) + data_len;
156     size_t pkt_len = sizeof(struct GNUNET_MessageHeader) + sizeof(struct pkt_tun) + net_len;
157
158     struct ip_udp_dns* pkt = alloca(pkt_len);
159     GNUNET_assert(pkt != NULL);
160     memset(pkt, 0, pkt_len);
161
162     /* set the gnunet-header */
163     pkt->shdr.size = htons(pkt_len);
164     pkt->shdr.type = htons(GNUNET_MESSAGE_TYPE_VPN_HELPER);
165
166     /* set the tun-header (no flags and ethertype of IPv4) */
167     pkt->tun.flags = 0;
168     pkt->tun.type = htons(0x0800);
169
170     /* set the ip-header */
171     pkt->ip_hdr.version = 4;
172     pkt->ip_hdr.hdr_lngth = 5;
173     pkt->ip_hdr.diff_serv = 0;
174     pkt->ip_hdr.tot_lngth = htons(net_len);
175     pkt->ip_hdr.ident = 0;
176     pkt->ip_hdr.flags = 0;
177     pkt->ip_hdr.frag_off = 0;
178     pkt->ip_hdr.ttl = 255;
179     pkt->ip_hdr.proto = 0x11; /* UDP */
180     pkt->ip_hdr.chks = 0; /* Will be calculated later*/
181     pkt->ip_hdr.sadr = ans->pkt.from;
182     pkt->ip_hdr.dadr = ans->pkt.to;
183
184     pkt->ip_hdr.chks = calculate_ip_checksum((uint16_t*)&pkt->ip_hdr, 5*4);
185
186     /* set the udp-header */
187     pkt->udp_dns.udp_hdr.spt = htons(53);
188     pkt->udp_dns.udp_hdr.dpt = ans->pkt.dst_port;
189     pkt->udp_dns.udp_hdr.len = htons(net_len - sizeof(struct ip_hdr));
190     pkt->udp_dns.udp_hdr.crc = 0; /* Optional for IPv4 */
191
192     memcpy(&pkt->udp_dns.data, ans->pkt.data, data_len);
193
194     GNUNET_CONTAINER_DLL_remove (answer_proc_head, answer_proc_tail, ans);
195     GNUNET_free(ans);
196
197     if (GNUNET_DISK_file_write(helper_handle->fh_to_helper, pkt, pkt_len) < 0)
198       {
199         cleanup_helper(helper_handle);
200         GNUNET_SCHEDULER_add_now(start_helper_and_schedule, NULL);
201         return;
202       }
203
204     /* if more packets are available, reschedule */
205     if (answer_proc_head != NULL)
206       GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL,
207                                        helper_handle->fh_to_helper,
208                                        &helper_write,
209                                        NULL);
210 }
211
212 /**
213  * Receive packets from the helper-process
214  */
215 void
216 message_token (void *cls,
217                void *client, const struct GNUNET_MessageHeader *message)
218 {
219   GNUNET_assert (ntohs (message->type) == GNUNET_MESSAGE_TYPE_VPN_HELPER);
220
221   struct tun_pkt *pkt_tun = (struct tun_pkt *) message;
222
223   /* ethertype is ipv6 */
224   if (ntohs (pkt_tun->tun.type) == 0x86dd)
225     {
226       struct ip6_pkt *pkt6 = (struct ip6_pkt *) message;
227       GNUNET_assert (pkt6->ip6_hdr.version == 6);
228       struct ip6_tcp *pkt6_tcp;
229       struct ip6_udp *pkt6_udp;
230       struct ip6_icmp *pkt6_icmp;
231       GNUNET_HashCode *key;
232
233       switch (pkt6->ip6_hdr.nxthdr)
234         {
235         case 0x06:             /* TCP */
236         case 0x11:             /* UDP */
237           pkt6_tcp = (struct ip6_tcp *) pkt6;
238           pkt6_udp = (struct ip6_udp *) pkt6;
239
240           if ((key = address_mapping_exists (pkt6->ip6_hdr.dadr)) != NULL)
241             {
242               struct map_entry *me =
243                 GNUNET_CONTAINER_multihashmap_get (hashmap, key);
244               GNUNET_assert (me != NULL);
245               GNUNET_free (key);
246
247               size_t size =
248                 sizeof (struct GNUNET_MESH_Tunnel *) +
249                 sizeof (struct GNUNET_MessageHeader) +
250                 sizeof (GNUNET_HashCode) + ntohs (pkt6->ip6_hdr.paylgth);
251
252               struct GNUNET_MESH_Tunnel **cls = GNUNET_malloc (size);
253               struct GNUNET_MessageHeader *hdr =
254                 (struct GNUNET_MessageHeader *) (cls + 1);
255               GNUNET_HashCode *hc = (GNUNET_HashCode *) (hdr + 1);
256
257               hdr->size = htons (sizeof (struct GNUNET_MessageHeader) +
258                                  sizeof (GNUNET_HashCode) +
259                                  ntohs (pkt6->ip6_hdr.paylgth));
260
261               GNUNET_MESH_ApplicationType app_type;
262               if (me->addrlen == 0)
263                 {
264                   /* This is a mapping to a gnunet-service */
265                   memcpy (hc, &me->desc.service_descriptor,
266                           sizeof (GNUNET_HashCode));
267
268                   if (0x11 == pkt6->ip6_hdr.nxthdr
269                       && (me->desc.
270                           service_type & htonl (GNUNET_DNS_SERVICE_TYPE_UDP))
271                       && (port_in_ports (me->desc.ports, pkt6_udp->udp_hdr.dpt)
272                           || testBit (me->additional_ports,
273                                       ntohs (pkt6_udp->udp_hdr.dpt))))
274                     {
275                       hdr->type = ntohs (GNUNET_MESSAGE_TYPE_SERVICE_UDP);
276
277                       memcpy (hc + 1, &pkt6_udp->udp_hdr,
278                               ntohs (pkt6_udp->udp_hdr.len));
279
280                     }
281                   else if (0x06 == pkt6->ip6_hdr.nxthdr
282                            && (me->desc.
283                                service_type & htonl (GNUNET_DNS_SERVICE_TYPE_TCP))
284                            &&
285                            (port_in_ports (me->desc.ports, pkt6_tcp->tcp_hdr.dpt)))
286                     {
287                       hdr->type = ntohs (GNUNET_MESSAGE_TYPE_SERVICE_TCP);
288
289                       memcpy (hc + 1, &pkt6_tcp->tcp_hdr,
290                               ntohs (pkt6->ip6_hdr.paylgth));
291
292                     }
293                   if (me->tunnel == NULL && NULL != cls)
294                     {
295                       *cls =
296                         GNUNET_MESH_peer_request_connect_all (mesh_handle,
297                                                               GNUNET_TIME_UNIT_FOREVER_REL,
298                                                               1,
299                                                               (struct
300                                                                GNUNET_PeerIdentity
301                                                                *) &me->desc.peer,
302                                                               send_pkt_to_peer,
303                                                               NULL, cls);
304                       me->tunnel = *cls;
305                     }
306                   else if (NULL != cls)
307                     {
308                       *cls = me->tunnel;
309                       send_pkt_to_peer (cls, (struct GNUNET_PeerIdentity *) 1,
310                                         NULL);
311                       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
312                                   "Queued to send to peer %x, type %d\n",
313                                   *((unsigned int *) &me->desc.peer), ntohs(hdr->type));
314                     }
315                 }
316               else
317                 {
318                   /* This is a mapping to a "real" address */
319                  struct remote_addr *s = (struct remote_addr*) hc;
320                  s->addrlen = me->addrlen;
321                  memcpy(s->addr, me->addr, me->addrlen);
322                  s->proto= pkt6->ip6_hdr.nxthdr;
323                  if (s->proto == 0x11)
324                    {
325                      hdr->type = GNUNET_MESSAGE_TYPE_REMOTE_UDP;
326                      memcpy (hc + 1, &pkt6_udp->udp_hdr,
327                              ntohs (pkt6_udp->udp_hdr.len));
328                      app_type = GNUNET_APPLICATION_TYPE_INTERNET_UDP_GATEWAY;
329                    }
330                  else if (s->proto == 0x06)
331                    {
332                      hdr->type = GNUNET_MESSAGE_TYPE_REMOTE_TCP;
333                      memcpy (hc + 1, &pkt6_tcp->tcp_hdr,
334                               ntohs (pkt6->ip6_hdr.paylgth));
335                      if (ntohs(pkt6_tcp->tcp_hdr.dpt) == 443)
336                        app_type = GNUNET_APPLICATION_TYPE_INTERNET_HTTPS_GATEWAY;
337                      else if (ntohs(pkt6_tcp->tcp_hdr.dpt) == 80)
338                        app_type = GNUNET_APPLICATION_TYPE_INTERNET_HTTP_GATEWAY;
339                      else
340                        app_type = GNUNET_APPLICATION_TYPE_INTERNET_TCP_GATEWAY;
341                    }
342                 }
343               if (me->tunnel == NULL && NULL != cls)
344                 {
345                   *cls = GNUNET_MESH_peer_request_connect_by_type(mesh_handle,
346                                                                   GNUNET_TIME_UNIT_FOREVER_REL,
347                                                                   app_type,
348                                                                   send_pkt_to_peer,
349                                                                   NULL,
350                                                                   cls);
351                   me->tunnel = *cls;
352                 }
353               else if (NULL != cls)
354                 {
355                   *cls = me->tunnel;
356                   send_pkt_to_peer(cls, (struct GNUNET_PeerIdentity*) 1, NULL);
357                 }
358             }
359           break;
360         case 0x3a:
361           /* ICMPv6 */
362           pkt6_icmp = (struct ip6_icmp *) pkt6;
363           /* If this packet is an icmp-echo-request and a mapping exists, answer */
364           if (pkt6_icmp->icmp_hdr.type == 0x80
365               && (key = address_mapping_exists (pkt6->ip6_hdr.dadr)) != NULL)
366             {
367               GNUNET_free (key);
368               pkt6_icmp = GNUNET_malloc (ntohs (pkt6->shdr.size));
369               memcpy (pkt6_icmp, pkt6, ntohs (pkt6->shdr.size));
370               GNUNET_SCHEDULER_add_now (&send_icmp_response, pkt6_icmp);
371             }
372           break;
373         }
374     }
375   /* ethertype is ipv4 */
376   else if (ntohs (pkt_tun->tun.type) == 0x0800)
377     {
378       struct ip_pkt *pkt = (struct ip_pkt *) message;
379       struct ip_udp *udp = (struct ip_udp *) message;
380       GNUNET_assert (pkt->ip_hdr.version == 4);
381
382       /* Send dns-packets to the service-dns */
383       if (pkt->ip_hdr.proto == 0x11 && ntohs (udp->udp_hdr.dpt) == 53)
384         {
385           /* 9 = 8 for the udp-header + 1 for the unsigned char data[1]; */
386           size_t len =
387             sizeof (struct query_packet) + ntohs (udp->udp_hdr.len) - 9;
388
389           struct query_packet_list *query =
390             GNUNET_malloc (len + 2 * sizeof (struct query_packet_list *));
391           query->pkt.hdr.type = htons (GNUNET_MESSAGE_TYPE_LOCAL_QUERY_DNS);
392           query->pkt.hdr.size = htons (len);
393           query->pkt.orig_to = pkt->ip_hdr.dadr;
394           query->pkt.orig_from = pkt->ip_hdr.sadr;
395           query->pkt.src_port = udp->udp_hdr.spt;
396           memcpy (query->pkt.data, udp->data, ntohs (udp->udp_hdr.len) - 8);
397
398           GNUNET_CONTAINER_DLL_insert_after (head, tail, tail, query);
399
400           GNUNET_assert (head != NULL);
401
402           if (dns_connection != NULL)
403             GNUNET_CLIENT_notify_transmit_ready (dns_connection,
404                                                  len,
405                                                  GNUNET_TIME_UNIT_FOREVER_REL,
406                                                  GNUNET_YES,
407                                                  &send_query, NULL);
408         }
409     }
410 }
411
412 void write_to_helper(void* buf, size_t len)
413 {
414   (void)GNUNET_DISK_file_write(helper_handle->fh_to_helper, buf, len);
415 }
416
417 void schedule_helper_write(struct GNUNET_TIME_Relative time, void* cls)
418 {
419   GNUNET_SCHEDULER_add_write_file (time, helper_handle->fh_to_helper, &helper_write, cls);
420 }