draft ATS API for TNG
[oweals/gnunet.git] / src / ats / ats_api2_application.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_api2_application.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_application_service.h"
26 #include "ats2.h"
27
28
29 #define LOG(kind,...) GNUNET_log_from(kind, "ats-application-api", __VA_ARGS__)
30
31
32 /**
33  * Handle for ATS address suggestion requests.
34  */
35 struct GNUNET_ATS_ApplicationSuggestHandle
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_ApplicationHandle *ch;
46
47   /**
48    * What preference is being expressed?
49    */
50   enum GNUNET_MQ_PreferenceKind pk;
51
52   /**
53    * How much bandwidth does the client expect?
54    */
55   struct GNUNET_BANDWIDTH_Value32NBO bw;
56 };
57
58
59 /**
60  * Handle to the ATS subsystem for application management.
61  */
62 struct GNUNET_ATS_ApplicationHandle
63 {
64
65   /**
66    * Our configuration.
67    */
68   const struct GNUNET_CONFIGURATION_Handle *cfg;
69
70   /**
71    * Map with the identities of all the peers for which we would
72    * like to have address suggestions.  The key is the PID, the
73    * value is currently the `struct GNUNET_ATS_ApplicationSuggestHandle`
74    */
75   struct GNUNET_CONTAINER_MultiPeerMap *sug_requests;
76
77   /**
78    * Message queue for sending requests to the ATS service.
79    */
80   struct GNUNET_MQ_Handle *mq;
81
82   /**
83    * Task to trigger reconnect.
84    */
85   struct GNUNET_SCHEDULER_Task *task;
86
87   /**
88    * Reconnect backoff delay.
89    */
90   struct GNUNET_TIME_Relative backoff;
91 };
92
93
94 /**
95  * Re-establish the connection to the ATS service.
96  *
97  * @param ch handle to use to re-connect.
98  */
99 static void
100 reconnect (struct GNUNET_ATS_ApplicationHandle *ch);
101
102
103 /**
104  * Re-establish the connection to the ATS service.
105  *
106  * @param cls handle to use to re-connect.
107  */
108 static void
109 reconnect_task (void *cls)
110 {
111   struct GNUNET_ATS_ApplicationHandle *ch = cls;
112
113   ch->task = NULL;
114   reconnect (ch);
115 }
116
117
118 /**
119  * Disconnect from ATS and then reconnect.
120  *
121  * @param ch our handle
122  */
123 static void
124 force_reconnect (struct GNUNET_ATS_ApplicationHandle *ch)
125 {
126   if (NULL != ch->mq)
127   {
128     GNUNET_MQ_destroy (ch->mq);
129     ch->mq = NULL;
130   }
131   ch->backoff = GNUNET_TIME_STD_BACKOFF (ch->backoff);
132   ch->task = GNUNET_SCHEDULER_add_delayed (ch->backoff,
133                                            &reconnect_task,
134                                            ch);
135 }
136
137
138 /**
139  * We encountered an error handling the MQ to the
140  * ATS service.  Reconnect.
141  *
142  * @param cls the `struct GNUNET_ATS_ApplicationHandle`
143  * @param error details about the error
144  */
145 static void
146 error_handler (void *cls,
147                enum GNUNET_MQ_Error error)
148 {
149   struct GNUNET_ATS_ApplicationHandle *ch = cls;
150
151   LOG (GNUNET_ERROR_TYPE_DEBUG,
152        "ATS connection died (code %d), reconnecting\n",
153        (int) error);
154   force_reconnect (ch);
155 }
156
157
158 /**
159  * Transmit request for an address suggestion.
160  *
161  * @param cls the `struct GNUNET_ATS_ApplicationHandle`
162  * @param peer peer to ask for an address suggestion for
163  * @param value the `struct GNUNET_ATS_SuggestHandle`
164  * @return #GNUNET_OK (continue to iterate), #GNUNET_SYSERR on
165  *         failure (message queue no longer exists)
166  */
167 static int
168 transmit_suggestion (void *cls,
169                      const struct GNUNET_PeerIdentity *peer,
170                      void *value)
171 {
172   struct GNUNET_ATS_ApplicationHandle *ch = cls;
173   struct GNUNET_ATS_ApplicationSuggestHandle *sh = value;
174   struct GNUNET_MQ_Envelope *ev;
175   struct ExpressPreferenceMessage *m;
176
177   if (NULL == ch->mq)
178     return GNUNET_SYSERR;
179   ev = GNUNET_MQ_msg (m,
180                       GNUNET_MESSAGE_TYPE_ATS_SUGGEST);
181   m->pk = htonl ((uint32_t) sh->pk);
182   m->bw = sh->bw;
183   m->peer = *peer;
184   GNUNET_MQ_send (ch->mq, ev);
185   return GNUNET_OK;
186 }
187
188
189 /**
190  * Re-establish the connection to the ATS service.
191  *
192  * @param ch handle to use to re-connect.
193  */
194 static void
195 reconnect (struct GNUNET_ATS_ApplicationHandle *ch)
196 {
197   static const struct GNUNET_MQ_MessageHandler handlers[] = {
198     { NULL, 0, 0 }
199   };
200
201   GNUNET_assert (NULL == ch->mq);
202   ch->mq = GNUNET_CLIENT_connect (ch->cfg,
203                                   "ats",
204                                   handlers,
205                                   &error_handler,
206                                   ch);
207   if (NULL == ch->mq)
208   {
209     force_reconnect (ch);
210     return;
211   }
212   GNUNET_CONTAINER_multipeermap_iterate (ch->sug_requests,
213                                          &transmit_suggestion,
214                                          ch);
215 }
216
217
218 /**
219  * Initialize the ATS application suggestion client handle.
220  *
221  * @param cfg configuration to use
222  * @return ats application handle, NULL on error
223  */
224 struct GNUNET_ATS_ApplicationHandle *
225 GNUNET_ATS_application_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
226 {
227   struct GNUNET_ATS_ApplicationHandle *ch;
228
229   ch = GNUNET_new (struct GNUNET_ATS_ApplicationHandle);
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_ApplicationSuggestHandle`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_ApplicationSuggestHandle *cur = value;
253
254   GNUNET_free (cur);
255   return GNUNET_OK;
256 }
257
258
259 /**
260  * Client is done with ATS application management, release resources.
261  *
262  * @param ch handle to release
263  */
264 void
265 GNUNET_ATS_application_done (struct GNUNET_ATS_ApplicationHandle *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_application_suggest_cancel().
290  *
291  * @param ch handle
292  * @param peer identity of the peer we need an address for
293  * @param pk what kind of application will the application require (can be
294  *         #GNUNET_MQ_PREFERENCE_NONE, we will still try to connect)
295  * @param bw desired bandwith, can be zero (we will still try to connect)
296  * @return suggest handle, NULL if a request is already pending
297  */
298 struct GNUNET_ATS_ApplicationSuggestHandle *
299 GNUNET_ATS_application_suggest (struct GNUNET_ATS_ApplicationHandle *ch,
300                                 const struct GNUNET_PeerIdentity *peer,
301                                 enum GNUNET_MQ_PreferenceKind pk,
302                                 struct GNUNET_BANDWIDTH_Value32NBO bw)
303 {
304   struct GNUNET_ATS_ApplicationSuggestHandle *s;
305
306   s = GNUNET_new (struct GNUNET_ATS_ApplicationSuggestHandle);
307   s->ch = ch;
308   s->id = *peer;
309   s->pk = pk;
310   s->bw = bw;
311   (void) GNUNET_CONTAINER_multipeermap_put (ch->sug_requests,
312                                             &s->id,
313                                             s,
314                                             GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
315   LOG (GNUNET_ERROR_TYPE_DEBUG,
316        "Requesting ATS to suggest address for `%s'\n",
317        GNUNET_i2s (peer));
318   if (NULL == ch->mq)
319     return s;
320   GNUNET_assert (GNUNET_OK ==
321                  transmit_suggestion (ch,
322                                       &s->id,
323                                       s));
324   return s;
325 }
326
327
328 /**
329  * We no longer care about being connected to a peer.
330  *
331  * @param sh handle to stop
332  */
333 void
334 GNUNET_ATS_application_suggest_cancel (struct GNUNET_ATS_ApplicationSuggestHandle *sh)
335 {
336   struct GNUNET_ATS_ApplicationHandle *ch = sh->ch;
337   struct GNUNET_MQ_Envelope *ev;
338   struct ExpressPreferenceMessage *m;
339
340   LOG (GNUNET_ERROR_TYPE_DEBUG,
341        "Telling ATS we no longer care for an address for `%s'\n",
342        GNUNET_i2s (&sh->id));
343   GNUNET_assert (GNUNET_OK ==
344                  GNUNET_CONTAINER_multipeermap_remove (ch->sug_requests,
345                                                        &sh->id,
346                                                        sh));
347   if (NULL == ch->mq)
348   {
349     GNUNET_free (sh);
350     return;
351   }
352   ev = GNUNET_MQ_msg (m,
353                       GNUNET_MESSAGE_TYPE_ATS_SUGGEST_CANCEL);
354   m->pk = htonl ((uint32_t) sh->pk);
355   m->bw = sh->bw;
356   m->peer = sh->id;
357   GNUNET_MQ_send (ch->mq,
358                   ev);
359   GNUNET_free (sh);
360 }
361
362
363 /* end of ats_api2_application.c */