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