47ba3312b3de8ac7aea8f6e282ac05b16406560f
[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 #include "gnunet-service-ats_addresses_simplistic.h"
38
39 #define ATS_BLOCKING_DELTA GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 100)
40
41
42 /**
43  * Available ressource assignment modes
44  */
45 enum ATS_Mode
46 {
47   /*
48    * Simplistic mode:
49    *
50    * Assign each peer an equal amount of bandwidth (bw)
51    *
52    * bw_per_peer = bw_total / #active addresses
53    */
54   MODE_SIMPLISTIC,
55
56   /*
57    * MLP mode:
58    *
59    * Solve ressource assignment as an optimization problem
60    * Uses an mixed integer programming solver
61    */
62   MODE_MLP
63 };
64
65 static struct GNUNET_CONTAINER_MultiHashMap *addresses;
66
67 static void *solver;
68
69 static unsigned long long wan_quota_in;
70
71 static unsigned long long wan_quota_out;
72
73 static unsigned int active_addr_count;
74
75 static int ats_mode;
76
77 static int running;
78
79
80 static unsigned int
81 assemble_ats_information (struct ATS_Address *aa,  struct GNUNET_ATS_Information **dest)
82 {
83   unsigned int ats_count = GNUNET_ATS_PropertyCount - 1;
84   struct GNUNET_ATS_Information *ats = GNUNET_malloc (ats_count * sizeof (struct GNUNET_ATS_Information));
85   (*dest) = ats;
86
87   ats[0].type = ntohl(GNUNET_ATS_UTILIZATION_UP);
88   ats[0].value = aa->atsp_utilization_out.value__;
89   ats[1].type = ntohl(GNUNET_ATS_UTILIZATION_DOWN);
90   ats[1].value = aa->atsp_utilization_in.value__;
91   ats[2].type = ntohl(GNUNET_ATS_NETWORK_TYPE);
92   ats[2].value = ntohl(aa->atsp_network_type);
93   ats[3].type = ntohl(GNUNET_ATS_QUALITY_NET_DELAY);
94   ats[3].value = ntohl(aa->atsp_latency.rel_value);
95   ats[4].type = ntohl(GNUNET_ATS_QUALITY_NET_DISTANCE);
96   ats[4].value = ntohl(aa->atsp_distance);
97   ats[5].type = ntohl(GNUNET_ATS_COST_WAN);
98   ats[5].value = ntohl (aa->atsp_cost_wan);
99   ats[6].type = ntohl(GNUNET_ATS_COST_LAN);
100   ats[6].value = ntohl (aa->atsp_cost_lan);
101   ats[7].type = ntohl(GNUNET_ATS_COST_WLAN);
102   ats[7].value = ntohl (aa->atsp_cost_wlan);
103   return ats_count;
104 }
105
106 static void
107 send_bw_notification (struct ATS_Address *aa)
108 {
109   struct GNUNET_ATS_Information *ats;
110   uint32_t ats_count;
111
112   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New bandwidth for peer %s is %u/%u\n",
113               GNUNET_i2s (&aa->peer), ntohl (aa->assigned_bw_in.value__),
114               ntohl (aa->assigned_bw_out.value__));
115   ats_count = assemble_ats_information (aa, &ats);
116
117   GAS_scheduling_transmit_address_suggestion (&aa->peer, aa->plugin, aa->addr,
118                                               aa->addr_len, aa->session_id,
119                                               ats, ats_count,
120                                               aa->assigned_bw_out,
121                                               aa->assigned_bw_in);
122   GAS_reservations_set_bandwidth (&aa->peer, aa->assigned_bw_in);
123   GAS_performance_notify_all_clients (&aa->peer, aa->plugin, aa->addr, aa->addr_len,
124                                   aa->active,
125                                   ats, ats_count, aa->assigned_bw_out,
126                                   aa->assigned_bw_in);
127   GNUNET_free (ats);
128 }
129
130 /**
131  * Update a bandwidth assignment for a peer.  This trivial method currently
132  * simply assigns the same share to all active connections.
133  *
134  * @param cls unused
135  * @param key unused
136  * @param value the 'struct ATS_Address'
137  * @return GNUNET_OK (continue to iterate)
138  */
139 static int
140 update_bw_simple_it (void *cls, const struct GNUNET_HashCode * key, void *value)
141 {
142   struct ATS_Address *aa = value;
143
144   if (GNUNET_YES != aa->active)
145     return GNUNET_OK;
146   GNUNET_assert (active_addr_count > 0);
147
148
149   /* Simple method */
150   aa->assigned_bw_in.value__ = htonl (wan_quota_in / active_addr_count);
151   aa->assigned_bw_out.value__ = htonl (wan_quota_out / active_addr_count);
152
153   send_bw_notification (aa);
154
155   return GNUNET_OK;
156 }
157
158
159 /**
160  * Some (significant) input changed, recalculate bandwidth assignment
161  * for all peers.
162  */
163 static void
164 recalculate_assigned_bw ()
165 {
166   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
167               "Recalculating bandwidth for all active connections\n");
168   GNUNET_STATISTICS_update (GSA_stats, "# bandwidth recalculations performed",
169                             1, GNUNET_NO);
170   GNUNET_STATISTICS_set (GSA_stats, "# active addresses", active_addr_count,
171                          GNUNET_NO);
172
173   GNUNET_CONTAINER_multihashmap_iterate (addresses, &update_bw_simple_it, NULL);
174 }
175
176 /**
177  * Free the given address
178  * @param addr address to destroy
179  */
180 static void
181 free_address (struct ATS_Address *addr)
182 {
183   GNUNET_free_non_null (addr->ats);
184   GNUNET_free (addr->plugin);
185   GNUNET_free (addr);
186 }
187
188 /**
189  * Create a ATS_address with the given information
190  * @param peer peer
191  * @param plugin_name plugin
192  * @param plugin_addr address
193  * @param plugin_addr_len address length
194  * @param session_id session
195  * @return the ATS_Address
196  */
197 static struct ATS_Address *
198 create_address (const struct GNUNET_PeerIdentity *peer,
199                 const char *plugin_name,
200                 const void *plugin_addr, size_t plugin_addr_len,
201                 uint32_t session_id)
202 {
203   struct ATS_Address *aa = NULL;
204
205   aa = GNUNET_malloc (sizeof (struct ATS_Address) + plugin_addr_len);
206   aa->peer = *peer;
207   aa->addr_len = plugin_addr_len;
208   aa->addr = &aa[1];
209   memcpy (&aa[1], plugin_addr, plugin_addr_len);
210   aa->plugin = GNUNET_strdup (plugin_name);
211   aa->session_id = session_id;
212   return aa;
213 }
214
215
216 /**
217  * Destroy the given address.
218  *
219  * @param addr address to destroy
220  * @return GNUNET_YES if bandwidth allocations should be recalcualted
221  */
222 static int
223 destroy_address (struct ATS_Address *addr)
224 {
225   int ret;
226
227   ret = GNUNET_NO;
228   GNUNET_assert (GNUNET_YES ==
229                  GNUNET_CONTAINER_multihashmap_remove (addresses,
230                                                        &addr->peer.hashPubKey,
231                                                        addr));
232
233 #if HAVE_LIBGLPK
234   if (ats_mode == MODE_MLP)
235     GAS_mlp_address_delete (solver, addresses, addr);
236 #endif
237
238   if (GNUNET_YES == addr->active)
239   {
240     active_addr_count--;
241     addr->active = GNUNET_NO;
242     ret = GNUNET_YES;
243   }
244   free_address (addr);
245   return ret;
246 }
247
248
249 struct CompareAddressContext
250 {
251   const struct ATS_Address *search;
252
253   /* exact_address != NULL if address and session is equal */
254   struct ATS_Address *exact_address;
255   /* exact_address != NULL if address and session is 0 */
256   struct ATS_Address *base_address;
257 };
258
259
260 static int
261 compare_address_it (void *cls, const struct GNUNET_HashCode * key, void *value)
262 {
263   struct CompareAddressContext *cac = cls;
264   struct ATS_Address *aa = value;
265
266   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Comparing peer %4s: address length %u session %u <-> address length %u session %u\n",
267       GNUNET_h2s (key),
268       aa->addr_len, aa->session_id,
269       cac->search->addr_len, cac->search->session_id);
270
271   /* Find an matching exact address:
272    *
273    * Compare by:
274    * aa->addr_len == cac->search->addr_len
275    * aa->plugin == cac->search->plugin
276    * aa->addr == cac->search->addr
277    * aa->session == cac->search->session
278    *
279    * return as exact address
280    */
281   if ((aa->addr_len == cac->search->addr_len) && (0 == strcmp (aa->plugin, cac->search->plugin)))
282   {
283       if ((0 == memcmp (aa->addr, cac->search->addr, aa->addr_len)) && (aa->session_id == cac->search->session_id))
284         cac->exact_address = aa;
285   }
286
287   /* Find an matching base address:
288    *
289    * Properties:
290    *
291    * aa->session_id == 0
292    *
293    * Compare by:
294    * aa->addr_len == cac->search->addr_len
295    * aa->plugin == cac->search->plugin
296    * aa->addr == cac->search->addr
297    *
298    * return as base address
299    */
300   if ((aa->addr_len == cac->search->addr_len) && (0 == strcmp (aa->plugin, cac->search->plugin)))
301   {
302       if ((0 == memcmp (aa->addr, cac->search->addr, aa->addr_len)) && (aa->session_id == 0))
303         cac->base_address = aa;
304   }
305
306   /* Find an matching exact address based on session:
307    *
308    * Properties:
309    *
310    * cac->search->addr_len == 0
311    *
312    * Compare by:
313    * aa->plugin == cac->search->plugin
314    * aa->session_id == cac->search->session_id
315    *
316    * return as exact address
317    */
318   if (0 == cac->search->addr_len)
319   {
320       if ((0 == strcmp (aa->plugin, cac->search->plugin)) && (aa->session_id == cac->search->session_id))
321         cac->exact_address = aa;
322   }
323
324   if (cac->exact_address == NULL)
325     return GNUNET_YES; /* Continue iteration to find exact address */
326   else
327     return GNUNET_NO; /* Stop iteration since we have an exact address */
328 }
329
330
331 /**
332  * Find an existing equivalent address record.
333  * Compares by peer identity and network address OR by session ID
334  * (one of the two must match).
335  *
336  * @param peer peer to lookup addresses for
337  * @param addr existing address record
338  * @return existing address record, NULL for none
339  */
340 struct ATS_Address *
341 find_address (const struct GNUNET_PeerIdentity *peer,
342               const struct ATS_Address *addr)
343 {
344   struct CompareAddressContext cac;
345
346   cac.exact_address = NULL;
347   cac.base_address = NULL;
348   cac.search = addr;
349   GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey,
350                                               &compare_address_it, &cac);
351
352 #if 0
353   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
354               "Found exact address: %s           base address: %s\n",
355               (cac.exact_address != NULL) ? "YES" : "NO",
356               (cac.base_address != NULL) ? "YES" : "NO");
357 #endif
358   if (cac.exact_address == NULL)
359     return cac.base_address;
360   return cac.exact_address;
361 }
362
363
364 static struct ATS_Address *
365 lookup_address (const struct GNUNET_PeerIdentity *peer,
366                 const char *plugin_name, const void *plugin_addr,
367                 size_t plugin_addr_len, uint32_t session_id,
368                 const struct GNUNET_ATS_Information *atsi,
369                 uint32_t atsi_count)
370 {
371   struct ATS_Address *aa;
372   struct ATS_Address *old;
373
374   aa = create_address (peer,
375                        plugin_name,
376                        plugin_addr, plugin_addr_len,
377                        session_id);
378
379   aa->mlp_information = NULL;
380   aa->ats = GNUNET_malloc (atsi_count * sizeof (struct GNUNET_ATS_Information));
381   aa->ats_count = atsi_count;
382   memcpy (aa->ats, atsi, atsi_count * sizeof (struct GNUNET_ATS_Information));
383
384   /* Get existing address or address with session == 0 */
385   old = find_address (peer, aa);
386   free_address (aa);
387   if (old == NULL)
388   {
389     return NULL;
390   }
391   else if (old->session_id != session_id)
392   {
393     return NULL;
394   }
395   return old;
396 }
397
398
399 #if DEADCODE
400 static int
401 compare_address_session_it (void *cls, const struct GNUNET_HashCode * key, void *value)
402 {
403   struct CompareAddressContext *cac = cls;
404   struct ATS_Address *aa = value;
405
406   if ((aa->addr_len == cac->search->addr_len) && (0 == strcmp (aa->plugin, cac->search->plugin)))
407   {
408       if ((0 == memcmp (aa->addr, cac->search->addr, aa->addr_len)) && (aa->session_id == cac->search->session_id))
409       {
410         cac->exact_address = aa;
411         return GNUNET_NO;
412       }
413   }
414   return GNUNET_YES;
415 }
416
417
418 /**
419  * Find an existing equivalent address record.
420  * Compares by peer identity and network address AND by session ID
421  * (one of the two must match).
422  *
423  * @param peer peer to lookup addresses for
424  * @param addr existing address record
425  * @return existing address record, NULL for none
426  */
427 static struct ATS_Address *
428 find_exact_address (const struct GNUNET_PeerIdentity *peer,
429               const struct ATS_Address *addr)
430 {
431   struct CompareAddressContext cac;
432
433   cac.exact_address = NULL;
434   cac.search = addr;
435   GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey,
436                                               &compare_address_session_it, &cac);
437   return cac.exact_address;
438 }
439 #endif
440
441
442 void
443 GAS_addresses_add (const struct GNUNET_PeerIdentity *peer,
444                       const char *plugin_name, const void *plugin_addr,
445                       size_t plugin_addr_len, uint32_t session_id,
446                       const struct GNUNET_ATS_Information *atsi,
447                       uint32_t atsi_count)
448 {
449   struct ATS_Address *aa;
450   struct ATS_Address *old;
451
452   if (GNUNET_NO == running)
453     return;
454
455   GNUNET_assert (NULL != addresses);
456
457   aa = create_address (peer,
458                        plugin_name,
459                        plugin_addr, plugin_addr_len,
460                        session_id);
461
462   aa->mlp_information = NULL;
463   aa->ats = GNUNET_malloc (atsi_count * sizeof (struct GNUNET_ATS_Information));
464   aa->ats_count = atsi_count;
465   memcpy (aa->ats, atsi, atsi_count * sizeof (struct GNUNET_ATS_Information));
466
467   /* Get existing address or address with session == 0 */
468   old = find_address (peer, aa);
469   if (old == NULL)
470   {
471     /* We have a new address */
472     GNUNET_assert (GNUNET_OK ==
473                    GNUNET_CONTAINER_multihashmap_put (addresses,
474                                                       &peer->hashPubKey, aa,
475                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
476     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Added new address for peer `%s' session id %u, %p\n",
477                 GNUNET_i2s (peer), session_id, aa);
478     return;
479   }
480
481   if (old->session_id == 0)
482   {
483     /* We have a base address with out an session, update this address */
484     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
485               "Updated existing address for peer `%s' %p with new session %u\n",
486               GNUNET_i2s (peer), old, session_id);
487     GNUNET_free_non_null (old->ats);
488     old->session_id = session_id;
489     old->ats = NULL;
490     old->ats_count = 0;
491     old->ats = aa->ats;
492     old->ats_count = aa->ats_count;
493     GNUNET_free (aa->plugin);
494     GNUNET_free (aa);
495     return;
496   }
497
498   /* This address and session is already existing */
499   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
500             "Added already existing address for peer `%s' `%s' %p with new session %u\n",
501             GNUNET_i2s (peer), plugin_name, session_id);
502   GNUNET_break (0);
503 }
504
505
506 void
507 GAS_addresses_update (const struct GNUNET_PeerIdentity *peer,
508                       const char *plugin_name, const void *plugin_addr,
509                       size_t plugin_addr_len, uint32_t session_id,
510                       const struct GNUNET_ATS_Information *atsi,
511                       uint32_t atsi_count)
512 {
513   struct ATS_Address *old;
514   uint32_t i;
515
516   if (GNUNET_NO == running)
517     return;
518
519   GNUNET_assert (NULL != addresses);
520
521   /* Get existing address */
522   old = lookup_address (peer, plugin_name, plugin_addr, plugin_addr_len,
523                        session_id, atsi, atsi_count);
524   if (old == NULL)
525   {
526     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Tried to update unknown address for peer `%s' `%s' session id %u\n",
527                 GNUNET_i2s (peer), plugin_name, session_id);
528     GNUNET_break (0);
529     return;
530   }
531
532   for (i = 0; i < atsi_count; i++)
533     switch (ntohl (atsi[i].type))
534     {
535     case GNUNET_ATS_UTILIZATION_UP:
536       old->atsp_utilization_out.value__ = atsi[i].value;
537       break;
538     case GNUNET_ATS_UTILIZATION_DOWN:
539       old->atsp_utilization_in.value__ = atsi[i].value;
540       break;
541     case GNUNET_ATS_QUALITY_NET_DELAY:
542       old->atsp_latency.rel_value = ntohl (atsi[i].value);
543       break;
544     case GNUNET_ATS_QUALITY_NET_DISTANCE:
545       old->atsp_distance = ntohl (atsi[i].value);
546       break;
547     case GNUNET_ATS_COST_WAN:
548       old->atsp_cost_wan = ntohl (atsi[i].value);
549       break;
550     case GNUNET_ATS_COST_LAN:
551       old->atsp_cost_lan = ntohl (atsi[i].value);
552       break;
553     case GNUNET_ATS_COST_WLAN:
554       old->atsp_cost_wlan = ntohl (atsi[i].value);
555       break;
556     case GNUNET_ATS_NETWORK_TYPE:
557       old->atsp_network_type = ntohl (atsi[i].value);
558       break;
559
560     default:
561       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
562                   "Received unsupported ATS type %u\n", ntohl (atsi[i].type));
563       GNUNET_break (0);
564       break;
565     }
566
567   switch (ats_mode)
568   {
569     case MODE_MLP:
570       GAS_mlp_address_update (solver, addresses, old);
571       break;
572     case MODE_SIMPLISTIC:
573       GAS_simplistic_address_update (solver, addresses, old);
574       break;
575     default:
576       GNUNET_break (0);
577       break;
578   }
579 }
580
581
582 /**
583  * Delete an address
584  *
585  * If session != 0, just the session is deleted, the address itself still exists
586  * If session == 0, remove full address
587  * If session == 0 and addrlen == 0, destroy inbound address
588  *
589  * @param cls unused
590  * @param key unused
591  * @param value the 'struct ATS_Address'
592  * @return GNUNET_OK (continue to iterate)
593  */
594 static int
595 destroy_by_session_id (void *cls, const struct GNUNET_HashCode * key, void *value)
596 {
597   const struct ATS_Address *info = cls;
598   struct ATS_Address *aa = value;
599
600   GNUNET_assert (0 ==
601                  memcmp (&aa->peer, &info->peer,
602                          sizeof (struct GNUNET_PeerIdentity)));
603   /* session == 0, remove full address  */
604   if ((info->session_id == 0) && (0 == strcmp (info->plugin, aa->plugin)) &&
605       (aa->addr_len == info->addr_len) &&
606       (0 == memcmp (info->addr, aa->addr, aa->addr_len)))
607   {
608
609     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
610                 "Deleting address for peer `%s': `%s' %u\n",
611                 GNUNET_i2s (&aa->peer), aa->plugin, aa->session_id);
612
613     if (GNUNET_YES == destroy_address (aa))
614       recalculate_assigned_bw ();
615     return GNUNET_OK;
616   }
617   /* session != 0, just remove session */
618   if (aa->session_id != info->session_id)
619     return GNUNET_OK;           /* irrelevant */
620   if (aa->session_id != 0)
621     GNUNET_break (0 == strcmp (info->plugin, aa->plugin));
622   /* session died */
623   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
624               "Deleting session for peer `%s': `%s' %u\n",
625               GNUNET_i2s (&aa->peer), aa->plugin, aa->session_id);
626   aa->session_id = 0;
627
628   if (GNUNET_YES == aa->active)
629   {
630     aa->active = GNUNET_NO;
631     active_addr_count--;
632     recalculate_assigned_bw ();
633   }
634
635   /* session == 0 and addrlen == 0 : destroy address */
636   if (aa->addr_len == 0)
637   {
638     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
639                 "Deleting session and address for peer `%s': `%s' %u\n",
640                 GNUNET_i2s (&aa->peer), aa->plugin, aa->session_id);
641     (void) destroy_address (aa);
642   }
643   else
644   {
645     /* session was set to 0, update address */
646 #if HAVE_LIBGLPK
647   if (ats_mode == MODE_MLP)
648     GAS_mlp_address_update (solver, addresses, aa);
649 #endif
650   }
651
652   return GNUNET_OK;
653 }
654
655
656 void
657 GAS_addresses_destroy (const struct GNUNET_PeerIdentity *peer,
658                        const char *plugin_name, const void *plugin_addr,
659                        size_t plugin_addr_len, uint32_t session_id)
660 {
661   struct ATS_Address *aa;
662   struct ATS_Address *old;
663
664   if (GNUNET_NO == running)
665     return;
666
667   /* Get existing address */
668   old = lookup_address (peer, plugin_name, plugin_addr, plugin_addr_len,
669                        session_id, NULL, 0);
670   if (old == NULL)
671   {
672     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Tried to destroy unknown address for peer `%s' `%s' session id %u\n",
673                 GNUNET_i2s (peer), plugin_name, session_id);
674     return;
675   }
676
677
678   GNUNET_break (0 < strlen (plugin_name));
679   aa = create_address (peer, plugin_name, plugin_addr, plugin_addr_len, session_id);
680
681   GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey,
682                                               &destroy_by_session_id, aa);
683
684   free_address (aa);
685 }
686
687
688 /**
689  * Find a "good" address to use for a peer.  If we already have an existing
690  * address, we stick to it.  Otherwise, we pick by lowest distance and then
691  * by lowest latency.
692  *
693  * @param cls the 'struct ATS_Address**' where we store the result
694  * @param key unused
695  * @param value another 'struct ATS_Address*' to consider using
696  * @return GNUNET_OK (continue to iterate)
697  */
698 static int
699 find_address_it (void *cls, const struct GNUNET_HashCode * key, void *value)
700 {
701   struct ATS_Address **ap = cls;
702   struct ATS_Address *aa = (struct ATS_Address *) value;
703   struct ATS_Address *ab = *ap;
704   struct GNUNET_TIME_Absolute now;
705
706   now = GNUNET_TIME_absolute_get();
707
708   if (aa->blocked_until.abs_value == GNUNET_TIME_absolute_max (now, aa->blocked_until).abs_value)
709   {
710     /* This address is blocked for suggestion */
711     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
712                 "Address %p blocked for suggestion for %llu ms \n",
713                 aa,
714                 GNUNET_TIME_absolute_get_difference(now, aa->blocked_until).rel_value);
715     return GNUNET_OK;
716   }
717
718   aa->block_interval = GNUNET_TIME_relative_add (aa->block_interval, ATS_BLOCKING_DELTA);
719   aa->blocked_until = GNUNET_TIME_absolute_add (now, aa->block_interval);
720
721   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
722               "Address %p ready for suggestion, block interval now %llu \n", aa, aa->block_interval);
723
724   /* FIXME this is a hack */
725
726
727   if (NULL != ab)
728   {
729     if ((0 == strcmp (ab->plugin, "tcp")) &&
730         (0 == strcmp (aa->plugin, "tcp")))
731     {
732       if ((0 != ab->addr_len) &&
733           (0 == aa->addr_len))
734       {
735         /* saved address was an outbound address, but we have an inbound address */
736         *ap = aa;
737         return GNUNET_OK;
738       }
739       if (0 == ab->addr_len)
740       {
741         /* saved address was an inbound address, so do not overwrite */
742         return GNUNET_OK;
743       }
744     }
745   }
746   /* FIXME end of hack */
747
748   if (NULL == ab)
749   {
750     *ap = aa;
751     return GNUNET_OK;
752   }
753   if ((ntohl (ab->assigned_bw_in.value__) == 0) &&
754       (ntohl (aa->assigned_bw_in.value__) > 0))
755   {
756     /* stick to existing connection */
757     *ap = aa;
758     return GNUNET_OK;
759   }
760   if (ab->atsp_distance > aa->atsp_distance)
761   {
762     /* user shorter distance */
763     *ap = aa;
764     return GNUNET_OK;
765   }
766   if (ab->atsp_latency.rel_value > aa->atsp_latency.rel_value)
767   {
768     /* user lower latency */
769     *ap = aa;
770     return GNUNET_OK;
771   }
772   /* don't care */
773   return GNUNET_OK;
774 }
775
776
777 int
778 GAS_addresses_in_use (const struct GNUNET_PeerIdentity *peer,
779                       const char *plugin_name, const void *plugin_addr,
780                       size_t plugin_addr_len, uint32_t session_id, int in_use)
781 {
782 #if DEBUG_ATS
783   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
784               "Received `%s' message for peer `%s': %i\n", "ADDRESS_IN_USE",
785               GNUNET_i2s (peer), in_use);
786 #endif
787
788   struct ATS_Address *old;
789
790   if (GNUNET_NO == running)
791     return GNUNET_SYSERR;
792
793   old = lookup_address (peer, plugin_name, plugin_addr, plugin_addr_len, session_id, NULL, 0);
794   if (NULL == old)
795   {
796     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
797                 "Trying to set unknown address `%s', %s %u %s \n",
798                 GNUNET_i2s (peer),
799                 plugin_name, session_id,
800                 (GNUNET_NO == in_use) ? "NO" : "YES");
801     GNUNET_break (0);
802     return GNUNET_SYSERR;
803   }
804   if (old->used == in_use)
805   {
806     GNUNET_break (0);
807     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
808                 "Address in use called multiple times for peer `%s': %s -> %s \n",
809                 GNUNET_i2s (peer),
810                 (GNUNET_NO == old->used) ? "NO" : "YES",
811                 (GNUNET_NO == in_use) ? "NO" : "YES");
812     return GNUNET_SYSERR;
813   }
814   old->used = in_use;
815 #if HAVE_LIBGLPK
816   if (ats_mode == MODE_MLP)
817      GAS_mlp_address_update (solver, addresses, old);
818 #endif
819   return GNUNET_OK;
820 }
821
822
823 static void 
824 request_address_mlp (const struct GNUNET_PeerIdentity *peer)
825 {
826   struct ATS_Address *aa;
827   aa = NULL;
828
829 #if HAVE_GLPK
830   /* Get preferred address from MODE_MLP */
831   struct ATS_PreferedAddress * paddr = NULL;
832   paddr = GAS_mlp_get_preferred_address (mlp, addresses, peer);
833   aa = paddr->address;
834   aa->assigned_bw_out = GNUNET_BANDWIDTH_value_init(paddr->bandwidth_out);
835   /* FIXME use bw in value */
836   paddr->bandwidth_in = paddr->bandwidth_out;
837   aa->assigned_bw_in = GNUNET_BANDWIDTH_value_init (paddr->bandwidth_in);
838   GNUNET_free (paddr);
839 #endif
840
841   if (aa == NULL)
842   {
843     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
844                 "Cannot suggest address for peer `%s'\n", GNUNET_i2s (peer));
845     return;
846   }
847   if (aa->active == GNUNET_NO)
848   {
849     aa->active = GNUNET_YES;
850     active_addr_count++;
851
852     send_bw_notification (aa);
853   }
854   else
855   {
856     /* just to be sure... */
857     GAS_scheduling_transmit_address_suggestion (peer, aa->plugin, aa->addr,
858                                                 aa->addr_len, aa->session_id,
859                                                 aa->ats, aa->ats_count,
860                                                 aa->assigned_bw_out,
861                                                 aa->assigned_bw_in);
862   }
863
864 }
865
866
867 static void 
868 request_address_simple (const struct GNUNET_PeerIdentity *peer)
869 {
870   struct ATS_Address *aa;
871   aa = NULL;
872
873   /* Get address with: stick to current address, lower distance, lower latency */
874   GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey,
875                                               &find_address_it, &aa);
876   if (aa == NULL)
877   {
878     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
879                 "Cannot suggest address for peer `%s'\n", GNUNET_i2s (peer));
880     return;
881   }
882
883   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
884               "Suggesting address %p for peer `%s'\n", aa, GNUNET_i2s (peer));
885
886   if (aa->active == GNUNET_NO)
887   {
888     aa->active = GNUNET_YES;
889     active_addr_count++;
890     if (ats_mode == MODE_SIMPLISTIC)
891     {
892       recalculate_assigned_bw ();
893     }
894   }
895   else
896   {
897     /* just to be sure... */
898     GAS_scheduling_transmit_address_suggestion (peer, aa->plugin, aa->addr,
899                                                 aa->addr_len, aa->session_id,
900                                                 aa->ats, aa->ats_count,
901                                                 aa->assigned_bw_out,
902                                                 aa->assigned_bw_in);
903   }
904 }
905
906
907 void
908 GAS_addresses_request_address (const struct GNUNET_PeerIdentity *peer)
909 {
910   if (GNUNET_NO == running)
911     return;
912
913   if (ats_mode == MODE_SIMPLISTIC)
914   {
915     request_address_simple (peer);
916   }
917   if (ats_mode == MODE_MLP)
918   {
919     request_address_mlp(peer);
920   }
921 }
922
923
924 static int
925 reset_address_it (void *cls, const struct GNUNET_HashCode * key, void *value)
926 {
927   struct ATS_Address *aa = value;
928
929   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
930               "Resetting interval for peer `%s' address %p from %llu to 0\n", GNUNET_i2s (&aa->peer), aa, aa->block_interval);
931
932   aa->blocked_until = GNUNET_TIME_UNIT_ZERO_ABS;
933   aa->block_interval = GNUNET_TIME_UNIT_ZERO;
934   return GNUNET_OK;
935 }
936
937
938 void
939 GAS_addresses_handle_backoff_reset (const struct GNUNET_PeerIdentity *peer)
940 {
941   GNUNET_break (GNUNET_SYSERR != GNUNET_CONTAINER_multihashmap_get_multiple (addresses,
942                                               &peer->hashPubKey,
943                                               &reset_address_it,
944                                               NULL));
945 }
946
947
948
949 // FIXME: this function should likely end up in the LP-subsystem and
950 // not with 'addresses' in the future...
951 void
952 GAS_addresses_change_preference (const struct GNUNET_PeerIdentity *peer,
953                                  enum GNUNET_ATS_PreferenceKind kind,
954                                  float score)
955 {
956   if (GNUNET_NO == running)
957     return;
958 #if HAVE_LIBGLPK
959   if (ats_mode == MODE_MLP)
960     GAS_mlp_address_change_preference (solver, peer, kind, score);
961 #endif
962 }
963
964
965
966 /**
967  * Initialize address subsystem.
968  *
969  * @param cfg configuration to use
970  * @param stats the statistics handle to use
971  */
972 void
973 GAS_addresses_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
974                     const struct GNUNET_STATISTICS_Handle *stats)
975 {
976   int c;
977   char *quota_wan_in_str;
978   char *quota_wan_out_str;
979   char *mode_str;
980   running = GNUNET_NO;
981
982   /* Initialize the system with configuration values */
983   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(cfg, "ats", "WAN_QUOTA_IN", &quota_wan_in_str))
984   {
985     if (0 == strcmp(quota_wan_in_str, "unlimited") ||
986         (GNUNET_SYSERR == GNUNET_STRINGS_fancy_size_to_bytes (quota_wan_in_str, &wan_quota_in)))
987       wan_quota_in = (UINT32_MAX) /10;
988
989     GNUNET_free (quota_wan_in_str);
990     quota_wan_in_str = NULL;
991   }
992   else
993   {
994     wan_quota_in = (UINT32_MAX) /10;
995   }
996
997   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(cfg, "ats", "WAN_QUOTA_OUT", &quota_wan_out_str))
998   {
999     if (0 == strcmp(quota_wan_out_str, "unlimited") ||
1000         (GNUNET_SYSERR == GNUNET_STRINGS_fancy_size_to_bytes (quota_wan_out_str, &wan_quota_out)))
1001       wan_quota_out = (UINT32_MAX) /10;
1002
1003     GNUNET_free (quota_wan_out_str);
1004     quota_wan_out_str = NULL;
1005   }
1006   else
1007   {
1008     wan_quota_out = (UINT32_MAX) /10;
1009   }
1010
1011   /* Initialize the addresses database */
1012   addresses = GNUNET_CONTAINER_multihashmap_create (128, GNUNET_NO);
1013   GNUNET_assert (NULL != addresses);
1014
1015   /* Figure out configured solution method */
1016   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string (cfg, "ats", "MODE", &mode_str))
1017   {
1018       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "No ressource assignment method configured, using simplistic approch\n");
1019       ats_mode = MODE_SIMPLISTIC;
1020   }
1021   else
1022   {
1023       for (c = 0; c < strlen (mode_str); c++)
1024         mode_str[c] = toupper (mode_str[c]);
1025       if (0 == strcmp (mode_str, "SIMPLISTIC"))
1026       {
1027           ats_mode = MODE_SIMPLISTIC;
1028       }
1029       else if (0 == strcmp (mode_str, "MLP"))
1030       {
1031           ats_mode = MODE_MLP;
1032 #if !HAVE_LIBGLPK
1033           GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Assignment method `%s' configured, but GLPK is not availabe, please install \n", mode_str);
1034           ats_mode = MODE_SIMPLISTIC;
1035 #endif
1036       }
1037       else
1038       {
1039           GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Invalid ressource assignment method `%s' configured, using simplistic approch\n", mode_str);
1040           ats_mode = MODE_SIMPLISTIC;
1041       }
1042   }
1043
1044   /* Start configured solution method */
1045   switch (ats_mode)
1046   {
1047     case MODE_MLP:
1048       /* Init the MLP solver with default values */
1049       solver = GAS_mlp_init (cfg, stats, MLP_MAX_EXEC_DURATION, MLP_MAX_ITERATIONS);
1050       if (NULL != solver)
1051       {
1052           ats_mode = MODE_MLP;
1053           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ATS started in %s mode\n", "MLP");
1054           break;
1055       }
1056       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to initialize MLP solver!\n");
1057     case MODE_SIMPLISTIC:
1058       /* Init the simplistic solver with default values */
1059       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ATS started in %s mode\n", "SIMPLISTIC");
1060       solver = GAS_simplistic_init (cfg, stats);
1061       if (NULL != solver)
1062       {
1063           ats_mode = MODE_SIMPLISTIC;
1064           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ATS started in %s mode\n", "SIMPLISTIC");
1065           break;
1066       }
1067       else
1068       {
1069         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to initialize simplistic solver!\n");
1070         return;
1071       }
1072       break;
1073     default:
1074       GNUNET_break (0);
1075       break;
1076   }
1077   /* up and running */
1078   running = GNUNET_YES;
1079 }
1080
1081
1082 /**
1083  * Free memory of address.
1084  *
1085  * @param cls NULL
1086  * @param key peer identity (unused)
1087  * @param value the 'struct ATS_Address' to free
1088  * @return GNUNET_OK (continue to iterate)
1089  */
1090 static int
1091 free_address_it (void *cls, const struct GNUNET_HashCode * key, void *value)
1092 {
1093   struct ATS_Address *aa = value;
1094
1095   destroy_address (aa);
1096   return GNUNET_OK;
1097 }
1098
1099
1100 void
1101 GAS_addresses_destroy_all ()
1102 {
1103   if (GNUNET_NO == running)
1104     return;
1105
1106   if (addresses != NULL)
1107     GNUNET_CONTAINER_multihashmap_iterate (addresses, &free_address_it, NULL);
1108   GNUNET_assert (active_addr_count == 0);
1109 }
1110
1111
1112 /**
1113  * Shutdown address subsystem.
1114  */
1115 void
1116 GAS_addresses_done ()
1117 {
1118   GAS_addresses_destroy_all ();
1119   running = GNUNET_NO;
1120   GNUNET_CONTAINER_multihashmap_destroy (addresses);
1121   addresses = NULL;
1122
1123   /* Stop configured solution method */
1124   switch (ats_mode)
1125   {
1126     case MODE_MLP:
1127       /* Init the MLP solver with default values */
1128       GAS_mlp_done (solver);
1129       break;
1130     case MODE_SIMPLISTIC:
1131       /* Init the simplistic solver with default values */
1132       GAS_simplistic_done (solver);
1133       break;
1134     default:
1135       break;
1136   }
1137 }
1138
1139 struct PeerIteratorContext
1140 {
1141   GNUNET_ATS_Peer_Iterator it;
1142   void *it_cls;
1143   struct GNUNET_CONTAINER_MultiHashMap *peers_returned;
1144 };
1145
1146 static int
1147 peer_it (void *cls,
1148          const struct GNUNET_HashCode * key,
1149          void *value)
1150 {
1151   struct PeerIteratorContext *ip_ctx = cls;
1152   struct GNUNET_PeerIdentity tmp;
1153
1154   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(ip_ctx->peers_returned, key))
1155   {
1156       GNUNET_CONTAINER_multihashmap_put(ip_ctx->peers_returned, key, NULL, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
1157       tmp.hashPubKey = (*key);
1158       ip_ctx->it (ip_ctx->it_cls, &tmp);
1159   }
1160
1161   return GNUNET_OK;
1162 }
1163
1164 /**
1165  * Return all peers currently known to ATS
1166  *
1167  * @param p_it the iterator to call for every peer, callbach with id == NULL
1168  *        when done
1169  * @param p_it_cls the closure for the iterator
1170  */
1171 void
1172 GAS_addresses_iterate_peers (GNUNET_ATS_Peer_Iterator p_it, void *p_it_cls)
1173 {
1174   struct PeerIteratorContext ip_ctx;
1175   unsigned int size;
1176
1177   if (NULL == p_it)
1178       return;
1179   GNUNET_assert (NULL != addresses);
1180
1181   size = GNUNET_CONTAINER_multihashmap_size(addresses);
1182   if (0 != size)
1183   {
1184     ip_ctx.it = p_it;
1185     ip_ctx.it_cls = p_it_cls;
1186     ip_ctx.peers_returned = GNUNET_CONTAINER_multihashmap_create (size, GNUNET_NO);
1187     GNUNET_CONTAINER_multihashmap_iterate (addresses, &peer_it, &ip_ctx);
1188     GNUNET_CONTAINER_multihashmap_destroy (ip_ctx.peers_returned);
1189   }
1190   p_it (p_it_cls, NULL);
1191 }
1192
1193 struct PeerInfoIteratorContext
1194 {
1195   GNUNET_ATS_PeerInfo_Iterator it;
1196   void *it_cls;
1197 };
1198
1199
1200 static int 
1201 peerinfo_it (void *cls,
1202              const struct GNUNET_HashCode * key,
1203              void *value)
1204 {
1205   struct PeerInfoIteratorContext *pi_ctx = cls;
1206   struct ATS_Address *addr = (struct ATS_Address *)  value;
1207   struct GNUNET_ATS_Information *ats;
1208   uint32_t ats_count;
1209
1210   if (NULL != pi_ctx->it)
1211   {
1212     ats_count = assemble_ats_information (addr, &ats);
1213
1214     pi_ctx->it (pi_ctx->it_cls,
1215                 &addr->peer,
1216                 addr->plugin,
1217                 addr->addr, addr->addr_len,
1218                 addr->active,
1219                 ats, ats_count,
1220                 addr->assigned_bw_out,
1221                 addr->assigned_bw_in);
1222     GNUNET_free (ats);
1223   }
1224   return GNUNET_YES;
1225 }
1226
1227
1228 /**
1229  * Return all peers currently known to ATS
1230  *
1231  * @param peer the respective peer
1232  * @param pi_it the iterator to call for every peer
1233  * @param pi_it_cls the closure for the iterator
1234  */
1235 void
1236 GAS_addresses_get_peer_info (const struct GNUNET_PeerIdentity *peer, GNUNET_ATS_PeerInfo_Iterator pi_it, void *pi_it_cls)
1237 {
1238   struct PeerInfoIteratorContext pi_ctx;
1239   struct GNUNET_BANDWIDTH_Value32NBO zero_bw;
1240   GNUNET_assert (NULL != peer);
1241   GNUNET_assert (NULL != addresses);
1242   if (NULL == pi_it)
1243     return; /* does not make sense without callback */
1244
1245   zero_bw = GNUNET_BANDWIDTH_value_init (0);
1246   pi_ctx.it = pi_it;
1247   pi_ctx.it_cls = pi_it_cls;
1248
1249   GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey, &peerinfo_it, &pi_ctx);
1250
1251   if (NULL != pi_it)
1252     pi_it (pi_it_cls, NULL, NULL, NULL, 0, GNUNET_NO, NULL, 0, zero_bw, zero_bw);
1253
1254 }
1255
1256
1257 /* end of gnunet-service-ats_addresses.c */