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