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