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