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