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