345409d547712b91f8b580686f9ac945361ff38a
[oweals/gnunet.git] / src / ats / gnunet-service-ats_addresses.c
1 /*
2  This file is part of GNUnet.
3  Copyright (C) 2011-2015 GNUnet e.V.
4
5  GNUnet is free software: you can redistribute it and/or modify it
6  under the terms of the GNU Affero General Public License as published
7  by the Free Software Foundation, either version 3 of the License,
8  or (at your 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  Affero General Public License for more details.
14
15  You should have received a copy of the GNU Affero General Public License
16  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 /**
20  * @file ats/gnunet-service-ats_addresses.c
21  * @brief ats service address management
22  * @author Matthias Wachs
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #include "gnunet-service-ats_addresses.h"
27 #include "gnunet-service-ats_performance.h"
28 #include "gnunet-service-ats_normalization.h"
29 #include "gnunet-service-ats_plugins.h"
30
31
32 /**
33  * A multihashmap to store all addresses
34  */
35 struct GNUNET_CONTAINER_MultiPeerMap *GSA_addresses;
36
37
38 /**
39  * Update statistic on number of addresses.
40  */
41 static void
42 update_addresses_stat ()
43 {
44   GNUNET_STATISTICS_set (GSA_stats,
45                          "# addresses",
46                          GNUNET_CONTAINER_multipeermap_size (GSA_addresses),
47                          GNUNET_NO);
48 }
49
50
51 /**
52  * Free the given address
53  *
54  * @param addr address to destroy
55  */
56 static void
57 free_address (struct ATS_Address *addr)
58 {
59   GNUNET_assert (GNUNET_YES ==
60                  GNUNET_CONTAINER_multipeermap_remove (GSA_addresses,
61                                                        &addr->peer,
62                                                        addr));
63   update_addresses_stat ();
64   GAS_plugin_delete_address (addr);
65   GAS_performance_notify_all_clients (&addr->peer,
66                                       addr->plugin,
67                                       addr->addr,
68                                       addr->addr_len,
69                                       GNUNET_NO,
70                                       NULL,
71                                       addr->local_address_info,
72                                       GNUNET_BANDWIDTH_ZERO,
73                                       GNUNET_BANDWIDTH_ZERO);
74   GNUNET_free (addr->plugin);
75   GNUNET_free (addr);
76 }
77
78
79 /**
80  * Initialize @a norm.  Sets all historic values to undefined.
81  *
82  * @param norm normalization data to initialize
83  */
84 static void
85 init_norm (struct GAS_NormalizationInfo *norm)
86 {
87   unsigned int c;
88
89   for (c = 0; c < GAS_normalization_queue_length; c++)
90     norm->atsi_abs[c] = UINT64_MAX;
91 }
92
93
94 /**
95  * Create a ATS_address with the given information
96  *
97  * @param peer peer
98  * @param plugin_name plugin
99  * @param plugin_addr address
100  * @param plugin_addr_len address length
101  * @param local_address_info additional local info for the address
102  * @param session_id session identifier, can never be 0
103  * @return the ATS_Address
104  */
105 static struct ATS_Address *
106 create_address (const struct GNUNET_PeerIdentity *peer,
107                 const char *plugin_name,
108                 const void *plugin_addr,
109                 size_t plugin_addr_len,
110                 uint32_t local_address_info,
111                 uint32_t session_id)
112 {
113   struct ATS_Address *aa;
114
115   aa = GNUNET_malloc (sizeof (struct ATS_Address) + plugin_addr_len);
116   aa->peer = *peer;
117   aa->addr_len = plugin_addr_len;
118   aa->addr = &aa[1];
119   GNUNET_memcpy (&aa[1],
120           plugin_addr,
121           plugin_addr_len);
122   aa->plugin = GNUNET_strdup (plugin_name);
123   aa->session_id = session_id;
124   aa->local_address_info = local_address_info;
125   init_norm (&aa->norm_delay);
126   init_norm (&aa->norm_distance);
127   init_norm (&aa->norm_utilization_in);
128   init_norm (&aa->norm_utilization_out);
129   return aa;
130 }
131
132
133 /**
134  * Closure for #find_address_cb()
135  */
136 struct FindAddressContext
137 {
138   /**
139    * Session Id to look for.
140    */
141   uint32_t session_id;
142
143   /**
144    * Where to store matching address result.
145    */
146   struct ATS_Address *exact_address;
147
148 };
149
150
151 /**
152  * Find session matching given session ID.
153  *
154  * @param cls a `struct FindAddressContext`
155  * @param key peer id
156  * @param value the address to compare with
157  * @return #GNUNET_YES to continue, #GNUNET_NO if address is found
158  */
159 static int
160 find_address_cb (void *cls,
161                  const struct GNUNET_PeerIdentity *key,
162                  void *value)
163 {
164   struct FindAddressContext *fac = cls;
165   struct ATS_Address *aa = value;
166
167   if (aa->session_id == fac->session_id)
168   {
169     fac->exact_address = aa;
170     return GNUNET_NO;
171   }
172   return GNUNET_YES;
173 }
174
175
176 /**
177  * Find the exact address
178  *
179  * @param peer peer
180  * @param session_id session id, can never be 0
181  * @return an ATS_address or NULL
182  */
183 static struct ATS_Address *
184 find_exact_address (const struct GNUNET_PeerIdentity *peer,
185                     uint32_t session_id)
186 {
187   struct FindAddressContext fac;
188
189   fac.exact_address = NULL;
190   fac.session_id = session_id;
191   GNUNET_CONTAINER_multipeermap_get_multiple (GSA_addresses,
192                                               peer,
193                                               &find_address_cb, &fac);
194   return fac.exact_address;
195 }
196
197
198 /**
199  * Add a new address for a peer.
200  *
201  * @param peer peer
202  * @param plugin_name transport plugin name
203  * @param plugin_addr plugin address
204  * @param plugin_addr_len length of the plugin address in @a plugin_addr
205  * @param local_address_info the local address for the address
206  * @param session_id session id, can be 0
207  * @param prop performance information for this address
208  */
209 void
210 GAS_addresses_add (const struct GNUNET_PeerIdentity *peer,
211                    const char *plugin_name,
212                    const void *plugin_addr,
213                    size_t plugin_addr_len,
214                    uint32_t local_address_info,
215                    uint32_t session_id,
216                    const struct GNUNET_ATS_Properties *prop)
217 {
218   struct ATS_Address *new_address;
219
220   if (NULL != find_exact_address (peer,
221                                   session_id))
222   {
223     GNUNET_break (0);
224     return;
225   }
226   GNUNET_break (GNUNET_NT_UNSPECIFIED != prop->scope);
227   new_address = create_address (peer,
228                                 plugin_name,
229                                 plugin_addr,
230                                 plugin_addr_len,
231                                 local_address_info,
232                                 session_id);
233   /* Add a new address */
234   new_address->properties = *prop;
235   new_address->t_added = GNUNET_TIME_absolute_get();
236   new_address->t_last_activity = GNUNET_TIME_absolute_get();
237   GNUNET_assert(GNUNET_OK ==
238                 GNUNET_CONTAINER_multipeermap_put (GSA_addresses,
239                                                    peer,
240                                                    new_address,
241                                                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
242   update_addresses_stat ();
243   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
244               "Adding new address for peer `%s' slot %u\n",
245               GNUNET_i2s (peer),
246               session_id);
247   /* Tell solver about new address */
248   GAS_plugin_solver_lock ();
249   GAS_plugin_new_address (new_address);
250   GAS_normalization_update_property (new_address); // FIXME: needed?
251   GAS_plugin_solver_unlock ();
252   /* Notify performance clients about new address */
253   GAS_performance_notify_all_clients (&new_address->peer,
254                                       new_address->plugin,
255                                       new_address->addr,
256                                       new_address->addr_len,
257                                       new_address->active,
258                                       &new_address->properties,
259                                       new_address->local_address_info,
260                                       GNUNET_BANDWIDTH_value_init (new_address->assigned_bw_out),
261                                       GNUNET_BANDWIDTH_value_init (new_address->assigned_bw_in));
262 }
263
264
265 /**
266  * Update an address with new performance information for a peer.
267  *
268  * @param peer peer
269  * @param session_id session id, never 0
270  * @param prop performance information for this address
271  */
272 void
273 GAS_addresses_update (const struct GNUNET_PeerIdentity *peer,
274                       uint32_t session_id,
275                       const struct GNUNET_ATS_Properties *prop)
276 {
277   struct ATS_Address *aa;
278
279   /* Get existing address */
280   aa = find_exact_address (peer,
281                            session_id);
282   if (NULL == aa)
283   {
284     GNUNET_break (0);
285     return;
286   }
287   if (NULL == aa->solver_information)
288   {
289     GNUNET_break (0);
290     return;
291   }
292   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
293               "Received ADDRESS_UPDATE for peer `%s' slot %u\n",
294               GNUNET_i2s (peer),
295               (unsigned int) session_id);
296   GNUNET_break (GNUNET_NT_UNSPECIFIED != prop->scope);
297   /* Update address */
298   aa->t_last_activity = GNUNET_TIME_absolute_get();
299   aa->properties = *prop;
300   /* Notify performance clients about updated address */
301   GAS_performance_notify_all_clients (&aa->peer,
302                                       aa->plugin,
303                                       aa->addr,
304                                       aa->addr_len,
305                                       aa->active,
306                                       prop,
307                                       aa->local_address_info,
308                                       GNUNET_BANDWIDTH_value_init (aa->assigned_bw_out),
309                                       GNUNET_BANDWIDTH_value_init (aa->assigned_bw_in));
310
311   GAS_normalization_update_property (aa);
312 }
313
314
315 /**
316  * Remove an address for a peer.
317  *
318  * @param peer peer
319  * @param session_id session id, can never be 0
320  */
321 void
322 GAS_addresses_destroy (const struct GNUNET_PeerIdentity *peer,
323                        uint32_t session_id)
324 {
325   struct ATS_Address *ea;
326
327   /* Get existing address */
328   ea = find_exact_address (peer,
329                            session_id);
330   if (NULL == ea)
331   {
332     GNUNET_break (0);
333     return;
334   }
335   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
336               "Received ADDRESS_DESTROYED for peer `%s' session %u\n",
337               GNUNET_i2s (peer),
338               session_id);
339   free_address (ea);
340 }
341
342
343 /**
344  * Initialize address subsystem. The addresses subsystem manages the addresses
345  * known and current performance information. It has a solver component
346  * responsible for the resource allocation. It tells the solver about changes
347  * and receives updates when the solver changes the resource allocation.
348  */
349 void
350 GAS_addresses_init ()
351 {
352   GSA_addresses
353     = GNUNET_CONTAINER_multipeermap_create (128,
354                                             GNUNET_NO);
355   update_addresses_stat ();
356 }
357
358
359 /**
360  * Destroy all addresses iterator
361  *
362  * @param cls NULL
363  * @param key peer identity (unused)
364  * @param value the 'struct ATS_Address' to free
365  * @return #GNUNET_OK (continue to iterate)
366  */
367 static int
368 destroy_all_address_it (void *cls,
369                         const struct GNUNET_PeerIdentity *key,
370                         void *value)
371 {
372   struct ATS_Address *aa = value;
373
374   free_address (aa);
375   return GNUNET_OK;
376 }
377
378
379 /**
380  * Remove all addresses
381  */
382 void
383 GAS_addresses_destroy_all ()
384 {
385   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
386               "Destroying all addresses\n");
387   if (0 ==
388       GNUNET_CONTAINER_multipeermap_size (GSA_addresses))
389     return;
390   GAS_plugin_solver_lock ();
391   GNUNET_CONTAINER_multipeermap_iterate (GSA_addresses,
392                                          &destroy_all_address_it,
393                                          NULL);
394   GAS_plugin_solver_unlock ();
395 }
396
397
398 /**
399  * Shutdown address subsystem.
400  */
401 void
402 GAS_addresses_done ()
403 {
404   GNUNET_log(GNUNET_ERROR_TYPE_INFO,
405              "Shutting down addresses\n");
406   GAS_plugin_solver_lock ();
407   GAS_addresses_destroy_all ();
408   GAS_plugin_solver_unlock ();
409   GNUNET_CONTAINER_multipeermap_destroy (GSA_addresses);
410   GSA_addresses = NULL;
411 }
412
413
414 /**
415  * Closure for #peerinfo_it().
416  */
417 struct PeerInfoIteratorContext
418 {
419   /**
420    * Function to call for each address.
421    */
422   GNUNET_ATS_PeerInfo_Iterator it;
423
424   /**
425    * Closure for @e it.
426    */
427   void *it_cls;
428 };
429
430
431 /**
432  * Iterator to iterate over a peer's addresses
433  *
434  * @param cls a `struct PeerInfoIteratorContext`
435  * @param key the peer id
436  * @param value the `struct ATS_address`
437  * @return #GNUNET_OK to continue
438  */
439 static int
440 peerinfo_it (void *cls,
441              const struct GNUNET_PeerIdentity *key,
442              void *value)
443 {
444   struct PeerInfoIteratorContext *pi_ctx = cls;
445   struct ATS_Address *addr = value;
446
447   pi_ctx->it (pi_ctx->it_cls,
448               &addr->peer,
449               addr->plugin,
450               addr->addr,
451               addr->addr_len,
452               addr->active,
453               &addr->properties,
454               addr->local_address_info,
455               GNUNET_BANDWIDTH_value_init (addr->assigned_bw_out),
456               GNUNET_BANDWIDTH_value_init (addr->assigned_bw_in));
457   return GNUNET_OK;
458 }
459
460
461 /**
462  * Return information all peers currently known to ATS
463  *
464  * @param peer the respective peer, NULL for 'all' peers
465  * @param pi_it the iterator to call for every peer
466  * @param pi_it_cls the closure for @a pi_it
467  */
468 void
469 GAS_addresses_get_peer_info (const struct GNUNET_PeerIdentity *peer,
470                              GNUNET_ATS_PeerInfo_Iterator pi_it,
471                              void *pi_it_cls)
472 {
473   struct PeerInfoIteratorContext pi_ctx;
474
475   if (NULL == pi_it)
476   {
477     /* does not make sense without callback */
478     GNUNET_break (0);
479     return;
480   }
481   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
482               "Returning information for %s from a total of %u known addresses\n",
483               (NULL == peer)
484               ? "all peers"
485               : GNUNET_i2s (peer),
486               (unsigned int) GNUNET_CONTAINER_multipeermap_size (GSA_addresses));
487   pi_ctx.it = pi_it;
488   pi_ctx.it_cls = pi_it_cls;
489   if (NULL == peer)
490     GNUNET_CONTAINER_multipeermap_iterate (GSA_addresses,
491                                            &peerinfo_it,
492                                            &pi_ctx);
493   else
494     GNUNET_CONTAINER_multipeermap_get_multiple (GSA_addresses,
495                                                 peer,
496                                                 &peerinfo_it, &pi_ctx);
497   pi_it (pi_it_cls,
498          NULL, NULL, NULL, 0,
499          GNUNET_NO,
500          NULL,
501          GNUNET_HELLO_ADDRESS_INFO_NONE,
502          GNUNET_BANDWIDTH_ZERO,
503          GNUNET_BANDWIDTH_ZERO);
504 }
505
506
507 /**
508  * Information we need for the callbacks to return a list of addresses
509  * back to the client.
510  */
511 struct AddressIteration
512 {
513   /**
514    * Actual handle to the client.
515    */
516   struct GNUNET_SERVICE_Client *client;
517
518   /**
519    * Are we sending all addresses, or only those that are active?
520    */
521   int all;
522
523   /**
524    * Which ID should be included in the response?
525    */
526   uint32_t id;
527
528 };
529
530
531 /**
532  * Send a #GNUNET_MESSAGE_TYPE_ATS_ADDRESSLIST_RESPONSE with the
533  * given address details to the client identified in @a ai.
534  *
535  * @param ai our address information context (identifies the client)
536  * @param id the peer id this address is for
537  * @param plugin_name name of the plugin that supports this address
538  * @param plugin_addr address
539  * @param plugin_addr_len length of @a plugin_addr
540  * @param active #GNUNET_YES if this address is actively used
541  * @param prop performance information
542  * @param local_address_info flags for the address
543  * @param bandwidth_out current outbound bandwidth assigned to address
544  * @param bandwidth_in current inbound bandwidth assigned to address
545  */
546 static void
547 transmit_req_addr (struct AddressIteration *ai,
548                    const struct GNUNET_PeerIdentity *id,
549                    const char *plugin_name,
550                    const void *plugin_addr,
551                    size_t plugin_addr_len,
552                    int active,
553                    const struct GNUNET_ATS_Properties *prop,
554                    enum GNUNET_HELLO_AddressInfo local_address_info,
555                    struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
556                    struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
557
558 {
559   struct GNUNET_MQ_Envelope *env;
560   struct PeerInformationMessage *msg;
561   char *addrp;
562   size_t plugin_name_length;
563   size_t msize;
564
565   if (NULL != plugin_name)
566     plugin_name_length = strlen (plugin_name) + 1;
567   else
568     plugin_name_length = 0;
569   msize = plugin_addr_len + plugin_name_length;
570
571   GNUNET_assert (sizeof (struct PeerInformationMessage) + msize
572                  < GNUNET_MAX_MESSAGE_SIZE);
573   env = GNUNET_MQ_msg_extra (msg,
574                              msize,
575                              GNUNET_MESSAGE_TYPE_ATS_ADDRESSLIST_RESPONSE);
576   msg->id = htonl (ai->id);
577   if (NULL != id)
578     msg->peer = *id;
579   msg->address_length = htons (plugin_addr_len);
580   msg->address_active = ntohl (active);
581   msg->plugin_name_length = htons (plugin_name_length);
582   msg->bandwidth_out = bandwidth_out;
583   msg->bandwidth_in = bandwidth_in;
584   if (NULL != prop)
585     GNUNET_ATS_properties_hton (&msg->properties,
586                                 prop);
587   msg->address_local_info = htonl ((uint32_t) local_address_info);
588   addrp = (char *) &msg[1];
589   GNUNET_memcpy (addrp,
590                  plugin_addr,
591                  plugin_addr_len);
592   if (NULL != plugin_name)
593     strcpy (&addrp[plugin_addr_len],
594             plugin_name);
595   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (ai->client),
596                   env);
597 }
598
599
600 /**
601  * Iterator for #GAS_addresses_get_peer_info(), called with peer-specific
602  * information to be passed back to the client.
603  *
604  * @param cls closure with our `struct AddressIteration *`
605  * @param id the peer id
606  * @param plugin_name plugin name
607  * @param plugin_addr address
608  * @param plugin_addr_len length of @a plugin_addr
609  * @param active is address actively used
610  * @param prop performance information
611  * @param local_address_info additional local info for the address
612  * @param bandwidth_out current outbound bandwidth assigned to address
613  * @param bandwidth_in current inbound bandwidth assigned to address
614  */
615 static void
616 req_addr_peerinfo_it (void *cls,
617                       const struct GNUNET_PeerIdentity *id,
618                       const char *plugin_name,
619                       const void *plugin_addr,
620                       size_t plugin_addr_len,
621                       int active,
622                       const struct GNUNET_ATS_Properties *prop,
623                       enum GNUNET_HELLO_AddressInfo local_address_info,
624                       struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
625                       struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
626 {
627   struct AddressIteration *ai = cls;
628
629   if ( (NULL == id) &&
630        (NULL == plugin_name) &&
631        (NULL == plugin_addr) )
632   {
633     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
634                 "Address iteration done for one peer\n");
635     return;
636   }
637   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
638               "Callback for %s peer `%s' plugin `%s' BW out %u, BW in %u\n",
639               (active == GNUNET_YES) ? "ACTIVE" : "INACTIVE",
640               GNUNET_i2s (id),
641               plugin_name,
642               (unsigned int) ntohl (bandwidth_out.value__),
643               (unsigned int) ntohl (bandwidth_in.value__));
644   /* Transmit result (either if address is active, or if
645      client wanted all addresses) */
646   if ( (GNUNET_YES != ai->all) &&
647        (GNUNET_YES != active))
648     return;
649   transmit_req_addr (ai,
650                      id,
651                      plugin_name,
652                      plugin_addr, plugin_addr_len,
653                      active,
654                      prop,
655                      local_address_info,
656                      bandwidth_out,
657                      bandwidth_in);
658 }
659
660
661 /**
662  * Handle 'address list request' messages from clients.
663  *
664  * @param cls client that sent the request
665  * @param alrm the request message
666  */
667 void
668 GAS_handle_request_address_list (struct GNUNET_SERVICE_Client *client,
669                                  const struct AddressListRequestMessage *alrm)
670 {
671   struct AddressIteration ai;
672   struct GNUNET_PeerIdentity allzeros;
673
674   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
675               "Received ADDRESSLIST_REQUEST message\n");
676   ai.all = ntohl (alrm->all);
677   ai.id = ntohl (alrm->id);
678   ai.client = client;
679
680   memset (&allzeros,
681           '\0',
682           sizeof (struct GNUNET_PeerIdentity));
683   if (0 == memcmp (&alrm->peer,
684                    &allzeros,
685                    sizeof (struct GNUNET_PeerIdentity)))
686   {
687     /* Return addresses for all peers */
688     GAS_addresses_get_peer_info (NULL,
689                                  &req_addr_peerinfo_it,
690                                  &ai);
691   }
692   else
693   {
694     /* Return addresses for a specific peer */
695     GAS_addresses_get_peer_info (&alrm->peer,
696                                  &req_addr_peerinfo_it,
697                                  &ai);
698   }
699   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
700               "Finished handling `%s' message\n",
701               "ADDRESSLIST_REQUEST");
702   transmit_req_addr (&ai,
703                      NULL, NULL, NULL,
704                      0, GNUNET_NO,
705                      NULL,
706                      GNUNET_HELLO_ADDRESS_INFO_NONE,
707                      GNUNET_BANDWIDTH_ZERO,
708                      GNUNET_BANDWIDTH_ZERO);
709 }
710
711
712
713 /* end of gnunet-service-ats_addresses.c */