adding change preference API to ATS
[oweals/gnunet.git] / src / ats / ats_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2010,2011 Christian Grothoff (and other contributing authors)
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., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20 /**
21  * @file ats/ats_api.c
22  * @brief automatic transport selection API
23  * @author Christian Grothoff
24  * @author Matthias Wachs
25  *
26  * TODO:
27  * - write test case
28  * - extend API to get performance data
29  * - implement simplistic strategy based on say 'lowest latency' or strict ordering
30  * - extend API to get peer preferences, implement proportional bandwidth assignment
31  * - re-implement API against a real ATS service (!)
32  */
33 #include "platform.h"
34 #include "gnunet_ats_service.h"
35 #include "ats_api.h"
36
37 #define DEBUG_ATS GNUNET_EXTRA_LOGGING
38
39 /**
40  * Receive and send buffer windows grow over time.  For
41  * how long can 'unused' bandwidth accumulate before we
42  * need to cap it?  (specified in seconds).
43  */
44 #define MAX_WINDOW_TIME_S (5 * 60)
45
46 // NOTE: this implementation is simply supposed
47 // to implement a simplistic strategy in-process;
48 // in the future, we plan to replace it with a real
49 // service implementation
50
51
52 /**
53  * Opaque handle to obtain address suggestions.
54  */
55 struct GNUNET_ATS_SuggestionContext
56 {
57
58   /**
59    * Function to call with our final suggestion.
60    */
61   GNUNET_ATS_AddressSuggestionCallback cb;
62
63   /**
64    * Closure for 'cb'.
65    */
66   void *cb_cls;
67
68   /**
69    * Global ATS handle.
70    */
71   struct GNUNET_ATS_Handle *atc;
72
73   /**
74    * Which peer are we monitoring?
75    */
76   struct GNUNET_PeerIdentity target;
77
78 };
79
80
81 /**
82  * Count number of connected records.
83  *
84  * @param cls pointer to counter
85  * @param key identity of the peer associated with the records
86  * @param value a 'struct AllocationRecord'
87  * @return GNUNET_YES (continue iteration)
88  */
89 static int
90 count_connections (void *cls, const GNUNET_HashCode * key, void *value)
91 {
92   unsigned int *ac = cls;
93   struct AllocationRecord *ar = value;
94
95   if (GNUNET_YES == ar->connected)
96     (*ac)++;
97   return GNUNET_YES;
98 }
99
100
101 /**
102  * Closure for 'set_bw_connections'.
103  */
104 struct SetBandwidthContext
105 {
106   /**
107    * ATS handle.
108    */
109   struct GNUNET_ATS_Handle *atc;
110
111   /**
112    * Inbound bandwidth to assign.
113    */
114   struct GNUNET_BANDWIDTH_Value32NBO bw_in;
115
116   /**
117    * Outbound bandwidth to assign.
118    */
119   struct GNUNET_BANDWIDTH_Value32NBO bw_out;
120 };
121
122
123 /**
124  * Set bandwidth based on record.
125  *
126  * @param cls 'struct SetBandwidthContext'
127  * @param key identity of the peer associated with the records
128  * @param value a 'struct AllocationRecord'
129  * @return GNUNET_YES (continue iteration)
130  */
131 static int
132 set_bw_connections (void *cls, const GNUNET_HashCode * key, void *value)
133 {
134   struct SetBandwidthContext *sbc = cls;
135   struct AllocationRecord *ar = value;
136
137   if (GNUNET_YES == ar->connected)
138   {
139     ar->bandwidth_in = sbc->bw_in;
140     ar->bandwidth_out = sbc->bw_out;
141     GNUNET_BANDWIDTH_tracker_update_quota (&ar->available_recv_window,
142                                            ar->bandwidth_in);
143     sbc->atc->alloc_cb (sbc->atc->alloc_cb_cls,
144                         (const struct GNUNET_PeerIdentity *) key,
145                         ar->plugin_name, ar->session, ar->plugin_addr,
146                         ar->plugin_addr_len, ar->bandwidth_out, ar->bandwidth_in);
147   }
148   else if (ntohl (ar->bandwidth_out.value__) > 0)
149   {
150     ar->bandwidth_in = GNUNET_BANDWIDTH_value_init (0);
151     ar->bandwidth_out = GNUNET_BANDWIDTH_value_init (0);
152     sbc->atc->alloc_cb (sbc->atc->alloc_cb_cls,
153                         (const struct GNUNET_PeerIdentity *) key,
154                         ar->plugin_name, ar->session, ar->plugin_addr,
155                         ar->plugin_addr_len, ar->bandwidth_out, ar->bandwidth_in);
156   }
157   return GNUNET_YES;
158 }
159
160
161 /**
162  * Task run to update bandwidth assignments.
163  *
164  * @param cls the 'struct GNUNET_ATS_Handle'
165  * @param tc scheduler context
166  */
167 static void
168 update_bandwidth_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
169 {
170   struct GNUNET_ATS_Handle *atc = cls;
171   unsigned int ac = 0;
172   struct SetBandwidthContext bwc;
173
174   atc->ba_task = GNUNET_SCHEDULER_NO_TASK;
175   /* FIXME: update calculations NICELY; what follows is a naive version */
176   GNUNET_CONTAINER_multihashmap_iterate (atc->peers, &count_connections, &ac);
177   bwc.atc = atc;
178   if (ac == 0)
179     ac++;
180   GNUNET_assert (ac > 0);
181   bwc.bw_in = GNUNET_BANDWIDTH_value_init (atc->total_bps_in / ac);
182   bwc.bw_out = GNUNET_BANDWIDTH_value_init (atc->total_bps_out / ac);
183   GNUNET_CONTAINER_multihashmap_iterate (atc->peers, &set_bw_connections, &bwc);
184 }
185
186
187 /**
188  * Calculate an updated bandwidth assignment and notify.
189  *
190  * @param atc handle
191  * @param change which allocation record changed?
192  */
193 static void
194 update_bandwidth_assignment (struct GNUNET_ATS_Handle *atc,
195                              struct AllocationRecord *change)
196 {
197   /* FIXME: based on the 'change', update the LP-problem... */
198   if (atc->ba_task == GNUNET_SCHEDULER_NO_TASK)
199     atc->ba_task = GNUNET_SCHEDULER_add_now (&update_bandwidth_task, atc);
200 }
201
202
203 /**
204  * Function called with feasbile addresses we might want to suggest.
205  *
206  * @param cls the 'struct GNUNET_ATS_SuggestionContext'
207  * @param key identity of the peer
208  * @param value a 'struct AllocationRecord' for the peer
209  * @return GNUNET_NO if we're done, GNUNET_YES if we did not suggest an address yet
210  */
211 static int
212 suggest_address (void *cls, const GNUNET_HashCode * key, void *value)
213 {
214   struct GNUNET_ATS_SuggestionContext *asc = cls;
215   struct AllocationRecord *ar = value;
216
217 #if DEBUG_ATS
218   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-api",
219                    "Suggesting address for peer `%s'\n", GNUNET_h2s (key));
220 #endif
221
222   /* trivial strategy: pick first available address... */
223   asc->cb (asc->cb_cls, &asc->target, ar->plugin_name, ar->plugin_addr,
224            ar->plugin_addr_len, ar->session,
225            GNUNET_BANDWIDTH_value_init (asc->atc->total_bps_out / 32), 
226            GNUNET_BANDWIDTH_value_init (asc->atc->total_bps_in / 32), 
227            ar->ats,
228            ar->ats_count);
229   asc->cb = NULL;
230   return GNUNET_NO;
231 }
232
233 int
234 map_it (void *cls, const GNUNET_HashCode * key, void *value)
235 {
236   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-api", "Found entry for %s\n",
237                    GNUNET_h2s (key));
238   return GNUNET_YES;
239 }
240
241 /**
242  * We would like to establish a new connection with a peer.
243  * ATS should suggest a good address to begin with.
244  *
245  * @param atc handle
246  * @param peer identity of the new peer
247  * @param cb function to call with the address
248  * @param cb_cls closure for cb
249  */
250 struct GNUNET_ATS_SuggestionContext *
251 GNUNET_ATS_suggest_address (struct GNUNET_ATS_Handle *atc,
252                             const struct GNUNET_PeerIdentity *peer,
253                             GNUNET_ATS_AddressSuggestionCallback cb,
254                             void *cb_cls)
255 {
256   struct GNUNET_ATS_SuggestionContext *asc;
257
258 #if DEBUG_ATS
259   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-api",
260                    "Looking up suggested address for peer `%s'\n",
261                    GNUNET_i2s (peer));
262 #endif
263   asc = GNUNET_malloc (sizeof (struct GNUNET_ATS_SuggestionContext));
264   asc->cb = cb;
265   asc->cb_cls = cb_cls;
266   asc->atc = atc;
267   asc->target = *peer;
268   (void) GNUNET_CONTAINER_multihashmap_get_multiple (atc->peers, &peer->hashPubKey,
269                                                      &suggest_address, asc);
270
271   if (NULL == asc->cb)
272   {
273     GNUNET_free (asc);
274     return NULL;
275   }
276   GNUNET_CONTAINER_multihashmap_put (atc->notify_map, &peer->hashPubKey, asc,
277                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
278   return asc;
279 }
280
281
282 /**
283  * Cancel suggestion request.
284  *
285  * @param asc handle of the request to cancel
286  */
287 void
288 GNUNET_ATS_suggest_address_cancel (struct GNUNET_ATS_SuggestionContext *asc)
289 {
290   GNUNET_assert (GNUNET_OK ==
291                  GNUNET_CONTAINER_multihashmap_remove (asc->atc->notify_map,
292                                                        &asc->target.hashPubKey,
293                                                        asc));
294   GNUNET_free (asc);
295 }
296
297
298 /**
299  * Initialize the ATS subsystem.
300  *
301  * @param cfg configuration to use
302  * @param alloc_cb notification to call whenever the allocation changed
303  * @param alloc_cb_cls closure for 'alloc_cb'
304  * @return ats context
305  */
306 struct GNUNET_ATS_Handle *
307 GNUNET_ATS_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
308                  GNUNET_TRANSPORT_ATS_AllocationNotification alloc_cb,
309                  void *alloc_cb_cls)
310 {
311   struct GNUNET_ATS_Handle *atc;
312
313 #if DEBUG_ATS
314   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-api", "ATS init\n");
315 #endif
316   atc = GNUNET_malloc (sizeof (struct GNUNET_ATS_Handle));
317   atc->cfg = cfg;
318   atc->alloc_cb = alloc_cb;
319   atc->alloc_cb_cls = alloc_cb_cls;
320   atc->peers = GNUNET_CONTAINER_multihashmap_create (256);
321   atc->notify_map = GNUNET_CONTAINER_multihashmap_create (256);
322   GNUNET_CONFIGURATION_get_value_number (cfg, "core", "TOTAL_QUOTA_OUT",
323                                          &atc->total_bps_out);
324   GNUNET_CONFIGURATION_get_value_number (cfg, "core", "TOTAL_QUOTA_IN",
325                                          &atc->total_bps_in);
326   return atc;
327 }
328
329
330 /**
331  * Free an allocation record.
332  *
333  * @param cls unused
334  * @param key identity of the peer associated with the record
335  * @param value the 'struct AllocationRecord' to free
336  * @return GNUNET_OK (continue to iterate)
337  */
338 static int
339 destroy_allocation_record (void *cls, const GNUNET_HashCode * key, void *value)
340 {
341   struct AllocationRecord *ar = value;
342
343   GNUNET_array_grow (ar->ats, ar->ats_count, 0);
344   GNUNET_free (ar->plugin_name);
345   GNUNET_free (ar);
346   return GNUNET_OK;
347 }
348
349
350 /**
351  * Shutdown the ATS subsystem.
352  *
353  * @param atc handle
354  */
355 void
356 GNUNET_ATS_shutdown (struct GNUNET_ATS_Handle *atc)
357 {
358 #if DEBUG_ATS
359   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-api", "ATS shutdown\n");
360 #endif
361   if (GNUNET_SCHEDULER_NO_TASK != atc->ba_task)
362   {
363     GNUNET_SCHEDULER_cancel (atc->ba_task);
364     atc->ba_task = GNUNET_SCHEDULER_NO_TASK;
365   }
366   GNUNET_CONTAINER_multihashmap_iterate (atc->peers, &destroy_allocation_record,
367                                          NULL);
368   GNUNET_CONTAINER_multihashmap_destroy (atc->peers);
369   GNUNET_assert (GNUNET_CONTAINER_multihashmap_size (atc->notify_map) == 0);
370   GNUNET_CONTAINER_multihashmap_destroy (atc->notify_map);
371   atc->notify_map = NULL;
372   GNUNET_free (atc);
373 }
374
375
376 /**
377  * Closure for 'update_session'
378  */
379 struct UpdateSessionContext
380 {
381   /**
382    * Ats handle.
383    */
384   struct GNUNET_ATS_Handle *atc;
385
386   /**
387    * Allocation record with new information.
388    */
389   struct AllocationRecord *arnew;
390 };
391
392
393 /**
394  * Update an allocation record, merging with the new information
395  *
396  * @param cls a new 'struct AllocationRecord'
397  * @param key identity of the peer associated with the records
398  * @param value the old 'struct AllocationRecord'
399  * @return GNUNET_YES if the records do not match,
400  *         GNUNET_NO if the record do match and 'old' was updated
401  */
402 static int
403 update_session (void *cls, const GNUNET_HashCode * key, void *value)
404 {
405   struct UpdateSessionContext *usc = cls;
406   struct AllocationRecord *arnew = usc->arnew;
407   struct AllocationRecord *arold = value;
408   int change;
409   int c_old;
410   int c_new;
411   int found;
412
413
414 #if DEBUG_ATS
415   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-api",
416                    "Updating session for peer `%s' plugin `%s'\n",
417                    GNUNET_h2s (key), arold->plugin_name);
418 #endif
419
420   if (0 != strcmp (arnew->plugin_name, arold->plugin_name))
421     return GNUNET_YES;
422   if (((arnew->session == arold->session) && (arnew->session != NULL)) ||
423       ((arold->session == NULL) &&
424        (arold->plugin_addr_len == arnew->plugin_addr_len) &&
425        (0 ==
426         memcmp (arold->plugin_addr, arnew->plugin_addr,
427                 arnew->plugin_addr_len))))
428   {
429     change = GNUNET_NO;
430     /* records match */
431     if (arnew->session != arold->session)
432     {
433       arold->session = arnew->session;
434       change = GNUNET_YES;
435     }
436     if ((arnew->connected == GNUNET_YES) && (arold->connected == GNUNET_NO))
437     {
438       arold->connected = GNUNET_YES;
439       change = GNUNET_YES;
440     }
441
442     /* Update existing value */
443     c_new = 0;
444     while (c_new < arnew->ats_count)
445     {
446       c_old = 0;
447       found = GNUNET_NO;
448       while (c_old < arold->ats_count)
449       {
450         if (arold->ats[c_old].type == arnew->ats[c_new].type)
451         {
452 #if DEBUG_ATS
453           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
454                       "Found type %i, old value=%i new value=%i\n",
455                       ntohl (arold->ats[c_old].type),
456                       ntohl (arold->ats[c_old].value),
457                       ntohl (arnew->ats[c_new].value));
458 #endif
459           arold->ats[c_old].value = arnew->ats[c_new].value;
460           found = GNUNET_YES;
461         }
462         c_old++;
463       }
464       /* Add new value */
465       if (found == GNUNET_NO)
466       {
467 #if DEBUG_ATS
468         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Added new type %i new value=%i\n",
469                     ntohl (arnew->ats[c_new].type),
470                     ntohl (arnew->ats[c_new].value));
471         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Old array size: %u\n",
472                     arold->ats_count);
473 #endif
474         GNUNET_array_grow (arold->ats, arold->ats_count, arold->ats_count + 1);
475         GNUNET_assert (arold->ats_count >= 2);
476         arold->ats[arold->ats_count - 2].type = arnew->ats[c_new].type;
477         arold->ats[arold->ats_count - 2].value = arnew->ats[c_new].value;
478         arold->ats[arold->ats_count - 1].type = htonl (0);
479         arold->ats[arold->ats_count - 1].value = htonl (0);
480 #if DEBUG_ATS
481         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New array size: %i\n",
482                     arold->ats_count);
483 #endif
484       }
485       c_new++;
486     }
487
488     if (GNUNET_YES == change)
489       update_bandwidth_assignment (usc->atc, arold);
490     return GNUNET_NO;
491   }
492   return GNUNET_YES;
493 }
494
495
496 /**
497  * Create an allocation record with the given properties.
498  *
499  * @param plugin_name name of the currently used transport plugin
500  * @param session session in use (if available)
501  * @param plugin_addr address in use (if available)
502  * @param plugin_addr_len number of bytes in plugin_addr
503  * @param ats performance data for the connection
504  * @param ats_count number of performance records in 'ats'
505  */
506 static struct AllocationRecord *
507 create_allocation_record (const char *plugin_name, struct Session *session,
508                           const void *plugin_addr, size_t plugin_addr_len,
509                           const struct GNUNET_TRANSPORT_ATS_Information *ats,
510                           uint32_t ats_count)
511 {
512   struct AllocationRecord *ar;
513
514   ar = GNUNET_malloc (sizeof (struct AllocationRecord) + plugin_addr_len);
515   ar->plugin_name = GNUNET_strdup (plugin_name);
516   ar->plugin_addr = &ar[1];
517   memcpy (&ar[1], plugin_addr, plugin_addr_len);
518   ar->session = session;
519   ar->plugin_addr_len = plugin_addr_len;  
520   GNUNET_BANDWIDTH_tracker_init (&ar->available_recv_window, 
521                                  ar->bandwidth_in,
522                                  MAX_WINDOW_TIME_S);
523   GNUNET_assert (ats_count > 0);
524   GNUNET_array_grow (ar->ats, ar->ats_count, ats_count);
525   memcpy (ar->ats, ats,
526           ats_count * sizeof (struct GNUNET_TRANSPORT_ATS_Information));
527   return ar;
528 }
529
530
531 /**
532  * Mark all matching allocation records as not connected.
533  *
534  * @param cls 'struct GTS_AtsHandle'
535  * @param key identity of the peer associated with the record
536  * @param value the 'struct AllocationRecord' to clear the 'connected' flag
537  * @return GNUNET_OK (continue to iterate)
538  */
539 static int
540 disconnect_peer (void *cls, const GNUNET_HashCode * key, void *value)
541 {
542   struct GNUNET_ATS_Handle *atc = cls;
543   struct AllocationRecord *ar = value;
544
545   if (GNUNET_YES == ar->connected)
546   {
547     ar->connected = GNUNET_NO;
548     update_bandwidth_assignment (atc, ar);
549   }
550   return GNUNET_OK;
551 }
552
553
554 /**
555  * We established a new connection with a peer (for example, because
556  * core asked for it or because the other peer connected to us).
557  * Calculate bandwidth assignments including the new peer.
558  *
559  * @param atc handle
560  * @param peer identity of the new peer
561  * @param plugin_name name of the currently used transport plugin
562  * @param session session in use (if available)
563  * @param plugin_addr address in use (if available)
564  * @param plugin_addr_len number of bytes in plugin_addr
565  * @param ats performance data for the connection
566  * @param ats_count number of performance records in 'ats'
567  */
568 void
569 GNUNET_ATS_peer_connect (struct GNUNET_ATS_Handle *atc,
570                          const struct GNUNET_PeerIdentity *peer,
571                          const char *plugin_name, struct Session *session,
572                          const void *plugin_addr, size_t plugin_addr_len,
573                          const struct GNUNET_TRANSPORT_ATS_Information *ats,
574                          uint32_t ats_count)
575 {
576   struct AllocationRecord *ar;
577   struct UpdateSessionContext usc;
578
579   (void) GNUNET_CONTAINER_multihashmap_iterate (atc->peers, &disconnect_peer,
580                                                 atc);
581   ar = create_allocation_record (plugin_name, session, plugin_addr,
582                                  plugin_addr_len, ats, ats_count);
583   ar->connected = GNUNET_YES;
584   usc.atc = atc;
585   usc.arnew = ar;
586   if (GNUNET_SYSERR ==
587       GNUNET_CONTAINER_multihashmap_iterate (atc->peers, &update_session, &usc))
588   {
589     destroy_allocation_record (NULL, &peer->hashPubKey, ar);
590     return;
591   }
592   GNUNET_assert (GNUNET_OK ==
593                  GNUNET_CONTAINER_multihashmap_put (atc->peers,
594                                                     &peer->hashPubKey, ar,
595                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
596 }
597
598
599 /**
600  * We disconnected from the given peer (for example, because ats, core
601  * or blacklist asked for it or because the other peer disconnected).
602  * Calculate bandwidth assignments without the peer.
603  *
604  * @param atc handle
605  * @param peer identity of the new peer
606  */
607 void
608 GNUNET_ATS_peer_disconnect (struct GNUNET_ATS_Handle *atc,
609                             const struct GNUNET_PeerIdentity *peer)
610 {
611   (void) GNUNET_CONTAINER_multihashmap_get_multiple (atc->peers,
612                                                      &peer->hashPubKey,
613                                                      &disconnect_peer, atc);
614 }
615
616
617 /**
618  * Closure for 'destroy_allocation_record'
619  */
620 struct SessionDestroyContext
621 {
622   /**
623    * Ats handle.
624    */
625   struct GNUNET_ATS_Handle *atc;
626
627   /**
628    * Session being destroyed.
629    */
630   const struct Session *session;
631 };
632
633
634 /**
635  * Free an allocation record matching the given session.
636  *
637  * @param cls the 'struct SessionDestroyContext'
638  * @param key identity of the peer associated with the record
639  * @param value the 'struct AllocationRecord' to free
640  * @return GNUNET_OK (continue to iterate)
641  */
642 static int
643 destroy_session (void *cls, const GNUNET_HashCode * key, void *value)
644 {
645   struct SessionDestroyContext *sdc = cls;
646   struct AllocationRecord *ar = value;
647
648   if (ar->session != sdc->session)
649     return GNUNET_OK;
650   ar->session = NULL;
651   if (ar->plugin_addr != NULL)
652     return GNUNET_OK;
653   GNUNET_assert (GNUNET_OK ==
654                  GNUNET_CONTAINER_multihashmap_remove (sdc->atc->peers, key,
655                                                        ar));
656   if (GNUNET_YES == ar->connected) ;
657   {
658     /* FIXME: is this supposed to be allowed? What to do then? */
659     GNUNET_break (0);
660   }
661   destroy_allocation_record (NULL, key, ar);
662   return GNUNET_OK;
663 }
664
665
666 /**
667  * A session got destroyed, stop including it as a valid address.
668  *
669  * @param atc handle
670  * @param peer identity of the peer
671  * @param session session handle that is no longer valid
672  */
673 void
674 GNUNET_ATS_session_destroyed (struct GNUNET_ATS_Handle *atc,
675                               const struct GNUNET_PeerIdentity *peer,
676                               const struct Session *session)
677 {
678   struct SessionDestroyContext sdc;
679
680   sdc.atc = atc;
681   sdc.session = session;
682   (void) GNUNET_CONTAINER_multihashmap_iterate (atc->peers, &destroy_session,
683                                                 &sdc);
684 }
685
686
687 /**
688  * Notify validation watcher that an entry is now valid
689  *
690  * @param cls 'struct ValidationEntry' that is now valid
691  * @param key peer identity (unused)
692  * @param value a 'GST_ValidationIteratorContext' to notify
693  * @return GNUNET_YES (continue to iterate)
694  */
695 static int
696 notify_valid (void *cls, const GNUNET_HashCode * key, void *value)
697 {
698   struct AllocationRecord *ar = cls;
699   struct GNUNET_ATS_SuggestionContext *asc = value;
700
701   asc->cb (asc->cb_cls, &asc->target, ar->plugin_name, ar->plugin_addr,
702            ar->plugin_addr_len, ar->session,
703            GNUNET_BANDWIDTH_value_init (asc->atc->total_bps_out / 32), 
704            GNUNET_BANDWIDTH_value_init (asc->atc->total_bps_in / 32), 
705            ar->ats, ar->ats_count);
706   GNUNET_ATS_suggest_address_cancel (asc);
707   asc = NULL;
708   return GNUNET_OK;
709 }
710
711
712 /**
713  * We have updated performance statistics for a given address.  Note
714  * that this function can be called for addresses that are currently
715  * in use as well as addresses that are valid but not actively in use.
716  * Furthermore, the peer may not even be connected to us right now (in
717  * which case the call may be ignored or the information may be stored
718  * for later use).  Update bandwidth assignments.
719  *
720  * @param atc handle
721  * @param peer identity of the peer
722  * @param valid_until how long is the address valid?
723  * @param plugin_name name of the transport plugin
724  * @param session session handle (if available)
725  * @param plugin_addr address  (if available)
726  * @param plugin_addr_len number of bytes in plugin_addr
727  * @param ats performance data for the address
728  * @param ats_count number of performance records in 'ats'
729  */
730 void
731 GNUNET_ATS_address_update (struct GNUNET_ATS_Handle *atc,
732                            const struct GNUNET_PeerIdentity *peer,
733                            struct GNUNET_TIME_Absolute valid_until,
734                            const char *plugin_name, struct Session *session,
735                            const void *plugin_addr, size_t plugin_addr_len,
736                            const struct GNUNET_TRANSPORT_ATS_Information *ats,
737                            uint32_t ats_count)
738 {
739   struct AllocationRecord *ar;
740   struct UpdateSessionContext usc;
741
742 #if DEBUG_ATS
743   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-api",
744                    "Updating address for peer `%s', plugin `%s'\n",
745                    GNUNET_i2s (peer), plugin_name);
746 #endif
747   ar = create_allocation_record (plugin_name, session, plugin_addr,
748                                  plugin_addr_len, ats, ats_count);
749   usc.atc = atc;
750   usc.arnew = ar;
751   if (GNUNET_SYSERR ==
752       GNUNET_CONTAINER_multihashmap_iterate (atc->peers, &update_session, &usc))
753   {
754     destroy_allocation_record (NULL, &peer->hashPubKey, ar);
755     return;
756   }
757 #if DEBUG_ATS
758   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-api",
759                    "Adding new address for peer `%s', plugin `%s'\n",
760                    GNUNET_i2s (peer), plugin_name);
761 #endif
762   GNUNET_assert (GNUNET_OK ==
763                  GNUNET_CONTAINER_multihashmap_put (atc->peers,
764                                                     &peer->hashPubKey, ar,
765                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
766   GNUNET_CONTAINER_multihashmap_get_multiple (atc->notify_map,
767                                               &peer->hashPubKey, &notify_valid,
768                                               ar);
769 }
770
771 /* end of file gnunet-service-transport_ats.c */