- fix for 601 assertion
[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 enum ATS_Mode
41 {
42   /*
43    * Assign each peer an equal amount of bandwidth (bw)
44    *
45    * bw_per_peer = bw_total / #active addresses
46    */
47   SIMPLE,
48
49   /*
50    * Use MLP solver to assign bandwidth
51    */
52   MLP
53 };
54
55 static struct GNUNET_CONTAINER_MultiHashMap *addresses;
56
57 #if HAVE_LIBGLPK
58 static struct GAS_MLP_Handle *mlp;
59 #endif
60
61 static unsigned long long wan_quota_in;
62
63 static unsigned long long wan_quota_out;
64
65 static unsigned int active_addr_count;
66
67 static int ats_mode;
68
69 static int running;
70
71
72 static void
73 send_bw_notification (struct ATS_Address *aa)
74 {
75   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New bandwidth for peer %s is %u/%u\n",
76               GNUNET_i2s (&aa->peer), ntohl (aa->assigned_bw_in.value__),
77               ntohl (aa->assigned_bw_out.value__));
78   GAS_scheduling_transmit_address_suggestion (&aa->peer, aa->plugin, aa->addr,
79                                               aa->addr_len, aa->session_id,
80                                               aa->ats, aa->ats_count,
81                                               aa->assigned_bw_out,
82                                               aa->assigned_bw_in);
83   GAS_reservations_set_bandwidth (&aa->peer, aa->assigned_bw_in);
84   GAS_performance_notify_clients (&aa->peer, aa->plugin, aa->addr, aa->addr_len,
85                                   aa->ats, aa->ats_count, aa->assigned_bw_out,
86                                   aa->assigned_bw_in);
87 }
88
89 /**
90  * Update a bandwidth assignment for a peer.  This trivial method currently
91  * simply assigns the same share to all active connections.
92  *
93  * @param cls unused
94  * @param key unused
95  * @param value the 'struct ATS_Address'
96  * @return GNUNET_OK (continue to iterate)
97  */
98 static int
99 update_bw_simple_it (void *cls, const GNUNET_HashCode * key, void *value)
100 {
101   struct ATS_Address *aa = value;
102
103   if (GNUNET_YES != aa->active)
104     return GNUNET_OK;
105   GNUNET_assert (active_addr_count > 0);
106
107
108   /* Simple method */
109   aa->assigned_bw_in.value__ = htonl (wan_quota_in / active_addr_count);
110   aa->assigned_bw_out.value__ = htonl (wan_quota_out / active_addr_count);
111
112   send_bw_notification (aa);
113
114   return GNUNET_OK;
115 }
116
117
118 /**
119  * Some (significant) input changed, recalculate bandwidth assignment
120  * for all peers.
121  */
122 static void
123 recalculate_assigned_bw ()
124 {
125   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
126               "Recalculating bandwidth for all active connections\n");
127   GNUNET_STATISTICS_update (GSA_stats, "# bandwidth recalculations performed",
128                             1, GNUNET_NO);
129   GNUNET_STATISTICS_set (GSA_stats, "# active addresses", active_addr_count,
130                          GNUNET_NO);
131
132   GNUNET_CONTAINER_multihashmap_iterate (addresses, &update_bw_simple_it, NULL);
133 }
134
135 /**
136  * Free the given address
137  * @param addr address to destroy
138  */
139 static void
140 free_address (struct ATS_Address *addr)
141 {
142   GNUNET_free_non_null (addr->ats);
143   GNUNET_free (addr->plugin);
144   GNUNET_free (addr);
145 }
146
147 /**
148  * Create a ATS_address with the given information
149  * @param peer peer
150  * @param plugin_name plugin
151  * @param plugin_addr address
152  * @param plugin_addr_len address length
153  * @param session_id session
154  * @return the ATS_Address
155  */
156 static struct ATS_Address *
157 create_address (const struct GNUNET_PeerIdentity *peer,
158                 const char *plugin_name,
159                 const void *plugin_addr, size_t plugin_addr_len,
160                 uint32_t session_id)
161 {
162   struct ATS_Address *aa = NULL;
163
164   aa = GNUNET_malloc (sizeof (struct ATS_Address) + plugin_addr_len);
165   aa->peer = *peer;
166   aa->addr_len = plugin_addr_len;
167   aa->addr = &aa[1];
168   memcpy (&aa[1], plugin_addr, plugin_addr_len);
169   aa->plugin = GNUNET_strdup (plugin_name);
170   aa->session_id = session_id;
171   aa->mlp_information = NULL;
172   aa->next = NULL;
173   aa->prev = NULL;
174   return aa;
175 }
176
177
178 /**
179  * Destroy the given address.
180  *
181  * @param addr address to destroy
182  * @return GNUNET_YES if bandwidth allocations should be recalcualted
183  */
184 static int
185 destroy_address (struct ATS_Address *addr)
186 {
187   int ret;
188
189   ret = GNUNET_NO;
190   GNUNET_assert (GNUNET_YES ==
191                  GNUNET_CONTAINER_multihashmap_remove (addresses,
192                                                        &addr->peer.hashPubKey,
193                                                        addr));
194
195 #if HAVE_LIBGLPK
196   if (ats_mode == MLP)
197     GAS_mlp_address_delete (mlp, addresses, addr);
198 #endif
199
200   if (GNUNET_YES == addr->active)
201   {
202     active_addr_count--;
203     addr->active = GNUNET_NO;
204     ret = GNUNET_YES;
205   }
206   free_address (addr);
207   return ret;
208 }
209
210
211 struct CompareAddressContext
212 {
213   const struct ATS_Address *search;
214
215   /* exact_address != NULL if address and session is equal */
216   struct ATS_Address *exact_address;
217   /* exact_address != NULL if address and session is 0 */
218   struct ATS_Address *base_address;
219 };
220
221
222 static int
223 compare_address_it (void *cls, const GNUNET_HashCode * key, void *value)
224 {
225   struct CompareAddressContext *cac = cls;
226   struct ATS_Address *aa = value;
227 /*
228   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
229               "Comparing to: %s %s %u session %u\n",
230               GNUNET_i2s (&aa->peer), aa->plugin, aa->addr_len, aa->session_id);
231
232 */
233   /* find an exact matching address: aa->addr == cac->search->addr && aa->session == cac->search->session */
234   if ((aa->addr_len == cac->search->addr_len) && (0 == strcmp (aa->plugin, cac->search->plugin)))
235   {
236       if ((0 == memcmp (aa->addr, cac->search->addr, aa->addr_len)) && (aa->session_id == cac->search->session_id))
237       {
238         cac->exact_address = aa;
239       }
240   }
241
242   /* find an matching address: aa->addr == cac->search->addr && aa->session == 0 */
243   /* this address can be used to be updated */
244   if ((aa->addr_len == cac->search->addr_len) && (0 == strcmp (aa->plugin, cac->search->plugin)))
245   {
246       if ((0 == memcmp (aa->addr, cac->search->addr, aa->addr_len)) && (aa->session_id == 0))
247       {
248         cac->base_address = aa;
249       }
250   }
251
252   if (cac->exact_address == NULL)
253     return GNUNET_YES;
254   else
255     return GNUNET_NO;
256 }
257
258
259 /**
260  * Find an existing equivalent address record.
261  * Compares by peer identity and network address OR by session ID
262  * (one of the two must match).
263  *
264  * @param peer peer to lookup addresses for
265  * @param addr existing address record
266  * @return existing address record, NULL for none
267  */
268 struct ATS_Address *
269 find_address (const struct GNUNET_PeerIdentity *peer,
270               const struct ATS_Address *addr)
271 {
272   struct CompareAddressContext cac;
273
274   cac.exact_address = NULL;
275   cac.base_address = NULL;
276   cac.search = addr;
277   GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey,
278                                               &compare_address_it, &cac);
279
280 /*
281   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
282               "exact address: %s           base address: %s\n",
283               (cac.exact_address != NULL) ? "YES" : "NO",
284               (cac.base_address != NULL) ? "YES" : "NO");
285 */
286   if (cac.exact_address == NULL)
287     return cac.base_address;
288   return cac.exact_address;
289 }
290
291
292 static int
293 compare_address_session_it (void *cls, const GNUNET_HashCode * key, void *value)
294 {
295   struct CompareAddressContext *cac = cls;
296   struct ATS_Address *aa = value;
297
298   if ((aa->addr_len == cac->search->addr_len) && (0 == strcmp (aa->plugin, cac->search->plugin)))
299   {
300       if ((0 == memcmp (aa->addr, cac->search->addr, aa->addr_len)) && (aa->session_id == cac->search->session_id))
301       {
302         cac->exact_address = aa;
303         return GNUNET_NO;
304       }
305   }
306   return GNUNET_YES;
307 }
308
309
310 /**
311  * Find an existing equivalent address record.
312  * Compares by peer identity and network address AND by session ID
313  * (one of the two must match).
314  *
315  * @param peer peer to lookup addresses for
316  * @param addr existing address record
317  * @return existing address record, NULL for none
318  */
319 struct ATS_Address *
320 find_exact_address (const struct GNUNET_PeerIdentity *peer,
321               const struct ATS_Address *addr)
322 {
323   struct CompareAddressContext cac;
324
325   cac.exact_address = NULL;
326   cac.search = addr;
327   GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey,
328                                               &compare_address_session_it, &cac);
329   return cac.exact_address;
330 }
331
332
333 void
334 GAS_addresses_update (const struct GNUNET_PeerIdentity *peer,
335                       const char *plugin_name, const void *plugin_addr,
336                       size_t plugin_addr_len, uint32_t session_id,
337                       const struct GNUNET_ATS_Information *atsi,
338                       uint32_t atsi_count)
339 {
340   struct ATS_Address *aa;
341   struct ATS_Address *old;
342   uint32_t i;
343
344   if (GNUNET_NO == running)
345     return;
346
347   GNUNET_assert (NULL != addresses);
348
349   aa = create_address (peer,
350                        plugin_name,
351                        plugin_addr, plugin_addr_len,
352                        session_id);
353
354   aa->mlp_information = NULL;
355   aa->ats = GNUNET_malloc (atsi_count * sizeof (struct GNUNET_ATS_Information));
356   aa->ats_count = atsi_count;
357   memcpy (aa->ats, atsi, atsi_count * sizeof (struct GNUNET_ATS_Information));
358
359 #if DEBUG_ATS
360   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Updating address for peer `%s' %u\n",
361               GNUNET_i2s (peer),
362               session_id);
363 #endif
364   /* Get existing address or address with session == 0 */
365   old = find_address (peer, aa);
366   if (old == NULL)
367   {
368     GNUNET_assert (GNUNET_OK ==
369                    GNUNET_CONTAINER_multihashmap_put (addresses,
370                                                       &peer->hashPubKey, aa,
371                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
372 #if DEBUG_ATS
373     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Added new address for peer `%s' %X\n",
374                 GNUNET_i2s (peer), aa);
375 #endif
376     old = aa;
377   }
378   else
379   {
380 #if DEBUG_ATS
381       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
382                 "Updated existing address for peer `%s' %p old session %u new session %u\n",
383                 GNUNET_i2s (peer), old,
384                 old->session_id, session_id);
385 #endif
386     GNUNET_free_non_null (old->ats);
387     old->session_id = session_id;
388     old->ats = NULL;
389     old->ats_count = 0;
390     old->ats = aa->ats;
391     old->ats_count = aa->ats_count;
392     GNUNET_free (aa->plugin);
393     GNUNET_free (aa);
394   }
395   for (i = 0; i < atsi_count; i++)
396     switch (ntohl (atsi[i].type))
397     {
398     case GNUNET_ATS_UTILIZATION_UP:
399       old->atsp_utilization_out.value__ = atsi[i].value;
400       break;
401     case GNUNET_ATS_UTILIZATION_DOWN:
402       old->atsp_utilization_in.value__ = atsi[i].value;
403       break;
404     case GNUNET_ATS_QUALITY_NET_DELAY:
405       old->atsp_latency.rel_value = ntohl (atsi[i].value);
406       break;
407     case GNUNET_ATS_QUALITY_NET_DISTANCE:
408       old->atsp_distance = ntohl (atsi[i].value);
409       break;
410     case GNUNET_ATS_COST_WAN:
411       old->atsp_cost_wan = ntohl (atsi[i].value);
412       break;
413     case GNUNET_ATS_COST_LAN:
414       old->atsp_cost_lan = ntohl (atsi[i].value);
415       break;
416     case GNUNET_ATS_COST_WLAN:
417       old->atsp_cost_wlan = ntohl (atsi[i].value);
418       break;
419     case GNUNET_ATS_NETWORK_TYPE:
420       old->atsp_network_type = ntohl (atsi[i].value);
421       break;
422
423     default:
424       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
425                   "Received unsupported ATS type %u\n", ntohl (atsi[i].type));
426       GNUNET_break (0);
427       break;
428     }
429 #if HAVE_LIBGLPK
430   if (ats_mode == MLP)
431     GAS_mlp_address_update (mlp, addresses, old);
432 #endif
433 }
434
435
436 /**
437  * Delete an address
438  *
439  * If session != 0, just the session is deleted, the address itself still exists
440  * If session == 0, remove full address
441  * If session == 0 and addrlen == 0, destroy inbound address
442  *
443  * @param cls unused
444  * @param key unused
445  * @param value the 'struct ATS_Address'
446  * @return GNUNET_OK (continue to iterate)
447  */
448 static int
449 destroy_by_session_id (void *cls, const GNUNET_HashCode * key, void *value)
450 {
451   const struct ATS_Address *info = cls;
452   struct ATS_Address *aa = value;
453
454   GNUNET_assert (0 ==
455                  memcmp (&aa->peer, &info->peer,
456                          sizeof (struct GNUNET_PeerIdentity)));
457   /* session == 0, remove full address  */
458   if ((info->session_id == 0) && (0 == strcmp (info->plugin, aa->plugin)) &&
459       (aa->addr_len == info->addr_len) &&
460       (0 == memcmp (info->addr, aa->addr, aa->addr_len)))
461   {
462 #if VERBOSE
463     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
464                 "Deleting address for peer `%s': `%s' %u\n",
465                 GNUNET_i2s (&aa->peer), aa->plugin, aa->session_id);
466 #endif
467     if (GNUNET_YES == destroy_address (aa))
468       recalculate_assigned_bw ();
469     return GNUNET_OK;
470   }
471   /* session != 0, just remove session */
472   if (aa->session_id != info->session_id)
473     return GNUNET_OK;           /* irrelevant */
474   if (aa->session_id != 0)
475     GNUNET_break (0 == strcmp (info->plugin, aa->plugin));
476   /* session died */
477 #if VERBOSE
478   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
479               "Deleting session for peer `%s': `%s' %u\n",
480               GNUNET_i2s (&aa->peer), aa->plugin, aa->session_id);
481 #endif
482   aa->session_id = 0;
483
484   if (GNUNET_YES == aa->active)
485   {
486     aa->active = GNUNET_NO;
487     active_addr_count--;
488     recalculate_assigned_bw ();
489   }
490
491   /* session == 0 and addrlen == 0 : destroy address */
492   if (aa->addr_len == 0)
493   {
494 #if VERBOSE
495     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
496                 "Deleting session and address for peer `%s': `%s' %u\n",
497                 GNUNET_i2s (&aa->peer), aa->plugin, aa->session_id);
498 #endif
499     (void) destroy_address (aa);
500   }
501   else
502   {
503     /* session was set to 0, update address */
504 #if HAVE_LIBGLPK
505   if (ats_mode == MLP)
506     GAS_mlp_address_update (mlp, addresses, aa);
507 #endif
508   }
509
510   return GNUNET_OK;
511 }
512
513 void
514 GAS_addresses_destroy (const struct GNUNET_PeerIdentity *peer,
515                        const char *plugin_name, const void *plugin_addr,
516                        size_t plugin_addr_len, uint32_t session_id)
517 {
518   struct ATS_Address *aa;
519
520   if (GNUNET_NO == running)
521     return;
522
523   GNUNET_break (0 < strlen (plugin_name));
524   aa = create_address (peer, plugin_name, plugin_addr, plugin_addr_len, session_id);
525
526   GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey,
527                                               &destroy_by_session_id, aa);
528
529   free_address (aa);
530 }
531
532
533 /**
534  * Find a "good" address to use for a peer.  If we already have an existing
535  * address, we stick to it.  Otherwise, we pick by lowest distance and then
536  * by lowest latency.
537  *
538  * @param cls the 'struct ATS_Address**' where we store the result
539  * @param key unused
540  * @param value another 'struct ATS_Address*' to consider using
541  * @return GNUNET_OK (continue to iterate)
542  */
543 static int
544 find_address_it (void *cls, const GNUNET_HashCode * key, void *value)
545 {
546   struct ATS_Address **ap = cls;
547   struct ATS_Address *aa = (struct ATS_Address *) value;
548   struct ATS_Address *ab = *ap;
549
550   if (NULL == ab)
551   {
552     *ap = aa;
553     return GNUNET_OK;
554   }
555   if ((ntohl (ab->assigned_bw_in.value__) == 0) &&
556       (ntohl (aa->assigned_bw_in.value__) > 0))
557   {
558     /* stick to existing connection */
559     *ap = aa;
560     return GNUNET_OK;
561   }
562   if (ab->atsp_distance > aa->atsp_distance)
563   {
564     /* user shorter distance */
565     *ap = aa;
566     return GNUNET_OK;
567   }
568   if (ab->atsp_latency.rel_value > aa->atsp_latency.rel_value)
569   {
570     /* user lower latency */
571     *ap = aa;
572     return GNUNET_OK;
573   }
574   /* don't care */
575   return GNUNET_OK;
576 }
577
578
579 int
580 GAS_addresses_in_use (const struct GNUNET_PeerIdentity *peer,
581                       const char *plugin_name, const void *plugin_addr,
582                       size_t plugin_addr_len, uint32_t session_id, int in_use)
583 {
584 #if DEBUG_ATS
585   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
586               "Received `%s' message for peer `%s': %i\n", "ADDRESS_IN_USE",
587               GNUNET_i2s (peer), in_use);
588 #endif
589
590   struct ATS_Address *aa;
591   struct ATS_Address *old;
592
593   if (GNUNET_NO == running)
594     return GNUNET_SYSERR;
595
596   aa = create_address (peer, plugin_name, plugin_addr, plugin_addr_len, session_id);
597   old = find_exact_address (peer, aa);
598   free_address (aa);
599
600   if (NULL == old)
601   {
602     GNUNET_break (0);
603     return GNUNET_SYSERR;
604   }
605   if (old->used == in_use)
606   {
607     GNUNET_break (0);
608     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
609                 "Address in use called multiple times for peer `%s': %s -> %s \n",
610                 GNUNET_i2s (peer),
611                 (GNUNET_NO == old->used) ? "NO" : "YES",
612                 (GNUNET_NO == in_use) ? "NO" : "YES");
613     return GNUNET_SYSERR;
614   }
615   old->used = in_use;
616 #if HAVE_LIBGLPK
617   if (ats_mode == MLP)
618      GAS_mlp_address_update (mlp, addresses, old);
619 #endif
620   return GNUNET_OK;
621 }
622
623
624 void request_address_mlp (const struct GNUNET_PeerIdentity *peer)
625 {
626   struct ATS_Address *aa;
627   aa = NULL;
628
629 #if HAVE_GLPK
630   /* Get preferred address from MLP */
631   struct ATS_PreferedAddress * paddr = NULL;
632   paddr = GAS_mlp_get_preferred_address (mlp, addresses, peer);
633   aa = paddr->address;
634   aa->assigned_bw_out = GNUNET_BANDWIDTH_value_init(paddr->bandwidth_out);
635   /* FIXME use bw in value */
636   paddr->bandwidth_in = paddr->bandwidth_out;
637   aa->assigned_bw_in = GNUNET_BANDWIDTH_value_init (paddr->bandwidth_in);
638   GNUNET_free (paddr);
639 #endif
640
641   if (aa == NULL)
642   {
643     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
644                 "Cannot suggest address for peer `%s'\n", GNUNET_i2s (peer));
645     return;
646   }
647   if (aa->active == GNUNET_NO)
648   {
649     aa->active = GNUNET_YES;
650     active_addr_count++;
651
652     send_bw_notification (aa);
653   }
654   else
655   {
656     /* just to be sure... */
657     GAS_scheduling_transmit_address_suggestion (peer, aa->plugin, aa->addr,
658                                                 aa->addr_len, aa->session_id,
659                                                 aa->ats, aa->ats_count,
660                                                 aa->assigned_bw_out,
661                                                 aa->assigned_bw_in);
662   }
663
664 }
665
666 void request_address_simple (const struct GNUNET_PeerIdentity *peer)
667 {
668   struct ATS_Address *aa;
669   aa = NULL;
670
671   /* Get address with: stick to current address, lower distance, lower latency */
672   GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey,
673                                               &find_address_it, &aa);
674   if (aa == NULL)
675   {
676     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
677                 "Cannot suggest address for peer `%s'\n", GNUNET_i2s (peer));
678     return;
679   }
680
681   if (aa->active == GNUNET_NO)
682   {
683     aa->active = GNUNET_YES;
684     active_addr_count++;
685     if (ats_mode == SIMPLE)
686     {
687       recalculate_assigned_bw ();
688     }
689   }
690   else
691   {
692     /* just to be sure... */
693     GAS_scheduling_transmit_address_suggestion (peer, aa->plugin, aa->addr,
694                                                 aa->addr_len, aa->session_id,
695                                                 aa->ats, aa->ats_count,
696                                                 aa->assigned_bw_out,
697                                                 aa->assigned_bw_in);
698   }
699 }
700
701
702 void
703 GAS_addresses_request_address (const struct GNUNET_PeerIdentity *peer)
704 {
705   if (GNUNET_NO == running)
706     return;
707
708   if (ats_mode == SIMPLE)
709   {
710     request_address_simple (peer);
711   }
712   if (ats_mode == MLP)
713   {
714     request_address_mlp(peer);
715   }
716 }
717
718
719 // FIXME: this function should likely end up in the LP-subsystem and
720 // not with 'addresses' in the future...
721 void
722 GAS_addresses_change_preference (const struct GNUNET_PeerIdentity *peer,
723                                  enum GNUNET_ATS_PreferenceKind kind,
724                                  float score)
725 {
726   if (GNUNET_NO == running)
727     return;
728 #if HAVE_LIBGLPK
729   if (ats_mode == MLP)
730     GAS_mlp_address_change_preference (mlp, peer, kind, score);
731 #endif
732 }
733
734
735
736 /**
737  * Initialize address subsystem.
738  *
739  * @param cfg configuration to use
740  * @param stats the statistics handle to use
741  */
742 void
743 GAS_addresses_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
744                     const struct GNUNET_STATISTICS_Handle *stats)
745 {
746   int mode;
747
748   char *quota_wan_in_str;
749   char *quota_wan_out_str;
750
751   running = GNUNET_NO;
752
753   addresses = GNUNET_CONTAINER_multihashmap_create (128);
754   GNUNET_assert (NULL != addresses);
755
756   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(cfg, "ats", "WAN_QUOTA_IN", &quota_wan_in_str))
757   {
758     if (0 == strcmp(quota_wan_in_str, "unlimited") ||
759         (GNUNET_SYSERR == GNUNET_STRINGS_fancy_size_to_bytes (quota_wan_in_str, &wan_quota_in)))
760       wan_quota_in = (UINT32_MAX) /10;
761
762     GNUNET_free (quota_wan_in_str);
763     quota_wan_in_str = NULL;
764   }
765   else
766   {
767     wan_quota_in = (UINT32_MAX) /10;
768   }
769
770   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(cfg, "ats", "WAN_QUOTA_OUT", &quota_wan_out_str))
771   {
772     if (0 == strcmp(quota_wan_out_str, "unlimited") ||
773         (GNUNET_SYSERR == GNUNET_STRINGS_fancy_size_to_bytes (quota_wan_out_str, &wan_quota_out)))
774       wan_quota_out = (UINT32_MAX) /10;
775
776     GNUNET_free (quota_wan_out_str);
777     quota_wan_out_str = NULL;
778   }
779   else
780   {
781     wan_quota_out = (UINT32_MAX) /10;
782   }
783
784   mode = GNUNET_CONFIGURATION_get_value_yesno (cfg, "ats", "MLP");
785   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "MLP mode %u", mode);
786   switch (mode)
787   {
788     /* MLP = YES */
789     case GNUNET_YES:
790 #if HAVE_LIBGLPK
791       ats_mode = MLP;
792       /* Init the MLP solver with default values */
793       mlp = GAS_mlp_init (cfg, stats, MLP_MAX_EXEC_DURATION, MLP_MAX_ITERATIONS);
794       if (NULL == mlp)
795       {
796         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "MLP mode was configured, but libglpk is not installed, switching to simple mode\n");
797         GNUNET_STATISTICS_update (GSA_stats, "MLP mode enabled", 0, GNUNET_NO);
798         break;
799       }
800       else
801       {
802         GNUNET_STATISTICS_update (GSA_stats, "MLP enabled", 1, GNUNET_NO);
803         break;
804       }
805 #else
806       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "MLP mode was configured, but libglpk is not installed, switching to simple mode");
807       GNUNET_STATISTICS_update (GSA_stats, "MLP enabled", 0, GNUNET_NO);
808       ats_mode = SIMPLE;
809       break;
810 #endif
811     /* MLP = NO */
812     case GNUNET_NO:
813       GNUNET_STATISTICS_update (GSA_stats, "MLP enabled", 0, GNUNET_NO);
814       ats_mode = SIMPLE;
815       break;
816     /* No configuration value */
817     case GNUNET_SYSERR:
818       GNUNET_STATISTICS_update (GSA_stats, "MLP enabled", 0, GNUNET_NO);
819       ats_mode = SIMPLE;
820       break;
821     default:
822       break;
823   }
824   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ATS started with %s mode\n", (SIMPLE == ats_mode) ? "SIMPLE" : "MLP");
825   running = GNUNET_YES;
826 }
827
828
829 /**
830  * Free memory of address.
831  *
832  * @param cls NULL
833  * @param key peer identity (unused)
834  * @param value the 'struct ATS_Address' to free
835  * @return GNUNET_OK (continue to iterate)
836  */
837 static int
838 free_address_it (void *cls, const GNUNET_HashCode * key, void *value)
839 {
840   struct ATS_Address *aa = value;
841
842   destroy_address (aa);
843   return GNUNET_OK;
844 }
845
846
847 void
848 GAS_addresses_destroy_all ()
849 {
850   if (GNUNET_NO == running)
851     return;
852
853   if (addresses != NULL)
854     GNUNET_CONTAINER_multihashmap_iterate (addresses, &free_address_it, NULL);
855   GNUNET_assert (active_addr_count == 0);
856 }
857
858
859 /**
860  * Shutdown address subsystem.
861  */
862 void
863 GAS_addresses_done ()
864 {
865   running = GNUNET_NO;
866   GAS_addresses_destroy_all ();
867   GNUNET_CONTAINER_multihashmap_destroy (addresses);
868   addresses = NULL;
869 #if HAVE_LIBGLPK
870   if (ats_mode == MLP)
871   {
872     GAS_mlp_done (mlp);
873   }
874 #endif
875
876 }
877
878
879 /* end of gnunet-service-ats_addresses.c */