-test now passes
[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   if (0 == size) 
267   {
268     reconnect (vh);
269     return 0;
270   }
271
272   /* if first request, start receive loop */
273   if (0 == vh->request_id_gen)
274     GNUNET_CLIENT_receive (vh->client,
275                            &receive_response, vh,
276                            GNUNET_TIME_UNIT_FOREVER_REL); 
277   if (NULL == rr->addr)
278   {
279     ret = sizeof (struct RedirectToServiceRequestMessage);
280     GNUNET_assert (ret <= size);
281     rs.header.size = htons ((uint16_t) ret);
282     rs.header.type = htons (GNUNET_MESSAGE_TYPE_VPN_CLIENT_REDIRECT_TO_SERVICE);
283     rs.nac = htonl (rr->nac);
284     rs.expiration_time = GNUNET_TIME_absolute_hton (rr->expiration_time);
285     rs.protocol = htonl (rr->protocol);
286     rs.result_af = htonl (rr->result_af);
287     rs.target = rr->peer;
288     rs.service_descriptor = rr->serv;
289     rs.request_id = rr->request_id = ++vh->request_id_gen;    
290     memcpy (buf, &rs, sizeof (struct RedirectToServiceRequestMessage));
291   }
292   else
293   {
294     switch (rr->addr_af)
295     {
296     case AF_INET:
297       alen = sizeof (struct in_addr);
298       break;
299     case AF_INET6:
300       alen = sizeof (struct in6_addr);
301       break;
302     default:
303       GNUNET_assert (0);
304       return 0;
305     }
306     ret = alen + sizeof (struct RedirectToIpRequestMessage);
307     GNUNET_assert (ret <= size);
308     rip.header.size = htons ((uint16_t) ret);
309     rip.header.type = htons (GNUNET_MESSAGE_TYPE_VPN_CLIENT_REDIRECT_TO_IP);
310     rip.nac = htonl (rr->nac);
311     rip.expiration_time = GNUNET_TIME_absolute_hton (rr->expiration_time);
312     rip.result_af = htonl (rr->result_af);
313     rip.addr_af = htonl (rr->addr_af);
314     rip.request_id = rr->request_id = ++vh->request_id_gen;
315     cbuf = buf;
316     memcpy (cbuf, &rip, sizeof (struct RedirectToIpRequestMessage));
317     memcpy (&cbuf[sizeof (struct RedirectToIpRequestMessage)], rr->addr, alen);
318   }
319   /* test if there are more pending requests */
320   while ( (NULL != rr) &&
321           (0 != rr->request_id) )
322     rr = rr->next;
323   if (NULL != rr)
324     vh->th = GNUNET_CLIENT_notify_transmit_ready (vh->client,
325                                                   sizeof (struct RedirectToServiceRequestMessage),
326                                                   GNUNET_TIME_UNIT_FOREVER_REL,
327                                                   GNUNET_NO,
328                                                   &transmit_request,
329                                                   vh);
330   return ret;
331 }
332
333
334 /**
335  * Add a request to our request queue and transmit it.
336  * 
337  * @param rr request to queue and transmit.
338  */
339 static void
340 queue_request (struct GNUNET_VPN_RedirectionRequest *rr)
341 {
342   struct GNUNET_VPN_Handle *vh;
343
344   vh = rr->vh;
345   GNUNET_CONTAINER_DLL_insert_tail (vh->rr_head,
346                                     vh->rr_tail,
347                                     rr);
348   if ( (NULL == vh->th) &&
349        (NULL != vh->client) )
350     vh->th = GNUNET_CLIENT_notify_transmit_ready (vh->client,
351                                                   sizeof (struct RedirectToServiceRequestMessage),
352                                                   GNUNET_TIME_UNIT_FOREVER_REL,
353                                                   GNUNET_NO,
354                                                   &transmit_request,
355                                                   vh);
356 }
357
358
359 /**
360  * Connect to the VPN service and start again to transmit our requests.
361  *
362  * @param cls the 'struct GNUNET_VPN_Handle *'
363  * @param tc scheduler context
364  */
365 static void
366 connect_task (void *cls,
367               const struct GNUNET_SCHEDULER_TaskContext *tc)
368 {
369   struct GNUNET_VPN_Handle *vh = cls;
370   
371   vh->rt = GNUNET_SCHEDULER_NO_TASK;
372   vh->client = GNUNET_CLIENT_connect ("vpn", vh->cfg);
373   GNUNET_assert (NULL != vh->client);
374   GNUNET_assert (NULL == vh->th);
375   if (NULL != vh->rr_head) 
376     vh->th = GNUNET_CLIENT_notify_transmit_ready (vh->client,
377                                                   sizeof (struct RedirectToServiceRequestMessage),
378                                                   GNUNET_TIME_UNIT_FOREVER_REL,
379                                                   GNUNET_NO,
380                                                   &transmit_request,
381                                                   vh);
382 }
383
384
385 /**
386  * Disconnect from the service (communication error) and reconnect later.
387  *
388  * @param vh handle to reconnect.
389  */
390 static void
391 reconnect (struct GNUNET_VPN_Handle *vh)
392 {
393   struct GNUNET_VPN_RedirectionRequest *rr;
394
395   if (NULL != vh->th)
396   {
397     GNUNET_CLIENT_notify_transmit_ready_cancel (vh->th);
398     vh->th = NULL;
399   }  
400   GNUNET_CLIENT_disconnect (vh->client, GNUNET_NO);
401   vh->client = NULL;
402   vh->request_id_gen = 0;
403   for (rr = vh->rr_head; NULL != rr; rr = rr->next)
404     rr->request_id = 0;
405   vh->rt = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
406                                          &connect_task, 
407                                          vh);
408 }
409
410
411 /**
412  * Cancel redirection request with the service.
413  *
414  * @param rr request to cancel
415  */
416 void
417 GNUNET_VPN_cancel_request (struct GNUNET_VPN_RedirectionRequest *rr)
418 {
419   struct GNUNET_VPN_Handle *vh;
420
421   vh = rr->vh;
422   GNUNET_CONTAINER_DLL_remove (vh->rr_head,
423                                vh->rr_tail,
424                                rr);
425   GNUNET_free (rr);
426 }
427
428
429 /**
430  * Tell the VPN that a forwarding to a particular peer offering a
431  * particular service is requested.  The VPN is to reserve a
432  * particular IP for the redirection and return it.  The VPN will
433  * begin the redirection as soon as possible and maintain it as long
434  * as it is actively used and keeping it is feasible.  Given resource
435  * limitations, the longest inactive mappings will be destroyed.
436  *
437  * @param vh VPN handle
438  * @param result_af desired address family for the returned allocation
439  *                  can also be AF_UNSPEC
440  * @param protocol protocol, IPPROTO_UDP or IPPROTO_TCP
441  * @param peer target peer for the redirection
442  * @param serv service descriptor to give to the peer
443  * @param nac GNUNET_YES to notify via callback only after completion of
444  *            the MESH-level connection,
445  *            GNUNET_NO to notify as soon as the IP has been reserved
446  * @param expiration_time at what time should the redirection expire?
447  *        (this should not impact connections that are active at that time)
448  * @param cb function to call with the IP
449  * @param cb_cls closure for cb
450  * @return handle to cancel the request (means the callback won't be
451  *         invoked anymore; the mapping may or may not be established
452  *         anyway)
453  */
454 struct GNUNET_VPN_RedirectionRequest *
455 GNUNET_VPN_redirect_to_peer (struct GNUNET_VPN_Handle *vh,
456                              int result_af,
457                              uint8_t protocol,
458                              const struct GNUNET_PeerIdentity *peer,
459                              const GNUNET_HashCode *serv,
460                              int nac,
461                              struct GNUNET_TIME_Absolute expiration_time,
462                              GNUNET_VPN_AllocationCallback cb,
463                              void *cb_cls)
464 {
465   struct GNUNET_VPN_RedirectionRequest *rr;
466
467   rr = GNUNET_malloc (sizeof (struct GNUNET_VPN_RedirectionRequest));
468   rr->vh = vh;
469   rr->cb = cb;
470   rr->cb_cls = cb_cls;
471   rr->peer = *peer;
472   rr->serv = *serv;
473   rr->expiration_time = expiration_time;
474   rr->result_af = result_af;
475   rr->nac = nac;
476   rr->protocol = protocol;
477   queue_request (rr);
478   return rr;
479 }
480
481                 
482 /**
483  * Tell the VPN that forwarding to the Internet via some exit node is
484  * requested.  Note that both UDP and TCP traffic will be forwarded,
485  * but possibly to different exit nodes.  The VPN is to reserve a
486  * particular IP for the redirection and return it.  The VPN will
487  * begin the redirection as soon as possible and maintain it as long
488  * as it is actively used and keeping it is feasible.  Given resource
489  * limitations, the longest inactive mappings will be destroyed.
490  *
491  * @param vh VPN handle
492  * @param result_af desired address family for the returned allocation
493  * @param addr_af address family for 'addr', AF_INET or AF_INET6
494  * @param addr destination IP address on the Internet; destination
495  *             port is to be taken from the VPN packet itself
496  * @param nac GNUNET_YES to notify via callback only after completion of
497  *            the MESH-level connection,
498  *            GNUNET_NO to notify as soon as the IP has been reserved
499  * @param expiration_time at what time should the redirection expire?
500  *        (this should not impact connections that are active at that time)
501  * @param cb function to call with the IP
502  * @param cb_cls closure for cb
503  * @return handle to cancel the request (means the callback won't be
504  *         invoked anymore; the mapping may or may not be established
505  *         anyway)
506  */
507 struct GNUNET_VPN_RedirectionRequest *
508 GNUNET_VPN_redirect_to_ip (struct GNUNET_VPN_Handle *vh,
509                            int result_af,
510                            int addr_af,
511                            const void *addr,
512                            int nac,
513                            struct GNUNET_TIME_Absolute expiration_time,
514                            GNUNET_VPN_AllocationCallback cb,
515                            void *cb_cls)
516 {
517   struct GNUNET_VPN_RedirectionRequest *rr;
518   size_t alen;
519
520   switch (addr_af)
521   {
522   case AF_INET:
523     alen = sizeof (struct in_addr);
524     break;
525   case AF_INET6:
526     alen = sizeof (struct in6_addr);
527     break;
528   default:
529     GNUNET_break (0);
530     return NULL;
531   }
532   rr = GNUNET_malloc (sizeof (struct GNUNET_VPN_RedirectionRequest) + alen);
533   rr->vh = vh;
534   rr->addr = &rr[1];
535   rr->cb = cb;
536   rr->cb_cls = cb_cls;
537   rr->expiration_time = expiration_time;
538   rr->result_af = result_af;
539   rr->addr_af = addr_af;
540   rr->nac = nac;
541   memcpy (&rr[1], addr, alen);
542   queue_request (rr);
543   return rr;
544 }
545
546
547 /**
548  * Connect to the VPN service
549  *
550  * @param cfg configuration to use
551  * @return VPN handle 
552  */
553 struct GNUNET_VPN_Handle *
554 GNUNET_VPN_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
555 {
556   struct GNUNET_VPN_Handle *vh;
557
558   vh = GNUNET_malloc (sizeof (struct GNUNET_VPN_Handle));
559   vh->cfg = cfg;
560   vh->client = GNUNET_CLIENT_connect ("vpn", cfg);
561   if (NULL == vh->client)
562   {
563     GNUNET_free (vh);
564     return NULL;
565   }
566   return vh;
567 }
568
569
570 /**
571  * Disconnect from the VPN service.
572  *
573  * @param vh VPN handle
574  */
575 void
576 GNUNET_VPN_disconnect (struct GNUNET_VPN_Handle *vh)
577 {
578   GNUNET_assert (NULL == vh->rr_head);
579   if (NULL != vh->th)
580   {
581     GNUNET_CLIENT_notify_transmit_ready_cancel (vh->th);
582     vh->th = NULL;
583   }
584   if (NULL != vh->client)
585   {
586     GNUNET_CLIENT_disconnect (vh->client, GNUNET_NO);
587     vh->client = NULL;
588   }
589   if (GNUNET_SCHEDULER_NO_TASK != vh->rt)
590   {
591     GNUNET_SCHEDULER_cancel (vh->rt);
592     vh->rt = GNUNET_SCHEDULER_NO_TASK;
593   }
594   GNUNET_free (vh);
595 }
596
597 /* end of vpn_api.c */