Merge branch 'master' of gnunet.org:gnunet
[oweals/gnunet.git] / src / ats / ats_api_connectivity.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2010-2016 GNUnet e.V.
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  * @file ats/ats_api_connectivity.c
22  * @brief enable clients to ask ATS about establishing connections to peers
23  * @author Christian Grothoff
24  * @author Matthias Wachs
25  */
26 #include "platform.h"
27 #include "gnunet_ats_service.h"
28 #include "ats.h"
29
30
31 #define LOG(kind, ...) GNUNET_log_from (kind, "ats-connectivity-api", \
32                                         __VA_ARGS__)
33
34
35 /**
36  * Handle for ATS address suggestion requests.
37  */
38 struct GNUNET_ATS_ConnectivitySuggestHandle
39 {
40   /**
41    * ID of the peer for which address suggestion was requested.
42    */
43   struct GNUNET_PeerIdentity id;
44
45   /**
46    * Connecitivity handle this suggestion handle belongs to.
47    */
48   struct GNUNET_ATS_ConnectivityHandle *ch;
49
50   /**
51    * How urgent is the request.
52    */
53   uint32_t strength;
54 };
55
56
57 /**
58  * Handle to the ATS subsystem for connectivity management.
59  */
60 struct GNUNET_ATS_ConnectivityHandle
61 {
62   /**
63    * Our configuration.
64    */
65   const struct GNUNET_CONFIGURATION_Handle *cfg;
66
67   /**
68    * Map with the identities of all the peers for which we would
69    * like to have address suggestions.  The key is the PID, the
70    * value is currently the `struct GNUNET_ATS_ConnectivitySuggestHandle`
71    */
72   struct GNUNET_CONTAINER_MultiPeerMap *sug_requests;
73
74   /**
75    * Message queue for sending requests to the ATS service.
76    */
77   struct GNUNET_MQ_Handle *mq;
78
79   /**
80    * Task to trigger reconnect.
81    */
82   struct GNUNET_SCHEDULER_Task *task;
83
84   /**
85    * Reconnect backoff delay.
86    */
87   struct GNUNET_TIME_Relative backoff;
88 };
89
90
91 /**
92  * Re-establish the connection to the ATS service.
93  *
94  * @param ch handle to use to re-connect.
95  */
96 static void
97 reconnect (struct GNUNET_ATS_ConnectivityHandle *ch);
98
99
100 /**
101  * Re-establish the connection to the ATS service.
102  *
103  * @param cls handle to use to re-connect.
104  */
105 static void
106 reconnect_task (void *cls)
107 {
108   struct GNUNET_ATS_ConnectivityHandle *ch = cls;
109
110   ch->task = NULL;
111   reconnect (ch);
112 }
113
114
115 /**
116  * Disconnect from ATS and then reconnect.
117  *
118  * @param ch our handle
119  */
120 static void
121 force_reconnect (struct GNUNET_ATS_ConnectivityHandle *ch)
122 {
123   if (NULL != ch->mq)
124   {
125     GNUNET_MQ_destroy (ch->mq);
126     ch->mq = NULL;
127   }
128   ch->backoff = GNUNET_TIME_STD_BACKOFF (ch->backoff);
129   ch->task = GNUNET_SCHEDULER_add_delayed (ch->backoff,
130                                            &reconnect_task,
131                                            ch);
132 }
133
134
135 /**
136  * We encountered an error handling the MQ to the
137  * ATS service.  Reconnect.
138  *
139  * @param cls the `struct GNUNET_ATS_ConnectivityHandle`
140  * @param error details about the error
141  */
142 static void
143 error_handler (void *cls,
144                enum GNUNET_MQ_Error error)
145 {
146   struct GNUNET_ATS_ConnectivityHandle *ch = cls;
147
148   LOG (GNUNET_ERROR_TYPE_DEBUG,
149        "ATS connection died (code %d), reconnecting\n",
150        (int) error);
151   force_reconnect (ch);
152 }
153
154
155 /**
156  * Transmit request for an address suggestion.
157  *
158  * @param cls the `struct GNUNET_ATS_ConnectivityHandle`
159  * @param peer peer to ask for an address suggestion for
160  * @param value the `struct GNUNET_ATS_SuggestHandle`
161  * @return #GNUNET_OK (continue to iterate), #GNUNET_SYSERR on
162  *         failure (message queue no longer exists)
163  */
164 static int
165 transmit_suggestion (void *cls,
166                      const struct GNUNET_PeerIdentity *peer,
167                      void *value)
168 {
169   struct GNUNET_ATS_ConnectivityHandle *ch = cls;
170   struct GNUNET_ATS_ConnectivitySuggestHandle *sh = value;
171   struct GNUNET_MQ_Envelope *ev;
172   struct RequestAddressMessage *m;
173
174   if (NULL == ch->mq)
175     return GNUNET_SYSERR;
176   ev = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS);
177   m->strength = htonl (sh->strength);
178   m->peer = *peer;
179   GNUNET_MQ_send (ch->mq, ev);
180   return GNUNET_OK;
181 }
182
183
184 /**
185  * Re-establish the connection to the ATS service.
186  *
187  * @param ch handle to use to re-connect.
188  */
189 static void
190 reconnect (struct GNUNET_ATS_ConnectivityHandle *ch)
191 {
192   static const struct GNUNET_MQ_MessageHandler handlers[] =
193   { { NULL, 0, 0 } };
194   struct GNUNET_MQ_Envelope *ev;
195   struct ClientStartMessage *init;
196
197   GNUNET_assert (NULL == ch->mq);
198   ch->mq = GNUNET_CLIENT_connect (ch->cfg,
199                                   "ats",
200                                   handlers,
201                                   &error_handler,
202                                   ch);
203   if (NULL == ch->mq)
204   {
205     force_reconnect (ch);
206     return;
207   }
208   ev = GNUNET_MQ_msg (init,
209                       GNUNET_MESSAGE_TYPE_ATS_START);
210   init->start_flag = htonl (START_FLAG_CONNECTION_SUGGESTION);
211   GNUNET_MQ_send (ch->mq, ev);
212   if (NULL == ch->mq)
213     return;
214   GNUNET_CONTAINER_multipeermap_iterate (ch->sug_requests,
215                                          &transmit_suggestion,
216                                          ch);
217 }
218
219
220 /**
221  * Initialize the ATS connectivity suggestion client handle.
222  *
223  * @param cfg configuration to use
224  * @return ats connectivity handle, NULL on error
225  */
226 struct GNUNET_ATS_ConnectivityHandle *
227 GNUNET_ATS_connectivity_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
228 {
229   struct GNUNET_ATS_ConnectivityHandle *ch;
230
231   ch = GNUNET_new (struct GNUNET_ATS_ConnectivityHandle);
232   ch->cfg = cfg;
233   ch->sug_requests = GNUNET_CONTAINER_multipeermap_create (32,
234                                                            GNUNET_YES);
235   reconnect (ch);
236   return ch;
237 }
238
239
240 /**
241  * Function called to free all `struct GNUNET_ATS_ConnectivitySuggestHandle`s
242  * in the map.
243  *
244  * @param cls NULL
245  * @param key the key
246  * @param value the value to free
247  * @return #GNUNET_OK (continue to iterate)
248  */
249 static int
250 free_sug_handle (void *cls,
251                  const struct GNUNET_PeerIdentity *key,
252                  void *value)
253 {
254   struct GNUNET_ATS_ConnectivitySuggestHandle *cur = value;
255
256   GNUNET_free (cur);
257   return GNUNET_OK;
258 }
259
260
261 /**
262  * Client is done with ATS connectivity management, release resources.
263  *
264  * @param ch handle to release
265  */
266 void
267 GNUNET_ATS_connectivity_done (struct GNUNET_ATS_ConnectivityHandle *ch)
268 {
269   if (NULL != ch->mq)
270   {
271     GNUNET_MQ_destroy (ch->mq);
272     ch->mq = NULL;
273   }
274   if (NULL != ch->task)
275   {
276     GNUNET_SCHEDULER_cancel (ch->task);
277     ch->task = NULL;
278   }
279   GNUNET_CONTAINER_multipeermap_iterate (ch->sug_requests,
280                                          &free_sug_handle,
281                                          NULL);
282   GNUNET_CONTAINER_multipeermap_destroy (ch->sug_requests);
283   GNUNET_free (ch);
284 }
285
286
287 /**
288  * We would like to receive address suggestions for a peer. ATS will
289  * respond with a call to the continuation immediately containing an address or
290  * no address if none is available. ATS can suggest more addresses until we call
291  * #GNUNET_ATS_connectivity_suggest_cancel().
292  *
293  * @param ch handle
294  * @param peer identity of the peer we need an address for
295  * @param strength how urgent is the need for such a suggestion
296  * @return suggest handle, NULL if a request is already pending
297  */
298 struct GNUNET_ATS_ConnectivitySuggestHandle *
299 GNUNET_ATS_connectivity_suggest (struct GNUNET_ATS_ConnectivityHandle *ch,
300                                  const struct GNUNET_PeerIdentity *peer,
301                                  uint32_t strength)
302 {
303   struct GNUNET_ATS_ConnectivitySuggestHandle *s;
304
305   s = GNUNET_new (struct GNUNET_ATS_ConnectivitySuggestHandle);
306   s->ch = ch;
307   s->id = *peer;
308   s->strength = strength;
309   if (GNUNET_OK !=
310       GNUNET_CONTAINER_multipeermap_put (ch->sug_requests,
311                                          &s->id,
312                                          s,
313                                          GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
314   {
315     LOG (GNUNET_ERROR_TYPE_DEBUG,
316          "Not requesting ATS to suggest address for `%s', request already pending\n",
317          GNUNET_i2s (peer));
318     GNUNET_free (s);
319     return NULL;
320   }
321   LOG (GNUNET_ERROR_TYPE_DEBUG,
322        "Requesting ATS to suggest address for `%s'\n",
323        GNUNET_i2s (peer));
324   if (NULL == ch->mq)
325     return s;
326   (void) transmit_suggestion (ch,
327                               &s->id,
328                               s);
329   return s;
330 }
331
332
333 /**
334  * We no longer care about being connected to a peer.
335  *
336  * @param sh handle to stop
337  */
338 void
339 GNUNET_ATS_connectivity_suggest_cancel (struct
340                                         GNUNET_ATS_ConnectivitySuggestHandle *sh)
341 {
342   struct GNUNET_ATS_ConnectivityHandle *ch = sh->ch;
343   struct GNUNET_MQ_Envelope *ev;
344   struct RequestAddressMessage *m;
345
346   LOG (GNUNET_ERROR_TYPE_DEBUG,
347        "Telling ATS we no longer care for an address for `%s'\n",
348        GNUNET_i2s (&sh->id));
349   GNUNET_assert (GNUNET_OK ==
350                  GNUNET_CONTAINER_multipeermap_remove (ch->sug_requests,
351                                                        &sh->id,
352                                                        sh));
353   if (NULL == ch->mq)
354   {
355     GNUNET_free (sh);
356     return;
357   }
358   ev = GNUNET_MQ_msg (m,
359                       GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS_CANCEL);
360   m->strength = htonl (0);
361   m->peer = sh->id;
362   GNUNET_MQ_send (ch->mq, ev);
363   GNUNET_free (sh);
364 }
365
366
367 /* end of ats_api_connectivity.c */