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