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