2 This file is part of GNUnet.
3 Copyright (C) 2011-2015 GNUnet e.V.
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.
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.
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/>.
20 * @file ats/gnunet-service-ats_addresses.c
21 * @brief ats service address management
22 * @author Matthias Wachs
23 * @author Christian Grothoff
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"
33 * A multihashmap to store all addresses
35 struct GNUNET_CONTAINER_MultiPeerMap *GSA_addresses;
39 * Update statistic on number of addresses.
42 update_addresses_stat ()
44 GNUNET_STATISTICS_set (GSA_stats,
46 GNUNET_CONTAINER_multipeermap_size (GSA_addresses),
52 * Free the given address
54 * @param addr address to destroy
57 free_address (struct ATS_Address *addr)
59 GNUNET_assert (GNUNET_YES ==
60 GNUNET_CONTAINER_multipeermap_remove (GSA_addresses,
63 update_addresses_stat ();
64 GAS_plugin_delete_address (addr);
65 GAS_performance_notify_all_clients (&addr->peer,
71 addr->local_address_info,
72 GNUNET_BANDWIDTH_ZERO,
73 GNUNET_BANDWIDTH_ZERO);
74 GNUNET_free (addr->plugin);
80 * Initialize @a norm. Sets all historic values to undefined.
82 * @param norm normalization data to initialize
85 init_norm (struct GAS_NormalizationInfo *norm)
89 for (c = 0; c < GAS_normalization_queue_length; c++)
90 norm->atsi_abs[c] = UINT64_MAX;
95 * Create a ATS_address with the given information
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
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,
113 struct ATS_Address *aa;
115 aa = GNUNET_malloc (sizeof (struct ATS_Address) + plugin_addr_len);
117 aa->addr_len = plugin_addr_len;
119 GNUNET_memcpy (&aa[1],
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);
134 * Closure for #find_address_cb()
136 struct FindAddressContext
139 * Session Id to look for.
144 * Where to store matching address result.
146 struct ATS_Address *exact_address;
152 * Find session matching given session ID.
154 * @param cls a `struct FindAddressContext`
156 * @param value the address to compare with
157 * @return #GNUNET_YES to continue, #GNUNET_NO if address is found
160 find_address_cb (void *cls,
161 const struct GNUNET_PeerIdentity *key,
164 struct FindAddressContext *fac = cls;
165 struct ATS_Address *aa = value;
167 if (aa->session_id == fac->session_id)
169 fac->exact_address = aa;
177 * Find the exact address
180 * @param session_id session id, can never be 0
181 * @return an ATS_address or NULL
183 static struct ATS_Address *
184 find_exact_address (const struct GNUNET_PeerIdentity *peer,
187 struct FindAddressContext fac;
189 fac.exact_address = NULL;
190 fac.session_id = session_id;
191 GNUNET_CONTAINER_multipeermap_get_multiple (GSA_addresses,
193 &find_address_cb, &fac);
194 return fac.exact_address;
199 * Add a new address for a 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
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,
216 const struct GNUNET_ATS_Properties *prop)
218 struct ATS_Address *new_address;
220 if (NULL != find_exact_address (peer,
226 GNUNET_break (GNUNET_NT_UNSPECIFIED != prop->scope);
227 new_address = create_address (peer,
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,
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",
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,
256 new_address->addr_len,
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));
266 * Update an address with new performance information for a peer.
269 * @param session_id session id, never 0
270 * @param prop performance information for this address
273 GAS_addresses_update (const struct GNUNET_PeerIdentity *peer,
275 const struct GNUNET_ATS_Properties *prop)
277 struct ATS_Address *aa;
279 /* Get existing address */
280 aa = find_exact_address (peer,
287 if (NULL == aa->solver_information)
292 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
293 "Received ADDRESS_UPDATE for peer `%s' slot %u\n",
295 (unsigned int) session_id);
296 GNUNET_break (GNUNET_NT_UNSPECIFIED != prop->scope);
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,
307 aa->local_address_info,
308 GNUNET_BANDWIDTH_value_init (aa->assigned_bw_out),
309 GNUNET_BANDWIDTH_value_init (aa->assigned_bw_in));
311 GAS_normalization_update_property (aa);
316 * Remove an address for a peer.
319 * @param session_id session id, can never be 0
322 GAS_addresses_destroy (const struct GNUNET_PeerIdentity *peer,
325 struct ATS_Address *ea;
327 /* Get existing address */
328 ea = find_exact_address (peer,
335 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
336 "Received ADDRESS_DESTROYED for peer `%s' session %u\n",
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.
350 GAS_addresses_init ()
353 = GNUNET_CONTAINER_multipeermap_create (128,
355 update_addresses_stat ();
360 * Destroy all addresses iterator
363 * @param key peer identity (unused)
364 * @param value the 'struct ATS_Address' to free
365 * @return #GNUNET_OK (continue to iterate)
368 destroy_all_address_it (void *cls,
369 const struct GNUNET_PeerIdentity *key,
372 struct ATS_Address *aa = value;
380 * Remove all addresses
383 GAS_addresses_destroy_all ()
385 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
386 "Destroying all addresses\n");
388 GNUNET_CONTAINER_multipeermap_size (GSA_addresses))
390 GAS_plugin_solver_lock ();
391 GNUNET_CONTAINER_multipeermap_iterate (GSA_addresses,
392 &destroy_all_address_it,
394 GAS_plugin_solver_unlock ();
399 * Shutdown address subsystem.
402 GAS_addresses_done ()
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;
415 * Closure for #peerinfo_it().
417 struct PeerInfoIteratorContext
420 * Function to call for each address.
422 GNUNET_ATS_PeerInfo_Iterator it;
432 * Iterator to iterate over a peer's addresses
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
440 peerinfo_it (void *cls,
441 const struct GNUNET_PeerIdentity *key,
444 struct PeerInfoIteratorContext *pi_ctx = cls;
445 struct ATS_Address *addr = value;
447 pi_ctx->it (pi_ctx->it_cls,
454 addr->local_address_info,
455 GNUNET_BANDWIDTH_value_init (addr->assigned_bw_out),
456 GNUNET_BANDWIDTH_value_init (addr->assigned_bw_in));
462 * Return information all peers currently known to ATS
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
469 GAS_addresses_get_peer_info (const struct GNUNET_PeerIdentity *peer,
470 GNUNET_ATS_PeerInfo_Iterator pi_it,
473 struct PeerInfoIteratorContext pi_ctx;
477 /* does not make sense without callback */
481 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
482 "Returning information for %s from a total of %u known addresses\n",
486 (unsigned int) GNUNET_CONTAINER_multipeermap_size (GSA_addresses));
488 pi_ctx.it_cls = pi_it_cls;
490 GNUNET_CONTAINER_multipeermap_iterate (GSA_addresses,
494 GNUNET_CONTAINER_multipeermap_get_multiple (GSA_addresses,
496 &peerinfo_it, &pi_ctx);
501 GNUNET_HELLO_ADDRESS_INFO_NONE,
502 GNUNET_BANDWIDTH_ZERO,
503 GNUNET_BANDWIDTH_ZERO);
508 * Information we need for the callbacks to return a list of addresses
509 * back to the client.
511 struct AddressIteration
514 * Actual handle to the client.
516 struct GNUNET_SERVICE_Client *client;
519 * Are we sending all addresses, or only those that are active?
524 * Which ID should be included in the response?
532 * Send a #GNUNET_MESSAGE_TYPE_ATS_ADDRESSLIST_RESPONSE with the
533 * given address details to the client identified in @a ai.
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
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,
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)
559 struct GNUNET_MQ_Envelope *env;
560 struct PeerInformationMessage *msg;
562 size_t plugin_name_length;
565 if (NULL != plugin_name)
566 plugin_name_length = strlen (plugin_name) + 1;
568 plugin_name_length = 0;
569 msize = plugin_addr_len + plugin_name_length;
571 GNUNET_assert (sizeof (struct PeerInformationMessage) + msize
572 < GNUNET_MAX_MESSAGE_SIZE);
573 env = GNUNET_MQ_msg_extra (msg,
575 GNUNET_MESSAGE_TYPE_ATS_ADDRESSLIST_RESPONSE);
576 msg->id = htonl (ai->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;
585 GNUNET_ATS_properties_hton (&msg->properties,
587 msg->address_local_info = htonl ((uint32_t) local_address_info);
588 addrp = (char *) &msg[1];
589 GNUNET_memcpy (addrp,
592 if (NULL != plugin_name)
593 strcpy (&addrp[plugin_addr_len],
595 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (ai->client),
601 * Iterator for #GAS_addresses_get_peer_info(), called with peer-specific
602 * information to be passed back to the client.
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
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,
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)
627 struct AddressIteration *ai = cls;
630 (NULL == plugin_name) &&
631 (NULL == plugin_addr) )
633 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
634 "Address iteration done for one peer\n");
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",
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))
649 transmit_req_addr (ai,
652 plugin_addr, plugin_addr_len,
662 * Handle 'address list request' messages from clients.
664 * @param cls client that sent the request
665 * @param alrm the request message
668 GAS_handle_request_address_list (struct GNUNET_SERVICE_Client *client,
669 const struct AddressListRequestMessage *alrm)
671 struct AddressIteration ai;
672 struct GNUNET_PeerIdentity allzeros;
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);
682 sizeof (struct GNUNET_PeerIdentity));
683 if (0 == memcmp (&alrm->peer,
685 sizeof (struct GNUNET_PeerIdentity)))
687 /* Return addresses for all peers */
688 GAS_addresses_get_peer_info (NULL,
689 &req_addr_peerinfo_it,
694 /* Return addresses for a specific peer */
695 GAS_addresses_get_peer_info (&alrm->peer,
696 &req_addr_peerinfo_it,
699 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
700 "Finished handling `%s' message\n",
701 "ADDRESSLIST_REQUEST");
702 transmit_req_addr (&ai,
706 GNUNET_HELLO_ADDRESS_INFO_NONE,
707 GNUNET_BANDWIDTH_ZERO,
708 GNUNET_BANDWIDTH_ZERO);
713 /* end of gnunet-service-ats_addresses.c */