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