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