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