get_address_latency also does not use session
[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  * Provide an update on the `p2a` map size to statistics.
89  * This function should be called whenever the `p2a` map
90  * is changed.
91  */
92 static void
93 publish_p2a_stat_update ()
94 {
95   GNUNET_STATISTICS_set (GST_stats,
96                          gettext_noop ("# Addresses given to ATS"),
97                          GNUNET_CONTAINER_multipeermap_size (p2a),
98                          GNUNET_NO);
99 }
100
101
102 /**
103  * Find matching address info.
104  *
105  * @param cls the `struct FindClosure`
106  * @param key which peer is this about
107  * @param value the `struct AddressInfo`
108  * @return #GNUNET_YES to continue to iterate, #GNUNET_NO if we found the value
109  */
110 static int
111 find_ai_cb (void *cls,
112             const struct GNUNET_PeerIdentity *key,
113             void *value)
114 {
115   struct FindClosure *fc = cls;
116   struct AddressInfo *ai = value;
117
118   if ( (0 ==
119         GNUNET_HELLO_address_cmp (fc->address,
120                                   ai->address) ) &&
121        (fc->session == ai->session) )
122   {
123     fc->ret = ai;
124     return GNUNET_NO;
125   }
126   GNUNET_assert ( (fc->session != ai->session) ||
127                   (NULL == ai->session) );
128   return GNUNET_YES;
129 }
130
131
132 /**
133  * Find the address information struct for the
134  * given address and session.
135  *
136  * @param address address to look for
137  * @param session session to match for inbound connections
138  * @return NULL if this combination is unknown
139  */
140 static struct AddressInfo *
141 find_ai (const struct GNUNET_HELLO_Address *address,
142          struct Session *session)
143 {
144   struct FindClosure fc;
145
146   fc.address = address;
147   fc.session = session;
148   fc.ret = NULL;
149   GNUNET_CONTAINER_multipeermap_get_multiple (p2a,
150                                               &address->peer,
151                                               &find_ai_cb,
152                                               &fc);
153   return fc.ret;
154 }
155
156
157 /**
158  * Find matching address info, ignoring sessions.
159  *
160  * @param cls the `struct FindClosure`
161  * @param key which peer is this about
162  * @param value the `struct AddressInfo`
163  * @return #GNUNET_YES to continue to iterate, #GNUNET_NO if we found the value
164  */
165 static int
166 find_ai_no_session_cb (void *cls,
167                        const struct GNUNET_PeerIdentity *key,
168                        void *value)
169 {
170   struct FindClosure *fc = cls;
171   struct AddressInfo *ai = value;
172
173   if (0 ==
174       GNUNET_HELLO_address_cmp (fc->address,
175                                 ai->address))
176   {
177     fc->ret = ai;
178     return GNUNET_NO;
179   }
180   return GNUNET_YES;
181 }
182
183
184 /**
185  * Find the address information struct for the
186  * given address (ignoring sessions)
187  *
188  * @param address address to look for
189  * @return NULL if this combination is unknown
190  */
191 static struct AddressInfo *
192 find_ai_no_session (const struct GNUNET_HELLO_Address *address)
193 {
194   struct FindClosure fc;
195
196   fc.address = address;
197   fc.session = NULL;
198   fc.ret = NULL;
199   GNUNET_CONTAINER_multipeermap_get_multiple (p2a,
200                                               &address->peer,
201                                               &find_ai_no_session_cb,
202                                               &fc);
203   return fc.ret;
204 }
205
206
207 /**
208  * Test if ATS knows about this address.
209  *
210  * @param address the address
211  * @param session the session
212  * @return #GNUNET_YES if address is known, #GNUNET_NO if not.
213  */
214 int
215 GST_ats_is_known (const struct GNUNET_HELLO_Address *address,
216                   struct Session *session)
217 {
218   return (NULL != find_ai (address, session)) ? GNUNET_YES : GNUNET_NO;
219 }
220
221
222 /**
223  * Notify ATS about the new address including the network this address is
224  * located in.
225  *
226  * @param address the address
227  * @param session the session
228  * @param ats ats information
229  * @param ats_count number of @a ats information
230  */
231 void
232 GST_ats_add_address (const struct GNUNET_HELLO_Address *address,
233                      struct Session *session,
234                      const struct GNUNET_ATS_Information *ats,
235                      uint32_t ats_count)
236 {
237   struct GNUNET_TRANSPORT_PluginFunctions *papi;
238   struct GNUNET_ATS_Information ats2[ats_count + 1];
239   struct GNUNET_ATS_AddressRecord *ar;
240   struct AddressInfo *ai;
241   uint32_t net;
242
243   /* valid new address, let ATS know! */
244   if (NULL == address->transport_name)
245   {
246     GNUNET_break(0);
247     return;
248   }
249   if (GNUNET_YES ==
250       GNUNET_HELLO_address_check_option (address,
251                                          GNUNET_HELLO_ADDRESS_INFO_INBOUND))
252   {
253     GNUNET_break (NULL != session);
254   }
255   ai = (NULL == session)
256     ? find_ai_no_session (address)
257     : find_ai (address, session);
258   if (NULL != ai)
259     return;
260   if (NULL != session)
261   {
262     /* in this case, we must not find an existing
263        session-less address, as the caller should
264        have checked for this case if it were possible. */
265     ai = find_ai (address, NULL);
266     if (NULL != ai)
267     {
268       GNUNET_assert (0);
269       return;
270     }
271   }
272   if (NULL == (papi = GST_plugins_find (address->transport_name)))
273   {
274     /* we don't have the plugin for this address */
275     GNUNET_assert (0);
276     return;
277   }
278   if (NULL != session)
279   {
280     net = papi->get_network (papi->cls, session);
281     if (GNUNET_ATS_NET_UNSPECIFIED == net)
282     {
283       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
284                   _ ("Could not obtain a valid network for `%s' %s (%s)\n"),
285                   GNUNET_i2s (&address->peer),
286                   GST_plugins_a2s (address),
287                   address->transport_name);
288       return;
289     }
290     ats2[0].type = htonl (GNUNET_ATS_NETWORK_TYPE);
291     ats2[0].value = htonl (net);
292     memcpy (&ats2[1],
293             ats,
294             sizeof(struct GNUNET_ATS_Information) * ats_count);
295   }
296   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
297               "Notifying ATS about peer `%s''s new address `%s' session %p in network %s\n",
298               GNUNET_i2s (&address->peer),
299               (0 == address->address_length)
300               ? "<inbound>"
301               : GST_plugins_a2s (address),
302               session,
303               GNUNET_ATS_print_network_type (net));
304   ar = GNUNET_ATS_address_add (GST_ats,
305                                address,
306                                session,
307                                (NULL != session) ? ats2 : ats,
308                                (NULL != session) ? ats_count + 1 : ats_count);
309   ai = GNUNET_new (struct AddressInfo);
310   ai->address = GNUNET_HELLO_address_copy (address);
311   ai->session = session;
312   ai->ar = ar;
313   (void) GNUNET_CONTAINER_multipeermap_put (p2a,
314                                             &ai->address->peer,
315                                             ai,
316                                             GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
317   publish_p2a_stat_update ();
318 }
319
320
321 /**
322  * Notify ATS about a new session now existing for the given
323  * address.
324  *
325  * @param address the address
326  * @param session the session
327  */
328 void
329 GST_ats_new_session (const struct GNUNET_HELLO_Address *address,
330                      struct Session *session)
331 {
332   struct AddressInfo *ai;
333
334   ai = find_ai (address, NULL);
335   if (NULL == ai)
336   {
337     /* We may already be aware of the session, even if some other part
338        of the code could not tell if it just created a new session or
339        just got one recycled from the plugin; hence, we may be called
340        with "new" session even for an "old" session; in that case,
341        check that this is the case, but just ignore it. */
342     GNUNET_assert (NULL != (find_ai (address, session)));
343     return;
344   }
345   GNUNET_break (NULL == ai->session);
346   ai->session = session;
347   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
348                    "transport-ats",
349                    "Telling ATS about new session %p for peer %s\n",
350                    session,
351                    GNUNET_i2s (&address->peer));
352   GNUNET_ATS_address_add_session (ai->ar,
353                                   session);
354 }
355
356
357 /**
358  * Notify ATS that the session (but not the address) of
359  * a given address is no longer relevant.
360  *
361  * @param address the address
362  * @param session the session
363  */
364 void
365 GST_ats_del_session (const struct GNUNET_HELLO_Address *address,
366                      struct Session *session)
367 {
368   struct AddressInfo *ai;
369
370   if (NULL == session)
371   {
372     GNUNET_break (0);
373     return;
374   }
375   ai = find_ai (address, session);
376   if (NULL == ai)
377   {
378     /* We sometimes create sessions just for sending a PING,
379        and if those are destroyed they were never known to
380        ATS which means we end up here (however, in this
381        case, the address must be an outbound address). */
382     GNUNET_break (GNUNET_YES !=
383                   GNUNET_HELLO_address_check_option (address,
384                                                      GNUNET_HELLO_ADDRESS_INFO_INBOUND));
385
386     return;
387   }
388   GNUNET_assert (session == ai->session);
389   ai->session = NULL;
390   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
391                    "transport-ats",
392                    "Telling ATS to destroy session %p from peer %s\n",
393                    session,
394                    GNUNET_i2s (&address->peer));
395   if (GNUNET_YES ==
396       GNUNET_ATS_address_del_session (ai->ar, session))
397   {
398     ai->ar = NULL;
399     GST_ats_expire_address (address);
400   }
401 }
402
403
404 /**
405  * Notify ATS about property changes to an address.
406  *
407  * @param address our information about the address
408  * @param session the session
409  * @param ats performance information
410  * @param ats_count number of elements in @a ats
411  */
412 void
413 GST_ats_update_metrics (const struct GNUNET_HELLO_Address *address,
414                         struct Session *session,
415                         const struct GNUNET_ATS_Information *ats,
416                         uint32_t ats_count)
417 {
418   struct GNUNET_ATS_Information *ats_new;
419   struct AddressInfo *ai;
420
421   ai = find_ai (address, session);
422   if (NULL == ai)
423   {
424     /* We sometimes create sessions just for sending a PING,
425        and if we get metrics for those, they were never known to
426        ATS which means we end up here (however, in this
427        case, the address must be an outbound address). */
428     GNUNET_assert (GNUNET_YES !=
429                    GNUNET_HELLO_address_check_option (address,
430                                                       GNUNET_HELLO_ADDRESS_INFO_INBOUND));
431
432     return;
433   }
434   /* Call to manipulation to manipulate ATS information */
435   GNUNET_assert (NULL != GST_ats);
436   if ((NULL == ats) || (0 == ats_count))
437     return;
438   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
439               "Updating metrics for peer `%s' address %s session %p\n",
440               GNUNET_i2s (&address->peer),
441               GST_plugins_a2s (address),
442               session);
443   ats_new = GST_manipulation_manipulate_metrics (address,
444                                                  session,
445                                                  ats,
446                                                  ats_count);
447   GNUNET_ATS_address_update (ai->ar,
448                              ats_new, ats_count);
449   GNUNET_free_non_null (ats_new);
450 }
451
452
453 /**
454  * Notify ATS about a new session now being in use (or not).
455  *
456  * @param address the address
457  * @param session the session
458  * @param in_use #GNUNET_YES or #GNUNET_NO
459  */
460 void
461 GST_ats_set_in_use (const struct GNUNET_HELLO_Address *address,
462                     struct Session *session,
463                     int in_use)
464 {
465   struct AddressInfo *ai;
466
467   ai = find_ai (address, session);
468   if (NULL == ai)
469   {
470     GNUNET_break (0);
471     return;
472   }
473   GNUNET_ATS_address_set_in_use (ai->ar, in_use);
474 }
475
476
477 /**
478  * Notify ATS that the address has expired and thus cannot
479  * be used any longer.  This function must only be called
480  * if the corresponding session is already gone.
481  *
482  * @param address the address
483  */
484 void
485 GST_ats_expire_address (const struct GNUNET_HELLO_Address *address)
486 {
487   struct AddressInfo *ai;
488
489   ai = find_ai (address, NULL);
490   if (NULL == ai)
491   {
492     GNUNET_assert (0);
493     return;
494   }
495   GNUNET_assert (GNUNET_YES ==
496                  GNUNET_CONTAINER_multipeermap_remove (p2a,
497                                                        &address->peer,
498                                                        ai));
499   publish_p2a_stat_update ();
500   GNUNET_break (NULL == ai->session);
501   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
502                    "transport-ats",
503                    "Telling ATS to destroy address from peer %s\n",
504                    GNUNET_i2s (&address->peer));
505   if (NULL != ai->ar)
506     GNUNET_ATS_address_destroy (ai->ar);
507   GNUNET_HELLO_address_free (ai->address);
508   GNUNET_free (ai);
509 }
510
511
512 /**
513  * Initialize ATS subsystem.
514  */
515 void
516 GST_ats_init ()
517 {
518   p2a = GNUNET_CONTAINER_multipeermap_create (4, GNUNET_YES);
519 }
520
521
522 /**
523  * Release memory used by the given address data.
524  *
525  * @param cls NULL
526  * @param key which peer is this about
527  * @param value the `struct AddressInfo`
528  * @return #GNUNET_OK (continue to iterate)
529  */
530 static int
531 destroy_ai (void *cls,
532             const struct GNUNET_PeerIdentity *key,
533             void *value)
534 {
535   struct AddressInfo *ai = value;
536
537   GNUNET_assert (GNUNET_YES ==
538                  GNUNET_CONTAINER_multipeermap_remove (p2a,
539                                                        key,
540                                                        ai));
541   GNUNET_HELLO_address_free (ai->address);
542   GNUNET_free (ai);
543   return GNUNET_OK;
544 }
545
546
547 /**
548  * Shutdown ATS subsystem.
549  */
550 void
551 GST_ats_done ()
552 {
553   GNUNET_CONTAINER_multipeermap_iterate (p2a,
554                                          &destroy_ai,
555                                          NULL);
556   publish_p2a_stat_update ();
557   GNUNET_CONTAINER_multipeermap_destroy (p2a);
558   p2a = NULL;
559 }
560
561 /* end of gnunet-service-transport_ats.c */