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/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
22 * @file ats/gnunet-service-ats_addresses.c
23 * @brief ats service address management
24 * @author Matthias Wachs
25 * @author Christian Grothoff
28 #include "gnunet-service-ats_addresses.h"
29 #include "gnunet-service-ats_performance.h"
30 #include "gnunet-service-ats_normalization.h"
31 #include "gnunet-service-ats_plugins.h"
35 * A multihashmap to store all addresses
37 struct GNUNET_CONTAINER_MultiPeerMap *GSA_addresses;
41 * Update statistic on number of addresses.
44 update_addresses_stat ()
46 GNUNET_STATISTICS_set (GSA_stats,
48 GNUNET_CONTAINER_multipeermap_size (GSA_addresses),
54 * Free the given address
56 * @param addr address to destroy
59 free_address (struct ATS_Address *addr)
61 GNUNET_assert (GNUNET_YES ==
62 GNUNET_CONTAINER_multipeermap_remove (GSA_addresses,
65 update_addresses_stat ();
66 GAS_plugin_delete_address (addr);
67 GAS_performance_notify_all_clients (&addr->peer,
73 addr->local_address_info,
74 GNUNET_BANDWIDTH_ZERO,
75 GNUNET_BANDWIDTH_ZERO);
76 GNUNET_free (addr->plugin);
82 * Initialize @a norm. Sets all historic values to undefined.
84 * @param norm normalization data to initialize
87 init_norm (struct GAS_NormalizationInfo *norm)
91 for (c = 0; c < GAS_normalization_queue_length; c++)
92 norm->atsi_abs[c] = UINT64_MAX;
97 * Create a ATS_address with the given information
100 * @param plugin_name plugin
101 * @param plugin_addr address
102 * @param plugin_addr_len address length
103 * @param local_address_info additional local info for the address
104 * @param session_id session identifier, can never be 0
105 * @return the ATS_Address
107 static struct ATS_Address *
108 create_address (const struct GNUNET_PeerIdentity *peer,
109 const char *plugin_name,
110 const void *plugin_addr,
111 size_t plugin_addr_len,
112 uint32_t local_address_info,
115 struct ATS_Address *aa;
117 aa = GNUNET_malloc (sizeof (struct ATS_Address) + plugin_addr_len);
119 aa->addr_len = plugin_addr_len;
121 GNUNET_memcpy (&aa[1],
124 aa->plugin = GNUNET_strdup (plugin_name);
125 aa->session_id = session_id;
126 aa->local_address_info = local_address_info;
127 init_norm (&aa->norm_delay);
128 init_norm (&aa->norm_distance);
129 init_norm (&aa->norm_utilization_in);
130 init_norm (&aa->norm_utilization_out);
136 * Closure for #find_address_cb()
138 struct FindAddressContext
141 * Session Id to look for.
146 * Where to store matching address result.
148 struct ATS_Address *exact_address;
154 * Find session matching given session ID.
156 * @param cls a `struct FindAddressContext`
158 * @param value the address to compare with
159 * @return #GNUNET_YES to continue, #GNUNET_NO if address is found
162 find_address_cb (void *cls,
163 const struct GNUNET_PeerIdentity *key,
166 struct FindAddressContext *fac = cls;
167 struct ATS_Address *aa = value;
169 if (aa->session_id == fac->session_id)
171 fac->exact_address = aa;
179 * Find the exact address
182 * @param session_id session id, can never be 0
183 * @return an ATS_address or NULL
185 static struct ATS_Address *
186 find_exact_address (const struct GNUNET_PeerIdentity *peer,
189 struct FindAddressContext fac;
191 fac.exact_address = NULL;
192 fac.session_id = session_id;
193 GNUNET_CONTAINER_multipeermap_get_multiple (GSA_addresses,
195 &find_address_cb, &fac);
196 return fac.exact_address;
201 * Add a new address for a peer.
204 * @param plugin_name transport plugin name
205 * @param plugin_addr plugin address
206 * @param plugin_addr_len length of the plugin address in @a plugin_addr
207 * @param local_address_info the local address for the address
208 * @param session_id session id, can be 0
209 * @param prop performance information for this address
212 GAS_addresses_add (const struct GNUNET_PeerIdentity *peer,
213 const char *plugin_name,
214 const void *plugin_addr,
215 size_t plugin_addr_len,
216 uint32_t local_address_info,
218 const struct GNUNET_ATS_Properties *prop)
220 struct ATS_Address *new_address;
222 if (NULL != find_exact_address (peer,
228 GNUNET_break (GNUNET_NT_UNSPECIFIED != prop->scope);
229 new_address = create_address (peer,
235 /* Add a new address */
236 new_address->properties = *prop;
237 new_address->t_added = GNUNET_TIME_absolute_get();
238 new_address->t_last_activity = GNUNET_TIME_absolute_get();
239 GNUNET_assert(GNUNET_OK ==
240 GNUNET_CONTAINER_multipeermap_put (GSA_addresses,
243 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
244 update_addresses_stat ();
245 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
246 "Adding new address for peer `%s' slot %u\n",
249 /* Tell solver about new address */
250 GAS_plugin_solver_lock ();
251 GAS_plugin_new_address (new_address);
252 GAS_normalization_update_property (new_address); // FIXME: needed?
253 GAS_plugin_solver_unlock ();
254 /* Notify performance clients about new address */
255 GAS_performance_notify_all_clients (&new_address->peer,
258 new_address->addr_len,
260 &new_address->properties,
261 new_address->local_address_info,
262 GNUNET_BANDWIDTH_value_init (new_address->assigned_bw_out),
263 GNUNET_BANDWIDTH_value_init (new_address->assigned_bw_in));
268 * Update an address with new performance information for a peer.
271 * @param session_id session id, never 0
272 * @param prop performance information for this address
275 GAS_addresses_update (const struct GNUNET_PeerIdentity *peer,
277 const struct GNUNET_ATS_Properties *prop)
279 struct ATS_Address *aa;
281 /* Get existing address */
282 aa = find_exact_address (peer,
289 if (NULL == aa->solver_information)
294 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
295 "Received ADDRESS_UPDATE for peer `%s' slot %u\n",
297 (unsigned int) session_id);
298 GNUNET_break (GNUNET_NT_UNSPECIFIED != prop->scope);
300 aa->t_last_activity = GNUNET_TIME_absolute_get();
301 aa->properties = *prop;
302 /* Notify performance clients about updated address */
303 GAS_performance_notify_all_clients (&aa->peer,
309 aa->local_address_info,
310 GNUNET_BANDWIDTH_value_init (aa->assigned_bw_out),
311 GNUNET_BANDWIDTH_value_init (aa->assigned_bw_in));
313 GAS_normalization_update_property (aa);
318 * Remove an address for a peer.
321 * @param session_id session id, can never be 0
324 GAS_addresses_destroy (const struct GNUNET_PeerIdentity *peer,
327 struct ATS_Address *ea;
329 /* Get existing address */
330 ea = find_exact_address (peer,
337 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
338 "Received ADDRESS_DESTROYED for peer `%s' session %u\n",
346 * Initialize address subsystem. The addresses subsystem manages the addresses
347 * known and current performance information. It has a solver component
348 * responsible for the resource allocation. It tells the solver about changes
349 * and receives updates when the solver changes the resource allocation.
352 GAS_addresses_init ()
355 = GNUNET_CONTAINER_multipeermap_create (128,
357 update_addresses_stat ();
362 * Destroy all addresses iterator
365 * @param key peer identity (unused)
366 * @param value the 'struct ATS_Address' to free
367 * @return #GNUNET_OK (continue to iterate)
370 destroy_all_address_it (void *cls,
371 const struct GNUNET_PeerIdentity *key,
374 struct ATS_Address *aa = value;
382 * Remove all addresses
385 GAS_addresses_destroy_all ()
387 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
388 "Destroying all addresses\n");
390 GNUNET_CONTAINER_multipeermap_size (GSA_addresses))
392 GAS_plugin_solver_lock ();
393 GNUNET_CONTAINER_multipeermap_iterate (GSA_addresses,
394 &destroy_all_address_it,
396 GAS_plugin_solver_unlock ();
401 * Shutdown address subsystem.
404 GAS_addresses_done ()
406 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
407 "Shutting down addresses\n");
408 GAS_plugin_solver_lock ();
409 GAS_addresses_destroy_all ();
410 GAS_plugin_solver_unlock ();
411 GNUNET_CONTAINER_multipeermap_destroy (GSA_addresses);
412 GSA_addresses = NULL;
417 * Closure for #peerinfo_it().
419 struct PeerInfoIteratorContext
422 * Function to call for each address.
424 GNUNET_ATS_PeerInfo_Iterator it;
434 * Iterator to iterate over a peer's addresses
436 * @param cls a `struct PeerInfoIteratorContext`
437 * @param key the peer id
438 * @param value the `struct ATS_address`
439 * @return #GNUNET_OK to continue
442 peerinfo_it (void *cls,
443 const struct GNUNET_PeerIdentity *key,
446 struct PeerInfoIteratorContext *pi_ctx = cls;
447 struct ATS_Address *addr = value;
449 pi_ctx->it (pi_ctx->it_cls,
456 addr->local_address_info,
457 GNUNET_BANDWIDTH_value_init (addr->assigned_bw_out),
458 GNUNET_BANDWIDTH_value_init (addr->assigned_bw_in));
464 * Return information all peers currently known to ATS
466 * @param peer the respective peer, NULL for 'all' peers
467 * @param pi_it the iterator to call for every peer
468 * @param pi_it_cls the closure for @a pi_it
471 GAS_addresses_get_peer_info (const struct GNUNET_PeerIdentity *peer,
472 GNUNET_ATS_PeerInfo_Iterator pi_it,
475 struct PeerInfoIteratorContext pi_ctx;
479 /* does not make sense without callback */
483 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
484 "Returning information for %s from a total of %u known addresses\n",
488 (unsigned int) GNUNET_CONTAINER_multipeermap_size (GSA_addresses));
490 pi_ctx.it_cls = pi_it_cls;
492 GNUNET_CONTAINER_multipeermap_iterate (GSA_addresses,
496 GNUNET_CONTAINER_multipeermap_get_multiple (GSA_addresses,
498 &peerinfo_it, &pi_ctx);
503 GNUNET_HELLO_ADDRESS_INFO_NONE,
504 GNUNET_BANDWIDTH_ZERO,
505 GNUNET_BANDWIDTH_ZERO);
510 * Information we need for the callbacks to return a list of addresses
511 * back to the client.
513 struct AddressIteration
516 * Actual handle to the client.
518 struct GNUNET_SERVICE_Client *client;
521 * Are we sending all addresses, or only those that are active?
526 * Which ID should be included in the response?
534 * Send a #GNUNET_MESSAGE_TYPE_ATS_ADDRESSLIST_RESPONSE with the
535 * given address details to the client identified in @a ai.
537 * @param ai our address information context (identifies the client)
538 * @param id the peer id this address is for
539 * @param plugin_name name of the plugin that supports this address
540 * @param plugin_addr address
541 * @param plugin_addr_len length of @a plugin_addr
542 * @param active #GNUNET_YES if this address is actively used
543 * @param prop performance information
544 * @param local_address_info flags for the address
545 * @param bandwidth_out current outbound bandwidth assigned to address
546 * @param bandwidth_in current inbound bandwidth assigned to address
549 transmit_req_addr (struct AddressIteration *ai,
550 const struct GNUNET_PeerIdentity *id,
551 const char *plugin_name,
552 const void *plugin_addr,
553 size_t plugin_addr_len,
555 const struct GNUNET_ATS_Properties *prop,
556 enum GNUNET_HELLO_AddressInfo local_address_info,
557 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
558 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
561 struct GNUNET_MQ_Envelope *env;
562 struct PeerInformationMessage *msg;
564 size_t plugin_name_length;
567 if (NULL != plugin_name)
568 plugin_name_length = strlen (plugin_name) + 1;
570 plugin_name_length = 0;
571 msize = plugin_addr_len + plugin_name_length;
573 GNUNET_assert (sizeof (struct PeerInformationMessage) + msize
574 < GNUNET_MAX_MESSAGE_SIZE);
575 env = GNUNET_MQ_msg_extra (msg,
577 GNUNET_MESSAGE_TYPE_ATS_ADDRESSLIST_RESPONSE);
578 msg->id = htonl (ai->id);
581 msg->address_length = htons (plugin_addr_len);
582 msg->address_active = ntohl (active);
583 msg->plugin_name_length = htons (plugin_name_length);
584 msg->bandwidth_out = bandwidth_out;
585 msg->bandwidth_in = bandwidth_in;
587 GNUNET_ATS_properties_hton (&msg->properties,
589 msg->address_local_info = htonl ((uint32_t) local_address_info);
590 addrp = (char *) &msg[1];
591 GNUNET_memcpy (addrp,
594 if (NULL != plugin_name)
595 strcpy (&addrp[plugin_addr_len],
597 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (ai->client),
603 * Iterator for #GAS_addresses_get_peer_info(), called with peer-specific
604 * information to be passed back to the client.
606 * @param cls closure with our `struct AddressIteration *`
607 * @param id the peer id
608 * @param plugin_name plugin name
609 * @param plugin_addr address
610 * @param plugin_addr_len length of @a plugin_addr
611 * @param active is address actively used
612 * @param prop performance information
613 * @param local_address_info additional local info for the address
614 * @param bandwidth_out current outbound bandwidth assigned to address
615 * @param bandwidth_in current inbound bandwidth assigned to address
618 req_addr_peerinfo_it (void *cls,
619 const struct GNUNET_PeerIdentity *id,
620 const char *plugin_name,
621 const void *plugin_addr,
622 size_t plugin_addr_len,
624 const struct GNUNET_ATS_Properties *prop,
625 enum GNUNET_HELLO_AddressInfo local_address_info,
626 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
627 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
629 struct AddressIteration *ai = cls;
632 (NULL == plugin_name) &&
633 (NULL == plugin_addr) )
635 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
636 "Address iteration done for one peer\n");
639 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
640 "Callback for %s peer `%s' plugin `%s' BW out %u, BW in %u\n",
641 (active == GNUNET_YES) ? "ACTIVE" : "INACTIVE",
644 (unsigned int) ntohl (bandwidth_out.value__),
645 (unsigned int) ntohl (bandwidth_in.value__));
646 /* Transmit result (either if address is active, or if
647 client wanted all addresses) */
648 if ( (GNUNET_YES != ai->all) &&
649 (GNUNET_YES != active))
651 transmit_req_addr (ai,
654 plugin_addr, plugin_addr_len,
664 * Handle 'address list request' messages from clients.
666 * @param cls client that sent the request
667 * @param alrm the request message
670 GAS_handle_request_address_list (struct GNUNET_SERVICE_Client *client,
671 const struct AddressListRequestMessage *alrm)
673 struct AddressIteration ai;
674 struct GNUNET_PeerIdentity allzeros;
676 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
677 "Received ADDRESSLIST_REQUEST message\n");
678 ai.all = ntohl (alrm->all);
679 ai.id = ntohl (alrm->id);
684 sizeof (struct GNUNET_PeerIdentity));
685 if (0 == memcmp (&alrm->peer,
687 sizeof (struct GNUNET_PeerIdentity)))
689 /* Return addresses for all peers */
690 GAS_addresses_get_peer_info (NULL,
691 &req_addr_peerinfo_it,
696 /* Return addresses for a specific peer */
697 GAS_addresses_get_peer_info (&alrm->peer,
698 &req_addr_peerinfo_it,
701 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
702 "Finished handling `%s' message\n",
703 "ADDRESSLIST_REQUEST");
704 transmit_req_addr (&ai,
708 GNUNET_HELLO_ADDRESS_INFO_NONE,
709 GNUNET_BANDWIDTH_ZERO,
710 GNUNET_BANDWIDTH_ZERO);
715 /* end of gnunet-service-ats_addresses.c */