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