-fix NPE
[oweals/gnunet.git] / src / transport / gnunet-service-transport_ats.c
1 /*
2      This file is part of GNUnet.
3      Copyright (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., 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  * @param tc unused
298  */
299 static void
300 unblock_address (void *cls,
301                  const struct GNUNET_SCHEDULER_TaskContext *tc)
302 {
303   struct AddressInfo *ai = cls;
304
305   ai->unblock_task = NULL;
306   LOG (GNUNET_ERROR_TYPE_DEBUG,
307        "Unblocking address %s of peer %s\n",
308        GST_plugins_a2s (ai->address),
309        GNUNET_i2s (&ai->address->peer));
310   ai->ar = GNUNET_ATS_address_add (GST_ats,
311                                    ai->address,
312                                    ai->session,
313                                    &ai->properties);
314   GNUNET_break (NULL != ai->ar);
315   num_blocked--;
316   publish_p2a_stat_update ();
317 }
318
319
320 /**
321  * Temporarily block a valid address for use by ATS for address
322  * suggestions.  This function should be called if an address was
323  * suggested by ATS but failed to perform (i.e. failure to establish a
324  * session or to exchange the PING/PONG).
325  *
326  * @param address the address to block
327  * @param session the session (can be NULL)
328  */
329 void
330 GST_ats_block_address (const struct GNUNET_HELLO_Address *address,
331                        struct GNUNET_ATS_Session *session)
332 {
333   struct AddressInfo *ai;
334
335   ai = find_ai (address,
336                 session);
337   if (NULL == ai)
338   {
339     GNUNET_assert (0);
340     return;
341   }
342   if (NULL == ai->ar)
343   {
344     /* already blocked, how did it get used!? */
345     GNUNET_break (0);
346     return;
347   }
348   ai->back_off = GNUNET_TIME_STD_BACKOFF (ai->back_off);
349   if (GNUNET_YES ==
350       GNUNET_HELLO_address_check_option (address,
351                                          GNUNET_HELLO_ADDRESS_INFO_INBOUND))
352     LOG (GNUNET_ERROR_TYPE_DEBUG,
353          "Removing address %s of peer %s from use (inbound died)\n",
354          GST_plugins_a2s (address),
355          GNUNET_i2s (&address->peer));
356   else
357     LOG (GNUNET_ERROR_TYPE_INFO,
358          "Blocking address %s of peer %s from use for %s\n",
359          GST_plugins_a2s (address),
360          GNUNET_i2s (&address->peer),
361          GNUNET_STRINGS_relative_time_to_string (ai->back_off,
362                                                  GNUNET_YES));
363   /* destroy session and address */
364   if ( (NULL == session) ||
365        (GNUNET_NO ==
366         GNUNET_ATS_address_del_session (ai->ar,
367                                         session)) )
368   {
369     GNUNET_ATS_address_destroy (ai->ar);
370   }
371   /* "ar" has been freed, regardless how the branch
372      above played out: it was either freed in
373      #GNUNET_ATS_address_del_session() because it was
374      incoming, or explicitly in
375      #GNUNET_ATS_address_del_session(). */
376   ai->ar = NULL;
377
378   /* determine when the address should come back to life */
379   ai->blocked = GNUNET_TIME_relative_to_absolute (ai->back_off);
380   ai->unblock_task = GNUNET_SCHEDULER_add_delayed (ai->back_off,
381                                                    &unblock_address,
382                                                    ai);
383   num_blocked++;
384   publish_p2a_stat_update ();
385 }
386
387
388 /**
389  * Reset address blocking time.  Resets the exponential
390  * back-off timer for this address to zero.  Called when
391  * an address was used to create a successful connection.
392  *
393  * @param address the address to reset the blocking timer
394  * @param session the session (can be NULL)
395  */
396 void
397 GST_ats_block_reset (const struct GNUNET_HELLO_Address *address,
398                      struct GNUNET_ATS_Session *session)
399 {
400   struct AddressInfo *ai;
401
402   ai = find_ai (address, session);
403   if (NULL == ai)
404   {
405     GNUNET_break (0);
406     return;
407   }
408   /* address is in successful use, so it should not be blocked right now */
409   GNUNET_break (NULL == ai->unblock_task);
410   ai->back_off = GNUNET_TIME_UNIT_ZERO;
411 }
412
413
414 /**
415  * Notify ATS about a new inbound @a address. The @a address in
416  * combination with the @a session must be new, but this function will
417  * perform a santiy check.  If the @a address is indeed new, make it
418  * available to ATS.
419  *
420  * @param address the address
421  * @param session the session
422  * @param prop performance information
423  */
424 void
425 GST_ats_add_inbound_address (const struct GNUNET_HELLO_Address *address,
426                              struct GNUNET_ATS_Session *session,
427                              const struct GNUNET_ATS_Properties *prop)
428 {
429   struct GNUNET_ATS_AddressRecord *ar;
430   struct AddressInfo *ai;
431
432   /* Sanity checks for a valid inbound address */
433   if (NULL == address->transport_name)
434   {
435     GNUNET_break(0);
436     return;
437   }
438   GNUNET_break (GNUNET_ATS_NET_UNSPECIFIED != prop->scope);
439   GNUNET_assert (GNUNET_YES ==
440                  GNUNET_HELLO_address_check_option (address,
441                                                     GNUNET_HELLO_ADDRESS_INFO_INBOUND));
442   GNUNET_assert (NULL != session);
443   ai = find_ai (address, session);
444   if (NULL != ai)
445   {
446     /* This should only be called for new sessions, and thus
447        we should not already have the address */
448     GNUNET_break (0);
449     return;
450   }
451   /* Is indeed new, let's tell ATS */
452   LOG (GNUNET_ERROR_TYPE_DEBUG,
453        "Notifying ATS about peer `%s''s new inbound address `%s' session %p in network %s\n",
454        GNUNET_i2s (&address->peer),
455        GST_plugins_a2s (address),
456        session,
457        GNUNET_ATS_print_network_type (prop->scope));
458   ar = GNUNET_ATS_address_add (GST_ats,
459                                address,
460                                session,
461                                prop);
462   GNUNET_assert (NULL != ar);
463   ai = GNUNET_new (struct AddressInfo);
464   ai->address = GNUNET_HELLO_address_copy (address);
465   ai->session = session;
466   ai->properties = *prop;
467   ai->ar = ar;
468   (void) GNUNET_CONTAINER_multipeermap_put (p2a,
469                                             &ai->address->peer,
470                                             ai,
471                                             GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
472   publish_p2a_stat_update ();
473 }
474
475
476 /**
477  * Notify ATS about the new address including the network this address is
478  * located in.  The address must NOT be inbound and must be new to ATS.
479  *
480  * @param address the address
481  * @param prop performance information
482  */
483 void
484 GST_ats_add_address (const struct GNUNET_HELLO_Address *address,
485                      const struct GNUNET_ATS_Properties *prop)
486 {
487   struct GNUNET_ATS_AddressRecord *ar;
488   struct AddressInfo *ai;
489
490   /* validadte address */
491   if (NULL == address->transport_name)
492   {
493     GNUNET_break(0);
494     return;
495   }
496   GNUNET_assert (GNUNET_YES !=
497                  GNUNET_HELLO_address_check_option (address,
498                                                     GNUNET_HELLO_ADDRESS_INFO_INBOUND));
499   ai = find_ai_no_session (address);
500   GNUNET_assert (NULL == ai);
501   GNUNET_break (GNUNET_ATS_NET_UNSPECIFIED != prop->scope);
502
503   /* address seems sane, let's tell ATS */
504   LOG (GNUNET_ERROR_TYPE_INFO,
505        "Notifying ATS about peer %s's new address `%s'\n",
506        GNUNET_i2s (&address->peer),
507        GST_plugins_a2s (address));
508   ar = GNUNET_ATS_address_add (GST_ats,
509                                address,
510                                NULL,
511                                prop);
512   GNUNET_assert (NULL != ar);
513   ai = GNUNET_new (struct AddressInfo);
514   ai->address = GNUNET_HELLO_address_copy (address);
515   ai->ar = ar;
516   ai->properties = *prop;
517   (void) GNUNET_CONTAINER_multipeermap_put (p2a,
518                                             &ai->address->peer,
519                                             ai,
520                                             GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
521   publish_p2a_stat_update ();
522 }
523
524
525 /**
526  * Notify ATS about a new @a session now existing for the given
527  * @a address.  Essentially, an outbound @a address was used
528  * to establish a @a session.  It is safe to call this function
529  * repeatedly for the same @a address and @a session pair.
530  *
531  * @param address the address
532  * @param session the session
533  */
534 void
535 GST_ats_new_session (const struct GNUNET_HELLO_Address *address,
536                      struct GNUNET_ATS_Session *session)
537 {
538   struct AddressInfo *ai;
539
540   ai = find_ai (address, NULL);
541   if (NULL == ai)
542   {
543     /* We may simply already be aware of the session, even if some
544        other part of the code could not tell if it just created a new
545        session or just got one recycled from the plugin; hence, we may
546        be called with "new" session even for an "old" session; in that
547        case, check that this is the case, but just ignore it. */
548     GNUNET_assert (NULL != (find_ai (address, session)));
549     return;
550   }
551   GNUNET_assert (NULL == ai->session);
552   ai->session = session;
553   LOG (GNUNET_ERROR_TYPE_DEBUG,
554        "Telling ATS about new session for peer %s\n",
555        GNUNET_i2s (&address->peer));
556   /* Note that the address might currently be blocked; we only
557      tell ATS about the session if the address is currently not
558      blocked; otherwise, ATS will be told about the session on
559      unblock. */
560   if (NULL != ai->ar)
561     GNUNET_ATS_address_add_session (ai->ar,
562                                     session);
563   else
564     GNUNET_assert (NULL != ai->unblock_task);
565 }
566
567
568 /**
569  * Release memory used by the given address data.
570  *
571  * @param ai the `struct AddressInfo`
572  */
573 static void
574 destroy_ai (struct AddressInfo *ai)
575 {
576   GNUNET_assert (NULL == ai->session);
577   if (NULL != ai->unblock_task)
578   {
579     GNUNET_SCHEDULER_cancel (ai->unblock_task);
580     ai->unblock_task = NULL;
581     num_blocked--;
582   }
583   GNUNET_assert (GNUNET_YES ==
584                  GNUNET_CONTAINER_multipeermap_remove (p2a,
585                                                        &ai->address->peer,
586                                                        ai));
587   LOG (GNUNET_ERROR_TYPE_DEBUG,
588        "Telling ATS to destroy address from peer %s\n",
589        GNUNET_i2s (&ai->address->peer));
590   if (NULL != ai->ar)
591   {
592     GNUNET_ATS_address_destroy (ai->ar);
593     ai->ar = NULL;
594   }
595   publish_p2a_stat_update ();
596   GNUNET_HELLO_address_free (ai->address);
597   GNUNET_free (ai);
598 }
599
600
601 /**
602  * Notify ATS that the @a session (but not the @a address) of
603  * a given @a address is no longer relevant. (The @a session
604  * went down.)  This function may be called even if for the
605  * respective outbound address #GST_ats_new_session() was
606  * never called and thus the pair is unknown to ATS. In this
607  * case, the call is simply ignored.
608  *
609  * @param address the address
610  * @param session the session
611  */
612 void
613 GST_ats_del_session (const struct GNUNET_HELLO_Address *address,
614                      struct GNUNET_ATS_Session *session)
615 {
616   struct AddressInfo *ai;
617
618   if (NULL == session)
619   {
620     GNUNET_break (0);
621     return;
622   }
623   ai = find_ai (address,
624                 session);
625   if (NULL == ai)
626   {
627     /* We sometimes create sessions just for sending a PING,
628        and if those are destroyed they were never known to
629        ATS which means we end up here (however, in this
630        case, the address must be an outbound address). */
631     GNUNET_break (GNUNET_YES !=
632                   GNUNET_HELLO_address_check_option (address,
633                                                      GNUNET_HELLO_ADDRESS_INFO_INBOUND));
634     return;
635   }
636   GNUNET_assert (session == ai->session);
637   ai->session = NULL;
638   LOG (GNUNET_ERROR_TYPE_DEBUG,
639        "Telling ATS to destroy session %p from peer %s\n",
640        session,
641        GNUNET_i2s (&address->peer));
642   if (GNUNET_YES == ai->expired)
643   {
644     /* last reason to keep this 'ai' around is now gone, the
645        session is dead as well, clean up */
646     if (NULL != ai->ar)
647     {
648       /* Address expired but not blocked, and thus 'ar' was still
649          live because of the session; deleting just the session
650          will do for an inbound session, but for an outbound we
651          then also need to destroy the address with ATS. */
652       if (GNUNET_NO ==
653           GNUNET_ATS_address_del_session (ai->ar,
654                                           session))
655       {
656         GNUNET_ATS_address_destroy (ai->ar);
657       }
658       /* "ar" has been freed, regardless how the branch
659          above played out: it was either freed in
660          #GNUNET_ATS_address_del_session() because it was
661          incoming, or explicitly in
662          #GNUNET_ATS_address_del_session(). */
663       ai->ar = NULL;
664     }
665     destroy_ai (ai);
666     return;
667   }
668
669   if (NULL == ai->ar)
670   {
671     /* If ATS doesn't know about the address/session, this means
672        this address was blocked. */
673     if (GNUNET_YES ==
674         GNUNET_HELLO_address_check_option (address,
675                                            GNUNET_HELLO_ADDRESS_INFO_INBOUND))
676     {
677       /* This was a blocked inbound session, which now lost the
678          session.  But inbound addresses are by themselves useless,
679          so we must forget about the address as well. */
680       destroy_ai (ai);
681       return;
682     }
683     /* Otherwise, we are done as we have set `ai->session` to NULL
684        already and ATS will simply not be told about the session when
685        the connection is unblocked and the outbound address becomes
686        available again. . */
687     return;
688   }
689
690   /* This is the "simple" case where ATS knows about the session and
691      the address is neither blocked nor expired.  Delete the session,
692      and if it was inbound, free the address as well. */
693   if (GNUNET_YES ==
694       GNUNET_ATS_address_del_session (ai->ar,
695                                       session))
696   {
697     /* This was an inbound address, the session is now gone, so we
698        need to also forget about the address itself. */
699     ai->ar = NULL;
700     destroy_ai (ai);
701   }
702 }
703
704
705 /**
706  * Notify ATS about DV @a distance change to an @a address.
707  * Does nothing if the @a address is not known to us.
708  *
709  * @param address the address
710  * @param distance new distance value
711  */
712 void
713 GST_ats_update_distance (const struct GNUNET_HELLO_Address *address,
714                          uint32_t distance)
715 {
716   struct AddressInfo *ai;
717
718   ai = find_ai_no_session (address);
719   if (NULL == ai)
720   {
721     /* We do not know about this address, do nothing. */
722     return;
723   }
724   LOG (GNUNET_ERROR_TYPE_DEBUG,
725        "Updated distance for peer `%s' to %u\n",
726        GNUNET_i2s (&address->peer),
727        distance);
728   ai->properties.distance = distance;
729   /* Give manipulation its chance to change metrics */
730   GST_manipulation_manipulate_metrics (address,
731                                        ai->session,
732                                        &ai->properties);
733   /* Address may be blocked, only give ATS if address is
734      currently active. */
735   if (NULL != ai->ar)
736     GNUNET_ATS_address_update (ai->ar,
737                                &ai->properties);
738 }
739
740
741 /**
742  * Notify ATS about @a delay changes to properties of an @a address.
743  * Does nothing if the @a address is not known to us.
744  *
745  * @param address the address
746  * @param delay new delay value
747  */
748 void
749 GST_ats_update_delay (const struct GNUNET_HELLO_Address *address,
750                       struct GNUNET_TIME_Relative delay)
751 {
752   struct AddressInfo *ai;
753
754   ai = find_ai_no_session (address);
755   if (NULL == ai)
756   {
757     /* We do not know about this address, do nothing. */
758     return;
759   }
760   LOG (GNUNET_ERROR_TYPE_DEBUG,
761        "Updated latency for peer `%s' to %s\n",
762        GNUNET_i2s (&address->peer),
763        GNUNET_STRINGS_relative_time_to_string (delay,
764                                                GNUNET_YES));
765   ai->properties.delay = delay;
766   /* Give manipulation its chance to change metrics */
767   GST_manipulation_manipulate_metrics (address,
768                                        ai->session,
769                                        &ai->properties);
770   /* Address may be blocked, only give ATS if address is
771      currently active. */
772   if (NULL != ai->ar)
773     GNUNET_ATS_address_update (ai->ar,
774                                &ai->properties);
775 }
776
777
778 /**
779  * Notify ATS about utilization changes to an @a address.
780  * Does nothing if the @a address is not known to us.
781  *
782  * @param address our information about the address
783  * @param bps_in new utilization inbound
784  * @param bps_out new utilization outbound
785  */
786 void
787 GST_ats_update_utilization (const struct GNUNET_HELLO_Address *address,
788                             uint32_t bps_in,
789                             uint32_t bps_out)
790 {
791   struct AddressInfo *ai;
792
793   ai = find_ai_no_session (address);
794   if (NULL == ai)
795   {
796     /* We do not know about this address, do nothing. */
797     return;
798   }
799   LOG (GNUNET_ERROR_TYPE_DEBUG,
800        "Updating utilization for peer `%s' address %s: %u/%u\n",
801        GNUNET_i2s (&address->peer),
802        GST_plugins_a2s (address),
803        (unsigned int) bps_in,
804        (unsigned int) bps_out);
805   ai->properties.utilization_in = bps_in;
806   ai->properties.utilization_out = bps_out;
807   /* Give manipulation its chance to change metrics */
808   GST_manipulation_manipulate_metrics (address,
809                                        ai->session,
810                                        &ai->properties);
811   /* Address may be blocked, only give ATS if address is
812      currently active. */
813   if (NULL != ai->ar)
814     GNUNET_ATS_address_update (ai->ar,
815                                &ai->properties);
816 }
817
818
819 /**
820  * Notify ATS that the address has expired and thus cannot
821  * be used any longer.  This function must only be called
822  * if the corresponding session is already gone.
823  *
824  * @param address the address
825  */
826 void
827 GST_ats_expire_address (const struct GNUNET_HELLO_Address *address)
828 {
829   struct AddressInfo *ai;
830
831   LOG (GNUNET_ERROR_TYPE_DEBUG,
832        "Address %s of peer %s expired\n",
833        GST_plugins_a2s (address),
834        GNUNET_i2s (&address->peer));
835   ai = find_ai_no_session (address);
836   if (NULL == ai)
837   {
838     GNUNET_assert (0);
839     return;
840   }
841   if (NULL != ai->session)
842   {
843     /* Got an active session, just remember the expiration
844        and act upon it when the session goes down. */
845     ai->expired = GNUNET_YES;
846     return;
847   }
848   /* Address expired, no session, free resources */
849   destroy_ai (ai);
850 }
851
852
853 /**
854  * Initialize ATS subsystem.
855  */
856 void
857 GST_ats_init ()
858 {
859   p2a = GNUNET_CONTAINER_multipeermap_create (4, GNUNET_YES);
860 }
861
862
863 /**
864  * Release memory used by the given address data.
865  *
866  * @param cls NULL
867  * @param key which peer is this about
868  * @param value the `struct AddressInfo`
869  * @return #GNUNET_OK (continue to iterate)
870  */
871 static int
872 destroy_ai_cb (void *cls,
873                const struct GNUNET_PeerIdentity *key,
874                void *value)
875 {
876   struct AddressInfo *ai = value;
877
878   destroy_ai (ai);
879   return GNUNET_OK;
880 }
881
882
883 /**
884  * Shutdown ATS subsystem.
885  */
886 void
887 GST_ats_done ()
888 {
889   GNUNET_CONTAINER_multipeermap_iterate (p2a,
890                                          &destroy_ai_cb,
891                                          NULL);
892   publish_p2a_stat_update ();
893   GNUNET_CONTAINER_multipeermap_destroy (p2a);
894   p2a = NULL;
895 }
896
897 /* end of gnunet-service-transport_ats.c */