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