2 This file is part of GNUnet.
3 Copyright (C) 2011-2015 Christian Grothoff (and other contributing authors)
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.
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.
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.
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 GNUNET_BANDWIDTH_ZERO,
74 GNUNET_BANDWIDTH_ZERO);
75 GNUNET_free (addr->plugin);
81 * Initialize @a norm. Sets all historic values to undefined.
83 * @param norm normalization data to initialize
86 init_norm (struct GAS_NormalizationInfo *norm)
90 for (c = 0; c < GAS_normalization_queue_length; c++)
91 norm->atsi_abs[c] = UINT64_MAX;
96 * Create a ATS_address with the given information
99 * @param plugin_name plugin
100 * @param plugin_addr address
101 * @param plugin_addr_len address length
102 * @param local_address_info additional local info for the address
103 * @param session_id session identifier, can never be 0
104 * @return the ATS_Address
106 static struct ATS_Address *
107 create_address (const struct GNUNET_PeerIdentity *peer,
108 const char *plugin_name,
109 const void *plugin_addr,
110 size_t plugin_addr_len,
111 uint32_t local_address_info,
114 struct ATS_Address *aa;
116 aa = GNUNET_malloc (sizeof (struct ATS_Address) + plugin_addr_len);
118 aa->addr_len = plugin_addr_len;
123 aa->plugin = GNUNET_strdup (plugin_name);
124 aa->session_id = session_id;
125 aa->local_address_info = local_address_info;
126 init_norm (&aa->norm_delay);
127 init_norm (&aa->norm_distance);
128 init_norm (&aa->norm_utilization_in);
129 init_norm (&aa->norm_utilization_out);
135 * Closure for #find_address_cb()
137 struct FindAddressContext
140 * Session Id to look for.
145 * Where to store matching address result.
147 struct ATS_Address *exact_address;
153 * Find session matching given session ID.
155 * @param cls a `struct FindAddressContext`
157 * @param value the address to compare with
158 * @return #GNUNET_YES to continue, #GNUNET_NO if address is found
161 find_address_cb (void *cls,
162 const struct GNUNET_PeerIdentity *key,
165 struct FindAddressContext *fac = cls;
166 struct ATS_Address *aa = value;
168 if (aa->session_id == fac->session_id)
170 fac->exact_address = aa;
178 * Find the exact address
181 * @param session_id session id, can never be 0
182 * @return an ATS_address or NULL
184 static struct ATS_Address *
185 find_exact_address (const struct GNUNET_PeerIdentity *peer,
188 struct FindAddressContext fac;
190 fac.exact_address = NULL;
191 fac.session_id = session_id;
192 GNUNET_CONTAINER_multipeermap_get_multiple (GSA_addresses,
194 &find_address_cb, &fac);
195 return fac.exact_address;
200 * Add a new address for a peer.
203 * @param plugin_name transport plugin name
204 * @param plugin_addr plugin address
205 * @param plugin_addr_len length of the plugin address in @a plugin_addr
206 * @param local_address_info the local address for the address
207 * @param session_id session id, can be 0
208 * @param prop performance information for this address
211 GAS_addresses_add (const struct GNUNET_PeerIdentity *peer,
212 const char *plugin_name,
213 const void *plugin_addr,
214 size_t plugin_addr_len,
215 uint32_t local_address_info,
217 const struct GNUNET_ATS_Properties *prop)
219 struct ATS_Address *new_address;
221 if (NULL != find_exact_address (peer,
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 GNUNET_BANDWIDTH_value_init (new_address->assigned_bw_out),
260 GNUNET_BANDWIDTH_value_init (new_address->assigned_bw_in));
265 * Update an address with new performance information for a peer.
268 * @param session_id session id, never 0
269 * @param prop performance information for this address
272 GAS_addresses_update (const struct GNUNET_PeerIdentity *peer,
274 const struct GNUNET_ATS_Properties *prop)
276 struct ATS_Address *aa;
278 /* Get existing address */
279 aa = find_exact_address (peer,
286 if (NULL == aa->solver_information)
291 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
292 "Received ADDRESS_UPDATE for peer `%s' slot %u\n",
294 (unsigned int) session_id);
297 aa->t_last_activity = GNUNET_TIME_absolute_get();
298 aa->properties = *prop;
299 /* Notify performance clients about updated address */
300 GAS_performance_notify_all_clients (&aa->peer,
306 GNUNET_BANDWIDTH_value_init (aa->assigned_bw_out),
307 GNUNET_BANDWIDTH_value_init (aa->assigned_bw_in));
309 GAS_normalization_update_property (aa);
314 * Remove an address for a peer.
317 * @param session_id session id, can never be 0
320 GAS_addresses_destroy (const struct GNUNET_PeerIdentity *peer,
323 struct ATS_Address *ea;
325 /* Get existing address */
326 ea = find_exact_address (peer,
333 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
334 "Received ADDRESS_DESTROYED for peer `%s' session %u\n",
342 * Initialize address subsystem. The addresses subsystem manages the addresses
343 * known and current performance information. It has a solver component
344 * responsible for the resource allocation. It tells the solver about changes
345 * and receives updates when the solver changes the resource allocation.
347 * @param server handle to our server
350 GAS_addresses_init (struct GNUNET_SERVER_Handle *server)
352 GSA_addresses = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_NO);
353 update_addresses_stat ();
358 * Destroy all addresses iterator
361 * @param key peer identity (unused)
362 * @param value the 'struct ATS_Address' to free
363 * @return #GNUNET_OK (continue to iterate)
366 destroy_all_address_it (void *cls,
367 const struct GNUNET_PeerIdentity *key,
370 struct ATS_Address *aa = value;
378 * Remove all addresses
381 GAS_addresses_destroy_all ()
383 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
384 "Destroying all addresses\n");
386 GNUNET_CONTAINER_multipeermap_size (GSA_addresses))
388 GAS_plugin_solver_lock ();
389 GNUNET_CONTAINER_multipeermap_iterate (GSA_addresses,
390 &destroy_all_address_it,
392 GAS_plugin_solver_unlock ();
397 * Shutdown address subsystem.
400 GAS_addresses_done ()
402 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
403 "Shutting down addresses\n");
404 GAS_addresses_destroy_all ();
405 GNUNET_CONTAINER_multipeermap_destroy (GSA_addresses);
406 GSA_addresses = NULL;
411 * Closure for #peerinfo_it().
413 struct PeerInfoIteratorContext
416 * Function to call for each address.
418 GNUNET_ATS_PeerInfo_Iterator it;
428 * Iterator to iterate over a peer's addresses
430 * @param cls a `struct PeerInfoIteratorContext`
431 * @param key the peer id
432 * @param value the `struct ATS_address`
433 * @return #GNUNET_OK to continue
436 peerinfo_it (void *cls,
437 const struct GNUNET_PeerIdentity *key,
440 struct PeerInfoIteratorContext *pi_ctx = cls;
441 struct ATS_Address *addr = value;
443 pi_ctx->it (pi_ctx->it_cls,
450 GNUNET_BANDWIDTH_value_init (addr->assigned_bw_out),
451 GNUNET_BANDWIDTH_value_init (addr->assigned_bw_in));
457 * Return information all peers currently known to ATS
459 * @param peer the respective peer, NULL for 'all' peers
460 * @param pi_it the iterator to call for every peer
461 * @param pi_it_cls the closure for @a pi_it
464 GAS_addresses_get_peer_info (const struct GNUNET_PeerIdentity *peer,
465 GNUNET_ATS_PeerInfo_Iterator pi_it,
468 struct PeerInfoIteratorContext pi_ctx;
472 /* does not make sense without callback */
476 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
477 "Returning information for %s from a total of %u known addresses\n",
481 (unsigned int) GNUNET_CONTAINER_multipeermap_size (GSA_addresses));
483 pi_ctx.it_cls = pi_it_cls;
485 GNUNET_CONTAINER_multipeermap_iterate (GSA_addresses,
489 GNUNET_CONTAINER_multipeermap_get_multiple (GSA_addresses,
491 &peerinfo_it, &pi_ctx);
496 GNUNET_BANDWIDTH_ZERO,
497 GNUNET_BANDWIDTH_ZERO);
502 * Information we need for the callbacks to return a list of addresses
503 * back to the client.
505 struct AddressIteration
508 * Actual handle to the client.
510 struct GNUNET_SERVER_Client *client;
513 * Are we sending all addresses, or only those that are active?
518 * Which ID should be included in the response?
526 * Send a #GNUNET_MESSAGE_TYPE_ATS_ADDRESSLIST_RESPONSE with the
527 * given address details to the client identified in @a ai.
529 * @param ai our address information context (identifies the client)
530 * @param id the peer id this address is for
531 * @param plugin_name name of the plugin that supports this address
532 * @param plugin_addr address
533 * @param plugin_addr_len length of @a plugin_addr
534 * @param active #GNUNET_YES if this address is actively used
535 * @param prop performance information
536 * @param bandwidth_out current outbound bandwidth assigned to address
537 * @param bandwidth_in current inbound bandwidth assigned to address
540 transmit_req_addr (struct AddressIteration *ai,
541 const struct GNUNET_PeerIdentity *id,
542 const char *plugin_name,
543 const void *plugin_addr,
544 size_t plugin_addr_len,
546 const struct GNUNET_ATS_Properties *prop,
547 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
548 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
551 struct PeerInformationMessage *msg;
553 size_t plugin_name_length;
555 struct GNUNET_SERVER_NotificationContext **uc;
556 struct GNUNET_SERVER_NotificationContext *nc;
558 if (NULL != plugin_name)
559 plugin_name_length = strlen (plugin_name) + 1;
561 plugin_name_length = 0;
562 msize = sizeof (struct PeerInformationMessage) +
563 plugin_addr_len + plugin_name_length;
564 char buf[msize] GNUNET_ALIGN;
566 GNUNET_assert (msize < GNUNET_SERVER_MAX_MESSAGE_SIZE);
567 msg = (struct PeerInformationMessage *) buf;
568 msg->header.size = htons (msize);
569 msg->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESSLIST_RESPONSE);
570 msg->id = htonl (ai->id);
574 memset (&msg->peer, '\0', sizeof (struct GNUNET_PeerIdentity));
575 msg->address_length = htons (plugin_addr_len);
576 msg->address_active = ntohl (active);
577 msg->plugin_name_length = htons (plugin_name_length);
578 msg->bandwidth_out = bandwidth_out;
579 msg->bandwidth_in = bandwidth_in;
581 GNUNET_ATS_properties_hton (&msg->properties,
584 memset (&msg->properties,
586 sizeof (struct GNUNET_ATS_Properties));
587 addrp = (char *) &msg[1];
588 if (NULL != plugin_addr)
589 memcpy (addrp, plugin_addr, plugin_addr_len);
590 if (NULL != plugin_name)
591 strcpy (&addrp[plugin_addr_len], plugin_name);
592 uc = GNUNET_SERVER_client_get_user_context (ai->client,
593 struct GNUNET_SERVER_NotificationContext *);
600 GNUNET_SERVER_notification_context_unicast (nc,
608 * Iterator for #GAS_addresses_get_peer_info(), called with peer-specific
609 * information to be passed back to the client.
611 * @param cls closure with our `struct AddressIteration *`
612 * @param id the peer id
613 * @param plugin_name plugin name
614 * @param plugin_addr address
615 * @param plugin_addr_len length of @a plugin_addr
616 * @param active is address actively used
617 * @param prop performance information
618 * @param bandwidth_out current outbound bandwidth assigned to address
619 * @param bandwidth_in current inbound bandwidth assigned to address
622 req_addr_peerinfo_it (void *cls,
623 const struct GNUNET_PeerIdentity *id,
624 const char *plugin_name,
625 const void *plugin_addr,
626 size_t plugin_addr_len,
628 const struct GNUNET_ATS_Properties *prop,
629 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
630 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
632 struct AddressIteration *ai = cls;
635 (NULL == plugin_name) &&
636 (NULL == plugin_addr) )
638 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
639 "Address iteration done for one peer\n");
642 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
643 "Callback for %s peer `%s' plugin `%s' BW out %u, BW in %u\n",
644 (active == GNUNET_YES) ? "ACTIVE" : "INACTIVE",
647 (unsigned int) ntohl (bandwidth_out.value__),
648 (unsigned int) ntohl (bandwidth_in.value__));
649 /* Transmit result (either if address is active, or if
650 client wanted all addresses) */
651 if ( (GNUNET_YES != ai->all) &&
652 (GNUNET_YES != active))
654 transmit_req_addr (ai,
657 plugin_addr, plugin_addr_len,
666 * Handle 'address list request' messages from clients.
668 * @param cls unused, NULL
669 * @param client client that sent the request
670 * @param message the request message
673 GAS_handle_request_address_list (void *cls,
674 struct GNUNET_SERVER_Client *client,
675 const struct GNUNET_MessageHeader *message)
677 struct AddressIteration ai;
678 const struct AddressListRequestMessage *alrm;
679 struct GNUNET_PeerIdentity allzeros;
681 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
682 "Received ADDRESSLIST_REQUEST message\n");
683 alrm = (const struct AddressListRequestMessage *) message;
684 ai.all = ntohl (alrm->all);
685 ai.id = ntohl (alrm->id);
690 sizeof (struct GNUNET_PeerIdentity));
691 if (0 == memcmp (&alrm->peer,
693 sizeof (struct GNUNET_PeerIdentity)))
695 /* Return addresses for all peers */
696 GAS_addresses_get_peer_info (NULL,
697 &req_addr_peerinfo_it,
702 /* Return addresses for a specific peer */
703 GAS_addresses_get_peer_info (&alrm->peer,
704 &req_addr_peerinfo_it,
707 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
708 "Finished handling `%s' message\n",
709 "ADDRESSLIST_REQUEST");
710 transmit_req_addr (&ai,
714 GNUNET_BANDWIDTH_ZERO,
715 GNUNET_BANDWIDTH_ZERO);
716 GNUNET_SERVER_receive_done (client,
722 /* end of gnunet-service-ats_addresses.c */