c780f9a78f99cd788c7b6b39cb845a6be92749f6
[oweals/gnunet.git] / src / transport / gnunet-service-transport_ats.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2015 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20 /**
21  * @file transport/gnunet-service-transport_ats.c
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  * Log convenience function.
34  */
35 #define LOG(kind,...) GNUNET_log_from(kind, "transport-ats", __VA_ARGS__)
36
37
38 /**
39  * Information we track for each address known to ATS.
40  */
41 struct AddressInfo
42 {
43
44   /**
45    * The address (with peer identity).  Must never change
46    * while this struct is in the #p2a map.
47    */
48   struct GNUNET_HELLO_Address *address;
49
50   /**
51    * Session (can be NULL)
52    */
53   struct GNUNET_ATS_Session *session;
54
55   /**
56    * Record with ATS API for the address.
57    */
58   struct GNUNET_ATS_AddressRecord *ar;
59
60   /**
61    * Performance properties of this address.
62    */
63   struct GNUNET_ATS_Properties properties;
64
65   /**
66    * Time until when this address is blocked and should thus not be
67    * made available to ATS (@e ar should be NULL until this time).
68    * Used when transport determines that for some reason it
69    * (temporarily) cannot use an address, even though it has been
70    * validated.
71    */
72   struct GNUNET_TIME_Absolute blocked;
73
74   /**
75    * If an address is blocked as part of an exponential back-off,
76    * we track the current size of the backoff here.
77    */
78   struct GNUNET_TIME_Relative back_off;
79
80   /**
81    * Task scheduled to unblock an ATS-blocked address at
82    * @e blocked time, or NULL if the address is not blocked
83    * (and thus @e ar is non-NULL).
84    */
85   struct GNUNET_SCHEDULER_Task *unblock_task;
86
87   /**
88    * Set to #GNUNET_YES if the address has expired but we could
89    * not yet remove it because we still have a valid session.
90    */
91   int expired;
92
93 };
94
95
96 /**
97  * Map from peer identities to one or more `struct AddressInfo` values
98  * for the peer.
99  */
100 static struct GNUNET_CONTAINER_MultiPeerMap *p2a;
101
102 /**
103  * Number of blocked addresses.
104  */
105 static unsigned int num_blocked;
106
107
108 /**
109  * Closure for #find_ai_cb() and #find_ai_no_session_cb().
110  */
111 struct FindClosure
112 {
113
114   /**
115    * Session to look for (only used if the address is inbound).
116    */
117   struct GNUNET_ATS_Session *session;
118
119   /**
120    * Address to look for.
121    */
122   const struct GNUNET_HELLO_Address *address;
123
124   /**
125    * Where to store the result.
126    */
127   struct AddressInfo *ret;
128
129 };
130
131
132 /**
133  * Provide an update on the `p2a` map size to statistics.
134  * This function should be called whenever the `p2a` map
135  * is changed.
136  */
137 static void
138 publish_p2a_stat_update ()
139 {
140   GNUNET_STATISTICS_set (GST_stats,
141                          gettext_noop ("# Addresses given to ATS"),
142                          GNUNET_CONTAINER_multipeermap_size (p2a) - num_blocked,
143                          GNUNET_NO);
144   GNUNET_STATISTICS_set (GST_stats,
145                          "# blocked addresses",
146                          num_blocked,
147                          GNUNET_NO);
148 }
149
150
151 /**
152  * Find matching address info.  Both the address and the session
153  * must match; note that expired addresses are still found (as
154  * the session kind-of keeps those alive).
155  *
156  * @param cls the `struct FindClosure`
157  * @param key which peer is this about
158  * @param value the `struct AddressInfo`
159  * @return #GNUNET_YES to continue to iterate, #GNUNET_NO if we found the value
160  */
161 static int
162 find_ai_cb (void *cls,
163             const struct GNUNET_PeerIdentity *key,
164             void *value)
165 {
166   struct FindClosure *fc = cls;
167   struct AddressInfo *ai = value;
168
169   if ( (0 ==
170         GNUNET_HELLO_address_cmp (fc->address,
171                                   ai->address) ) &&
172        (fc->session == ai->session) )
173   {
174     fc->ret = ai;
175     return GNUNET_NO;
176   }
177   return GNUNET_YES;
178 }
179
180
181 /**
182  * Find the address information struct for the
183  * given @a address and @a session.
184  *
185  * @param address address to look for
186  * @param session session to match for inbound connections
187  * @return NULL if this combination is unknown
188  */
189 static struct AddressInfo *
190 find_ai (const struct GNUNET_HELLO_Address *address,
191          struct GNUNET_ATS_Session *session)
192 {
193   struct FindClosure fc;
194
195   fc.address = address;
196   fc.session = session;
197   fc.ret = NULL;
198   GNUNET_CONTAINER_multipeermap_get_multiple (p2a,
199                                               &address->peer,
200                                               &find_ai_cb,
201                                               &fc);
202   return fc.ret;
203 }
204
205
206 /**
207  * Find matching address info, ignoring sessions and expired
208  * addresses.
209  *
210  * @param cls the `struct FindClosure`
211  * @param key which peer is this about
212  * @param value the `struct AddressInfo`
213  * @return #GNUNET_YES to continue to iterate, #GNUNET_NO if we found the value
214  */
215 static int
216 find_ai_no_session_cb (void *cls,
217                        const struct GNUNET_PeerIdentity *key,
218                        void *value)
219 {
220   struct FindClosure *fc = cls;
221   struct AddressInfo *ai = value;
222
223   if (ai->expired)
224     return GNUNET_YES; /* expired do not count here */
225   if (0 ==
226       GNUNET_HELLO_address_cmp (fc->address,
227                                 ai->address))
228   {
229     fc->ret = ai;
230     return GNUNET_NO;
231   }
232   return GNUNET_YES;
233 }
234
235
236 /**
237  * Find the address information struct for the
238  * given address (ignoring sessions)
239  *
240  * @param address address to look for
241  * @return NULL if this combination is unknown
242  */
243 static struct AddressInfo *
244 find_ai_no_session (const struct GNUNET_HELLO_Address *address)
245 {
246   struct FindClosure fc;
247
248   fc.address = address;
249   fc.session = NULL;
250   fc.ret = NULL;
251   GNUNET_CONTAINER_multipeermap_get_multiple (p2a,
252                                               &address->peer,
253                                               &find_ai_no_session_cb,
254                                               &fc);
255   return fc.ret;
256 }
257
258
259 /**
260  * Test if ATS knows about this @a address and @a session.
261  * Note that even if the address is expired, we return
262  * #GNUNET_YES if the respective session matches.
263  *
264  * @param address the address
265  * @param session the session
266  * @return #GNUNET_YES if @a address is known, #GNUNET_NO if not.
267  */
268 int
269 GST_ats_is_known (const struct GNUNET_HELLO_Address *address,
270                   struct GNUNET_ATS_Session *session)
271 {
272   return (NULL != find_ai (address, session)) ? GNUNET_YES : GNUNET_NO;
273 }
274
275
276 /**
277  * Test if ATS knows about this @a address.  Note that
278  * expired addresses do not count.
279  *
280  * @param address the address
281  * @return #GNUNET_YES if @a address is known, #GNUNET_NO if not.
282  */
283 int
284 GST_ats_is_known_no_session (const struct GNUNET_HELLO_Address *address)
285 {
286   return (NULL != find_ai_no_session (address))
287     ? GNUNET_YES
288     : GNUNET_NO;
289 }
290
291
292 /**
293  * The blocking time for an address has expired, allow ATS to
294  * suggest it again.
295  *
296  * @param cls the `struct AddressInfo` of the address to unblock
297  */
298 static void
299 unblock_address (void *cls)
300 {
301   struct AddressInfo *ai = cls;
302
303   ai->unblock_task = NULL;
304   LOG (GNUNET_ERROR_TYPE_DEBUG,
305        "Unblocking address %s of peer %s\n",
306        GST_plugins_a2s (ai->address),
307        GNUNET_i2s (&ai->address->peer));
308   ai->ar = GNUNET_ATS_address_add (GST_ats,
309                                    ai->address,
310                                    ai->session,
311                                    &ai->properties);
312   GNUNET_break (NULL != ai->ar);
313   num_blocked--;
314   publish_p2a_stat_update ();
315 }
316
317
318 /**
319  * Temporarily block a valid address for use by ATS for address
320  * suggestions.  This function should be called if an address was
321  * suggested by ATS but failed to perform (i.e. failure to establish a
322  * session or to exchange the PING/PONG).
323  *
324  * @param address the address to block
325  * @param session the session (can be NULL)
326  */
327 void
328 GST_ats_block_address (const struct GNUNET_HELLO_Address *address,
329                        struct GNUNET_ATS_Session *session)
330 {
331   struct AddressInfo *ai;
332
333   if (0 ==
334       memcmp (&GST_my_identity,
335               &address->peer,
336               sizeof (struct GNUNET_PeerIdentity)))
337     return; /* our own, ignore! */
338   ai = find_ai (address,
339                 session);
340   if (NULL == ai)
341   {
342     GNUNET_assert (0);
343     return;
344   }
345   if (NULL == ai->ar)
346   {
347     /* already blocked but this might be a blacklist check callback */
348     return;
349   }
350   ai->back_off = GNUNET_TIME_STD_BACKOFF (ai->back_off);
351   if (GNUNET_YES ==
352       GNUNET_HELLO_address_check_option (address,
353                                          GNUNET_HELLO_ADDRESS_INFO_INBOUND))
354     LOG (GNUNET_ERROR_TYPE_DEBUG,
355          "Removing address %s of peer %s from use (inbound died)\n",
356          GST_plugins_a2s (address),
357          GNUNET_i2s (&address->peer));
358   else
359     LOG (GNUNET_ERROR_TYPE_INFO,
360          "Blocking address %s of peer %s from use for %s\n",
361          GST_plugins_a2s (address),
362          GNUNET_i2s (&address->peer),
363          GNUNET_STRINGS_relative_time_to_string (ai->back_off,
364                                                  GNUNET_YES));
365   /* destroy session and address */
366   if ( (NULL == session) ||
367        (GNUNET_NO ==
368         GNUNET_ATS_address_del_session (ai->ar,
369                                         session)) )
370   {
371     GNUNET_ATS_address_destroy (ai->ar);
372   }
373   /* "ar" has been freed, regardless how the branch
374      above played out: it was either freed in
375      #GNUNET_ATS_address_del_session() because it was
376      incoming, or explicitly in
377      #GNUNET_ATS_address_del_session(). */
378   ai->ar = NULL;
379
380   /* determine when the address should come back to life */
381   ai->blocked = GNUNET_TIME_relative_to_absolute (ai->back_off);
382   ai->unblock_task = GNUNET_SCHEDULER_add_delayed (ai->back_off,
383                                                    &unblock_address,
384                                                    ai);
385   num_blocked++;
386   publish_p2a_stat_update ();
387 }
388
389
390 /**
391  * Reset address blocking time.  Resets the exponential
392  * back-off timer for this address to zero.  Called when
393  * an address was used to create a successful connection.
394  *
395  * @param address the address to reset the blocking timer
396  * @param session the session (can be NULL)
397  */
398 void
399 GST_ats_block_reset (const struct GNUNET_HELLO_Address *address,
400                      struct GNUNET_ATS_Session *session)
401 {
402   struct AddressInfo *ai;
403
404   if (0 ==
405       memcmp (&GST_my_identity,
406               &address->peer,
407               sizeof (struct GNUNET_PeerIdentity)))
408     return; /* our own, ignore! */
409   ai = find_ai (address, session);
410   if (NULL == ai)
411   {
412     GNUNET_break (0);
413     return;
414   }
415   /* address is in successful use, so it should not be blocked right now */
416   GNUNET_break (NULL == ai->unblock_task);
417   ai->back_off = GNUNET_TIME_UNIT_ZERO;
418 }
419
420
421 /**
422  * Notify ATS about a new inbound @a address. The @a address in
423  * combination with the @a session must be new, but this function will
424  * perform a santiy check.  If the @a address is indeed new, make it
425  * available to ATS.
426  *
427  * @param address the address
428  * @param session the session
429  * @param prop performance information
430  */
431 void
432 GST_ats_add_inbound_address (const struct GNUNET_HELLO_Address *address,
433                              struct GNUNET_ATS_Session *session,
434                              const struct GNUNET_ATS_Properties *prop)
435 {
436   struct GNUNET_ATS_AddressRecord *ar;
437   struct AddressInfo *ai;
438
439   if (0 ==
440       memcmp (&GST_my_identity,
441               &address->peer,
442               sizeof (struct GNUNET_PeerIdentity)))
443     return; /* our own, ignore! */
444
445   /* Sanity checks for a valid inbound address */
446   if (NULL == address->transport_name)
447   {
448     GNUNET_break(0);
449     return;
450   }
451   GNUNET_break (GNUNET_ATS_NET_UNSPECIFIED != prop->scope);
452   GNUNET_assert (GNUNET_YES ==
453                  GNUNET_HELLO_address_check_option (address,
454                                                     GNUNET_HELLO_ADDRESS_INFO_INBOUND));
455   GNUNET_assert (NULL != session);
456   ai = find_ai (address, session);
457   if (NULL != ai)
458   {
459     /* This should only be called for new sessions, and thus
460        we should not already have the address */
461     GNUNET_break (0);
462     return;
463   }
464   /* Is indeed new, let's tell ATS */
465   LOG (GNUNET_ERROR_TYPE_DEBUG,
466        "Notifying ATS about peer `%s''s new inbound address `%s' session %p in network %s\n",
467        GNUNET_i2s (&address->peer),
468        GST_plugins_a2s (address),
469        session,
470        GNUNET_ATS_print_network_type (prop->scope));
471   ar = GNUNET_ATS_address_add (GST_ats,
472                                address,
473                                session,
474                                prop);
475   GNUNET_assert (NULL != ar);
476   ai = GNUNET_new (struct AddressInfo);
477   ai->address = GNUNET_HELLO_address_copy (address);
478   ai->session = session;
479   ai->properties = *prop;
480   ai->ar = ar;
481   (void) GNUNET_CONTAINER_multipeermap_put (p2a,
482                                             &ai->address->peer,
483                                             ai,
484                                             GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
485   publish_p2a_stat_update ();
486 }
487
488
489 /**
490  * Notify ATS about the new address including the network this address is
491  * located in.  The address must NOT be inbound and must be new to ATS.
492  *
493  * @param address the address
494  * @param prop performance information
495  */
496 void
497 GST_ats_add_address (const struct GNUNET_HELLO_Address *address,
498                      const struct GNUNET_ATS_Properties *prop)
499 {
500   struct GNUNET_ATS_AddressRecord *ar;
501   struct AddressInfo *ai;
502
503   if (0 ==
504       memcmp (&GST_my_identity,
505               &address->peer,
506               sizeof (struct GNUNET_PeerIdentity)))
507     return; /* our own, ignore! */
508   /* validadte address */
509   if (NULL == address->transport_name)
510   {
511     GNUNET_break(0);
512     return;
513   }
514   GNUNET_assert (GNUNET_YES !=
515                  GNUNET_HELLO_address_check_option (address,
516                                                     GNUNET_HELLO_ADDRESS_INFO_INBOUND));
517   ai = find_ai_no_session (address);
518   GNUNET_assert (NULL == ai);
519   GNUNET_break (GNUNET_ATS_NET_UNSPECIFIED != prop->scope);
520
521   /* address seems sane, let's tell ATS */
522   LOG (GNUNET_ERROR_TYPE_INFO,
523        "Notifying ATS about peer %s's new address `%s'\n",
524        GNUNET_i2s (&address->peer),
525        GST_plugins_a2s (address));
526   ar = GNUNET_ATS_address_add (GST_ats,
527                                address,
528                                NULL,
529                                prop);
530   GNUNET_assert (NULL != ar);
531   ai = GNUNET_new (struct AddressInfo);
532   ai->address = GNUNET_HELLO_address_copy (address);
533   ai->ar = ar;
534   ai->properties = *prop;
535   (void) GNUNET_CONTAINER_multipeermap_put (p2a,
536                                             &ai->address->peer,
537                                             ai,
538                                             GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
539   publish_p2a_stat_update ();
540 }
541
542
543 /**
544  * Notify ATS about a new @a session now existing for the given
545  * @a address.  Essentially, an outbound @a address was used
546  * to establish a @a session.  It is safe to call this function
547  * repeatedly for the same @a address and @a session pair.
548  *
549  * @param address the address
550  * @param session the session
551  */
552 void
553 GST_ats_new_session (const struct GNUNET_HELLO_Address *address,
554                      struct GNUNET_ATS_Session *session)
555 {
556   struct AddressInfo *ai;
557
558   if (0 ==
559       memcmp (&GST_my_identity,
560               &address->peer,
561               sizeof (struct GNUNET_PeerIdentity)))
562     return; /* our own, ignore! */
563   ai = find_ai (address, NULL);
564   if (NULL == ai)
565   {
566     /* We may simply already be aware of the session, even if some
567        other part of the code could not tell if it just created a new
568        session or just got one recycled from the plugin; hence, we may
569        be called with "new" session even for an "old" session; in that
570        case, check that this is the case, but just ignore it. */
571     GNUNET_assert (NULL != (find_ai (address, session)));
572     return;
573   }
574   GNUNET_assert (NULL == ai->session);
575   ai->session = session;
576   LOG (GNUNET_ERROR_TYPE_DEBUG,
577        "Telling ATS about new session for peer %s\n",
578        GNUNET_i2s (&address->peer));
579   /* Note that the address might currently be blocked; we only
580      tell ATS about the session if the address is currently not
581      blocked; otherwise, ATS will be told about the session on
582      unblock. */
583   if (NULL != ai->ar)
584     GNUNET_ATS_address_add_session (ai->ar,
585                                     session);
586   else
587     GNUNET_assert (NULL != ai->unblock_task);
588 }
589
590
591 /**
592  * Release memory used by the given address data.
593  *
594  * @param ai the `struct AddressInfo`
595  */
596 static void
597 destroy_ai (struct AddressInfo *ai)
598 {
599   GNUNET_assert (NULL == ai->session);
600   if (NULL != ai->unblock_task)
601   {
602     GNUNET_SCHEDULER_cancel (ai->unblock_task);
603     ai->unblock_task = NULL;
604     num_blocked--;
605   }
606   GNUNET_assert (GNUNET_YES ==
607                  GNUNET_CONTAINER_multipeermap_remove (p2a,
608                                                        &ai->address->peer,
609                                                        ai));
610   LOG (GNUNET_ERROR_TYPE_DEBUG,
611        "Telling ATS to destroy address from peer %s\n",
612        GNUNET_i2s (&ai->address->peer));
613   if (NULL != ai->ar)
614   {
615     GNUNET_ATS_address_destroy (ai->ar);
616     ai->ar = NULL;
617   }
618   publish_p2a_stat_update ();
619   GNUNET_HELLO_address_free (ai->address);
620   GNUNET_free (ai);
621 }
622
623
624 /**
625  * Notify ATS that the @a session (but not the @a address) of
626  * a given @a address is no longer relevant. (The @a session
627  * went down.)  This function may be called even if for the
628  * respective outbound address #GST_ats_new_session() was
629  * never called and thus the pair is unknown to ATS. In this
630  * case, the call is simply ignored.
631  *
632  * @param address the address
633  * @param session the session
634  */
635 void
636 GST_ats_del_session (const struct GNUNET_HELLO_Address *address,
637                      struct GNUNET_ATS_Session *session)
638 {
639   struct AddressInfo *ai;
640
641   if (0 ==
642       memcmp (&GST_my_identity,
643               &address->peer,
644               sizeof (struct GNUNET_PeerIdentity)))
645     return; /* our own, ignore! */
646   if (NULL == session)
647   {
648     GNUNET_break (0);
649     return;
650   }
651   ai = find_ai (address,
652                 session);
653   if (NULL == ai)
654   {
655     /* We sometimes create sessions just for sending a PING,
656        and if those are destroyed they were never known to
657        ATS which means we end up here (however, in this
658        case, the address must be an outbound address). */
659     GNUNET_break (GNUNET_YES !=
660                   GNUNET_HELLO_address_check_option (address,
661                                                      GNUNET_HELLO_ADDRESS_INFO_INBOUND));
662     return;
663   }
664   GNUNET_assert (session == ai->session);
665   ai->session = NULL;
666   LOG (GNUNET_ERROR_TYPE_DEBUG,
667        "Telling ATS to destroy session %p from peer %s\n",
668        session,
669        GNUNET_i2s (&address->peer));
670   if (GNUNET_YES == ai->expired)
671   {
672     /* last reason to keep this 'ai' around is now gone, the
673        session is dead as well, clean up */
674     if (NULL != ai->ar)
675     {
676       /* Address expired but not blocked, and thus 'ar' was still
677          live because of the session; deleting just the session
678          will do for an inbound session, but for an outbound we
679          then also need to destroy the address with ATS. */
680       if (GNUNET_NO ==
681           GNUNET_ATS_address_del_session (ai->ar,
682                                           session))
683       {
684         GNUNET_ATS_address_destroy (ai->ar);
685       }
686       /* "ar" has been freed, regardless how the branch
687          above played out: it was either freed in
688          #GNUNET_ATS_address_del_session() because it was
689          incoming, or explicitly in
690          #GNUNET_ATS_address_del_session(). */
691       ai->ar = NULL;
692     }
693     destroy_ai (ai);
694     return;
695   }
696
697   if (NULL == ai->ar)
698   {
699     /* If ATS doesn't know about the address/session, this means
700        this address was blocked. */
701     if (GNUNET_YES ==
702         GNUNET_HELLO_address_check_option (address,
703                                            GNUNET_HELLO_ADDRESS_INFO_INBOUND))
704     {
705       /* This was a blocked inbound session, which now lost the
706          session.  But inbound addresses are by themselves useless,
707          so we must forget about the address as well. */
708       destroy_ai (ai);
709       return;
710     }
711     /* Otherwise, we are done as we have set `ai->session` to NULL
712        already and ATS will simply not be told about the session when
713        the connection is unblocked and the outbound address becomes
714        available again. . */
715     return;
716   }
717
718   /* This is the "simple" case where ATS knows about the session and
719      the address is neither blocked nor expired.  Delete the session,
720      and if it was inbound, free the address as well. */
721   if (GNUNET_YES ==
722       GNUNET_ATS_address_del_session (ai->ar,
723                                       session))
724   {
725     /* This was an inbound address, the session is now gone, so we
726        need to also forget about the address itself. */
727     ai->ar = NULL;
728     destroy_ai (ai);
729   }
730 }
731
732
733 /**
734  * Notify ATS about DV @a distance change to an @a address.
735  * Does nothing if the @a address is not known to us.
736  *
737  * @param address the address
738  * @param distance new distance value
739  */
740 void
741 GST_ats_update_distance (const struct GNUNET_HELLO_Address *address,
742                          uint32_t distance)
743 {
744   struct AddressInfo *ai;
745
746   ai = find_ai_no_session (address);
747   if (NULL == ai)
748   {
749     /* We do not know about this address, do nothing. */
750     return;
751   }
752   LOG (GNUNET_ERROR_TYPE_DEBUG,
753        "Updated distance for peer `%s' to %u\n",
754        GNUNET_i2s (&address->peer),
755        distance);
756   ai->properties.distance = distance;
757   /* Give manipulation its chance to change metrics */
758   GST_manipulation_manipulate_metrics (address,
759                                        ai->session,
760                                        &ai->properties);
761   /* Address may be blocked, only give ATS if address is
762      currently active. */
763   if (NULL != ai->ar)
764     GNUNET_ATS_address_update (ai->ar,
765                                &ai->properties);
766 }
767
768
769 /**
770  * Notify ATS about @a delay changes to properties of an @a address.
771  * Does nothing if the @a address is not known to us.
772  *
773  * @param address the address
774  * @param delay new delay value
775  */
776 void
777 GST_ats_update_delay (const struct GNUNET_HELLO_Address *address,
778                       struct GNUNET_TIME_Relative delay)
779 {
780   struct AddressInfo *ai;
781
782   ai = find_ai_no_session (address);
783   if (NULL == ai)
784   {
785     /* We do not know about this address, do nothing. */
786     return;
787   }
788   LOG (GNUNET_ERROR_TYPE_DEBUG,
789        "Updated latency for peer `%s' to %s\n",
790        GNUNET_i2s (&address->peer),
791        GNUNET_STRINGS_relative_time_to_string (delay,
792                                                GNUNET_YES));
793   ai->properties.delay = delay;
794   /* Give manipulation its chance to change metrics */
795   GST_manipulation_manipulate_metrics (address,
796                                        ai->session,
797                                        &ai->properties);
798   /* Address may be blocked, only give ATS if address is
799      currently active. */
800   if (NULL != ai->ar)
801     GNUNET_ATS_address_update (ai->ar,
802                                &ai->properties);
803 }
804
805
806 /**
807  * Notify ATS about utilization changes to an @a address.
808  * Does nothing if the @a address is not known to us.
809  *
810  * @param address our information about the address
811  * @param bps_in new utilization inbound
812  * @param bps_out new utilization outbound
813  */
814 void
815 GST_ats_update_utilization (const struct GNUNET_HELLO_Address *address,
816                             uint32_t bps_in,
817                             uint32_t bps_out)
818 {
819   struct AddressInfo *ai;
820
821   ai = find_ai_no_session (address);
822   if (NULL == ai)
823   {
824     /* We do not know about this address, do nothing. */
825     return;
826   }
827   LOG (GNUNET_ERROR_TYPE_DEBUG,
828        "Updating utilization for peer `%s' address %s: %u/%u\n",
829        GNUNET_i2s (&address->peer),
830        GST_plugins_a2s (address),
831        (unsigned int) bps_in,
832        (unsigned int) bps_out);
833   ai->properties.utilization_in = bps_in;
834   ai->properties.utilization_out = bps_out;
835   /* Give manipulation its chance to change metrics */
836   GST_manipulation_manipulate_metrics (address,
837                                        ai->session,
838                                        &ai->properties);
839   /* Address may be blocked, only give ATS if address is
840      currently active. */
841   if (NULL != ai->ar)
842     GNUNET_ATS_address_update (ai->ar,
843                                &ai->properties);
844 }
845
846
847 /**
848  * Notify ATS that the address has expired and thus cannot
849  * be used any longer.  This function must only be called
850  * if the corresponding session is already gone.
851  *
852  * @param address the address
853  */
854 void
855 GST_ats_expire_address (const struct GNUNET_HELLO_Address *address)
856 {
857   struct AddressInfo *ai;
858
859   if (0 ==
860       memcmp (&GST_my_identity,
861               &address->peer,
862               sizeof (struct GNUNET_PeerIdentity)))
863     return; /* our own, ignore! */
864   LOG (GNUNET_ERROR_TYPE_DEBUG,
865        "Address %s of peer %s expired\n",
866        GST_plugins_a2s (address),
867        GNUNET_i2s (&address->peer));
868   ai = find_ai_no_session (address);
869   if (NULL == ai)
870   {
871     GNUNET_assert (0);
872     return;
873   }
874   if (NULL != ai->session)
875   {
876     /* Got an active session, just remember the expiration
877        and act upon it when the session goes down. */
878     ai->expired = GNUNET_YES;
879     return;
880   }
881   /* Address expired, no session, free resources */
882   destroy_ai (ai);
883 }
884
885
886 /**
887  * Initialize ATS subsystem.
888  */
889 void
890 GST_ats_init ()
891 {
892   p2a = GNUNET_CONTAINER_multipeermap_create (4, GNUNET_YES);
893 }
894
895
896 /**
897  * Release memory used by the given address data.
898  *
899  * @param cls NULL
900  * @param key which peer is this about
901  * @param value the `struct AddressInfo`
902  * @return #GNUNET_OK (continue to iterate)
903  */
904 static int
905 destroy_ai_cb (void *cls,
906                const struct GNUNET_PeerIdentity *key,
907                void *value)
908 {
909   struct AddressInfo *ai = value;
910
911   destroy_ai (ai);
912   return GNUNET_OK;
913 }
914
915
916 /**
917  * Shutdown ATS subsystem.
918  */
919 void
920 GST_ats_done ()
921 {
922   GNUNET_CONTAINER_multipeermap_iterate (p2a,
923                                          &destroy_ai_cb,
924                                          NULL);
925   publish_p2a_stat_update ();
926   GNUNET_CONTAINER_multipeermap_destroy (p2a);
927   p2a = NULL;
928 }
929
930 /* end of gnunet-service-transport_ats.c */