-building IPv4 TCP reply messages for TUN
[oweals/gnunet.git] / src / vpn / vpn_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2012 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/vpn_api.c
23  * @brief library to access the VPN service and tell it how to redirect traffic
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_vpn_service.h"
28 #include "vpn.h"
29
30
31 /**
32  * Opaque VPN handle
33  */
34 struct GNUNET_VPN_Handle
35 {
36   /**
37    * Configuration we use.
38    */
39   const struct GNUNET_CONFIGURATION_Handle *cfg;
40
41   /**
42    * Connection to VPN service.
43    */
44   struct GNUNET_CLIENT_Connection *client;
45
46   /**
47    * Active transmission request.
48    */
49   struct GNUNET_CLIENT_TransmitHandle *th;
50
51   /**
52    * Head of list of active redirection requests.
53    */
54   struct GNUNET_VPN_RedirectionRequest *rr_head;
55
56   /**
57    * Tail of list of active redirection requests.
58    */
59   struct GNUNET_VPN_RedirectionRequest *rr_tail;
60
61   /**
62    * Identifier of a reconnect task.
63    */
64   GNUNET_SCHEDULER_TaskIdentifier rt;
65
66   /**
67    * ID of the last request that was submitted to the service.
68    */
69   uint64_t request_id_gen;
70
71 };
72
73
74 /**
75  * Opaque redirection request handle.
76  */
77 struct GNUNET_VPN_RedirectionRequest
78 {
79   /**
80    * Element in DLL.
81    */
82   struct GNUNET_VPN_RedirectionRequest *next;
83
84   /**
85    * Element in DLL.
86    */
87   struct GNUNET_VPN_RedirectionRequest *prev;
88
89   /**
90    * Pointer to the VPN struct.
91    */
92   struct GNUNET_VPN_Handle *vh;
93
94   /**
95    * Target IP address for the redirection, or NULL for
96    * redirection to service.  Allocated after this struct.
97    */
98   const void *addr;
99
100   /**
101    * Function to call with the designated IP address.
102    */
103   GNUNET_VPN_AllocationCallback cb;
104   
105   /**
106    * Closure for 'cb'.
107    */
108   void *cb_cls;
109
110   /**
111    * For service redirection, identity of the peer offering the service.
112    */
113   struct GNUNET_PeerIdentity peer;
114
115   /**
116    * For service redirection, service descriptor.
117    */
118   GNUNET_HashCode serv;              
119
120   /**
121    * At what time should the created service mapping expire?
122    */
123   struct GNUNET_TIME_Absolute expiration_time;
124
125   /**
126    * non-zero if this request has been sent to the service.
127    */
128   uint64_t request_id;
129
130   /**
131    * Desired address family for the result.
132    */
133   int result_af;
134
135   /**
136    * Address family of 'addr'.  AF_INET or AF_INET6.
137    */
138   int addr_af;
139   
140   /**
141    * GNUNET_YES if we are to call the callback only after successful
142    * mesh tunnel creation.
143    */
144   int nac;
145   
146   /**
147    * For service redirection, IPPROT_UDP or IPPROTO_TCP.
148    */
149   uint8_t protocol;
150
151 };
152
153
154 /**
155  * Disconnect from the service (communication error) and reconnect later.
156  *
157  * @param vh handle to reconnect.
158  */
159 static void
160 reconnect (struct GNUNET_VPN_Handle *vh);
161
162
163 /**
164  * Function called when we receive a message from the VPN service.
165  *
166  * @param cls the 'struct GNUNET_VPN_Handle'
167  * @param msg message received, NULL on timeout or fatal error
168  */
169 static void 
170 receive_response (void *cls,
171                   const struct GNUNET_MessageHeader* msg)
172 {
173   struct GNUNET_VPN_Handle *vh = cls;
174   const struct RedirectToIpResponseMessage *rm;
175   struct GNUNET_VPN_RedirectionRequest *rr;
176   size_t msize;
177   size_t alen;
178   int af;
179
180   if (NULL == msg) 
181   {
182     reconnect (vh);
183     return;
184   }
185   if ( (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_VPN_CLIENT_USE_IP) ||
186        (sizeof (struct RedirectToIpResponseMessage) > (msize = ntohs (msg->size))) )
187   {
188     GNUNET_break (0);
189     reconnect (vh);
190     return;
191   }
192   rm = (const struct RedirectToIpResponseMessage *) msg;
193   af = (int) ntohl (rm->result_af);
194   switch (af)
195   {
196   case AF_UNSPEC:
197     alen = 0;
198     break;
199   case AF_INET:
200     alen = sizeof (struct in_addr);
201     break;
202   case AF_INET6:
203     alen = sizeof (struct in6_addr);
204     break;
205   default:
206     GNUNET_break (0);
207     reconnect (vh);
208     return;
209   }
210   if ( (msize != alen + sizeof (struct RedirectToIpResponseMessage)) ||
211        (0 == rm->request_id) )
212   {
213     GNUNET_break (0);
214     reconnect (vh);
215     return;
216   }  
217   for (rr = vh->rr_head; NULL != rr; rr = rr->next)
218   {
219     if (rr->request_id == rm->request_id)
220     {
221       GNUNET_CONTAINER_DLL_remove (vh->rr_head,
222                                    vh->rr_tail,
223                                    rr);
224       rr->cb (rr->cb_cls,
225               af,
226               (af == AF_UNSPEC) ? NULL : &rm[1]);
227       GNUNET_free (rr);
228       break;
229     }
230   }
231   GNUNET_CLIENT_receive (vh->client,
232                          &receive_response, vh,
233                          GNUNET_TIME_UNIT_FOREVER_REL);      
234 }
235
236
237 /**
238  * We're ready to transmit a request to the VPN service. Do it.
239  *
240  * @param cls the 'struct GNUNET_VPN_Handle*'
241  * @param size number of bytes available in buf
242  * @param buf where to copy the request
243  * @return number of bytes copied to 'buf'
244  */
245 static size_t
246 transmit_request (void *cls,
247                   size_t size,
248                   void *buf)
249 {
250   struct GNUNET_VPN_Handle *vh = cls;
251   struct GNUNET_VPN_RedirectionRequest *rr;
252   struct RedirectToIpRequestMessage rip;
253   struct RedirectToServiceRequestMessage rs;
254   char *cbuf;
255   size_t alen;
256   size_t ret;
257
258   vh->th = NULL;
259   /* find a pending request */
260   rr = vh->rr_head;
261   while ( (NULL != rr) &&
262           (0 != rr->request_id) )
263     rr = rr->next;
264   if (NULL == rr)
265     return 0;
266
267   /* if first request, start receive loop */
268   if (0 == vh->request_id_gen)
269     GNUNET_CLIENT_receive (vh->client,
270                            &receive_response, vh,
271                            GNUNET_TIME_UNIT_FOREVER_REL); 
272   if (NULL == rr->addr)
273   {
274     ret = sizeof (struct RedirectToServiceRequestMessage);
275     rs.header.size = htons ((uint16_t) ret);
276     rs.header.type = htons (GNUNET_MESSAGE_TYPE_VPN_CLIENT_REDIRECT_TO_SERVICE);
277     rs.nac = htonl (rr->nac);
278     rs.expiration_time = GNUNET_TIME_absolute_hton (rr->expiration_time);
279     rs.protocol = htonl (rr->protocol);
280     rs.result_af = htonl (rr->result_af);
281     rs.target = rr->peer;
282     rs.service_descriptor = rr->serv;
283     rs.request_id = rr->request_id = ++vh->request_id_gen;    
284     memcpy (buf, &rs, sizeof (struct RedirectToServiceRequestMessage));
285   }
286   else
287   {
288     switch (rr->addr_af)
289     {
290     case AF_INET:
291       alen = sizeof (struct in_addr);
292       break;
293     case AF_INET6:
294       alen = sizeof (struct in6_addr);
295       break;
296     default:
297       GNUNET_assert (0);
298       return 0;
299     }
300     ret = alen + sizeof (struct RedirectToIpRequestMessage);
301     rip.header.size = htons ((uint16_t) ret);
302     rip.header.type = htons (GNUNET_MESSAGE_TYPE_VPN_CLIENT_REDIRECT_TO_IP);
303     rip.nac = htonl (rr->nac);
304     rip.expiration_time = GNUNET_TIME_absolute_hton (rr->expiration_time);
305     rip.result_af = htonl (rr->result_af);
306     rip.addr_af = htonl (rr->addr_af);
307     rip.request_id = rr->request_id = ++vh->request_id_gen;
308     cbuf = buf;
309     memcpy (cbuf, &rip, sizeof (struct RedirectToIpRequestMessage));
310     memcpy (&cbuf[sizeof (struct RedirectToIpRequestMessage)], rr->addr, alen);
311   }
312   /* test if there are more pending requests */
313   while ( (NULL != rr) &&
314           (0 != rr->request_id) )
315     rr = rr->next;
316   if (NULL != rr)
317     vh->th = GNUNET_CLIENT_notify_transmit_ready (vh->client,
318                                                   sizeof (struct RedirectToServiceRequestMessage),
319                                                   GNUNET_TIME_UNIT_FOREVER_REL,
320                                                   GNUNET_NO,
321                                                   &transmit_request,
322                                                   vh);
323   return ret;
324 }
325
326
327 /**
328  * Add a request to our request queue and transmit it.
329  * 
330  * @param rr request to queue and transmit.
331  */
332 static void
333 queue_request (struct GNUNET_VPN_RedirectionRequest *rr)
334 {
335   struct GNUNET_VPN_Handle *vh;
336
337   vh = rr->vh;
338   GNUNET_CONTAINER_DLL_insert_tail (vh->rr_head,
339                                     vh->rr_tail,
340                                     rr);
341   if ( (NULL == vh->th) &&
342        (NULL != vh->client) )
343     vh->th = GNUNET_CLIENT_notify_transmit_ready (vh->client,
344                                                   sizeof (struct RedirectToServiceRequestMessage),
345                                                   GNUNET_TIME_UNIT_FOREVER_REL,
346                                                   GNUNET_NO,
347                                                   &transmit_request,
348                                                   vh);
349 }
350
351
352 /**
353  * Connect to the VPN service and start again to transmit our requests.
354  *
355  * @param cls the 'struct GNUNET_VPN_Handle *'
356  * @param tc scheduler context
357  */
358 static void
359 connect_task (void *cls,
360               const struct GNUNET_SCHEDULER_TaskContext *tc)
361 {
362   struct GNUNET_VPN_Handle *vh = cls;
363   
364   vh->client = GNUNET_CLIENT_connect ("vpn", vh->cfg);
365   GNUNET_assert (NULL != vh->client);
366   GNUNET_assert (NULL != vh->th);
367   if (NULL != vh->rr_head) 
368     vh->th = GNUNET_CLIENT_notify_transmit_ready (vh->client,
369                                                   sizeof (struct RedirectToServiceRequestMessage),
370                                                   GNUNET_TIME_UNIT_FOREVER_REL,
371                                                   GNUNET_NO,
372                                                   &transmit_request,
373                                                   vh);
374 }
375
376
377 /**
378  * Disconnect from the service (communication error) and reconnect later.
379  *
380  * @param vh handle to reconnect.
381  */
382 static void
383 reconnect (struct GNUNET_VPN_Handle *vh)
384 {
385   struct GNUNET_VPN_RedirectionRequest *rr;
386
387   if (NULL != vh->th)
388   {
389     GNUNET_CLIENT_notify_transmit_ready_cancel (vh->th);
390     vh->th = NULL;
391   }  
392   GNUNET_CLIENT_disconnect (vh->client, GNUNET_NO);
393   vh->client = NULL;
394   vh->request_id_gen = 0;
395   for (rr = vh->rr_head; NULL != rr; rr = rr->next)
396     rr->request_id = 0;
397   vh->rt = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
398                                          &connect_task, 
399                                          vh);
400 }
401
402
403 /**
404  * Cancel redirection request with the service.
405  *
406  * @param rr request to cancel
407  */
408 void
409 GNUNET_VPN_cancel_request (struct GNUNET_VPN_RedirectionRequest *rr)
410 {
411   struct GNUNET_VPN_Handle *vh;
412
413   vh = rr->vh;
414   GNUNET_CONTAINER_DLL_remove (vh->rr_head,
415                                vh->rr_tail,
416                                rr);
417   GNUNET_free (rr);
418 }
419
420
421 /**
422  * Tell the VPN that a forwarding to a particular peer offering a
423  * particular service is requested.  The VPN is to reserve a
424  * particular IP for the redirection and return it.  The VPN will
425  * begin the redirection as soon as possible and maintain it as long
426  * as it is actively used and keeping it is feasible.  Given resource
427  * limitations, the longest inactive mappings will be destroyed.
428  *
429  * @param vh VPN handle
430  * @param result_af desired address family for the returned allocation
431  *                  can also be AF_UNSPEC
432  * @param protocol protocol, IPPROTO_UDP or IPPROTO_TCP
433  * @param peer target peer for the redirection
434  * @param serv service descriptor to give to the peer
435  * @param nac GNUNET_YES to notify via callback only after completion of
436  *            the MESH-level connection,
437  *            GNUNET_NO to notify as soon as the IP has been reserved
438  * @param expiration_time at what time should the redirection expire?
439  *        (this should not impact connections that are active at that time)
440  * @param cb function to call with the IP
441  * @param cb_cls closure for cb
442  * @return handle to cancel the request (means the callback won't be
443  *         invoked anymore; the mapping may or may not be established
444  *         anyway)
445  */
446 struct GNUNET_VPN_RedirectionRequest *
447 GNUNET_VPN_redirect_to_peer (struct GNUNET_VPN_Handle *vh,
448                              int result_af,
449                              uint8_t protocol,
450                              const struct GNUNET_PeerIdentity *peer,
451                              const GNUNET_HashCode *serv,
452                              int nac,
453                              struct GNUNET_TIME_Absolute expiration_time,
454                              GNUNET_VPN_AllocationCallback cb,
455                              void *cb_cls)
456 {
457   struct GNUNET_VPN_RedirectionRequest *rr;
458
459   rr = GNUNET_malloc (sizeof (struct GNUNET_VPN_RedirectionRequest));
460   rr->vh = vh;
461   rr->cb = cb;
462   rr->cb_cls = cb_cls;
463   rr->peer = *peer;
464   rr->serv = *serv;
465   rr->expiration_time = expiration_time;
466   rr->result_af = result_af;
467   rr->nac = nac;
468   rr->protocol = protocol;
469   queue_request (rr);
470   return rr;
471 }
472
473                 
474 /**
475  * Tell the VPN that forwarding to the Internet via some exit node is
476  * requested.  Note that both UDP and TCP traffic will be forwarded,
477  * but possibly to different exit nodes.  The VPN is to reserve a
478  * particular IP for the redirection and return it.  The VPN will
479  * begin the redirection as soon as possible and maintain it as long
480  * as it is actively used and keeping it is feasible.  Given resource
481  * limitations, the longest inactive mappings will be destroyed.
482  *
483  * @param vh VPN handle
484  * @param result_af desired address family for the returned allocation
485  * @param addr_af address family for 'addr', AF_INET or AF_INET6
486  * @param addr destination IP address on the Internet; destination
487  *             port is to be taken from the VPN packet itself
488  * @param nac GNUNET_YES to notify via callback only after completion of
489  *            the MESH-level connection,
490  *            GNUNET_NO to notify as soon as the IP has been reserved
491  * @param expiration_time at what time should the redirection expire?
492  *        (this should not impact connections that are active at that time)
493  * @param cb function to call with the IP
494  * @param cb_cls closure for cb
495  * @return handle to cancel the request (means the callback won't be
496  *         invoked anymore; the mapping may or may not be established
497  *         anyway)
498  */
499 struct GNUNET_VPN_RedirectionRequest *
500 GNUNET_VPN_redirect_to_ip (struct GNUNET_VPN_Handle *vh,
501                            int result_af,
502                            int addr_af,
503                            const void *addr,
504                            int nac,
505                            struct GNUNET_TIME_Absolute expiration_time,
506                            GNUNET_VPN_AllocationCallback cb,
507                            void *cb_cls)
508 {
509   struct GNUNET_VPN_RedirectionRequest *rr;
510   size_t alen;
511
512   switch (addr_af)
513   {
514   case AF_INET:
515     alen = sizeof (struct in_addr);
516     break;
517   case AF_INET6:
518     alen = sizeof (struct in6_addr);
519     break;
520   default:
521     GNUNET_break (0);
522     return NULL;
523   }
524   rr = GNUNET_malloc (sizeof (struct GNUNET_VPN_RedirectionRequest) + alen);
525   rr->vh = vh;
526   rr->addr = &rr[1];
527   rr->cb = cb;
528   rr->cb_cls = cb_cls;
529   rr->expiration_time = expiration_time;
530   rr->result_af = result_af;
531   rr->addr_af = addr_af;
532   rr->nac = nac;
533   memcpy (&rr[1], addr, alen);
534   queue_request (rr);
535   return rr;
536 }
537
538
539 /**
540  * Connect to the VPN service
541  *
542  * @param cfg configuration to use
543  * @return VPN handle 
544  */
545 struct GNUNET_VPN_Handle *
546 GNUNET_VPN_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
547 {
548   struct GNUNET_VPN_Handle *vh;
549
550   vh = GNUNET_malloc (sizeof (struct GNUNET_VPN_Handle));
551   vh->cfg = cfg;
552   vh->client = GNUNET_CLIENT_connect ("vpn", cfg);
553   if (NULL == vh->client)
554   {
555     GNUNET_free (vh);
556     return NULL;
557   }
558   return vh;
559 }
560
561
562 /**
563  * Disconnect from the VPN service.
564  *
565  * @param vh VPN handle
566  */
567 void
568 GNUNET_VPN_disconnect (struct GNUNET_VPN_Handle *vh)
569 {
570   GNUNET_assert (NULL == vh->rr_head);
571   if (NULL != vh->th)
572   {
573     GNUNET_CLIENT_notify_transmit_ready_cancel (vh->th);
574     vh->th = NULL;
575   }
576   if (NULL != vh->client)
577   {
578     GNUNET_CLIENT_disconnect (vh->client, GNUNET_NO);
579     vh->client = NULL;
580   }
581   if (GNUNET_SCHEDULER_NO_TASK != vh->rt)
582   {
583     GNUNET_SCHEDULER_cancel (vh->rt);
584     vh->rt = GNUNET_SCHEDULER_NO_TASK;
585   }
586   GNUNET_free (vh);
587 }
588
589 /* end of vpn_api.c */