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