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