a660f252fbf20b0537a8e9324950f6404ab02b27
[oweals/gnunet.git] / src / transport / gnunet-service-transport_ats.c
1 /*
2      This file is part of GNUnet.
3      (C) 2015 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20 /**
21  * @file transport/gnunet-service-transport_ats.h
22  * @brief interfacing between transport and ATS service
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #include "gnunet-service-transport.h"
27 #include "gnunet-service-transport_ats.h"
28 #include "gnunet-service-transport_manipulation.h"
29 #include "gnunet-service-transport_plugins.h"
30 #include "gnunet_ats_service.h"
31
32
33 /**
34  * Information we track for each address known to ATS.
35  */
36 struct AddressInfo
37 {
38
39   /**
40    * The address (with peer identity).
41    */
42   struct GNUNET_HELLO_Address *address;
43
44   /**
45    * Session (can be NULL)
46    */
47   struct Session *session;
48
49   /**
50    * Record with ATS API for the address.
51    */
52   struct GNUNET_ATS_AddressRecord *ar;
53 };
54
55
56 /**
57  * Map from peer identities to one or more `struct AddressInfo` values
58  * for the peer.
59  */
60 static struct GNUNET_CONTAINER_MultiPeerMap *p2a;
61
62
63 /**
64  * Closure for #find_ai().
65  */
66 struct FindClosure
67 {
68
69   /**
70    * Session to look for (only used if the address is inbound).
71    */
72   struct Session *session;
73
74   /**
75    * Address to look for.
76    */
77   const struct GNUNET_HELLO_Address *address;
78
79   /**
80    * Where to store the result.
81    */
82   struct AddressInfo *ret;
83
84 };
85
86
87 /**
88  * Find matching address info.
89  *
90  * @param cls the `struct FindClosure`
91  * @param key which peer is this about
92  * @param value the `struct AddressInfo`
93  * @return #GNUNET_YES to continue to iterate, #GNUNET_NO if we found the value
94  */
95 static int
96 find_ai_cb (void *cls,
97             const struct GNUNET_PeerIdentity *key,
98             void *value)
99 {
100   struct FindClosure *fc = cls;
101   struct AddressInfo *ai = value;
102
103   if ( (0 ==
104         GNUNET_HELLO_address_cmp (fc->address,
105                                   ai->address) ) &&
106        (fc->session == ai->session) )
107   {
108     fc->ret = ai;
109     return GNUNET_NO;
110   }
111   GNUNET_assert ( (fc->session != ai->session) ||
112                   (NULL == ai->session) );
113   return GNUNET_YES;
114 }
115
116
117 /**
118  * Find the address information struct for the
119  * given address and session.
120  *
121  * @param address address to look for
122  * @param session session to match for inbound connections
123  * @return NULL if this combination is unknown
124  */
125 static struct AddressInfo *
126 find_ai (const struct GNUNET_HELLO_Address *address,
127          struct Session *session)
128 {
129   struct FindClosure fc;
130
131   fc.address = address;
132   fc.session = session;
133   fc.ret = NULL;
134   GNUNET_CONTAINER_multipeermap_get_multiple (p2a,
135                                               &address->peer,
136                                               &find_ai_cb,
137                                               &fc);
138   return fc.ret;
139 }
140
141
142 /**
143  * Test if ATS knows about this address.
144  *
145  * @param address the address
146  * @param session the session
147  * @return #GNUNET_YES if address is known, #GNUNET_NO if not.
148  */
149 int
150 GST_ats_is_known (const struct GNUNET_HELLO_Address *address,
151                   struct Session *session)
152 {
153   return (NULL != find_ai (address, session)) ? GNUNET_YES : GNUNET_NO;
154 }
155
156
157 /**
158  * Notify ATS about the new address including the network this address is
159  * located in.
160  *
161  * @param address the address
162  * @param session the session
163  * @param ats ats information
164  * @param ats_count number of @a ats information
165  */
166 void
167 GST_ats_add_address (const struct GNUNET_HELLO_Address *address,
168                      struct Session *session,
169                      const struct GNUNET_ATS_Information *ats,
170                      uint32_t ats_count)
171 {
172   struct GNUNET_TRANSPORT_PluginFunctions *papi;
173   struct GNUNET_ATS_Information ats2[ats_count + 1];
174   struct GNUNET_ATS_AddressRecord *ar;
175   struct AddressInfo *ai;
176   uint32_t net;
177
178   /* valid new address, let ATS know! */
179   if (NULL == address->transport_name)
180   {
181     GNUNET_break(0);
182     return;
183   }
184   if (GNUNET_YES ==
185       GNUNET_HELLO_address_check_option (address,
186                                          GNUNET_HELLO_ADDRESS_INFO_INBOUND))
187   {
188     GNUNET_break (NULL != session);
189   }
190   else
191   {
192     GNUNET_break (NULL == session);
193   }
194   ai = find_ai (address, session);
195   if (NULL != ai)
196   {
197     GNUNET_break (0);
198     return;
199   }
200   if (NULL == (papi = GST_plugins_find (address->transport_name)))
201   {
202     /* we don't have the plugin for this address */
203     GNUNET_assert (0);
204     return;
205   }
206   if (NULL != session)
207   {
208     net = papi->get_network (papi->cls, session);
209     if (GNUNET_ATS_NET_UNSPECIFIED == net)
210     {
211       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
212                   _ ("Could not obtain a valid network for `%s' %s (%s)\n"),
213                   GNUNET_i2s (&address->peer),
214                   GST_plugins_a2s (address),
215                   address->transport_name);
216       return;
217     }
218     ats2[0].type = htonl (GNUNET_ATS_NETWORK_TYPE);
219     ats2[0].value = htonl (net);
220     memcpy (&ats2[1],
221             ats,
222             sizeof(struct GNUNET_ATS_Information) * ats_count);
223   }
224   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
225               "Notifying ATS about peer `%s''s new address `%s' session %p in network %s\n",
226               GNUNET_i2s (&address->peer),
227               (0 == address->address_length)
228               ? "<inbound>"
229               : GST_plugins_a2s (address),
230               session,
231               GNUNET_ATS_print_network_type (net));
232   ar = GNUNET_ATS_address_add (GST_ats,
233                                address,
234                                session,
235                                (NULL != session) ? ats2 : ats,
236                                (NULL != session) ? ats_count + 1 : ats_count);
237   ai = GNUNET_new (struct AddressInfo);
238   ai->address = GNUNET_HELLO_address_copy (address);
239   ai->session = session;
240   ai->ar = ar;
241   (void) GNUNET_CONTAINER_multipeermap_put (p2a,
242                                             &ai->address->peer,
243                                             ai,
244                                             GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
245 }
246
247
248 /**
249  * Notify ATS about a new session now existing for the given
250  * address.
251  *
252  * @param address the address
253  * @param session the session
254  */
255 void
256 GST_ats_new_session (const struct GNUNET_HELLO_Address *address,
257                      struct Session *session)
258 {
259   struct AddressInfo *ai;
260
261   ai = find_ai (address, NULL);
262   if (NULL == ai)
263   {
264     /* We may already be aware of the session, even if some other part
265        of the code could not tell if it just created a new session or
266        just got one recycled from the plugin; hence, we may be called
267        with "new" session even for an "old" session; in that case,
268        check that this is the case, but just ignore it. */
269     GNUNET_assert (NULL != (find_ai (address, session)));
270     return;
271   }
272   GNUNET_break (NULL == ai->session);
273   ai->session = session;
274   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
275                    "transport-ats",
276                    "Telling ATS about new session %p for peer %s\n",
277                    session,
278                    GNUNET_i2s (&address->peer));
279   GNUNET_ATS_address_add_session (ai->ar,
280                                   session);
281 }
282
283
284 /**
285  * Notify ATS that the session (but not the address) of
286  * a given address is no longer relevant.
287  *
288  * @param address the address
289  * @param session the session
290  */
291 void
292 GST_ats_del_session (const struct GNUNET_HELLO_Address *address,
293                      struct Session *session)
294 {
295   struct AddressInfo *ai;
296
297   if (NULL == session)
298   {
299     GNUNET_break (0);
300     return;
301   }
302   ai = find_ai (address, session);
303   if (NULL == ai)
304   {
305     /* We sometimes create sessions just for sending a PING,
306        and if those are destroyed they were never known to
307        ATS which means we end up here (however, in this
308        case, the address must be an outbound address). */
309     GNUNET_break (GNUNET_YES !=
310                   GNUNET_HELLO_address_check_option (address,
311                                                      GNUNET_HELLO_ADDRESS_INFO_INBOUND));
312
313     return;
314   }
315   GNUNET_assert (session == ai->session);
316   ai->session = NULL;
317   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
318                    "transport-ats",
319                    "Telling ATS to destroy session %p from peer %s\n",
320                    session,
321                    GNUNET_i2s (&address->peer));
322   if (GNUNET_YES ==
323       GNUNET_ATS_address_del_session (ai->ar, session))
324   {
325     ai->ar = NULL;
326     GST_ats_expire_address (address);
327   }
328 }
329
330
331 /**
332  * Notify ATS about property changes to an address.
333  *
334  * @param address our information about the address
335  * @param session the session
336  * @param ats performance information
337  * @param ats_count number of elements in @a ats
338  */
339 void
340 GST_ats_update_metrics (const struct GNUNET_HELLO_Address *address,
341                         struct Session *session,
342                         const struct GNUNET_ATS_Information *ats,
343                         uint32_t ats_count)
344 {
345   struct GNUNET_ATS_Information *ats_new;
346   struct AddressInfo *ai;
347
348   ai = find_ai (address, session);
349   if (NULL == ai)
350   {
351     /* We sometimes create sessions just for sending a PING,
352        and if we get metrics for those, they were never known to
353        ATS which means we end up here (however, in this
354        case, the address must be an outbound address). */
355     GNUNET_assert (GNUNET_YES !=
356                    GNUNET_HELLO_address_check_option (address,
357                                                       GNUNET_HELLO_ADDRESS_INFO_INBOUND));
358
359     return;
360   }
361   /* Call to manipulation to manipulate ATS information */
362   GNUNET_assert (NULL != GST_ats);
363   if ((NULL == ats) || (0 == ats_count))
364     return;
365   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
366               "Updating metrics for peer `%s' address %s session %p\n",
367               GNUNET_i2s (&address->peer),
368               GST_plugins_a2s (address),
369               session);
370   ats_new = GST_manipulation_manipulate_metrics (address,
371                                                  session,
372                                                  ats,
373                                                  ats_count);
374   GNUNET_ATS_address_update (ai->ar,
375                              ats_new, ats_count);
376   GNUNET_free_non_null (ats_new);
377 }
378
379
380 /**
381  * Notify ATS about a new session now being in use (or not).
382  *
383  * @param address the address
384  * @param session the session
385  * @param in_use #GNUNET_YES or #GNUNET_NO
386  */
387 void
388 GST_ats_set_in_use (const struct GNUNET_HELLO_Address *address,
389                     struct Session *session,
390                     int in_use)
391 {
392   struct AddressInfo *ai;
393
394   ai = find_ai (address, session);
395   if (NULL == ai)
396   {
397     GNUNET_break (0);
398     return;
399   }
400   GNUNET_ATS_address_set_in_use (ai->ar, in_use);
401 }
402
403
404 /**
405  * Notify ATS that the address has expired and thus cannot
406  * be used any longer.  This function must only be called
407  * if the corresponding session is already gone.
408  *
409  * @param address the address
410  */
411 void
412 GST_ats_expire_address (const struct GNUNET_HELLO_Address *address)
413 {
414   struct AddressInfo *ai;
415
416   ai = find_ai (address, NULL);
417   if (NULL == ai)
418   {
419     GNUNET_assert (0);
420     return;
421   }
422   GNUNET_assert (GNUNET_YES ==
423                  GNUNET_CONTAINER_multipeermap_remove (p2a,
424                                                        &address->peer,
425                                                        ai));
426   GNUNET_break (NULL == ai->session);
427   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
428                    "transport-ats",
429                    "Telling ATS to destroy address from peer %s\n",
430                    GNUNET_i2s (&address->peer));
431   if (NULL != ai->ar)
432     GNUNET_ATS_address_destroy (ai->ar);
433   GNUNET_HELLO_address_free (ai->address);
434   GNUNET_free (ai);
435 }
436
437
438 /**
439  * Initialize ATS subsystem.
440  */
441 void
442 GST_ats_init ()
443 {
444   p2a = GNUNET_CONTAINER_multipeermap_create (4, GNUNET_YES);
445 }
446
447
448 /**
449  * Release memory used by the given address data.
450  *
451  * @param cls NULL
452  * @param key which peer is this about
453  * @param value the `struct AddressInfo`
454  * @return #GNUNET_OK (continue to iterate)
455  */
456 static int
457 destroy_ai (void *cls,
458             const struct GNUNET_PeerIdentity *key,
459             void *value)
460 {
461   struct AddressInfo *ai = value;
462
463   GNUNET_HELLO_address_free (ai->address);
464   GNUNET_free (ai);
465   return GNUNET_OK;
466 }
467
468
469 /**
470  * Shutdown ATS subsystem.
471  */
472 void
473 GST_ats_done ()
474 {
475   GNUNET_CONTAINER_multipeermap_iterate (p2a,
476                                          &destroy_ai,
477                                          NULL);
478   GNUNET_CONTAINER_multipeermap_destroy (p2a);
479   p2a = NULL;
480 }
481
482 /* end of gnunet-service-transport_ats.c */