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