changes
[oweals/gnunet.git] / src / ats / gnunet-service-ats_addresses.c
1 /*
2      This file is part of GNUnet.
3      (C) 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 /**
22  * @file ats/gnunet-service-ats_addresses.c
23  * @brief ats service address management
24  * @author Matthias Wachs
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
28 #include "gnunet_ats_service.h"
29 #include "gnunet-service-ats.h"
30 #include "gnunet-service-ats_addresses.h"
31 #include "gnunet-service-ats_performance.h"
32 #include "gnunet-service-ats_scheduling.h"
33 #include "gnunet-service-ats_reservations.h"
34 #if HAVE_LIBGLPK
35 #include "gnunet-service-ats_addresses_mlp.h"
36 #endif
37
38 #define VERBOSE GNUNET_NO
39
40 #define ATS_BLOCKING_DELTA GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 100)
41
42 enum ATS_Mode
43 {
44   /*
45    * Assign each peer an equal amount of bandwidth (bw)
46    *
47    * bw_per_peer = bw_total / #active addresses
48    */
49   SIMPLE,
50
51   /*
52    * Use MLP solver to assign bandwidth
53    */
54   MLP
55 };
56
57 static struct GNUNET_CONTAINER_MultiHashMap *addresses;
58
59 #if HAVE_LIBGLPK
60 static struct GAS_MLP_Handle *mlp;
61 #endif
62
63 static unsigned long long wan_quota_in;
64
65 static unsigned long long wan_quota_out;
66
67 static unsigned int active_addr_count;
68
69 static int ats_mode;
70
71 static int running;
72
73
74 static void
75 send_bw_notification (struct ATS_Address *aa)
76 {
77   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New bandwidth for peer %s is %u/%u\n",
78               GNUNET_i2s (&aa->peer), ntohl (aa->assigned_bw_in.value__),
79               ntohl (aa->assigned_bw_out.value__));
80   GAS_scheduling_transmit_address_suggestion (&aa->peer, aa->plugin, aa->addr,
81                                               aa->addr_len, aa->session_id,
82                                               aa->ats, aa->ats_count,
83                                               aa->assigned_bw_out,
84                                               aa->assigned_bw_in);
85   GAS_reservations_set_bandwidth (&aa->peer, aa->assigned_bw_in);
86   GAS_performance_notify_clients (&aa->peer, aa->plugin, aa->addr, aa->addr_len,
87                                   aa->ats, aa->ats_count, aa->assigned_bw_out,
88                                   aa->assigned_bw_in);
89 }
90
91 /**
92  * Update a bandwidth assignment for a peer.  This trivial method currently
93  * simply assigns the same share to all active connections.
94  *
95  * @param cls unused
96  * @param key unused
97  * @param value the 'struct ATS_Address'
98  * @return GNUNET_OK (continue to iterate)
99  */
100 static int
101 update_bw_simple_it (void *cls, const struct GNUNET_HashCode * key, void *value)
102 {
103   struct ATS_Address *aa = value;
104
105   if (GNUNET_YES != aa->active)
106     return GNUNET_OK;
107   GNUNET_assert (active_addr_count > 0);
108
109
110   /* Simple method */
111   aa->assigned_bw_in.value__ = htonl (wan_quota_in / active_addr_count);
112   aa->assigned_bw_out.value__ = htonl (wan_quota_out / active_addr_count);
113
114   send_bw_notification (aa);
115
116   return GNUNET_OK;
117 }
118
119
120 /**
121  * Some (significant) input changed, recalculate bandwidth assignment
122  * for all peers.
123  */
124 static void
125 recalculate_assigned_bw ()
126 {
127   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
128               "Recalculating bandwidth for all active connections\n");
129   GNUNET_STATISTICS_update (GSA_stats, "# bandwidth recalculations performed",
130                             1, GNUNET_NO);
131   GNUNET_STATISTICS_set (GSA_stats, "# active addresses", active_addr_count,
132                          GNUNET_NO);
133
134   GNUNET_CONTAINER_multihashmap_iterate (addresses, &update_bw_simple_it, NULL);
135 }
136
137 /**
138  * Free the given address
139  * @param addr address to destroy
140  */
141 static void
142 free_address (struct ATS_Address *addr)
143 {
144   GNUNET_free_non_null (addr->ats);
145   GNUNET_free (addr->plugin);
146   GNUNET_free (addr);
147 }
148
149 /**
150  * Create a ATS_address with the given information
151  * @param peer peer
152  * @param plugin_name plugin
153  * @param plugin_addr address
154  * @param plugin_addr_len address length
155  * @param session_id session
156  * @return the ATS_Address
157  */
158 static struct ATS_Address *
159 create_address (const struct GNUNET_PeerIdentity *peer,
160                 const char *plugin_name,
161                 const void *plugin_addr, size_t plugin_addr_len,
162                 uint32_t session_id)
163 {
164   struct ATS_Address *aa = NULL;
165
166   aa = GNUNET_malloc (sizeof (struct ATS_Address) + plugin_addr_len);
167   aa->peer = *peer;
168   aa->addr_len = plugin_addr_len;
169   aa->addr = &aa[1];
170   memcpy (&aa[1], plugin_addr, plugin_addr_len);
171   aa->plugin = GNUNET_strdup (plugin_name);
172   aa->session_id = session_id;
173   return aa;
174 }
175
176
177 /**
178  * Destroy the given address.
179  *
180  * @param addr address to destroy
181  * @return GNUNET_YES if bandwidth allocations should be recalcualted
182  */
183 static int
184 destroy_address (struct ATS_Address *addr)
185 {
186   int ret;
187
188   ret = GNUNET_NO;
189   GNUNET_assert (GNUNET_YES ==
190                  GNUNET_CONTAINER_multihashmap_remove (addresses,
191                                                        &addr->peer.hashPubKey,
192                                                        addr));
193
194 #if HAVE_LIBGLPK
195   if (ats_mode == MLP)
196     GAS_mlp_address_delete (mlp, addresses, addr);
197 #endif
198
199   if (GNUNET_YES == addr->active)
200   {
201     active_addr_count--;
202     addr->active = GNUNET_NO;
203     ret = GNUNET_YES;
204   }
205   free_address (addr);
206   return ret;
207 }
208
209
210 struct CompareAddressContext
211 {
212   const struct ATS_Address *search;
213
214   /* exact_address != NULL if address and session is equal */
215   struct ATS_Address *exact_address;
216   /* exact_address != NULL if address and session is 0 */
217   struct ATS_Address *base_address;
218 };
219
220
221 static int
222 compare_address_it (void *cls, const struct GNUNET_HashCode * key, void *value)
223 {
224   struct CompareAddressContext *cac = cls;
225   struct ATS_Address *aa = value;
226
227   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Comparing peer %4s: address length %u session %u <-> address length %u session %u\n",
228       GNUNET_h2s (key),
229       aa->addr_len, aa->session_id,
230       cac->search->addr_len, cac->search->session_id);
231
232   /* Find an matching exact address:
233    *
234    * Compare by:
235    * aa->addr_len == cac->search->addr_len
236    * aa->plugin == cac->search->plugin
237    * aa->addr == cac->search->addr
238    * aa->session == cac->search->session
239    *
240    * return as exact address
241    */
242   if ((aa->addr_len == cac->search->addr_len) && (0 == strcmp (aa->plugin, cac->search->plugin)))
243   {
244       if ((0 == memcmp (aa->addr, cac->search->addr, aa->addr_len)) && (aa->session_id == cac->search->session_id))
245         cac->exact_address = aa;
246   }
247
248   /* Find an matching base address:
249    *
250    * Properties:
251    *
252    * aa->session_id == 0
253    *
254    * Compare by:
255    * aa->addr_len == cac->search->addr_len
256    * aa->plugin == cac->search->plugin
257    * aa->addr == cac->search->addr
258    *
259    * return as base address
260    */
261   if ((aa->addr_len == cac->search->addr_len) && (0 == strcmp (aa->plugin, cac->search->plugin)))
262   {
263       if ((0 == memcmp (aa->addr, cac->search->addr, aa->addr_len)) && (aa->session_id == 0))
264         cac->base_address = aa;
265   }
266
267   /* Find an matching exact address based on session:
268    *
269    * Properties:
270    *
271    * cac->search->addr_len == 0
272    *
273    * Compare by:
274    * aa->plugin == cac->search->plugin
275    * aa->session_id == cac->search->session_id
276    *
277    * return as exact address
278    */
279   if (0 == cac->search->addr_len)
280   {
281       if ((0 == strcmp (aa->plugin, cac->search->plugin)) && (aa->session_id == cac->search->session_id))
282         cac->exact_address = aa;
283   }
284
285   if (cac->exact_address == NULL)
286     return GNUNET_YES; /* Continue iteration to find exact address */
287   else
288     return GNUNET_NO; /* Stop iteration since we have an exact address */
289 }
290
291
292 /**
293  * Find an existing equivalent address record.
294  * Compares by peer identity and network address OR by session ID
295  * (one of the two must match).
296  *
297  * @param peer peer to lookup addresses for
298  * @param addr existing address record
299  * @return existing address record, NULL for none
300  */
301 struct ATS_Address *
302 find_address (const struct GNUNET_PeerIdentity *peer,
303               const struct ATS_Address *addr)
304 {
305   struct CompareAddressContext cac;
306
307   cac.exact_address = NULL;
308   cac.base_address = NULL;
309   cac.search = addr;
310   GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey,
311                                               &compare_address_it, &cac);
312
313 #if 0
314   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
315               "Found exact address: %s           base address: %s\n",
316               (cac.exact_address != NULL) ? "YES" : "NO",
317               (cac.base_address != NULL) ? "YES" : "NO");
318 #endif
319   if (cac.exact_address == NULL)
320     return cac.base_address;
321   return cac.exact_address;
322 }
323
324
325 static struct ATS_Address *
326 lookup_address (const struct GNUNET_PeerIdentity *peer,
327                 const char *plugin_name, const void *plugin_addr,
328                 size_t plugin_addr_len, uint32_t session_id,
329                 const struct GNUNET_ATS_Information *atsi,
330                 uint32_t atsi_count)
331 {
332   struct ATS_Address *aa;
333   struct ATS_Address *old;
334
335   aa = create_address (peer,
336                        plugin_name,
337                        plugin_addr, plugin_addr_len,
338                        session_id);
339
340   aa->mlp_information = NULL;
341   aa->ats = GNUNET_malloc (atsi_count * sizeof (struct GNUNET_ATS_Information));
342   aa->ats_count = atsi_count;
343   memcpy (aa->ats, atsi, atsi_count * sizeof (struct GNUNET_ATS_Information));
344
345   /* Get existing address or address with session == 0 */
346   old = find_address (peer, aa);
347   free_address (aa);
348   if (old == NULL)
349   {
350     return NULL;
351   }
352   else if (old->session_id != session_id)
353   {
354     return NULL;
355   }
356   return old;
357 }
358
359 static int
360 compare_address_session_it (void *cls, const struct GNUNET_HashCode * key, void *value)
361 {
362   struct CompareAddressContext *cac = cls;
363   struct ATS_Address *aa = value;
364
365   if ((aa->addr_len == cac->search->addr_len) && (0 == strcmp (aa->plugin, cac->search->plugin)))
366   {
367       if ((0 == memcmp (aa->addr, cac->search->addr, aa->addr_len)) && (aa->session_id == cac->search->session_id))
368       {
369         cac->exact_address = aa;
370         return GNUNET_NO;
371       }
372   }
373   return GNUNET_YES;
374 }
375
376
377 /**
378  * Find an existing equivalent address record.
379  * Compares by peer identity and network address AND by session ID
380  * (one of the two must match).
381  *
382  * @param peer peer to lookup addresses for
383  * @param addr existing address record
384  * @return existing address record, NULL for none
385  */
386 struct ATS_Address *
387 find_exact_address (const struct GNUNET_PeerIdentity *peer,
388               const struct ATS_Address *addr)
389 {
390   struct CompareAddressContext cac;
391
392   cac.exact_address = NULL;
393   cac.search = addr;
394   GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey,
395                                               &compare_address_session_it, &cac);
396   return cac.exact_address;
397 }
398
399
400 void
401 GAS_addresses_add (const struct GNUNET_PeerIdentity *peer,
402                       const char *plugin_name, const void *plugin_addr,
403                       size_t plugin_addr_len, uint32_t session_id,
404                       const struct GNUNET_ATS_Information *atsi,
405                       uint32_t atsi_count)
406 {
407   struct ATS_Address *aa;
408   struct ATS_Address *old;
409
410   if (GNUNET_NO == running)
411     return;
412
413   GNUNET_assert (NULL != addresses);
414
415   aa = create_address (peer,
416                        plugin_name,
417                        plugin_addr, plugin_addr_len,
418                        session_id);
419
420   aa->mlp_information = NULL;
421   aa->ats = GNUNET_malloc (atsi_count * sizeof (struct GNUNET_ATS_Information));
422   aa->ats_count = atsi_count;
423   memcpy (aa->ats, atsi, atsi_count * sizeof (struct GNUNET_ATS_Information));
424
425   /* Get existing address or address with session == 0 */
426   old = find_address (peer, aa);
427   if (old == NULL)
428   {
429     /* We have a new address */
430     GNUNET_assert (GNUNET_OK ==
431                    GNUNET_CONTAINER_multihashmap_put (addresses,
432                                                       &peer->hashPubKey, aa,
433                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
434     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Added new address for peer `%s' session id %u, %p\n",
435                 GNUNET_i2s (peer), session_id, aa);
436     return;
437   }
438
439   if (old->session_id == 0)
440   {
441     /* We have a base address with out an session, update this address */
442     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
443               "Updated existing address for peer `%s' %p with new session %u\n",
444               GNUNET_i2s (peer), old, session_id);
445     GNUNET_free_non_null (old->ats);
446     old->session_id = session_id;
447     old->ats = NULL;
448     old->ats_count = 0;
449     old->ats = aa->ats;
450     old->ats_count = aa->ats_count;
451     GNUNET_free (aa->plugin);
452     GNUNET_free (aa);
453     return;
454   }
455
456   /* This address and session is already existing */
457   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
458             "Added already existing address for peer `%s' `%s' %p with new session %u\n",
459             GNUNET_i2s (peer), plugin_name, session_id);
460   GNUNET_break (0);
461 }
462
463
464 void
465 GAS_addresses_update (const struct GNUNET_PeerIdentity *peer,
466                       const char *plugin_name, const void *plugin_addr,
467                       size_t plugin_addr_len, uint32_t session_id,
468                       const struct GNUNET_ATS_Information *atsi,
469                       uint32_t atsi_count)
470 {
471   struct ATS_Address *old;
472   uint32_t i;
473
474   if (GNUNET_NO == running)
475     return;
476
477   GNUNET_assert (NULL != addresses);
478
479   /* Get existing address */
480   old = lookup_address (peer, plugin_name, plugin_addr, plugin_addr_len,
481                        session_id, atsi, atsi_count);
482   if (old == NULL)
483   {
484     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Tried to update unknown address for peer `%s' `%s' session id %u\n",
485                 GNUNET_i2s (peer), plugin_name, session_id);
486     GNUNET_break (0);
487     return;
488   }
489
490   for (i = 0; i < atsi_count; i++)
491     switch (ntohl (atsi[i].type))
492     {
493     case GNUNET_ATS_UTILIZATION_UP:
494       old->atsp_utilization_out.value__ = atsi[i].value;
495       break;
496     case GNUNET_ATS_UTILIZATION_DOWN:
497       old->atsp_utilization_in.value__ = atsi[i].value;
498       break;
499     case GNUNET_ATS_QUALITY_NET_DELAY:
500       old->atsp_latency.rel_value = ntohl (atsi[i].value);
501       break;
502     case GNUNET_ATS_QUALITY_NET_DISTANCE:
503       old->atsp_distance = ntohl (atsi[i].value);
504       break;
505     case GNUNET_ATS_COST_WAN:
506       old->atsp_cost_wan = ntohl (atsi[i].value);
507       break;
508     case GNUNET_ATS_COST_LAN:
509       old->atsp_cost_lan = ntohl (atsi[i].value);
510       break;
511     case GNUNET_ATS_COST_WLAN:
512       old->atsp_cost_wlan = ntohl (atsi[i].value);
513       break;
514     case GNUNET_ATS_NETWORK_TYPE:
515       old->atsp_network_type = ntohl (atsi[i].value);
516       break;
517
518     default:
519       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
520                   "Received unsupported ATS type %u\n", ntohl (atsi[i].type));
521       GNUNET_break (0);
522       break;
523     }
524 #if HAVE_LIBGLPK
525   if (ats_mode == MLP)
526     GAS_mlp_address_update (mlp, addresses, old);
527 #endif
528 }
529
530
531 /**
532  * Delete an address
533  *
534  * If session != 0, just the session is deleted, the address itself still exists
535  * If session == 0, remove full address
536  * If session == 0 and addrlen == 0, destroy inbound address
537  *
538  * @param cls unused
539  * @param key unused
540  * @param value the 'struct ATS_Address'
541  * @return GNUNET_OK (continue to iterate)
542  */
543 static int
544 destroy_by_session_id (void *cls, const struct GNUNET_HashCode * key, void *value)
545 {
546   const struct ATS_Address *info = cls;
547   struct ATS_Address *aa = value;
548
549   GNUNET_assert (0 ==
550                  memcmp (&aa->peer, &info->peer,
551                          sizeof (struct GNUNET_PeerIdentity)));
552   /* session == 0, remove full address  */
553   if ((info->session_id == 0) && (0 == strcmp (info->plugin, aa->plugin)) &&
554       (aa->addr_len == info->addr_len) &&
555       (0 == memcmp (info->addr, aa->addr, aa->addr_len)))
556   {
557
558     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
559                 "Deleting address for peer `%s': `%s' %u\n",
560                 GNUNET_i2s (&aa->peer), aa->plugin, aa->session_id);
561
562     if (GNUNET_YES == destroy_address (aa))
563       recalculate_assigned_bw ();
564     return GNUNET_OK;
565   }
566   /* session != 0, just remove session */
567   if (aa->session_id != info->session_id)
568     return GNUNET_OK;           /* irrelevant */
569   if (aa->session_id != 0)
570     GNUNET_break (0 == strcmp (info->plugin, aa->plugin));
571   /* session died */
572 #if VERBOSE
573   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
574               "Deleting session for peer `%s': `%s' %u\n",
575               GNUNET_i2s (&aa->peer), aa->plugin, aa->session_id);
576 #endif
577   aa->session_id = 0;
578
579   if (GNUNET_YES == aa->active)
580   {
581     aa->active = GNUNET_NO;
582     active_addr_count--;
583     recalculate_assigned_bw ();
584   }
585
586   /* session == 0 and addrlen == 0 : destroy address */
587   if (aa->addr_len == 0)
588   {
589 #if VERBOSE
590     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
591                 "Deleting session and address for peer `%s': `%s' %u\n",
592                 GNUNET_i2s (&aa->peer), aa->plugin, aa->session_id);
593 #endif
594     (void) destroy_address (aa);
595   }
596   else
597   {
598     /* session was set to 0, update address */
599 #if HAVE_LIBGLPK
600   if (ats_mode == MLP)
601     GAS_mlp_address_update (mlp, addresses, aa);
602 #endif
603   }
604
605   return GNUNET_OK;
606 }
607
608 void
609 GAS_addresses_destroy (const struct GNUNET_PeerIdentity *peer,
610                        const char *plugin_name, const void *plugin_addr,
611                        size_t plugin_addr_len, uint32_t session_id)
612 {
613   struct ATS_Address *aa;
614   struct ATS_Address *old;
615
616   if (GNUNET_NO == running)
617     return;
618
619   /* Get existing address */
620   old = lookup_address (peer, plugin_name, plugin_addr, plugin_addr_len,
621                        session_id, NULL, 0);
622   if (old == NULL)
623   {
624     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Tried to destroy unknown address for peer `%s' `%s' session id %u\n",
625                 GNUNET_i2s (peer), plugin_name, session_id);
626     GNUNET_break (0);
627     return;
628   }
629
630
631   GNUNET_break (0 < strlen (plugin_name));
632   aa = create_address (peer, plugin_name, plugin_addr, plugin_addr_len, session_id);
633
634   GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey,
635                                               &destroy_by_session_id, aa);
636
637   free_address (aa);
638 }
639
640
641 /**
642  * Find a "good" address to use for a peer.  If we already have an existing
643  * address, we stick to it.  Otherwise, we pick by lowest distance and then
644  * by lowest latency.
645  *
646  * @param cls the 'struct ATS_Address**' where we store the result
647  * @param key unused
648  * @param value another 'struct ATS_Address*' to consider using
649  * @return GNUNET_OK (continue to iterate)
650  */
651 static int
652 find_address_it (void *cls, const struct GNUNET_HashCode * key, void *value)
653 {
654   struct ATS_Address **ap = cls;
655   struct ATS_Address *aa = (struct ATS_Address *) value;
656   struct ATS_Address *ab = *ap;
657   struct GNUNET_TIME_Absolute now;
658
659   now = GNUNET_TIME_absolute_get();
660
661   if (aa->blocked_until.abs_value == GNUNET_TIME_absolute_max (now, aa->blocked_until).abs_value)
662   {
663     /* This address is blocked for suggestion */
664     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
665                 "Address %p blocked for suggestion for %llu ms \n",
666                 aa,
667                 GNUNET_TIME_absolute_get_difference(now, aa->blocked_until).rel_value);
668     return GNUNET_OK;
669   }
670
671   aa->block_interval = GNUNET_TIME_relative_add (aa->block_interval, ATS_BLOCKING_DELTA);
672   aa->blocked_until = GNUNET_TIME_absolute_add (now, aa->block_interval);
673
674   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
675               "Address %p ready for suggestion, block interval now %llu \n", aa, aa->block_interval);
676
677   /* FIXME this is a hack */
678
679
680   if (NULL != ab)
681   {
682     if ((0 == strcmp (ab->plugin, "tcp")) &&
683         (0 == strcmp (aa->plugin, "tcp")))
684     {
685       if ((0 != ab->addr_len) &&
686           (0 == aa->addr_len))
687       {
688         /* saved address was an outbound address, but we have an inbound address */
689         *ap = aa;
690         return GNUNET_OK;
691       }
692       if (0 == ab->addr_len)
693       {
694         /* saved address was an inbound address, so do not overwrite */
695         return GNUNET_OK;
696       }
697     }
698   }
699   /* FIXME end of hack */
700
701   if (NULL == ab)
702   {
703     *ap = aa;
704     return GNUNET_OK;
705   }
706   if ((ntohl (ab->assigned_bw_in.value__) == 0) &&
707       (ntohl (aa->assigned_bw_in.value__) > 0))
708   {
709     /* stick to existing connection */
710     *ap = aa;
711     return GNUNET_OK;
712   }
713   if (ab->atsp_distance > aa->atsp_distance)
714   {
715     /* user shorter distance */
716     *ap = aa;
717     return GNUNET_OK;
718   }
719   if (ab->atsp_latency.rel_value > aa->atsp_latency.rel_value)
720   {
721     /* user lower latency */
722     *ap = aa;
723     return GNUNET_OK;
724   }
725   /* don't care */
726   return GNUNET_OK;
727 }
728
729
730 int
731 GAS_addresses_in_use (const struct GNUNET_PeerIdentity *peer,
732                       const char *plugin_name, const void *plugin_addr,
733                       size_t plugin_addr_len, uint32_t session_id, int in_use)
734 {
735 #if DEBUG_ATS
736   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
737               "Received `%s' message for peer `%s': %i\n", "ADDRESS_IN_USE",
738               GNUNET_i2s (peer), in_use);
739 #endif
740
741   struct ATS_Address *old;
742
743   if (GNUNET_NO == running)
744     return GNUNET_SYSERR;
745
746   old = lookup_address (peer, plugin_name, plugin_addr, plugin_addr_len, session_id, NULL, 0);
747   if (NULL == old)
748   {
749     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
750                 "Trying to set unknown address `%s', %s %u %s \n",
751                 GNUNET_i2s (peer),
752                 plugin_name, session_id,
753                 (GNUNET_NO == in_use) ? "NO" : "YES");
754     GNUNET_break (0);
755     return GNUNET_SYSERR;
756   }
757   if (old->used == in_use)
758   {
759     GNUNET_break (0);
760     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
761                 "Address in use called multiple times for peer `%s': %s -> %s \n",
762                 GNUNET_i2s (peer),
763                 (GNUNET_NO == old->used) ? "NO" : "YES",
764                 (GNUNET_NO == in_use) ? "NO" : "YES");
765     return GNUNET_SYSERR;
766   }
767   old->used = in_use;
768 #if HAVE_LIBGLPK
769   if (ats_mode == MLP)
770      GAS_mlp_address_update (mlp, addresses, old);
771 #endif
772   return GNUNET_OK;
773 }
774
775
776 void request_address_mlp (const struct GNUNET_PeerIdentity *peer)
777 {
778   struct ATS_Address *aa;
779   aa = NULL;
780
781 #if HAVE_GLPK
782   /* Get preferred address from MLP */
783   struct ATS_PreferedAddress * paddr = NULL;
784   paddr = GAS_mlp_get_preferred_address (mlp, addresses, peer);
785   aa = paddr->address;
786   aa->assigned_bw_out = GNUNET_BANDWIDTH_value_init(paddr->bandwidth_out);
787   /* FIXME use bw in value */
788   paddr->bandwidth_in = paddr->bandwidth_out;
789   aa->assigned_bw_in = GNUNET_BANDWIDTH_value_init (paddr->bandwidth_in);
790   GNUNET_free (paddr);
791 #endif
792
793   if (aa == NULL)
794   {
795     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
796                 "Cannot suggest address for peer `%s'\n", GNUNET_i2s (peer));
797     return;
798   }
799   if (aa->active == GNUNET_NO)
800   {
801     aa->active = GNUNET_YES;
802     active_addr_count++;
803
804     send_bw_notification (aa);
805   }
806   else
807   {
808     /* just to be sure... */
809     GAS_scheduling_transmit_address_suggestion (peer, aa->plugin, aa->addr,
810                                                 aa->addr_len, aa->session_id,
811                                                 aa->ats, aa->ats_count,
812                                                 aa->assigned_bw_out,
813                                                 aa->assigned_bw_in);
814   }
815
816 }
817
818 void request_address_simple (const struct GNUNET_PeerIdentity *peer)
819 {
820   struct ATS_Address *aa;
821   aa = NULL;
822
823   /* Get address with: stick to current address, lower distance, lower latency */
824   GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey,
825                                               &find_address_it, &aa);
826   if (aa == NULL)
827   {
828     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
829                 "Cannot suggest address for peer `%s'\n", GNUNET_i2s (peer));
830     return;
831   }
832
833   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
834               "Suggesting address %p for peer `%s'\n", aa, GNUNET_i2s (peer));
835
836   if (aa->active == GNUNET_NO)
837   {
838     aa->active = GNUNET_YES;
839     active_addr_count++;
840     if (ats_mode == SIMPLE)
841     {
842       recalculate_assigned_bw ();
843     }
844   }
845   else
846   {
847     /* just to be sure... */
848     GAS_scheduling_transmit_address_suggestion (peer, aa->plugin, aa->addr,
849                                                 aa->addr_len, aa->session_id,
850                                                 aa->ats, aa->ats_count,
851                                                 aa->assigned_bw_out,
852                                                 aa->assigned_bw_in);
853   }
854 }
855
856
857 void
858 GAS_addresses_request_address (const struct GNUNET_PeerIdentity *peer)
859 {
860   if (GNUNET_NO == running)
861     return;
862
863   if (ats_mode == SIMPLE)
864   {
865     request_address_simple (peer);
866   }
867   if (ats_mode == MLP)
868   {
869     request_address_mlp(peer);
870   }
871 }
872
873
874 static int
875 reset_address_it (void *cls, const struct GNUNET_HashCode * key, void *value)
876 {
877   struct ATS_Address *aa = value;
878
879   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
880               "Resetting interval for peer `%s' address %p from %llu to 0\n", GNUNET_i2s (&aa->peer), aa, aa->block_interval);
881
882   aa->blocked_until = GNUNET_TIME_UNIT_ZERO_ABS;
883   aa->block_interval = GNUNET_TIME_UNIT_ZERO;
884   return GNUNET_OK;
885 }
886
887 void
888 GAS_addresses_handle_backoff_reset (const struct GNUNET_PeerIdentity *peer)
889 {
890   GNUNET_break (GNUNET_SYSERR != GNUNET_CONTAINER_multihashmap_get_multiple (addresses,
891                                               &peer->hashPubKey,
892                                               &reset_address_it,
893                                               NULL));
894 }
895
896
897
898 // FIXME: this function should likely end up in the LP-subsystem and
899 // not with 'addresses' in the future...
900 void
901 GAS_addresses_change_preference (const struct GNUNET_PeerIdentity *peer,
902                                  enum GNUNET_ATS_PreferenceKind kind,
903                                  float score)
904 {
905   if (GNUNET_NO == running)
906     return;
907 #if HAVE_LIBGLPK
908   if (ats_mode == MLP)
909     GAS_mlp_address_change_preference (mlp, peer, kind, score);
910 #endif
911 }
912
913
914
915 /**
916  * Initialize address subsystem.
917  *
918  * @param cfg configuration to use
919  * @param stats the statistics handle to use
920  */
921 void
922 GAS_addresses_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
923                     const struct GNUNET_STATISTICS_Handle *stats)
924 {
925   int mode;
926
927   char *quota_wan_in_str;
928   char *quota_wan_out_str;
929
930   running = GNUNET_NO;
931
932   addresses = GNUNET_CONTAINER_multihashmap_create (128, GNUNET_NO);
933   GNUNET_assert (NULL != addresses);
934
935   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(cfg, "ats", "WAN_QUOTA_IN", &quota_wan_in_str))
936   {
937     if (0 == strcmp(quota_wan_in_str, "unlimited") ||
938         (GNUNET_SYSERR == GNUNET_STRINGS_fancy_size_to_bytes (quota_wan_in_str, &wan_quota_in)))
939       wan_quota_in = (UINT32_MAX) /10;
940
941     GNUNET_free (quota_wan_in_str);
942     quota_wan_in_str = NULL;
943   }
944   else
945   {
946     wan_quota_in = (UINT32_MAX) /10;
947   }
948
949   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(cfg, "ats", "WAN_QUOTA_OUT", &quota_wan_out_str))
950   {
951     if (0 == strcmp(quota_wan_out_str, "unlimited") ||
952         (GNUNET_SYSERR == GNUNET_STRINGS_fancy_size_to_bytes (quota_wan_out_str, &wan_quota_out)))
953       wan_quota_out = (UINT32_MAX) /10;
954
955     GNUNET_free (quota_wan_out_str);
956     quota_wan_out_str = NULL;
957   }
958   else
959   {
960     wan_quota_out = (UINT32_MAX) /10;
961   }
962
963   mode = GNUNET_CONFIGURATION_get_value_yesno (cfg, "ats", "MLP");
964   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "MLP mode %u", mode);
965   switch (mode)
966   {
967     /* MLP = YES */
968     case GNUNET_YES:
969 #if HAVE_LIBGLPK
970       ats_mode = MLP;
971       /* Init the MLP solver with default values */
972       mlp = GAS_mlp_init (cfg, stats, MLP_MAX_EXEC_DURATION, MLP_MAX_ITERATIONS);
973       if (NULL == mlp)
974       {
975         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "MLP mode was configured, but libglpk is not installed, switching to simple mode\n");
976         GNUNET_STATISTICS_update (GSA_stats, "MLP mode enabled", 0, GNUNET_NO);
977         break;
978       }
979       else
980       {
981         GNUNET_STATISTICS_update (GSA_stats, "MLP enabled", 1, GNUNET_NO);
982         break;
983       }
984 #else
985       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "MLP mode was configured, but libglpk is not installed, switching to simple mode");
986       GNUNET_STATISTICS_update (GSA_stats, "MLP enabled", 0, GNUNET_NO);
987       ats_mode = SIMPLE;
988       break;
989 #endif
990     /* MLP = NO */
991     case GNUNET_NO:
992       GNUNET_STATISTICS_update (GSA_stats, "MLP enabled", 0, GNUNET_NO);
993       ats_mode = SIMPLE;
994       break;
995     /* No configuration value */
996     case GNUNET_SYSERR:
997       GNUNET_STATISTICS_update (GSA_stats, "MLP enabled", 0, GNUNET_NO);
998       ats_mode = SIMPLE;
999       break;
1000     default:
1001       break;
1002   }
1003   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ATS started with %s mode\n", (SIMPLE == ats_mode) ? "SIMPLE" : "MLP");
1004   running = GNUNET_YES;
1005 }
1006
1007
1008 /**
1009  * Free memory of address.
1010  *
1011  * @param cls NULL
1012  * @param key peer identity (unused)
1013  * @param value the 'struct ATS_Address' to free
1014  * @return GNUNET_OK (continue to iterate)
1015  */
1016 static int
1017 free_address_it (void *cls, const struct GNUNET_HashCode * key, void *value)
1018 {
1019   struct ATS_Address *aa = value;
1020
1021   destroy_address (aa);
1022   return GNUNET_OK;
1023 }
1024
1025
1026 void
1027 GAS_addresses_destroy_all ()
1028 {
1029   if (GNUNET_NO == running)
1030     return;
1031
1032   if (addresses != NULL)
1033     GNUNET_CONTAINER_multihashmap_iterate (addresses, &free_address_it, NULL);
1034   GNUNET_assert (active_addr_count == 0);
1035 }
1036
1037
1038 /**
1039  * Shutdown address subsystem.
1040  */
1041 void
1042 GAS_addresses_done ()
1043 {
1044   GAS_addresses_destroy_all ();
1045   running = GNUNET_NO;
1046   GNUNET_CONTAINER_multihashmap_destroy (addresses);
1047   addresses = NULL;
1048 #if HAVE_LIBGLPK
1049   if (ats_mode == MLP)
1050   {
1051     GAS_mlp_done (mlp);
1052   }
1053 #endif
1054 }
1055
1056 struct PeerIteratorContext
1057 {
1058   GNUNET_ATS_Peer_Iterator it;
1059   void *it_cls;
1060   struct GNUNET_CONTAINER_MultiHashMap *peers_returned;
1061 };
1062
1063 static int
1064 peer_it (void *cls,
1065          const struct GNUNET_HashCode * key,
1066          void *value)
1067 {
1068   struct PeerIteratorContext *ip_ctx = cls;
1069   struct GNUNET_PeerIdentity tmp;
1070
1071   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(ip_ctx->peers_returned, key))
1072   {
1073       GNUNET_CONTAINER_multihashmap_put(ip_ctx->peers_returned, key, NULL, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
1074       tmp.hashPubKey = (*key);
1075       ip_ctx->it (ip_ctx, &tmp);
1076   }
1077
1078   return GNUNET_OK;
1079 }
1080
1081
1082 void
1083 GAS_addresses_iterate_peers (GNUNET_ATS_Peer_Iterator it, void *it_cls)
1084 {
1085   struct PeerIteratorContext ip_ctx;
1086   unsigned int size;
1087
1088   if (NULL == it)
1089       return;
1090   GNUNET_assert (NULL != addresses);
1091
1092   size = GNUNET_CONTAINER_multihashmap_size(addresses);
1093   if (0 != size)
1094   {
1095     ip_ctx.it = it;
1096     ip_ctx.it_cls = it_cls;
1097     ip_ctx.peers_returned = GNUNET_CONTAINER_multihashmap_create (size, GNUNET_NO);
1098     GNUNET_CONTAINER_multihashmap_iterate (addresses, &peer_it, &ip_ctx);
1099     GNUNET_CONTAINER_multihashmap_destroy (ip_ctx.peers_returned);
1100   }
1101   it (it_cls, NULL);
1102 }
1103
1104 struct PeerInfoIteratorContext
1105 {
1106   GNUNET_ATS_PeerInfo_Iterator it;
1107   void *it_cls;
1108 };
1109
1110 int peerinfo_it (void *cls,
1111                  const struct GNUNET_HashCode * key,
1112                  void *value)
1113 {
1114   struct PeerInfoIteratorContext *pi_ctx = cls;
1115   struct ATS_Address *addr = (struct ATS_Address *)  value;
1116
1117   if (NULL != pi_ctx->it)
1118     pi_ctx->it (pi_ctx->it_cls,
1119                 &addr->peer,
1120                 addr->plugin,
1121                 addr->addr, addr->addr_len,
1122                 addr->ats, addr->ats_count,
1123                 addr->assigned_bw_out,
1124                 addr->assigned_bw_in);
1125   return GNUNET_YES;
1126 }
1127
1128 void
1129 GAS_addresses_get_peer_info (const struct GNUNET_PeerIdentity *peer, GNUNET_ATS_PeerInfo_Iterator pi_it, void *pi_it_cls)
1130 {
1131   struct PeerInfoIteratorContext pi_ctx;
1132   struct GNUNET_BANDWIDTH_Value32NBO zero_bw;
1133   GNUNET_assert (NULL != peer);
1134   GNUNET_assert (NULL != addresses);
1135
1136   zero_bw = GNUNET_BANDWIDTH_value_init (0);
1137   pi_ctx.it = pi_it;
1138   pi_ctx.it_cls = pi_it_cls;
1139
1140   GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey, &peerinfo_it, &pi_ctx);
1141
1142   if (NULL != pi_it)
1143     pi_it (pi_it_cls, NULL, NULL, NULL, 0, NULL, 0, zero_bw, zero_bw);
1144
1145 }
1146
1147
1148 /* end of gnunet-service-ats_addresses.c */