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