rename configs to match
[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    * The address (with peer identity).  Must never change
45    * while this struct is in the #p2a map.
46    */
47   struct GNUNET_HELLO_Address *address;
48
49   /**
50    * Session (can be NULL)
51    */
52   struct GNUNET_ATS_Session *session;
53
54   /**
55    * Record with ATS API for the address.
56    */
57   struct GNUNET_ATS_AddressRecord *ar;
58
59   /**
60    * Performance properties of this address.
61    */
62   struct GNUNET_ATS_Properties properties;
63
64   /**
65    * Time until when this address is blocked and should thus not be
66    * made available to ATS (@e ar should be NULL until this time).
67    * Used when transport determines that for some reason it
68    * (temporarily) cannot use an address, even though it has been
69    * validated.
70    */
71   struct GNUNET_TIME_Absolute blocked;
72
73   /**
74    * If an address is blocked as part of an exponential back-off,
75    * we track the current size of the backoff here.
76    */
77   struct GNUNET_TIME_Relative back_off;
78
79   /**
80    * Task scheduled to unblock an ATS-blocked address at
81    * @e blocked time, or NULL if the address is not blocked
82    * (and thus @e ar is non-NULL).
83    */
84   struct GNUNET_SCHEDULER_Task *unblock_task;
85
86   /**
87    * Set to #GNUNET_YES if the address has expired but we could
88    * not yet remove it because we still have a valid session.
89    */
90   int expired;
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    * Session to look for (only used if the address is inbound).
113    */
114   struct GNUNET_ATS_Session *session;
115
116   /**
117    * Address to look for.
118    */
119   const struct GNUNET_HELLO_Address *address;
120
121   /**
122    * Where to store the result.
123    */
124   struct AddressInfo *ret;
125 };
126
127
128 /**
129  * Provide an update on the `p2a` map size to statistics.
130  * This function should be called whenever the `p2a` map
131  * is changed.
132  */
133 static void
134 publish_p2a_stat_update ()
135 {
136   GNUNET_STATISTICS_set (GST_stats,
137                          gettext_noop ("# Addresses given to ATS"),
138                          GNUNET_CONTAINER_multipeermap_size (p2a) - num_blocked,
139                          GNUNET_NO);
140   GNUNET_STATISTICS_set (GST_stats,
141                          "# blocked addresses",
142                          num_blocked,
143                          GNUNET_NO);
144 }
145
146
147 /**
148  * Find matching address info.  Both the address and the session
149  * must match; note that expired addresses are still found (as
150  * the session kind-of keeps those alive).
151  *
152  * @param cls the `struct FindClosure`
153  * @param key which peer is this about
154  * @param value the `struct AddressInfo`
155  * @return #GNUNET_YES to continue to iterate, #GNUNET_NO if we found the value
156  */
157 static int
158 find_ai_cb (void *cls,
159             const struct GNUNET_PeerIdentity *key,
160             void *value)
161 {
162   struct FindClosure *fc = cls;
163   struct AddressInfo *ai = value;
164
165   if ((0 ==
166        GNUNET_HELLO_address_cmp (fc->address,
167                                  ai->address)) &&
168       (fc->session == ai->session))
169   {
170     fc->ret = ai;
171     return GNUNET_NO;
172   }
173   return GNUNET_YES;
174 }
175
176
177 /**
178  * Find the address information struct for the
179  * given @a address and @a session.
180  *
181  * @param address address to look for
182  * @param session session to match for inbound connections
183  * @return NULL if this combination is unknown
184  */
185 static struct AddressInfo *
186 find_ai (const struct GNUNET_HELLO_Address *address,
187          struct GNUNET_ATS_Session *session)
188 {
189   struct FindClosure fc;
190
191   fc.address = address;
192   fc.session = session;
193   fc.ret = NULL;
194   GNUNET_CONTAINER_multipeermap_get_multiple (p2a,
195                                               &address->peer,
196                                               &find_ai_cb,
197                                               &fc);
198   return fc.ret;
199 }
200
201
202 /**
203  * Find matching address info, ignoring sessions and expired
204  * addresses.
205  *
206  * @param cls the `struct FindClosure`
207  * @param key which peer is this about
208  * @param value the `struct AddressInfo`
209  * @return #GNUNET_YES to continue to iterate, #GNUNET_NO if we found the value
210  */
211 static int
212 find_ai_no_session_cb (void *cls,
213                        const struct GNUNET_PeerIdentity *key,
214                        void *value)
215 {
216   struct FindClosure *fc = cls;
217   struct AddressInfo *ai = value;
218
219   if (ai->expired)
220     return GNUNET_YES; /* expired do not count here */
221   if (0 ==
222       GNUNET_HELLO_address_cmp (fc->address,
223                                 ai->address))
224   {
225     fc->ret = ai;
226     return GNUNET_NO;
227   }
228   return GNUNET_YES;
229 }
230
231
232 /**
233  * Find the address information struct for the
234  * given address (ignoring sessions)
235  *
236  * @param address address to look for
237  * @return NULL if this combination is unknown
238  */
239 static struct AddressInfo *
240 find_ai_no_session (const struct GNUNET_HELLO_Address *address)
241 {
242   struct FindClosure fc;
243
244   fc.address = address;
245   fc.session = NULL;
246   fc.ret = NULL;
247   GNUNET_CONTAINER_multipeermap_get_multiple (p2a,
248                                               &address->peer,
249                                               &find_ai_no_session_cb,
250                                               &fc);
251   return fc.ret;
252 }
253
254
255 /**
256  * Test if ATS knows about this @a address and @a session.
257  * Note that even if the address is expired, we return
258  * #GNUNET_YES if the respective session matches.
259  *
260  * @param address the address
261  * @param session the session
262  * @return #GNUNET_YES if @a address is known, #GNUNET_NO if not.
263  */
264 int
265 GST_ats_is_known (const struct GNUNET_HELLO_Address *address,
266                   struct GNUNET_ATS_Session *session)
267 {
268   return (NULL != find_ai (address, session)) ? GNUNET_YES : GNUNET_NO;
269 }
270
271
272 /**
273  * Test if ATS knows about this @a address.  Note that
274  * expired addresses do not count.
275  *
276  * @param address the address
277  * @return #GNUNET_YES if @a address is known, #GNUNET_NO if not.
278  */
279 int
280 GST_ats_is_known_no_session (const struct GNUNET_HELLO_Address *address)
281 {
282   return (NULL != find_ai_no_session (address))
283          ? GNUNET_YES
284          : GNUNET_NO;
285 }
286
287
288 /**
289  * The blocking time for an address has expired, allow ATS to
290  * suggest it again.
291  *
292  * @param cls the `struct AddressInfo` of the address to unblock
293  */
294 static void
295 unblock_address (void *cls)
296 {
297   struct AddressInfo *ai = cls;
298
299   ai->unblock_task = NULL;
300   LOG (GNUNET_ERROR_TYPE_DEBUG,
301        "Unblocking address %s of peer %s\n",
302        GST_plugins_a2s (ai->address),
303        GNUNET_i2s (&ai->address->peer));
304   ai->ar = GNUNET_ATS_address_add (GST_ats,
305                                    ai->address,
306                                    ai->session,
307                                    &ai->properties);
308   GNUNET_break (NULL != ai->ar);
309   num_blocked--;
310   publish_p2a_stat_update ();
311 }
312
313
314 /**
315  * Temporarily block a valid address for use by ATS for address
316  * suggestions.  This function should be called if an address was
317  * suggested by ATS but failed to perform (i.e. failure to establish a
318  * session or to exchange the PING/PONG).
319  *
320  * @param address the address to block
321  * @param session the session (can be NULL)
322  */
323 void
324 GST_ats_block_address (const struct GNUNET_HELLO_Address *address,
325                        struct GNUNET_ATS_Session *session)
326 {
327   struct AddressInfo *ai;
328
329   if (0 ==
330       memcmp (&GST_my_identity,
331               &address->peer,
332               sizeof(struct GNUNET_PeerIdentity)))
333     return; /* our own, ignore! */
334   ai = find_ai (address,
335                 session);
336   if ((NULL == ai) || (NULL == ai->ar))
337   {
338     /* The address is already gone/blocked, this can happen during a blacklist
339      * callback. */
340     return;
341   }
342   ai->back_off = GNUNET_TIME_STD_BACKOFF (ai->back_off);
343   if (GNUNET_YES ==
344       GNUNET_HELLO_address_check_option (address,
345                                          GNUNET_HELLO_ADDRESS_INFO_INBOUND))
346     LOG (GNUNET_ERROR_TYPE_DEBUG,
347          "Removing address %s of peer %s from use (inbound died)\n",
348          GST_plugins_a2s (address),
349          GNUNET_i2s (&address->peer));
350   else
351     LOG (GNUNET_ERROR_TYPE_INFO,
352          "Blocking address %s of peer %s from use for %s\n",
353          GST_plugins_a2s (address),
354          GNUNET_i2s (&address->peer),
355          GNUNET_STRINGS_relative_time_to_string (ai->back_off,
356                                                  GNUNET_YES));
357   /* destroy session and address */
358   if ((NULL == session) ||
359       (GNUNET_NO ==
360        GNUNET_ATS_address_del_session (ai->ar,
361                                        session)))
362   {
363     GNUNET_ATS_address_destroy (ai->ar);
364   }
365   /* "ar" has been freed, regardless how the branch
366      above played out: it was either freed in
367    #GNUNET_ATS_address_del_session() because it was
368      incoming, or explicitly in
369    #GNUNET_ATS_address_del_session(). */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_NT_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_NT_to_string (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_NT_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. */GNUNET_assert (NULL != (find_ai (address, session)));
562     return;
563   }
564   GNUNET_assert (NULL == ai->session);
565   ai->session = session;
566   LOG (GNUNET_ERROR_TYPE_DEBUG,
567        "Telling ATS about new session for peer %s\n",
568        GNUNET_i2s (&address->peer));
569   /* Note that the address might currently be blocked; we only
570      tell ATS about the session if the address is currently not
571      blocked; otherwise, ATS will be told about the session on
572      unblock. */
573   if (NULL != ai->ar)
574     GNUNET_ATS_address_add_session (ai->ar,
575                                     session);
576   else
577     GNUNET_assert (NULL != ai->unblock_task);
578 }
579
580
581 /**
582  * Release memory used by the given address data.
583  *
584  * @param ai the `struct AddressInfo`
585  */
586 static void
587 destroy_ai (struct AddressInfo *ai)
588 {
589   GNUNET_assert (NULL == ai->session);
590   if (NULL != ai->unblock_task)
591   {
592     GNUNET_SCHEDULER_cancel (ai->unblock_task);
593     ai->unblock_task = NULL;
594     num_blocked--;
595   }
596   GNUNET_assert (GNUNET_YES ==
597                  GNUNET_CONTAINER_multipeermap_remove (p2a,
598                                                        &ai->address->peer,
599                                                        ai));
600   LOG (GNUNET_ERROR_TYPE_DEBUG,
601        "Telling ATS to destroy address from peer %s\n",
602        GNUNET_i2s (&ai->address->peer));
603   if (NULL != ai->ar)
604   {
605     GNUNET_ATS_address_destroy (ai->ar);
606     ai->ar = NULL;
607   }
608   publish_p2a_stat_update ();
609   GNUNET_HELLO_address_free (ai->address);
610   GNUNET_free (ai);
611 }
612
613
614 /**
615  * Notify ATS that the @a session (but not the @a address) of
616  * a given @a address is no longer relevant. (The @a session
617  * went down.)  This function may be called even if for the
618  * respective outbound address #GST_ats_new_session() was
619  * never called and thus the pair is unknown to ATS. In this
620  * case, the call is simply ignored.
621  *
622  * @param address the address
623  * @param session the session
624  */
625 void
626 GST_ats_del_session (const struct GNUNET_HELLO_Address *address,
627                      struct GNUNET_ATS_Session *session)
628 {
629   struct AddressInfo *ai;
630
631   if (0 ==
632       memcmp (&GST_my_identity,
633               &address->peer,
634               sizeof(struct GNUNET_PeerIdentity)))
635     return; /* our own, ignore! */
636   if (NULL == session)
637   {
638     GNUNET_break (0);
639     return;
640   }
641   ai = find_ai (address,
642                 session);
643   if (NULL == ai)
644   {
645     /* We sometimes create sessions just for sending a PING,
646        and if those are destroyed they were never known to
647        ATS which means we end up here (however, in this
648        case, the address must be an outbound address). */
649     GNUNET_break (GNUNET_YES !=
650                   GNUNET_HELLO_address_check_option (address,
651                                                      GNUNET_HELLO_ADDRESS_INFO_INBOUND));
652     return;
653   }
654   GNUNET_assert (session == ai->session);
655   ai->session = NULL;
656   LOG (GNUNET_ERROR_TYPE_DEBUG,
657        "Telling ATS to destroy session %p from peer %s\n",
658        session,
659        GNUNET_i2s (&address->peer));
660   if (GNUNET_YES == ai->expired)
661   {
662     /* last reason to keep this 'ai' around is now gone, the
663        session is dead as well, clean up */
664     if (NULL != ai->ar)
665     {
666       /* Address expired but not blocked, and thus 'ar' was still
667          live because of the session; deleting just the session
668          will do for an inbound session, but for an outbound we
669          then also need to destroy the address with ATS. */
670       if (GNUNET_NO ==
671           GNUNET_ATS_address_del_session (ai->ar,
672                                           session))
673       {
674         GNUNET_ATS_address_destroy (ai->ar);
675       }
676       /* "ar" has been freed, regardless how the branch
677          above played out: it was either freed in
678        #GNUNET_ATS_address_del_session() because it was
679          incoming, or explicitly in
680        #GNUNET_ATS_address_del_session(). */ai->ar = NULL;
681     }
682     destroy_ai (ai);
683     return;
684   }
685
686   if (NULL == ai->ar)
687   {
688     /* If ATS doesn't know about the address/session, this means
689        this address was blocked. */
690     if (GNUNET_YES ==
691         GNUNET_HELLO_address_check_option (address,
692                                            GNUNET_HELLO_ADDRESS_INFO_INBOUND))
693     {
694       /* This was a blocked inbound session, which now lost the
695          session.  But inbound addresses are by themselves useless,
696          so we must forget about the address as well. */
697       destroy_ai (ai);
698       return;
699     }
700     /* Otherwise, we are done as we have set `ai->session` to NULL
701        already and ATS will simply not be told about the session when
702        the connection is unblocked and the outbound address becomes
703        available again. . */
704     return;
705   }
706
707   /* This is the "simple" case where ATS knows about the session and
708      the address is neither blocked nor expired.  Delete the session,
709      and if it was inbound, free the address as well. */
710   if (GNUNET_YES ==
711       GNUNET_ATS_address_del_session (ai->ar,
712                                       session))
713   {
714     /* This was an inbound address, the session is now gone, so we
715        need to also forget about the address itself. */
716     ai->ar = NULL;
717     destroy_ai (ai);
718   }
719 }
720
721
722 /**
723  * Notify ATS about DV @a distance change to an @a address.
724  * Does nothing if the @a address is not known to us.
725  *
726  * @param address the address
727  * @param distance new distance value
728  */
729 void
730 GST_ats_update_distance (const struct GNUNET_HELLO_Address *address,
731                          uint32_t distance)
732 {
733   struct AddressInfo *ai;
734
735   ai = find_ai_no_session (address);
736   if (NULL == ai)
737   {
738     /* We do not know about this address, do nothing. */
739     return;
740   }
741   LOG (GNUNET_ERROR_TYPE_DEBUG,
742        "Updated distance for peer `%s' to %u\n",
743        GNUNET_i2s (&address->peer),
744        distance);
745   ai->properties.distance = distance;
746   /* Give manipulation its chance to change metrics */
747   GST_manipulation_manipulate_metrics (address,
748                                        ai->session,
749                                        &ai->properties);
750   /* Address may be blocked, only give ATS if address is
751      currently active. */
752   if (NULL != ai->ar)
753     GNUNET_ATS_address_update (ai->ar,
754                                &ai->properties);
755 }
756
757
758 /**
759  * Notify ATS about @a delay changes to properties of an @a address.
760  * Does nothing if the @a address is not known to us.
761  *
762  * @param address the address
763  * @param delay new delay value
764  */
765 void
766 GST_ats_update_delay (const struct GNUNET_HELLO_Address *address,
767                       struct GNUNET_TIME_Relative delay)
768 {
769   struct AddressInfo *ai;
770
771   ai = find_ai_no_session (address);
772   if (NULL == ai)
773   {
774     /* We do not know about this address, do nothing. */
775     return;
776   }
777   LOG (GNUNET_ERROR_TYPE_DEBUG,
778        "Updated latency for peer `%s' to %s\n",
779        GNUNET_i2s (&address->peer),
780        GNUNET_STRINGS_relative_time_to_string (delay,
781                                                GNUNET_YES));
782   ai->properties.delay = delay;
783   /* Give manipulation its chance to change metrics */
784   GST_manipulation_manipulate_metrics (address,
785                                        ai->session,
786                                        &ai->properties);
787   /* Address may be blocked, only give ATS if address is
788      currently active. */
789   if (NULL != ai->ar)
790     GNUNET_ATS_address_update (ai->ar,
791                                &ai->properties);
792 }
793
794
795 /**
796  * Notify ATS about utilization changes to an @a address.
797  * Does nothing if the @a address is not known to us.
798  *
799  * @param address our information about the address
800  * @param bps_in new utilization inbound
801  * @param bps_out new utilization outbound
802  */
803 void
804 GST_ats_update_utilization (const struct GNUNET_HELLO_Address *address,
805                             uint32_t bps_in,
806                             uint32_t bps_out)
807 {
808   struct AddressInfo *ai;
809
810   ai = find_ai_no_session (address);
811   if (NULL == ai)
812   {
813     /* We do not know about this address, do nothing. */
814     return;
815   }
816   LOG (GNUNET_ERROR_TYPE_DEBUG,
817        "Updating utilization for peer `%s' address %s: %u/%u\n",
818        GNUNET_i2s (&address->peer),
819        GST_plugins_a2s (address),
820        (unsigned int) bps_in,
821        (unsigned int) bps_out);
822   ai->properties.utilization_in = bps_in;
823   ai->properties.utilization_out = bps_out;
824   /* Give manipulation its chance to change metrics */
825   GST_manipulation_manipulate_metrics (address,
826                                        ai->session,
827                                        &ai->properties);
828   /* Address may be blocked, only give ATS if address is
829      currently active. */
830   if (NULL != ai->ar)
831     GNUNET_ATS_address_update (ai->ar,
832                                &ai->properties);
833 }
834
835
836 /**
837  * Notify ATS that the address has expired and thus cannot
838  * be used any longer.  This function must only be called
839  * if the corresponding session is already gone.
840  *
841  * @param address the address
842  */
843 void
844 GST_ats_expire_address (const struct GNUNET_HELLO_Address *address)
845 {
846   struct AddressInfo *ai;
847
848   if (0 ==
849       memcmp (&GST_my_identity,
850               &address->peer,
851               sizeof(struct GNUNET_PeerIdentity)))
852     return; /* our own, ignore! */
853   LOG (GNUNET_ERROR_TYPE_DEBUG,
854        "Address %s of peer %s expired\n",
855        GST_plugins_a2s (address),
856        GNUNET_i2s (&address->peer));
857   ai = find_ai_no_session (address);
858   if (NULL == ai)
859   {
860     GNUNET_assert (0);
861     return;
862   }
863   if (NULL != ai->session)
864   {
865     /* Got an active session, just remember the expiration
866        and act upon it when the session goes down. */
867     ai->expired = GNUNET_YES;
868     return;
869   }
870   /* Address expired, no session, free resources */
871   destroy_ai (ai);
872 }
873
874
875 /**
876  * Initialize ATS subsystem.
877  */
878 void
879 GST_ats_init ()
880 {
881   p2a = GNUNET_CONTAINER_multipeermap_create (4, GNUNET_YES);
882 }
883
884
885 /**
886  * Release memory used by the given address data.
887  *
888  * @param cls NULL
889  * @param key which peer is this about
890  * @param value the `struct AddressInfo`
891  * @return #GNUNET_OK (continue to iterate)
892  */
893 static int
894 destroy_ai_cb (void *cls,
895                const struct GNUNET_PeerIdentity *key,
896                void *value)
897 {
898   struct AddressInfo *ai = value;
899
900   destroy_ai (ai);
901   return GNUNET_OK;
902 }
903
904
905 /**
906  * Shutdown ATS subsystem.
907  */
908 void
909 GST_ats_done ()
910 {
911   GNUNET_CONTAINER_multipeermap_iterate (p2a,
912                                          &destroy_ai_cb,
913                                          NULL);
914   publish_p2a_stat_update ();
915   GNUNET_CONTAINER_multipeermap_destroy (p2a);
916   p2a = NULL;
917 }
918
919
920 /* end of gnunet-service-transport_ats.c */