7412cee1931bb396888a40f2557b47a5974ea1f7
[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 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 GNUNET_HashCode * key, void *value)
223 {
224   struct CompareAddressContext *cac = cls;
225   struct ATS_Address *aa = value;
226 /*
227   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
228               "Comparing to: %s %s %u session %u\n",
229               GNUNET_i2s (&aa->peer), aa->plugin, aa->addr_len, aa->session_id);
230
231 */
232   /* find an exact matching address: aa->addr == cac->search->addr && aa->session == cac->search->session */
233   if ((aa->addr_len == cac->search->addr_len) && (0 == strcmp (aa->plugin, cac->search->plugin)))
234   {
235       if ((0 == memcmp (aa->addr, cac->search->addr, aa->addr_len)) && (aa->session_id == cac->search->session_id))
236       {
237         cac->exact_address = aa;
238       }
239   }
240
241   /* find an matching address: aa->addr == cac->search->addr && aa->session == 0 */
242   /* this address can be used to be updated */
243   if ((aa->addr_len == cac->search->addr_len) && (0 == strcmp (aa->plugin, cac->search->plugin)))
244   {
245       if ((0 == memcmp (aa->addr, cac->search->addr, aa->addr_len)) && (aa->session_id == 0))
246       {
247         cac->base_address = aa;
248       }
249   }
250
251   if (cac->exact_address == NULL)
252     return GNUNET_YES;
253   else
254     return GNUNET_NO;
255 }
256
257
258 /**
259  * Find an existing equivalent address record.
260  * Compares by peer identity and network address OR by session ID
261  * (one of the two must match).
262  *
263  * @param peer peer to lookup addresses for
264  * @param addr existing address record
265  * @return existing address record, NULL for none
266  */
267 struct ATS_Address *
268 find_address (const struct GNUNET_PeerIdentity *peer,
269               const struct ATS_Address *addr)
270 {
271   struct CompareAddressContext cac;
272
273   cac.exact_address = NULL;
274   cac.base_address = NULL;
275   cac.search = addr;
276   GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey,
277                                               &compare_address_it, &cac);
278
279 /*
280   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
281               "exact address: %s           base address: %s\n",
282               (cac.exact_address != NULL) ? "YES" : "NO",
283               (cac.base_address != NULL) ? "YES" : "NO");
284 */
285   if (cac.exact_address == NULL)
286     return cac.base_address;
287   return cac.exact_address;
288 }
289
290
291 static int
292 compare_address_session_it (void *cls, const GNUNET_HashCode * key, void *value)
293 {
294   struct CompareAddressContext *cac = cls;
295   struct ATS_Address *aa = value;
296
297   if ((aa->addr_len == cac->search->addr_len) && (0 == strcmp (aa->plugin, cac->search->plugin)))
298   {
299       if ((0 == memcmp (aa->addr, cac->search->addr, aa->addr_len)) && (aa->session_id == cac->search->session_id))
300       {
301         cac->exact_address = aa;
302         return GNUNET_NO;
303       }
304   }
305   return GNUNET_YES;
306 }
307
308
309 /**
310  * Find an existing equivalent address record.
311  * Compares by peer identity and network address AND by session ID
312  * (one of the two must match).
313  *
314  * @param peer peer to lookup addresses for
315  * @param addr existing address record
316  * @return existing address record, NULL for none
317  */
318 struct ATS_Address *
319 find_exact_address (const struct GNUNET_PeerIdentity *peer,
320               const struct ATS_Address *addr)
321 {
322   struct CompareAddressContext cac;
323
324   cac.exact_address = NULL;
325   cac.search = addr;
326   GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey,
327                                               &compare_address_session_it, &cac);
328   return cac.exact_address;
329 }
330
331
332 void
333 GAS_addresses_update (const struct GNUNET_PeerIdentity *peer,
334                       const char *plugin_name, const void *plugin_addr,
335                       size_t plugin_addr_len, uint32_t session_id,
336                       const struct GNUNET_ATS_Information *atsi,
337                       uint32_t atsi_count)
338 {
339   struct ATS_Address *aa;
340   struct ATS_Address *old;
341   uint32_t i;
342
343   if (GNUNET_NO == running)
344     return;
345
346   GNUNET_assert (NULL != addresses);
347
348   aa = create_address (peer,
349                        plugin_name,
350                        plugin_addr, plugin_addr_len,
351                        session_id);
352
353   aa->mlp_information = NULL;
354   aa->ats = GNUNET_malloc (atsi_count * sizeof (struct GNUNET_ATS_Information));
355   aa->ats_count = atsi_count;
356   memcpy (aa->ats, atsi, atsi_count * sizeof (struct GNUNET_ATS_Information));
357
358 #if DEBUG_ATS
359   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Updating address for peer `%s' %u\n",
360               GNUNET_i2s (peer),
361               session_id);
362 #endif
363   /* Get existing address or address with session == 0 */
364   old = find_address (peer, aa);
365   if (old == NULL)
366   {
367     GNUNET_assert (GNUNET_OK ==
368                    GNUNET_CONTAINER_multihashmap_put (addresses,
369                                                       &peer->hashPubKey, aa,
370                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
371 #if DEBUG_ATS
372     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Added new address for peer `%s' %X\n",
373                 GNUNET_i2s (peer), aa);
374 #endif
375     old = aa;
376   }
377   else
378   {
379 #if DEBUG_ATS
380       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
381                 "Updated existing address for peer `%s' %p old session %u new session %u\n",
382                 GNUNET_i2s (peer), old,
383                 old->session_id, session_id);
384 #endif
385     GNUNET_free_non_null (old->ats);
386     old->session_id = session_id;
387     old->ats = NULL;
388     old->ats_count = 0;
389     old->ats = aa->ats;
390     old->ats_count = aa->ats_count;
391     GNUNET_free (aa->plugin);
392     GNUNET_free (aa);
393   }
394   for (i = 0; i < atsi_count; i++)
395     switch (ntohl (atsi[i].type))
396     {
397     case GNUNET_ATS_UTILIZATION_UP:
398       old->atsp_utilization_out.value__ = atsi[i].value;
399       break;
400     case GNUNET_ATS_UTILIZATION_DOWN:
401       old->atsp_utilization_in.value__ = atsi[i].value;
402       break;
403     case GNUNET_ATS_QUALITY_NET_DELAY:
404       old->atsp_latency.rel_value = ntohl (atsi[i].value);
405       break;
406     case GNUNET_ATS_QUALITY_NET_DISTANCE:
407       old->atsp_distance = ntohl (atsi[i].value);
408       break;
409     case GNUNET_ATS_COST_WAN:
410       old->atsp_cost_wan = ntohl (atsi[i].value);
411       break;
412     case GNUNET_ATS_COST_LAN:
413       old->atsp_cost_lan = ntohl (atsi[i].value);
414       break;
415     case GNUNET_ATS_COST_WLAN:
416       old->atsp_cost_wlan = ntohl (atsi[i].value);
417       break;
418     case GNUNET_ATS_NETWORK_TYPE:
419       old->atsp_network_type = ntohl (atsi[i].value);
420       break;
421
422     default:
423       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
424                   "Received unsupported ATS type %u\n", ntohl (atsi[i].type));
425       GNUNET_break (0);
426       break;
427     }
428 #if HAVE_LIBGLPK
429   if (ats_mode == MLP)
430     GAS_mlp_address_update (mlp, addresses, old);
431 #endif
432 }
433
434
435 /**
436  * Delete an address
437  *
438  * If session != 0, just the session is deleted, the address itself still exists
439  * If session == 0, remove full address
440  * If session == 0 and addrlen == 0, destroy inbound address
441  *
442  * @param cls unused
443  * @param key unused
444  * @param value the 'struct ATS_Address'
445  * @return GNUNET_OK (continue to iterate)
446  */
447 static int
448 destroy_by_session_id (void *cls, const GNUNET_HashCode * key, void *value)
449 {
450   const struct ATS_Address *info = cls;
451   struct ATS_Address *aa = value;
452
453   GNUNET_assert (0 ==
454                  memcmp (&aa->peer, &info->peer,
455                          sizeof (struct GNUNET_PeerIdentity)));
456   /* session == 0, remove full address  */
457   if ((info->session_id == 0) && (0 == strcmp (info->plugin, aa->plugin)) &&
458       (aa->addr_len == info->addr_len) &&
459       (0 == memcmp (info->addr, aa->addr, aa->addr_len)))
460   {
461 #if VERBOSE
462     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
463                 "Deleting address for peer `%s': `%s' %u\n",
464                 GNUNET_i2s (&aa->peer), aa->plugin, aa->session_id);
465 #endif
466     if (GNUNET_YES == destroy_address (aa))
467       recalculate_assigned_bw ();
468     return GNUNET_OK;
469   }
470   /* session != 0, just remove session */
471   if (aa->session_id != info->session_id)
472     return GNUNET_OK;           /* irrelevant */
473   if (aa->session_id != 0)
474     GNUNET_break (0 == strcmp (info->plugin, aa->plugin));
475   /* session died */
476 #if VERBOSE
477   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
478               "Deleting session for peer `%s': `%s' %u\n",
479               GNUNET_i2s (&aa->peer), aa->plugin, aa->session_id);
480 #endif
481   aa->session_id = 0;
482
483   if (GNUNET_YES == aa->active)
484   {
485     aa->active = GNUNET_NO;
486     active_addr_count--;
487     recalculate_assigned_bw ();
488   }
489
490   /* session == 0 and addrlen == 0 : destroy address */
491   if (aa->addr_len == 0)
492   {
493 #if VERBOSE
494     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
495                 "Deleting session and address for peer `%s': `%s' %u\n",
496                 GNUNET_i2s (&aa->peer), aa->plugin, aa->session_id);
497 #endif
498     (void) destroy_address (aa);
499   }
500   else
501   {
502     /* session was set to 0, update address */
503 #if HAVE_LIBGLPK
504   if (ats_mode == MLP)
505     GAS_mlp_address_update (mlp, addresses, aa);
506 #endif
507   }
508
509   return GNUNET_OK;
510 }
511
512 void
513 GAS_addresses_destroy (const struct GNUNET_PeerIdentity *peer,
514                        const char *plugin_name, const void *plugin_addr,
515                        size_t plugin_addr_len, uint32_t session_id)
516 {
517   struct ATS_Address *aa;
518
519   if (GNUNET_NO == running)
520     return;
521
522   GNUNET_break (0 < strlen (plugin_name));
523   aa = create_address (peer, plugin_name, plugin_addr, plugin_addr_len, session_id);
524
525   GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey,
526                                               &destroy_by_session_id, aa);
527
528   free_address (aa);
529 }
530
531
532 /**
533  * Find a "good" address to use for a peer.  If we already have an existing
534  * address, we stick to it.  Otherwise, we pick by lowest distance and then
535  * by lowest latency.
536  *
537  * @param cls the 'struct ATS_Address**' where we store the result
538  * @param key unused
539  * @param value another 'struct ATS_Address*' to consider using
540  * @return GNUNET_OK (continue to iterate)
541  */
542 static int
543 find_address_it (void *cls, const GNUNET_HashCode * key, void *value)
544 {
545   struct ATS_Address **ap = cls;
546   struct ATS_Address *aa = (struct ATS_Address *) value;
547   struct ATS_Address *ab = *ap;
548   struct GNUNET_TIME_Absolute now;
549
550   now = GNUNET_TIME_absolute_get();
551
552   if (aa->blocked_until.abs_value == GNUNET_TIME_absolute_max (now, aa->blocked_until).abs_value)
553   {
554     /* This address is blocked for suggestion */
555     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
556                 "Address %p blocked for suggestion for %llu ms \n",
557                 aa,
558                 GNUNET_TIME_absolute_get_difference(now, aa->blocked_until).rel_value);
559     return GNUNET_OK;
560   }
561
562
563   aa->block_interval = GNUNET_TIME_relative_add (aa->block_interval, ATS_BLOCKING_DELTA);
564   aa->blocked_until = GNUNET_TIME_absolute_add (now, aa->block_interval);
565
566   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
567               "Address %p ready for suggestion, block interval now %llu \n", aa, aa->block_interval);
568
569   if (NULL == ab)
570   {
571     *ap = aa;
572     return GNUNET_OK;
573   }
574   if ((ntohl (ab->assigned_bw_in.value__) == 0) &&
575       (ntohl (aa->assigned_bw_in.value__) > 0))
576   {
577     /* stick to existing connection */
578     *ap = aa;
579     return GNUNET_OK;
580   }
581   if (ab->atsp_distance > aa->atsp_distance)
582   {
583     /* user shorter distance */
584     *ap = aa;
585     return GNUNET_OK;
586   }
587   if (ab->atsp_latency.rel_value > aa->atsp_latency.rel_value)
588   {
589     /* user lower latency */
590     *ap = aa;
591     return GNUNET_OK;
592   }
593   /* don't care */
594   return GNUNET_OK;
595 }
596
597
598 int
599 GAS_addresses_in_use (const struct GNUNET_PeerIdentity *peer,
600                       const char *plugin_name, const void *plugin_addr,
601                       size_t plugin_addr_len, uint32_t session_id, int in_use)
602 {
603 #if DEBUG_ATS
604   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
605               "Received `%s' message for peer `%s': %i\n", "ADDRESS_IN_USE",
606               GNUNET_i2s (peer), in_use);
607 #endif
608
609   struct ATS_Address *aa;
610   struct ATS_Address *old;
611
612   if (GNUNET_NO == running)
613     return GNUNET_SYSERR;
614
615   aa = create_address (peer, plugin_name, plugin_addr, plugin_addr_len, session_id);
616   old = find_exact_address (peer, aa);
617   free_address (aa);
618
619   if (NULL == old)
620   {
621     GNUNET_break (0);
622     return GNUNET_SYSERR;
623   }
624   if (old->used == in_use)
625   {
626     GNUNET_break (0);
627     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
628                 "Address in use called multiple times for peer `%s': %s -> %s \n",
629                 GNUNET_i2s (peer),
630                 (GNUNET_NO == old->used) ? "NO" : "YES",
631                 (GNUNET_NO == in_use) ? "NO" : "YES");
632     return GNUNET_SYSERR;
633   }
634   old->used = in_use;
635 #if HAVE_LIBGLPK
636   if (ats_mode == MLP)
637      GAS_mlp_address_update (mlp, addresses, old);
638 #endif
639   return GNUNET_OK;
640 }
641
642
643 void request_address_mlp (const struct GNUNET_PeerIdentity *peer)
644 {
645   struct ATS_Address *aa;
646   aa = NULL;
647
648 #if HAVE_GLPK
649   /* Get preferred address from MLP */
650   struct ATS_PreferedAddress * paddr = NULL;
651   paddr = GAS_mlp_get_preferred_address (mlp, addresses, peer);
652   aa = paddr->address;
653   aa->assigned_bw_out = GNUNET_BANDWIDTH_value_init(paddr->bandwidth_out);
654   /* FIXME use bw in value */
655   paddr->bandwidth_in = paddr->bandwidth_out;
656   aa->assigned_bw_in = GNUNET_BANDWIDTH_value_init (paddr->bandwidth_in);
657   GNUNET_free (paddr);
658 #endif
659
660   if (aa == NULL)
661   {
662     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
663                 "Cannot suggest address for peer `%s'\n", GNUNET_i2s (peer));
664     return;
665   }
666   if (aa->active == GNUNET_NO)
667   {
668     aa->active = GNUNET_YES;
669     active_addr_count++;
670
671     send_bw_notification (aa);
672   }
673   else
674   {
675     /* just to be sure... */
676     GAS_scheduling_transmit_address_suggestion (peer, aa->plugin, aa->addr,
677                                                 aa->addr_len, aa->session_id,
678                                                 aa->ats, aa->ats_count,
679                                                 aa->assigned_bw_out,
680                                                 aa->assigned_bw_in);
681   }
682
683 }
684
685 void request_address_simple (const struct GNUNET_PeerIdentity *peer)
686 {
687   struct ATS_Address *aa;
688   aa = NULL;
689
690   /* Get address with: stick to current address, lower distance, lower latency */
691   GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey,
692                                               &find_address_it, &aa);
693   if (aa == NULL)
694   {
695     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
696                 "Cannot suggest address for peer `%s'\n", GNUNET_i2s (peer));
697     return;
698   }
699
700   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
701               "Suggesting address %p for peer `%s'\n", aa, GNUNET_i2s (peer));
702
703   if (aa->active == GNUNET_NO)
704   {
705     aa->active = GNUNET_YES;
706     active_addr_count++;
707     if (ats_mode == SIMPLE)
708     {
709       recalculate_assigned_bw ();
710     }
711   }
712   else
713   {
714     /* just to be sure... */
715     GAS_scheduling_transmit_address_suggestion (peer, aa->plugin, aa->addr,
716                                                 aa->addr_len, aa->session_id,
717                                                 aa->ats, aa->ats_count,
718                                                 aa->assigned_bw_out,
719                                                 aa->assigned_bw_in);
720   }
721 }
722
723
724 void
725 GAS_addresses_request_address (const struct GNUNET_PeerIdentity *peer)
726 {
727   if (GNUNET_NO == running)
728     return;
729
730   if (ats_mode == SIMPLE)
731   {
732     request_address_simple (peer);
733   }
734   if (ats_mode == MLP)
735   {
736     request_address_mlp(peer);
737   }
738 }
739
740
741 static int
742 reset_address_it (void *cls, const GNUNET_HashCode * key, void *value)
743 {
744   struct ATS_Address *aa = value;
745
746   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
747               "Resetting interval for peer `%s' address %p from %llu to 0\n", GNUNET_i2s (&aa->peer), aa, aa->block_interval);
748
749   aa->blocked_until = GNUNET_TIME_UNIT_ZERO_ABS;
750   aa->block_interval = GNUNET_TIME_UNIT_ZERO;
751   return GNUNET_OK;
752 }
753
754 void
755 GAS_addresses_handle_backoff_reset (const struct GNUNET_PeerIdentity *peer)
756 {
757   GNUNET_break (GNUNET_SYSERR != GNUNET_CONTAINER_multihashmap_get_multiple (addresses,
758                                               &peer->hashPubKey,
759                                               &reset_address_it,
760                                               NULL));
761 }
762
763
764
765 // FIXME: this function should likely end up in the LP-subsystem and
766 // not with 'addresses' in the future...
767 void
768 GAS_addresses_change_preference (const struct GNUNET_PeerIdentity *peer,
769                                  enum GNUNET_ATS_PreferenceKind kind,
770                                  float score)
771 {
772   if (GNUNET_NO == running)
773     return;
774 #if HAVE_LIBGLPK
775   if (ats_mode == MLP)
776     GAS_mlp_address_change_preference (mlp, peer, kind, score);
777 #endif
778 }
779
780
781
782 /**
783  * Initialize address subsystem.
784  *
785  * @param cfg configuration to use
786  * @param stats the statistics handle to use
787  */
788 void
789 GAS_addresses_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
790                     const struct GNUNET_STATISTICS_Handle *stats)
791 {
792   int mode;
793
794   char *quota_wan_in_str;
795   char *quota_wan_out_str;
796
797   running = GNUNET_NO;
798
799   addresses = GNUNET_CONTAINER_multihashmap_create (128);
800   GNUNET_assert (NULL != addresses);
801
802   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(cfg, "ats", "WAN_QUOTA_IN", &quota_wan_in_str))
803   {
804     if (0 == strcmp(quota_wan_in_str, "unlimited") ||
805         (GNUNET_SYSERR == GNUNET_STRINGS_fancy_size_to_bytes (quota_wan_in_str, &wan_quota_in)))
806       wan_quota_in = (UINT32_MAX) /10;
807
808     GNUNET_free (quota_wan_in_str);
809     quota_wan_in_str = NULL;
810   }
811   else
812   {
813     wan_quota_in = (UINT32_MAX) /10;
814   }
815
816   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(cfg, "ats", "WAN_QUOTA_OUT", &quota_wan_out_str))
817   {
818     if (0 == strcmp(quota_wan_out_str, "unlimited") ||
819         (GNUNET_SYSERR == GNUNET_STRINGS_fancy_size_to_bytes (quota_wan_out_str, &wan_quota_out)))
820       wan_quota_out = (UINT32_MAX) /10;
821
822     GNUNET_free (quota_wan_out_str);
823     quota_wan_out_str = NULL;
824   }
825   else
826   {
827     wan_quota_out = (UINT32_MAX) /10;
828   }
829
830   mode = GNUNET_CONFIGURATION_get_value_yesno (cfg, "ats", "MLP");
831   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "MLP mode %u", mode);
832   switch (mode)
833   {
834     /* MLP = YES */
835     case GNUNET_YES:
836 #if HAVE_LIBGLPK
837       ats_mode = MLP;
838       /* Init the MLP solver with default values */
839       mlp = GAS_mlp_init (cfg, stats, MLP_MAX_EXEC_DURATION, MLP_MAX_ITERATIONS);
840       if (NULL == mlp)
841       {
842         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "MLP mode was configured, but libglpk is not installed, switching to simple mode\n");
843         GNUNET_STATISTICS_update (GSA_stats, "MLP mode enabled", 0, GNUNET_NO);
844         break;
845       }
846       else
847       {
848         GNUNET_STATISTICS_update (GSA_stats, "MLP enabled", 1, GNUNET_NO);
849         break;
850       }
851 #else
852       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "MLP mode was configured, but libglpk is not installed, switching to simple mode");
853       GNUNET_STATISTICS_update (GSA_stats, "MLP enabled", 0, GNUNET_NO);
854       ats_mode = SIMPLE;
855       break;
856 #endif
857     /* MLP = NO */
858     case GNUNET_NO:
859       GNUNET_STATISTICS_update (GSA_stats, "MLP enabled", 0, GNUNET_NO);
860       ats_mode = SIMPLE;
861       break;
862     /* No configuration value */
863     case GNUNET_SYSERR:
864       GNUNET_STATISTICS_update (GSA_stats, "MLP enabled", 0, GNUNET_NO);
865       ats_mode = SIMPLE;
866       break;
867     default:
868       break;
869   }
870   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ATS started with %s mode\n", (SIMPLE == ats_mode) ? "SIMPLE" : "MLP");
871   running = GNUNET_YES;
872 }
873
874
875 /**
876  * Free memory of address.
877  *
878  * @param cls NULL
879  * @param key peer identity (unused)
880  * @param value the 'struct ATS_Address' to free
881  * @return GNUNET_OK (continue to iterate)
882  */
883 static int
884 free_address_it (void *cls, const GNUNET_HashCode * key, void *value)
885 {
886   struct ATS_Address *aa = value;
887
888   destroy_address (aa);
889   return GNUNET_OK;
890 }
891
892
893 void
894 GAS_addresses_destroy_all ()
895 {
896   if (GNUNET_NO == running)
897     return;
898
899   if (addresses != NULL)
900     GNUNET_CONTAINER_multihashmap_iterate (addresses, &free_address_it, NULL);
901   GNUNET_assert (active_addr_count == 0);
902 }
903
904
905 /**
906  * Shutdown address subsystem.
907  */
908 void
909 GAS_addresses_done ()
910 {
911   GAS_addresses_destroy_all ();
912   running = GNUNET_NO;
913   GNUNET_CONTAINER_multihashmap_destroy (addresses);
914   addresses = NULL;
915 #if HAVE_LIBGLPK
916   if (ats_mode == MLP)
917   {
918     GAS_mlp_done (mlp);
919   }
920 #endif
921
922 }
923
924
925 /* end of gnunet-service-ats_addresses.c */