790c30e8c880dbe3765612f7d1f32621ba8b6d2a
[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   if (old == NULL)
348   {
349     GNUNET_free (aa);
350     return NULL;
351   }
352   else if (old->session_id != session_id)
353   {
354     GNUNET_free (aa);
355     GNUNET_break (0);
356     return NULL;
357   }
358
359   return old;
360 }
361
362 static int
363 compare_address_session_it (void *cls, const struct GNUNET_HashCode * key, void *value)
364 {
365   struct CompareAddressContext *cac = cls;
366   struct ATS_Address *aa = value;
367
368   if ((aa->addr_len == cac->search->addr_len) && (0 == strcmp (aa->plugin, cac->search->plugin)))
369   {
370       if ((0 == memcmp (aa->addr, cac->search->addr, aa->addr_len)) && (aa->session_id == cac->search->session_id))
371       {
372         cac->exact_address = aa;
373         return GNUNET_NO;
374       }
375   }
376   return GNUNET_YES;
377 }
378
379
380 /**
381  * Find an existing equivalent address record.
382  * Compares by peer identity and network address AND by session ID
383  * (one of the two must match).
384  *
385  * @param peer peer to lookup addresses for
386  * @param addr existing address record
387  * @return existing address record, NULL for none
388  */
389 struct ATS_Address *
390 find_exact_address (const struct GNUNET_PeerIdentity *peer,
391               const struct ATS_Address *addr)
392 {
393   struct CompareAddressContext cac;
394
395   cac.exact_address = NULL;
396   cac.search = addr;
397   GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey,
398                                               &compare_address_session_it, &cac);
399   return cac.exact_address;
400 }
401
402
403 void
404 GAS_addresses_add (const struct GNUNET_PeerIdentity *peer,
405                       const char *plugin_name, const void *plugin_addr,
406                       size_t plugin_addr_len, uint32_t session_id,
407                       const struct GNUNET_ATS_Information *atsi,
408                       uint32_t atsi_count)
409 {
410   struct ATS_Address *aa;
411   struct ATS_Address *old;
412
413   if (GNUNET_NO == running)
414     return;
415
416   GNUNET_assert (NULL != addresses);
417
418   aa = create_address (peer,
419                        plugin_name,
420                        plugin_addr, plugin_addr_len,
421                        session_id);
422
423   aa->mlp_information = NULL;
424   aa->ats = GNUNET_malloc (atsi_count * sizeof (struct GNUNET_ATS_Information));
425   aa->ats_count = atsi_count;
426   memcpy (aa->ats, atsi, atsi_count * sizeof (struct GNUNET_ATS_Information));
427
428   /* Get existing address or address with session == 0 */
429   old = find_address (peer, aa);
430   if (old == NULL)
431   {
432     /* We have a new address */
433     GNUNET_assert (GNUNET_OK ==
434                    GNUNET_CONTAINER_multihashmap_put (addresses,
435                                                       &peer->hashPubKey, aa,
436                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
437     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Added new address for peer `%s' session id %u, %p\n",
438                 GNUNET_i2s (peer), session_id, aa);
439     return;
440   }
441
442   if (old->session_id == 0)
443   {
444     /* We have a base address with out an session, update this address */
445     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
446               "Updated existing address for peer `%s' %p with new session %u\n",
447               GNUNET_i2s (peer), old, session_id);
448     GNUNET_free_non_null (old->ats);
449     old->session_id = session_id;
450     old->ats = NULL;
451     old->ats_count = 0;
452     old->ats = aa->ats;
453     old->ats_count = aa->ats_count;
454     GNUNET_free (aa->plugin);
455     GNUNET_free (aa);
456     return;
457   }
458
459   /* This address and session is already existing */
460   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
461             "Added already existing address for peer `%s' `%s' %p with new session %u\n",
462             GNUNET_i2s (peer), plugin_name, session_id);
463   GNUNET_break (0);
464 }
465
466
467 void
468 GAS_addresses_update (const struct GNUNET_PeerIdentity *peer,
469                       const char *plugin_name, const void *plugin_addr,
470                       size_t plugin_addr_len, uint32_t session_id,
471                       const struct GNUNET_ATS_Information *atsi,
472                       uint32_t atsi_count)
473 {
474   struct ATS_Address *old;
475   uint32_t i;
476
477   if (GNUNET_NO == running)
478     return;
479
480   GNUNET_assert (NULL != addresses);
481
482   /* Get existing address */
483   old = lookup_address (peer, plugin_name, plugin_addr, plugin_addr_len,
484                        session_id, atsi, atsi_count);
485   if (old == NULL)
486   {
487     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Tried to update unknown address for peer `%s' `%s' session id %u\n",
488                 GNUNET_i2s (peer), plugin_name, session_id);
489     GNUNET_break (0);
490     return;
491   }
492
493   for (i = 0; i < atsi_count; i++)
494     switch (ntohl (atsi[i].type))
495     {
496     case GNUNET_ATS_UTILIZATION_UP:
497       old->atsp_utilization_out.value__ = atsi[i].value;
498       break;
499     case GNUNET_ATS_UTILIZATION_DOWN:
500       old->atsp_utilization_in.value__ = atsi[i].value;
501       break;
502     case GNUNET_ATS_QUALITY_NET_DELAY:
503       old->atsp_latency.rel_value = ntohl (atsi[i].value);
504       break;
505     case GNUNET_ATS_QUALITY_NET_DISTANCE:
506       old->atsp_distance = ntohl (atsi[i].value);
507       break;
508     case GNUNET_ATS_COST_WAN:
509       old->atsp_cost_wan = ntohl (atsi[i].value);
510       break;
511     case GNUNET_ATS_COST_LAN:
512       old->atsp_cost_lan = ntohl (atsi[i].value);
513       break;
514     case GNUNET_ATS_COST_WLAN:
515       old->atsp_cost_wlan = ntohl (atsi[i].value);
516       break;
517     case GNUNET_ATS_NETWORK_TYPE:
518       old->atsp_network_type = ntohl (atsi[i].value);
519       break;
520
521     default:
522       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
523                   "Received unsupported ATS type %u\n", ntohl (atsi[i].type));
524       GNUNET_break (0);
525       break;
526     }
527 #if HAVE_LIBGLPK
528   if (ats_mode == MLP)
529     GAS_mlp_address_update (mlp, addresses, old);
530 #endif
531 }
532
533
534 /**
535  * Delete an address
536  *
537  * If session != 0, just the session is deleted, the address itself still exists
538  * If session == 0, remove full address
539  * If session == 0 and addrlen == 0, destroy inbound address
540  *
541  * @param cls unused
542  * @param key unused
543  * @param value the 'struct ATS_Address'
544  * @return GNUNET_OK (continue to iterate)
545  */
546 static int
547 destroy_by_session_id (void *cls, const struct GNUNET_HashCode * key, void *value)
548 {
549   const struct ATS_Address *info = cls;
550   struct ATS_Address *aa = value;
551
552   GNUNET_assert (0 ==
553                  memcmp (&aa->peer, &info->peer,
554                          sizeof (struct GNUNET_PeerIdentity)));
555   /* session == 0, remove full address  */
556   if ((info->session_id == 0) && (0 == strcmp (info->plugin, aa->plugin)) &&
557       (aa->addr_len == info->addr_len) &&
558       (0 == memcmp (info->addr, aa->addr, aa->addr_len)))
559   {
560
561     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
562                 "Deleting address for peer `%s': `%s' %u\n",
563                 GNUNET_i2s (&aa->peer), aa->plugin, aa->session_id);
564
565     if (GNUNET_YES == destroy_address (aa))
566       recalculate_assigned_bw ();
567     return GNUNET_OK;
568   }
569   /* session != 0, just remove session */
570   if (aa->session_id != info->session_id)
571     return GNUNET_OK;           /* irrelevant */
572   if (aa->session_id != 0)
573     GNUNET_break (0 == strcmp (info->plugin, aa->plugin));
574   /* session died */
575 #if VERBOSE
576   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
577               "Deleting session for peer `%s': `%s' %u\n",
578               GNUNET_i2s (&aa->peer), aa->plugin, aa->session_id);
579 #endif
580   aa->session_id = 0;
581
582   if (GNUNET_YES == aa->active)
583   {
584     aa->active = GNUNET_NO;
585     active_addr_count--;
586     recalculate_assigned_bw ();
587   }
588
589   /* session == 0 and addrlen == 0 : destroy address */
590   if (aa->addr_len == 0)
591   {
592 #if VERBOSE
593     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
594                 "Deleting session and address for peer `%s': `%s' %u\n",
595                 GNUNET_i2s (&aa->peer), aa->plugin, aa->session_id);
596 #endif
597     (void) destroy_address (aa);
598   }
599   else
600   {
601     /* session was set to 0, update address */
602 #if HAVE_LIBGLPK
603   if (ats_mode == MLP)
604     GAS_mlp_address_update (mlp, addresses, aa);
605 #endif
606   }
607
608   return GNUNET_OK;
609 }
610
611 void
612 GAS_addresses_destroy (const struct GNUNET_PeerIdentity *peer,
613                        const char *plugin_name, const void *plugin_addr,
614                        size_t plugin_addr_len, uint32_t session_id)
615 {
616   struct ATS_Address *aa;
617   struct ATS_Address *old;
618
619   if (GNUNET_NO == running)
620     return;
621
622   /* Get existing address */
623   old = lookup_address (peer, plugin_name, plugin_addr, plugin_addr_len,
624                        session_id, NULL, 0);
625   if (old == NULL)
626   {
627     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Tried to destroy unknown address for peer `%s' `%s' session id %u\n",
628                 GNUNET_i2s (peer), plugin_name, session_id);
629     GNUNET_break (0);
630     return;
631   }
632
633
634   GNUNET_break (0 < strlen (plugin_name));
635   aa = create_address (peer, plugin_name, plugin_addr, plugin_addr_len, session_id);
636
637   GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey,
638                                               &destroy_by_session_id, aa);
639
640   free_address (aa);
641 }
642
643
644 /**
645  * Find a "good" address to use for a peer.  If we already have an existing
646  * address, we stick to it.  Otherwise, we pick by lowest distance and then
647  * by lowest latency.
648  *
649  * @param cls the 'struct ATS_Address**' where we store the result
650  * @param key unused
651  * @param value another 'struct ATS_Address*' to consider using
652  * @return GNUNET_OK (continue to iterate)
653  */
654 static int
655 find_address_it (void *cls, const struct GNUNET_HashCode * key, void *value)
656 {
657   struct ATS_Address **ap = cls;
658   struct ATS_Address *aa = (struct ATS_Address *) value;
659   struct ATS_Address *ab = *ap;
660   struct GNUNET_TIME_Absolute now;
661
662   now = GNUNET_TIME_absolute_get();
663
664   if (aa->blocked_until.abs_value == GNUNET_TIME_absolute_max (now, aa->blocked_until).abs_value)
665   {
666     /* This address is blocked for suggestion */
667     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
668                 "Address %p blocked for suggestion for %llu ms \n",
669                 aa,
670                 GNUNET_TIME_absolute_get_difference(now, aa->blocked_until).rel_value);
671     return GNUNET_OK;
672   }
673
674   aa->block_interval = GNUNET_TIME_relative_add (aa->block_interval, ATS_BLOCKING_DELTA);
675   aa->blocked_until = GNUNET_TIME_absolute_add (now, aa->block_interval);
676
677   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
678               "Address %p ready for suggestion, block interval now %llu \n", aa, aa->block_interval);
679
680   /* FIXME this is a hack */
681
682
683   if (NULL != ab)
684   {
685     if ((0 == strcmp (ab->plugin, "tcp")) &&
686         (0 == strcmp (aa->plugin, "tcp")))
687     {
688       if ((0 != ab->addr_len) &&
689           (0 == aa->addr_len))
690       {
691         /* saved address was an outbound address, but we have an inbound address */
692         *ap = aa;
693         return GNUNET_OK;
694       }
695       if (0 == ab->addr_len)
696       {
697         /* saved address was an inbound address, so do not overwrite */
698         return GNUNET_OK;
699       }
700     }
701   }
702   /* FIXME end of hack */
703
704   if (NULL == ab)
705   {
706     *ap = aa;
707     return GNUNET_OK;
708   }
709   if ((ntohl (ab->assigned_bw_in.value__) == 0) &&
710       (ntohl (aa->assigned_bw_in.value__) > 0))
711   {
712     /* stick to existing connection */
713     *ap = aa;
714     return GNUNET_OK;
715   }
716   if (ab->atsp_distance > aa->atsp_distance)
717   {
718     /* user shorter distance */
719     *ap = aa;
720     return GNUNET_OK;
721   }
722   if (ab->atsp_latency.rel_value > aa->atsp_latency.rel_value)
723   {
724     /* user lower latency */
725     *ap = aa;
726     return GNUNET_OK;
727   }
728   /* don't care */
729   return GNUNET_OK;
730 }
731
732
733 int
734 GAS_addresses_in_use (const struct GNUNET_PeerIdentity *peer,
735                       const char *plugin_name, const void *plugin_addr,
736                       size_t plugin_addr_len, uint32_t session_id, int in_use)
737 {
738 #if DEBUG_ATS
739   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
740               "Received `%s' message for peer `%s': %i\n", "ADDRESS_IN_USE",
741               GNUNET_i2s (peer), in_use);
742 #endif
743
744   struct ATS_Address *old;
745
746   if (GNUNET_NO == running)
747     return GNUNET_SYSERR;
748
749   old = lookup_address (peer, plugin_name, plugin_addr, plugin_addr_len, session_id, NULL, 0);
750   if (NULL == old)
751   {
752     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
753                 "Trying to set unknown address `%s', %s %u %s \n",
754                 GNUNET_i2s (peer),
755                 plugin_name, session_id,
756                 (GNUNET_NO == in_use) ? "NO" : "YES");
757     GNUNET_break (0);
758     return GNUNET_SYSERR;
759   }
760   if (old->used == in_use)
761   {
762     GNUNET_break (0);
763     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
764                 "Address in use called multiple times for peer `%s': %s -> %s \n",
765                 GNUNET_i2s (peer),
766                 (GNUNET_NO == old->used) ? "NO" : "YES",
767                 (GNUNET_NO == in_use) ? "NO" : "YES");
768     return GNUNET_SYSERR;
769   }
770   old->used = in_use;
771 #if HAVE_LIBGLPK
772   if (ats_mode == MLP)
773      GAS_mlp_address_update (mlp, addresses, old);
774 #endif
775   return GNUNET_OK;
776 }
777
778
779 void request_address_mlp (const struct GNUNET_PeerIdentity *peer)
780 {
781   struct ATS_Address *aa;
782   aa = NULL;
783
784 #if HAVE_GLPK
785   /* Get preferred address from MLP */
786   struct ATS_PreferedAddress * paddr = NULL;
787   paddr = GAS_mlp_get_preferred_address (mlp, addresses, peer);
788   aa = paddr->address;
789   aa->assigned_bw_out = GNUNET_BANDWIDTH_value_init(paddr->bandwidth_out);
790   /* FIXME use bw in value */
791   paddr->bandwidth_in = paddr->bandwidth_out;
792   aa->assigned_bw_in = GNUNET_BANDWIDTH_value_init (paddr->bandwidth_in);
793   GNUNET_free (paddr);
794 #endif
795
796   if (aa == NULL)
797   {
798     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
799                 "Cannot suggest address for peer `%s'\n", GNUNET_i2s (peer));
800     return;
801   }
802   if (aa->active == GNUNET_NO)
803   {
804     aa->active = GNUNET_YES;
805     active_addr_count++;
806
807     send_bw_notification (aa);
808   }
809   else
810   {
811     /* just to be sure... */
812     GAS_scheduling_transmit_address_suggestion (peer, aa->plugin, aa->addr,
813                                                 aa->addr_len, aa->session_id,
814                                                 aa->ats, aa->ats_count,
815                                                 aa->assigned_bw_out,
816                                                 aa->assigned_bw_in);
817   }
818
819 }
820
821 void request_address_simple (const struct GNUNET_PeerIdentity *peer)
822 {
823   struct ATS_Address *aa;
824   aa = NULL;
825
826   /* Get address with: stick to current address, lower distance, lower latency */
827   GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey,
828                                               &find_address_it, &aa);
829   if (aa == NULL)
830   {
831     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
832                 "Cannot suggest address for peer `%s'\n", GNUNET_i2s (peer));
833     return;
834   }
835
836   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
837               "Suggesting address %p for peer `%s'\n", aa, GNUNET_i2s (peer));
838
839   if (aa->active == GNUNET_NO)
840   {
841     aa->active = GNUNET_YES;
842     active_addr_count++;
843     if (ats_mode == SIMPLE)
844     {
845       recalculate_assigned_bw ();
846     }
847   }
848   else
849   {
850     /* just to be sure... */
851     GAS_scheduling_transmit_address_suggestion (peer, aa->plugin, aa->addr,
852                                                 aa->addr_len, aa->session_id,
853                                                 aa->ats, aa->ats_count,
854                                                 aa->assigned_bw_out,
855                                                 aa->assigned_bw_in);
856   }
857 }
858
859
860 void
861 GAS_addresses_request_address (const struct GNUNET_PeerIdentity *peer)
862 {
863   if (GNUNET_NO == running)
864     return;
865
866   if (ats_mode == SIMPLE)
867   {
868     request_address_simple (peer);
869   }
870   if (ats_mode == MLP)
871   {
872     request_address_mlp(peer);
873   }
874 }
875
876
877 static int
878 reset_address_it (void *cls, const struct GNUNET_HashCode * key, void *value)
879 {
880   struct ATS_Address *aa = value;
881
882   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
883               "Resetting interval for peer `%s' address %p from %llu to 0\n", GNUNET_i2s (&aa->peer), aa, aa->block_interval);
884
885   aa->blocked_until = GNUNET_TIME_UNIT_ZERO_ABS;
886   aa->block_interval = GNUNET_TIME_UNIT_ZERO;
887   return GNUNET_OK;
888 }
889
890 void
891 GAS_addresses_handle_backoff_reset (const struct GNUNET_PeerIdentity *peer)
892 {
893   GNUNET_break (GNUNET_SYSERR != GNUNET_CONTAINER_multihashmap_get_multiple (addresses,
894                                               &peer->hashPubKey,
895                                               &reset_address_it,
896                                               NULL));
897 }
898
899
900
901 // FIXME: this function should likely end up in the LP-subsystem and
902 // not with 'addresses' in the future...
903 void
904 GAS_addresses_change_preference (const struct GNUNET_PeerIdentity *peer,
905                                  enum GNUNET_ATS_PreferenceKind kind,
906                                  float score)
907 {
908   if (GNUNET_NO == running)
909     return;
910 #if HAVE_LIBGLPK
911   if (ats_mode == MLP)
912     GAS_mlp_address_change_preference (mlp, peer, kind, score);
913 #endif
914 }
915
916
917
918 /**
919  * Initialize address subsystem.
920  *
921  * @param cfg configuration to use
922  * @param stats the statistics handle to use
923  */
924 void
925 GAS_addresses_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
926                     const struct GNUNET_STATISTICS_Handle *stats)
927 {
928   int mode;
929
930   char *quota_wan_in_str;
931   char *quota_wan_out_str;
932
933   running = GNUNET_NO;
934
935   addresses = GNUNET_CONTAINER_multihashmap_create (128);
936   GNUNET_assert (NULL != addresses);
937
938   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(cfg, "ats", "WAN_QUOTA_IN", &quota_wan_in_str))
939   {
940     if (0 == strcmp(quota_wan_in_str, "unlimited") ||
941         (GNUNET_SYSERR == GNUNET_STRINGS_fancy_size_to_bytes (quota_wan_in_str, &wan_quota_in)))
942       wan_quota_in = (UINT32_MAX) /10;
943
944     GNUNET_free (quota_wan_in_str);
945     quota_wan_in_str = NULL;
946   }
947   else
948   {
949     wan_quota_in = (UINT32_MAX) /10;
950   }
951
952   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(cfg, "ats", "WAN_QUOTA_OUT", &quota_wan_out_str))
953   {
954     if (0 == strcmp(quota_wan_out_str, "unlimited") ||
955         (GNUNET_SYSERR == GNUNET_STRINGS_fancy_size_to_bytes (quota_wan_out_str, &wan_quota_out)))
956       wan_quota_out = (UINT32_MAX) /10;
957
958     GNUNET_free (quota_wan_out_str);
959     quota_wan_out_str = NULL;
960   }
961   else
962   {
963     wan_quota_out = (UINT32_MAX) /10;
964   }
965
966   mode = GNUNET_CONFIGURATION_get_value_yesno (cfg, "ats", "MLP");
967   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "MLP mode %u", mode);
968   switch (mode)
969   {
970     /* MLP = YES */
971     case GNUNET_YES:
972 #if HAVE_LIBGLPK
973       ats_mode = MLP;
974       /* Init the MLP solver with default values */
975       mlp = GAS_mlp_init (cfg, stats, MLP_MAX_EXEC_DURATION, MLP_MAX_ITERATIONS);
976       if (NULL == mlp)
977       {
978         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "MLP mode was configured, but libglpk is not installed, switching to simple mode\n");
979         GNUNET_STATISTICS_update (GSA_stats, "MLP mode enabled", 0, GNUNET_NO);
980         break;
981       }
982       else
983       {
984         GNUNET_STATISTICS_update (GSA_stats, "MLP enabled", 1, GNUNET_NO);
985         break;
986       }
987 #else
988       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "MLP mode was configured, but libglpk is not installed, switching to simple mode");
989       GNUNET_STATISTICS_update (GSA_stats, "MLP enabled", 0, GNUNET_NO);
990       ats_mode = SIMPLE;
991       break;
992 #endif
993     /* MLP = NO */
994     case GNUNET_NO:
995       GNUNET_STATISTICS_update (GSA_stats, "MLP enabled", 0, GNUNET_NO);
996       ats_mode = SIMPLE;
997       break;
998     /* No configuration value */
999     case GNUNET_SYSERR:
1000       GNUNET_STATISTICS_update (GSA_stats, "MLP enabled", 0, GNUNET_NO);
1001       ats_mode = SIMPLE;
1002       break;
1003     default:
1004       break;
1005   }
1006   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ATS started with %s mode\n", (SIMPLE == ats_mode) ? "SIMPLE" : "MLP");
1007   running = GNUNET_YES;
1008 }
1009
1010
1011 /**
1012  * Free memory of address.
1013  *
1014  * @param cls NULL
1015  * @param key peer identity (unused)
1016  * @param value the 'struct ATS_Address' to free
1017  * @return GNUNET_OK (continue to iterate)
1018  */
1019 static int
1020 free_address_it (void *cls, const struct GNUNET_HashCode * key, void *value)
1021 {
1022   struct ATS_Address *aa = value;
1023
1024   destroy_address (aa);
1025   return GNUNET_OK;
1026 }
1027
1028
1029 void
1030 GAS_addresses_destroy_all ()
1031 {
1032   if (GNUNET_NO == running)
1033     return;
1034
1035   if (addresses != NULL)
1036     GNUNET_CONTAINER_multihashmap_iterate (addresses, &free_address_it, NULL);
1037   GNUNET_assert (active_addr_count == 0);
1038 }
1039
1040
1041 /**
1042  * Shutdown address subsystem.
1043  */
1044 void
1045 GAS_addresses_done ()
1046 {
1047   GAS_addresses_destroy_all ();
1048   running = GNUNET_NO;
1049   GNUNET_CONTAINER_multihashmap_destroy (addresses);
1050   addresses = NULL;
1051 #if HAVE_LIBGLPK
1052   if (ats_mode == MLP)
1053   {
1054     GAS_mlp_done (mlp);
1055   }
1056 #endif
1057
1058 }
1059
1060
1061 /* end of gnunet-service-ats_addresses.c */