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