190ed0f992ecab89ff8bccd5f91e52058873ffcb
[oweals/gnunet.git] / src / vpn / vpn_api.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2012, 2016 Christian Grothoff
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14     
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /**
20  * @file vpn/vpn_api.c
21  * @brief library to access the VPN service and tell it how to redirect traffic
22  * @author Christian Grothoff
23  */
24 #include "platform.h"
25 #include "gnunet_vpn_service.h"
26 #include "vpn.h"
27
28
29 /**
30  * Opaque VPN handle
31  */
32 struct GNUNET_VPN_Handle
33 {
34   /**
35    * Configuration we use.
36    */
37   const struct GNUNET_CONFIGURATION_Handle *cfg;
38
39   /**
40    * Connection to VPN service.
41    */
42   struct GNUNET_MQ_Handle *mq;
43
44   /**
45    * Head of list of active redirection requests.
46    */
47   struct GNUNET_VPN_RedirectionRequest *rr_head;
48
49   /**
50    * Tail of list of active redirection requests.
51    */
52   struct GNUNET_VPN_RedirectionRequest *rr_tail;
53
54   /**
55    * Identifier of a reconnect task.
56    */
57   struct GNUNET_SCHEDULER_Task *rt;
58
59   /**
60    * How long do we wait until we try to reconnect?
61    */
62   struct GNUNET_TIME_Relative backoff;
63
64   /**
65    * ID of the last request that was submitted to the service.
66    */
67   uint64_t request_id_gen;
68
69 };
70
71
72 /**
73  * Opaque redirection request handle.
74  */
75 struct GNUNET_VPN_RedirectionRequest
76 {
77   /**
78    * Element in DLL.
79    */
80   struct GNUNET_VPN_RedirectionRequest *next;
81
82   /**
83    * Element in DLL.
84    */
85   struct GNUNET_VPN_RedirectionRequest *prev;
86
87   /**
88    * Pointer to the VPN struct.
89    */
90   struct GNUNET_VPN_Handle *vh;
91
92   /**
93    * Target IP address for the redirection, or NULL for
94    * redirection to service.  Allocated after this struct.
95    */
96   const void *addr;
97
98   /**
99    * Function to call with the designated IP address.
100    */
101   GNUNET_VPN_AllocationCallback cb;
102
103   /**
104    * Closure for @e cb.
105    */
106   void *cb_cls;
107
108   /**
109    * For service redirection, identity of the peer offering the service.
110    */
111   struct GNUNET_PeerIdentity peer;
112
113   /**
114    * For service redirection, service descriptor.
115    */
116   struct GNUNET_HashCode serv;
117
118   /**
119    * At what time should the created service mapping expire?
120    */
121   struct GNUNET_TIME_Absolute expiration_time;
122
123   /**
124    * non-zero if this request has been sent to the service.
125    */
126   uint64_t request_id;
127
128   /**
129    * Desired address family for the result.
130    */
131   int result_af;
132
133   /**
134    * Address family of @e addr.  AF_INET or AF_INET6.
135    */
136   int addr_af;
137
138   /**
139    * For service redirection, IPPROT_UDP or IPPROTO_TCP.
140    */
141   uint8_t protocol;
142
143 };
144
145
146 /**
147  * Disconnect from the service (communication error) and reconnect later.
148  *
149  * @param vh handle to reconnect.
150  */
151 static void
152 reconnect (struct GNUNET_VPN_Handle *vh);
153
154
155 /**
156  * Check a #GNUNET_MESSAGE_TYPE_VPN_CLIENT_USE_IP message from the
157  * VPN service.
158  *
159  * @param cls the `struct GNUNET_VPN_Handle`
160  * @param rm message received
161  * @return #GNUNET_OK if @a rm is well-formed
162  */
163 static int
164 check_use_ip (void *cls,
165               const struct RedirectToIpResponseMessage *rm)
166 {
167   size_t alen;
168   int af;
169
170   af = (int) ntohl (rm->result_af);
171   switch (af)
172   {
173   case AF_UNSPEC:
174     alen = 0;
175     break;
176   case AF_INET:
177     alen = sizeof (struct in_addr);
178     break;
179   case AF_INET6:
180     alen = sizeof (struct in6_addr);
181     break;
182   default:
183     GNUNET_break (0);
184     return GNUNET_SYSERR;
185   }
186   if ( (ntohs (rm->header.size) != alen + sizeof (*rm)) ||
187        (0 == rm->request_id) )
188   {
189     GNUNET_break (0);
190     return GNUNET_SYSERR;
191   }
192   return GNUNET_OK;
193 }
194
195
196 /**
197  * Handle a #GNUNET_MESSAGE_TYPE_VPN_CLIENT_USE_IP message from the
198  * VPN service.
199  *
200  * @param cls the `struct GNUNET_VPN_Handle`
201  * @param rm message received
202  */
203 static void
204 handle_use_ip (void *cls,
205                const struct RedirectToIpResponseMessage *rm)
206 {
207   struct GNUNET_VPN_Handle *vh = cls;
208   struct GNUNET_VPN_RedirectionRequest *rr;
209   int af;
210
211   af = (int) ntohl (rm->result_af);
212   for (rr = vh->rr_head; NULL != rr; rr = rr->next)
213   {
214     if (rr->request_id == rm->request_id)
215     {
216       GNUNET_CONTAINER_DLL_remove (vh->rr_head,
217                                    vh->rr_tail,
218                                    rr);
219       rr->cb (rr->cb_cls,
220               af,
221               (af == AF_UNSPEC) ? NULL : &rm[1]);
222       GNUNET_free (rr);
223       break;
224     }
225   }
226 }
227
228
229 /**
230  * Add a request to our request queue and transmit it.
231  *
232  * @param rr request to queue and transmit.
233  */
234 static void
235 send_request (struct GNUNET_VPN_RedirectionRequest *rr)
236 {
237   struct GNUNET_VPN_Handle *vh = rr->vh;
238   struct RedirectToIpRequestMessage *rip;
239   struct RedirectToServiceRequestMessage *rs;
240   struct GNUNET_MQ_Envelope *env;
241   size_t alen;
242
243   if (NULL == vh->mq)
244     return;
245   if (NULL == rr->addr)
246   {
247     env = GNUNET_MQ_msg (rs,
248                          GNUNET_MESSAGE_TYPE_VPN_CLIENT_REDIRECT_TO_SERVICE);
249     rs->reserved = htonl (0);
250     rs->expiration_time = GNUNET_TIME_absolute_hton (rr->expiration_time);
251     rs->protocol = htonl (rr->protocol);
252     rs->result_af = htonl (rr->result_af);
253     rs->target = rr->peer;
254     rs->service_descriptor = rr->serv;
255     rs->request_id = rr->request_id = ++vh->request_id_gen;
256   }
257   else
258   {
259     switch (rr->addr_af)
260     {
261     case AF_INET:
262       alen = sizeof (struct in_addr);
263       break;
264     case AF_INET6:
265       alen = sizeof (struct in6_addr);
266       break;
267     default:
268       GNUNET_assert (0);
269       return;
270     }
271     env = GNUNET_MQ_msg_extra (rip,
272                                alen,
273                                GNUNET_MESSAGE_TYPE_VPN_CLIENT_REDIRECT_TO_IP);
274     rip->reserved = htonl (0);
275     rip->expiration_time = GNUNET_TIME_absolute_hton (rr->expiration_time);
276     rip->result_af = htonl (rr->result_af);
277     rip->addr_af = htonl (rr->addr_af);
278     rip->request_id = rr->request_id = ++vh->request_id_gen;
279     GNUNET_memcpy (&rip[1],
280                    rr->addr,
281                    alen);
282   }
283   GNUNET_MQ_send (vh->mq,
284                   env);
285 }
286
287
288 /**
289  * Generic error handler, called with the appropriate error code and
290  * the same closure specified at the creation of the message queue.
291  * Not every message queue implementation supports an error handler.
292  *
293  * @param cls closure with the `struct GNUNET_VPN_Handle *`
294  * @param error error code
295  */
296 static void
297 mq_error_handler (void *cls,
298                   enum GNUNET_MQ_Error error)
299 {
300   struct GNUNET_VPN_Handle *vh = cls;
301
302   reconnect (vh);
303 }
304
305
306 /**
307  * Connect to the VPN service and start again to transmit our requests.
308  *
309  * @param cls the `struct GNUNET_VPN_Handle *`
310  */
311 static void
312 connect_task (void *cls)
313 {
314   struct GNUNET_VPN_Handle *vh = cls;
315   struct GNUNET_MQ_MessageHandler handlers[] = {
316     GNUNET_MQ_hd_var_size (use_ip,
317                            GNUNET_MESSAGE_TYPE_VPN_CLIENT_USE_IP,
318                            struct RedirectToIpResponseMessage,
319                            cls),
320     GNUNET_MQ_handler_end ()
321   };
322   struct GNUNET_VPN_RedirectionRequest *rr;
323
324   vh->rt = NULL;
325   vh->mq = GNUNET_CLIENT_connect (vh->cfg,
326                                   "vpn",
327                                   handlers,
328                                   &mq_error_handler,
329                                   vh);
330   if (NULL == vh->mq)
331     return;
332   for (rr = vh->rr_head; NULL != rr; rr = rr->next)
333     send_request (rr);
334 }
335
336
337 /**
338  * Disconnect from the service (communication error) and reconnect later.
339  *
340  * @param vh handle to reconnect.
341  */
342 static void
343 reconnect (struct GNUNET_VPN_Handle *vh)
344 {
345   struct GNUNET_VPN_RedirectionRequest *rr;
346
347   GNUNET_MQ_destroy (vh->mq);
348   vh->mq = NULL;
349   vh->request_id_gen = 0;
350   for (rr = vh->rr_head; NULL != rr; rr = rr->next)
351     rr->request_id = 0;
352   vh->backoff = GNUNET_TIME_relative_max (GNUNET_TIME_UNIT_MILLISECONDS,
353                                           GNUNET_TIME_relative_min (GNUNET_TIME_relative_saturating_multiply (vh->backoff, 2),
354                                                                     GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)));
355   vh->rt = GNUNET_SCHEDULER_add_delayed (vh->backoff,
356                                          &connect_task,
357                                          vh);
358 }
359
360
361 /**
362  * Cancel redirection request with the service.
363  *
364  * @param rr request to cancel
365  */
366 void
367 GNUNET_VPN_cancel_request (struct GNUNET_VPN_RedirectionRequest *rr)
368 {
369   struct GNUNET_VPN_Handle *vh;
370
371   vh = rr->vh;
372   GNUNET_CONTAINER_DLL_remove (vh->rr_head,
373                                vh->rr_tail,
374                                rr);
375   GNUNET_free (rr);
376 }
377
378
379 /**
380  * Tell the VPN that a forwarding to a particular peer offering a
381  * particular service is requested.  The VPN is to reserve a
382  * particular IP for the redirection and return it.  The VPN will
383  * begin the redirection as soon as possible and maintain it as long
384  * as it is actively used and keeping it is feasible.  Given resource
385  * limitations, the longest inactive mappings will be destroyed.
386  *
387  * @param vh VPN handle
388  * @param result_af desired address family for the returned allocation
389  *                  can also be AF_UNSPEC
390  * @param protocol protocol, IPPROTO_UDP or IPPROTO_TCP
391  * @param peer target peer for the redirection
392  * @param serv service descriptor to give to the peer
393  * @param expiration_time at what time should the redirection expire?
394  *        (this should not impact connections that are active at that time)
395  * @param cb function to call with the IP
396  * @param cb_cls closure for @a cb
397  * @return handle to cancel the request (means the callback won't be
398  *         invoked anymore; the mapping may or may not be established
399  *         anyway)
400  */
401 struct GNUNET_VPN_RedirectionRequest *
402 GNUNET_VPN_redirect_to_peer (struct GNUNET_VPN_Handle *vh,
403                              int result_af,
404                              uint8_t protocol,
405                              const struct GNUNET_PeerIdentity *peer,
406                              const struct GNUNET_HashCode *serv,
407                              struct GNUNET_TIME_Absolute expiration_time,
408                              GNUNET_VPN_AllocationCallback cb,
409                              void *cb_cls)
410 {
411   struct GNUNET_VPN_RedirectionRequest *rr;
412
413   rr = GNUNET_new (struct GNUNET_VPN_RedirectionRequest);
414   rr->vh = vh;
415   rr->cb = cb;
416   rr->cb_cls = cb_cls;
417   rr->peer = *peer;
418   rr->serv = *serv;
419   rr->expiration_time = expiration_time;
420   rr->result_af = result_af;
421   rr->protocol = protocol;
422   GNUNET_CONTAINER_DLL_insert_tail (vh->rr_head,
423                                     vh->rr_tail,
424                                     rr);
425   send_request (rr);
426   return rr;
427 }
428
429
430 /**
431  * Tell the VPN that forwarding to the Internet via some exit node is
432  * requested.  Note that both UDP and TCP traffic will be forwarded,
433  * but possibly to different exit nodes.  The VPN is to reserve a
434  * particular IP for the redirection and return it.  The VPN will
435  * begin the redirection as soon as possible and maintain it as long
436  * as it is actively used and keeping it is feasible.  Given resource
437  * limitations, the longest inactive mappings will be destroyed.
438  *
439  * @param vh VPN handle
440  * @param result_af desired address family for the returned allocation
441  * @param addr_af address family for @a addr, AF_INET or AF_INET6
442  * @param addr destination IP address on the Internet; destination
443  *             port is to be taken from the VPN packet itself
444  * @param expiration_time at what time should the redirection expire?
445  *        (this should not impact connections that are active at that time)
446  * @param cb function to call with the IP
447  * @param cb_cls closure for @a cb
448  * @return handle to cancel the request (means the callback won't be
449  *         invoked anymore; the mapping may or may not be established
450  *         anyway)
451  */
452 struct GNUNET_VPN_RedirectionRequest *
453 GNUNET_VPN_redirect_to_ip (struct GNUNET_VPN_Handle *vh,
454                            int result_af,
455                            int addr_af,
456                            const void *addr,
457                            struct GNUNET_TIME_Absolute expiration_time,
458                            GNUNET_VPN_AllocationCallback cb,
459                            void *cb_cls)
460 {
461   struct GNUNET_VPN_RedirectionRequest *rr;
462   size_t alen;
463
464   switch (addr_af)
465   {
466   case AF_INET:
467     alen = sizeof (struct in_addr);
468     break;
469   case AF_INET6:
470     alen = sizeof (struct in6_addr);
471     break;
472   default:
473     GNUNET_break (0);
474     return NULL;
475   }
476   rr = GNUNET_malloc (sizeof (struct GNUNET_VPN_RedirectionRequest) + alen);
477   rr->vh = vh;
478   rr->addr = &rr[1];
479   rr->cb = cb;
480   rr->cb_cls = cb_cls;
481   rr->expiration_time = expiration_time;
482   rr->result_af = result_af;
483   rr->addr_af = addr_af;
484   GNUNET_memcpy (&rr[1],
485           addr,
486           alen);
487   GNUNET_CONTAINER_DLL_insert_tail (vh->rr_head,
488                                     vh->rr_tail,
489                                     rr);
490   send_request (rr);
491   return rr;
492 }
493
494
495 /**
496  * Connect to the VPN service
497  *
498  * @param cfg configuration to use
499  * @return VPN handle
500  */
501 struct GNUNET_VPN_Handle *
502 GNUNET_VPN_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
503 {
504   struct GNUNET_VPN_Handle *vh
505     = GNUNET_new (struct GNUNET_VPN_Handle);
506
507   vh->cfg = cfg;
508   connect_task (vh);
509   if (NULL == vh->mq)
510   {
511     GNUNET_free (vh);
512     return NULL;
513   }
514   return vh;
515 }
516
517
518 /**
519  * Disconnect from the VPN service.
520  *
521  * @param vh VPN handle
522  */
523 void
524 GNUNET_VPN_disconnect (struct GNUNET_VPN_Handle *vh)
525 {
526   GNUNET_assert (NULL == vh->rr_head);
527   if (NULL != vh->mq)
528   {
529     GNUNET_MQ_destroy (vh->mq);
530     vh->mq = NULL;
531   }
532   if (NULL != vh->rt)
533   {
534     GNUNET_SCHEDULER_cancel (vh->rt);
535     vh->rt = NULL;
536   }
537   GNUNET_free (vh);
538 }
539
540 /* end of vpn_api.c */