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