b13de0df1a0c79e45f7a6de29c42adb58329ee24
[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_addresses_mlp.h"
32 #include "gnunet-service-ats_performance.h"
33 #include "gnunet-service-ats_scheduling.h"
34 #include "gnunet-service-ats_reservations.h"
35
36 enum ATS_Mode
37 {
38         SIMPLE,
39         MLP
40 };
41
42 static struct GNUNET_CONTAINER_MultiHashMap *addresses;
43
44 static unsigned long long wan_quota_in;
45
46 static unsigned long long wan_quota_out;
47
48 static unsigned int active_addr_count;
49
50 static int ats_mode;
51
52 /**
53  * Update a bandwidth assignment for a peer.  This trivial method currently
54  * simply assigns the same share to all active connections.
55  *
56  * @param cls unused
57  * @param key unused
58  * @param value the 'struct ATS_Address'
59  * @return GNUNET_OK (continue to iterate)
60  */
61 static int
62 update_bw_it (void *cls, const GNUNET_HashCode * key, void *value)
63 {
64   struct ATS_Address *aa = value;
65
66   if (GNUNET_YES != aa->active)
67     return GNUNET_OK;
68   GNUNET_assert (active_addr_count > 0);
69   aa->assigned_bw_in.value__ = htonl (wan_quota_in / active_addr_count);
70   aa->assigned_bw_out.value__ = htonl (wan_quota_out / active_addr_count);
71   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New bandwidth for peer %s is %u/%u\n",
72               GNUNET_i2s (&aa->peer), ntohl (aa->assigned_bw_in.value__),
73               ntohl (aa->assigned_bw_out.value__));
74   GAS_scheduling_transmit_address_suggestion (&aa->peer, aa->plugin, aa->addr,
75                                               aa->addr_len, aa->session_id,
76                                               aa->ats, aa->ats_count,
77                                               aa->assigned_bw_out,
78                                               aa->assigned_bw_in);
79   GAS_reservations_set_bandwidth (&aa->peer, aa->assigned_bw_in);
80   GAS_performance_notify_clients (&aa->peer, aa->plugin, aa->addr, aa->addr_len,
81                                   aa->ats, aa->ats_count, aa->assigned_bw_out,
82                                   aa->assigned_bw_in);
83   return GNUNET_OK;
84 }
85
86
87 /**
88  * Some (significant) input changed, recalculate bandwidth assignment
89  * for all peers.
90  */
91 static void
92 recalculate_assigned_bw ()
93 {
94   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
95               "Recalculating bandwidth for all active connections\n");
96   GNUNET_STATISTICS_update (GSA_stats, "# bandwidth recalculations performed",
97                             1, GNUNET_NO);
98   GNUNET_STATISTICS_set (GSA_stats, "# active addresses", active_addr_count,
99                          GNUNET_NO);
100   GNUNET_CONTAINER_multihashmap_iterate (addresses, &update_bw_it, NULL);
101 }
102
103
104 /**
105  * Destroy the given address.
106  *
107  * @param addr address to destroy
108  * @return GNUNET_YES if bandwidth allocations should be recalcualted
109  */
110 static int
111 destroy_address (struct ATS_Address *addr)
112 {
113   int ret;
114
115   ret = GNUNET_NO;
116   GNUNET_assert (GNUNET_YES ==
117                  GNUNET_CONTAINER_multihashmap_remove (addresses,
118                                                        &addr->peer.hashPubKey,
119                                                        addr));
120   if (GNUNET_YES == addr->active)
121   {
122     active_addr_count--;
123     addr->active = GNUNET_NO;
124     ret = GNUNET_YES;
125   }
126   GNUNET_free_non_null (addr->ats);
127   GNUNET_free (addr->plugin);
128   GNUNET_free (addr);
129   return ret;
130 }
131
132
133 struct CompareAddressContext
134 {
135   const struct ATS_Address *search;
136   struct ATS_Address *result;
137 };
138
139
140 static int
141 compare_address_it (void *cls, const GNUNET_HashCode * key, void *value)
142 {
143   struct CompareAddressContext *cac = cls;
144   struct ATS_Address *aa = value;
145
146   if (((aa->addr_len != cac->search->addr_len) ||
147        (0 != strcmp (aa->plugin, cac->search->plugin)) ||
148        (0 != memcmp (aa->addr, cac->search->addr, aa->addr_len))) &&
149       ((aa->session_id != cac->search->session_id) ||
150        (cac->search->session_id == 0)))
151     return GNUNET_YES;
152   cac->result = aa;
153   return GNUNET_NO;
154 }
155
156
157 /**
158  * Find an existing equivalent address record.
159  * Compares by peer identity and network address OR by session ID
160  * (one of the two must match).
161  *
162  * @param peer peer to lookup addresses for
163  * @param addr existing address record
164  * @return existing address record, NULL for none
165  */
166 struct ATS_Address *
167 find_address (const struct GNUNET_PeerIdentity *peer,
168               const struct ATS_Address *addr)
169 {
170   struct CompareAddressContext cac;
171
172   cac.result = NULL;
173   cac.search = addr;
174   GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey,
175                                               &compare_address_it, &cac);
176   return cac.result;
177 }
178
179
180 void
181 GAS_addresses_update (const struct GNUNET_PeerIdentity *peer,
182                       const char *plugin_name, const void *plugin_addr,
183                       size_t plugin_addr_len, uint32_t session_id,
184                       const struct GNUNET_ATS_Information *atsi,
185                       uint32_t atsi_count)
186 {
187   struct ATS_Address *aa;
188   struct ATS_Address *old;
189   uint32_t i;
190
191   aa = GNUNET_malloc (sizeof (struct ATS_Address) + plugin_addr_len);
192   aa->ats = GNUNET_malloc (atsi_count * sizeof (struct GNUNET_ATS_Information));
193   aa->peer = *peer;
194   aa->addr_len = plugin_addr_len;
195   aa->ats_count = atsi_count;
196   memcpy (aa->ats, atsi, atsi_count * sizeof (struct GNUNET_ATS_Information));
197   aa->addr = &aa[1];
198   memcpy (&aa[1], plugin_addr, plugin_addr_len);
199   aa->plugin = GNUNET_strdup (plugin_name);
200   aa->session_id = session_id;
201   old = find_address (peer, aa);
202   if (old == NULL)
203   {
204     GNUNET_assert (GNUNET_OK ==
205                    GNUNET_CONTAINER_multihashmap_put (addresses,
206                                                       &peer->hashPubKey, aa,
207                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
208     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Added new address for peer `%s' %X\n",
209                 GNUNET_i2s (peer), aa);
210     old = aa;
211   }
212   else
213   {
214     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
215                 "Updated existing address for peer `%s' %X \n",
216                 GNUNET_i2s (peer), old);
217     GNUNET_free_non_null (old->ats);
218     old->session_id = session_id;
219     old->ats = NULL;
220     old->ats_count = 0;
221     old->ats = aa->ats;
222     old->ats_count = aa->ats_count;
223     GNUNET_free (aa->plugin);
224     GNUNET_free (aa);
225   }
226   for (i = 0; i < atsi_count; i++)
227     switch (ntohl (atsi[i].type))
228     {
229     case GNUNET_ATS_UTILIZATION_UP:
230       old->atsp_utilization_out.value__ = atsi[i].value;
231       break;
232     case GNUNET_ATS_UTILIZATION_DOWN:
233       old->atsp_utilization_in.value__ = atsi[i].value;
234       break;
235     case GNUNET_ATS_QUALITY_NET_DELAY:
236       old->atsp_latency.rel_value = ntohl (atsi[i].value);
237       break;
238     case GNUNET_ATS_QUALITY_NET_DISTANCE:
239       old->atsp_distance = ntohl (atsi[i].value);
240       break;
241     case GNUNET_ATS_COST_WAN:
242       old->atsp_cost_wan = ntohl (atsi[i].value);
243       break;
244     case GNUNET_ATS_COST_LAN:
245       old->atsp_cost_lan = ntohl (atsi[i].value);
246       break;
247     case GNUNET_ATS_COST_WLAN:
248       old->atsp_cost_wlan = ntohl (atsi[i].value);
249       break;
250     case GNUNET_ATS_NETWORK_TYPE:
251       old->atsp_network_type = ntohl (atsi[i].value);
252       break;
253
254     default:
255       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
256                   "Received unsupported ATS type %u\n", ntohl (atsi[i].type));
257       GNUNET_break (0);
258       break;
259     }
260 }
261
262
263 /**
264  * Delete an address
265  *
266  * If session != 0, just the session is deleted, the address itself still exists
267  * If session == 0, remove full address
268  * If session == 0 and addrlen == 0, destroy inbound address
269  *
270  * @param cls unused
271  * @param key unused
272  * @param value the 'struct ATS_Address'
273  * @return GNUNET_OK (continue to iterate)
274  */
275 static int
276 destroy_by_session_id (void *cls, const GNUNET_HashCode * key, void *value)
277 {
278   const struct ATS_Address *info = cls;
279   struct ATS_Address *aa = value;
280
281   GNUNET_assert (0 ==
282                  memcmp (&aa->peer, &info->peer,
283                          sizeof (struct GNUNET_PeerIdentity)));
284   /* session == 0, remove full address  */
285   if ((info->session_id == 0) && (0 == strcmp (info->plugin, aa->plugin)) &&
286       (aa->addr_len == info->addr_len) &&
287       (0 == memcmp (info->addr, aa->addr, aa->addr_len)))
288   {
289     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
290                 "Deleting address for peer `%s': `%s'\n",
291                 GNUNET_i2s (&aa->peer), aa->plugin);
292     if (GNUNET_YES == destroy_address (aa))
293       recalculate_assigned_bw ();
294     return GNUNET_OK;
295   }
296   /* session != 0, just remove session */
297   if (aa->session_id != info->session_id)
298     return GNUNET_OK;           /* irrelevant */
299   if (aa->session_id != 0)
300     GNUNET_break (0 == strcmp (info->plugin, aa->plugin));
301   /* session died */
302   aa->session_id = 0;
303
304   if (GNUNET_YES == aa->active)
305   {
306     aa->active = GNUNET_NO;
307     active_addr_count--;
308     recalculate_assigned_bw ();
309   }
310
311   /* session == 0 and addrlen == 0 : destroy address */
312   if (aa->addr_len == 0)
313     (void) destroy_address (aa);
314
315   return GNUNET_OK;
316 }
317
318
319 void
320 GAS_addresses_destroy (const struct GNUNET_PeerIdentity *peer,
321                        const char *plugin_name, const void *plugin_addr,
322                        size_t plugin_addr_len, uint32_t session_id)
323 {
324   struct ATS_Address aa;
325
326   GNUNET_break (0 < strlen (plugin_name));
327   aa.peer = *peer;
328   aa.addr_len = plugin_addr_len;
329   aa.addr = plugin_addr;
330   aa.plugin = (char *) plugin_name;
331   aa.session_id = session_id;
332   GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey,
333                                               &destroy_by_session_id, &aa);
334 }
335
336
337 /**
338  * Find a "good" address to use for a peer.  If we already have an existing
339  * address, we stick to it.  Otherwise, we pick by lowest distance and then
340  * by lowest latency.
341  *
342  * @param cls the 'struct ATS_Address**' where we store the result
343  * @param key unused
344  * @param value another 'struct ATS_Address*' to consider using
345  * @return GNUNET_OK (continue to iterate)
346  */
347 static int
348 find_address_it (void *cls, const GNUNET_HashCode * key, void *value)
349 {
350   struct ATS_Address **ap = cls;
351   struct ATS_Address *aa = (struct ATS_Address *) value;
352   struct ATS_Address *ab = *ap;
353
354   if (NULL == ab)
355   {
356     *ap = aa;
357     return GNUNET_OK;
358   }
359   if ((ntohl (ab->assigned_bw_in.value__) == 0) &&
360       (ntohl (aa->assigned_bw_in.value__) > 0))
361   {
362     /* stick to existing connection */
363     *ap = aa;
364     return GNUNET_OK;
365   }
366   if (ab->atsp_distance > aa->atsp_distance)
367   {
368     /* user shorter distance */
369     *ap = aa;
370     return GNUNET_OK;
371   }
372   if (ab->atsp_latency.rel_value > aa->atsp_latency.rel_value)
373   {
374     /* user lower latency */
375     *ap = aa;
376     return GNUNET_OK;
377   }
378   /* don't care */
379   return GNUNET_OK;
380 }
381
382
383 void
384 GAS_addresses_in_use (const struct GNUNET_PeerIdentity *peer,
385                       const char *plugin_name, const void *plugin_addr,
386                       size_t plugin_addr_len, uint32_t session_id, int in_use)
387 {
388
389   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
390               "Received `%s' message for peer `%s': %i\n", "ADDRESS_IN_USE",
391               GNUNET_i2s (peer), in_use);
392 }
393
394 void
395 GAS_addresses_request_address (const struct GNUNET_PeerIdentity *peer)
396 {
397   struct ATS_Address *aa;
398
399   aa = NULL;
400   GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey,
401                                               &find_address_it, &aa);
402   if (aa == NULL)
403   {
404     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
405                 "Cannot suggest address for peer `%s'\n", GNUNET_i2s (peer));
406     return;
407   }
408   if (aa->active == GNUNET_NO)
409   {
410     aa->active = GNUNET_YES;
411     active_addr_count++;
412     recalculate_assigned_bw ();
413   }
414   else
415   {
416     /* just to be sure... */
417     GAS_scheduling_transmit_address_suggestion (peer, aa->plugin, aa->addr,
418                                                 aa->addr_len, aa->session_id,
419                                                 aa->ats, aa->ats_count,
420                                                 aa->assigned_bw_out,
421                                                 aa->assigned_bw_in);
422   }
423 }
424
425
426 // FIXME: this function should likely end up in the LP-subsystem and
427 // not with 'addresses' in the future...
428 void
429 GAS_addresses_change_preference (const struct GNUNET_PeerIdentity *peer,
430                                  enum GNUNET_ATS_PreferenceKind kind,
431                                  float score)
432 {
433   // do nothing for now...
434 }
435
436
437
438 /**
439  * Initialize address subsystem.
440  *
441  * @param cfg configuration to use
442  */
443 void
444 GAS_addresses_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
445 {
446   GNUNET_assert (GNUNET_OK ==
447                  GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
448                                                       "WAN_QUOTA_IN",
449                                                       &wan_quota_in));
450   GNUNET_assert (GNUNET_OK ==
451                  GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
452                                                       "WAN_QUOTA_OUT",
453                                                       &wan_quota_out));
454
455
456
457   switch (GNUNET_CONFIGURATION_get_value_yesno (cfg, "ats", "MLP"))
458   {
459         /* MLP = YES */
460         case GNUNET_YES:
461 #if HAVE_LIBGLPK
462           ats_mode = MLP;
463           /* Init the MLP solver with default values */
464           GAS_mlp_init (MLP_MAX_EXEC_DURATION, MLP_MAX_ITERATIONS);
465           break;
466 #else
467           GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "MLP mode was configured, but libglpk is not installed, switching to simple mode");
468           ats_mode = SIMPLE;
469           break;
470 #endif
471         /* MLP = NO */
472         case GNUNET_NO:
473                 ats_mode = SIMPLE;
474                 break;
475         /* No configuration value */
476         case GNUNET_SYSERR:
477                 ats_mode = SIMPLE;
478                 break;
479         default:
480                 break;
481   }
482
483   addresses = GNUNET_CONTAINER_multihashmap_create (128);
484 }
485
486
487 /**
488  * Free memory of address.
489  *
490  * @param cls NULL
491  * @param key peer identity (unused)
492  * @param value the 'struct ATS_Address' to free
493  * @return GNUNET_OK (continue to iterate)
494  */
495 static int
496 free_address_it (void *cls, const GNUNET_HashCode * key, void *value)
497 {
498   struct ATS_Address *aa = value;
499
500   destroy_address (aa);
501   return GNUNET_OK;
502 }
503
504
505 void
506 GAS_addresses_destroy_all ()
507 {
508   if (addresses != NULL)
509     GNUNET_CONTAINER_multihashmap_iterate (addresses, &free_address_it, NULL);
510   GNUNET_assert (active_addr_count == 0);
511 }
512
513
514 /**
515  * Shutdown address subsystem.
516  */
517 void
518 GAS_addresses_done ()
519 {
520   GAS_addresses_destroy_all ();
521   GNUNET_CONTAINER_multihashmap_destroy (addresses);
522   addresses = NULL;
523 #if HAVE_LIBGLPK
524   if (ats_mode == MLP)
525   {
526     GAS_mlp_done ();
527   }
528 #endif
529
530 }
531
532
533 /* end of gnunet-service-ats_addresses.c */