From f735158d94616b75ade351a3cce226483b8af55e Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 19 Jan 2015 01:08:03 +0000 Subject: -towards improved ATS API, adding return value with address record when adding address, adding new subsystem with peer-to-address map to transport; causes various new assertions to fail, but no major regression -- not finished --- src/transport/gnunet-service-transport_ats.c | 441 +++++++++++++++++++++++++++ 1 file changed, 441 insertions(+) create mode 100644 src/transport/gnunet-service-transport_ats.c (limited to 'src/transport/gnunet-service-transport_ats.c') diff --git a/src/transport/gnunet-service-transport_ats.c b/src/transport/gnunet-service-transport_ats.c new file mode 100644 index 000000000..c6975e526 --- /dev/null +++ b/src/transport/gnunet-service-transport_ats.c @@ -0,0 +1,441 @@ +/* + This file is part of GNUnet. + (C) 2015 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file transport/gnunet-service-transport_ats.h + * @brief interfacing between transport and ATS service + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet-service-transport.h" +#include "gnunet-service-transport_ats.h" +#include "gnunet-service-transport_manipulation.h" +#include "gnunet-service-transport_plugins.h" +#include "gnunet_ats_service.h" + + +/** + * Information we track for each address known to ATS. + */ +struct AddressInfo +{ + + /** + * The address (with peer identity). + */ + struct GNUNET_HELLO_Address *address; + + /** + * Session (can be NULL) + */ + struct Session *session; + + /** + * Record with ATS API for the address. + */ + struct GNUNET_ATS_AddressRecord *ar; +}; + + +/** + * Map from peer identities to one or more `struct AddressInfo` values + * for the peer. + */ +static struct GNUNET_CONTAINER_MultiPeerMap *p2a; + + +/** + * Closure for #find_ai(). + */ +struct FindClosure +{ + + /** + * Session to look for (only used if the address is inbound). + */ + struct Session *session; + + /** + * Address to look for. + */ + const struct GNUNET_HELLO_Address *address; + + /** + * Where to store the result. + */ + struct AddressInfo *ret; + +}; + + +/** + * Find matching address info. + * + * @param cls the `struct FindClosure` + * @param key which peer is this about + * @param value the `struct AddressInfo` + * @return #GNUNET_YES to continue to iterate, #GNUNET_NO if we found the value + */ +static int +find_ai_cb (void *cls, + const struct GNUNET_PeerIdentity *key, + void *value) +{ + struct FindClosure *fc = cls; + struct AddressInfo *ai = value; + + if ( (0 == + GNUNET_HELLO_address_cmp (fc->address, + ai->address) ) && + (fc->session == ai->session) ) + { + fc->ret = ai; + return GNUNET_NO; + } + return GNUNET_YES; +} + + +/** + * Find the address information struct for the + * given address and session. + * + * @param address address to look for + * @param session session to match for inbound connections + * @return NULL if this combination is unknown + */ +static struct AddressInfo * +find_ai (const struct GNUNET_HELLO_Address *address, + struct Session *session) +{ + struct FindClosure fc; + + fc.address = address; + fc.session = session; + fc.ret = NULL; + GNUNET_CONTAINER_multipeermap_get_multiple (p2a, + &address->peer, + &find_ai_cb, + &fc); + return fc.ret; +} + + +/** + * Notify ATS about the new address including the network this address is + * located in. + * + * @param address the address + * @param session the session + * @param ats ats information + * @param ats_count number of @a ats information + */ +void +GST_ats_add_address (const struct GNUNET_HELLO_Address *address, + struct Session *session, + const struct GNUNET_ATS_Information *ats, + uint32_t ats_count) +{ + struct GNUNET_TRANSPORT_PluginFunctions *papi; + struct GNUNET_ATS_Information ats2[ats_count + 1]; + struct GNUNET_ATS_AddressRecord *ar; + struct AddressInfo *ai; + uint32_t net; + + /* valid new address, let ATS know! */ + if (NULL == address->transport_name) + { + GNUNET_break(0); + return; + } + if (GNUNET_YES == + GNUNET_HELLO_address_check_option (address, + GNUNET_HELLO_ADDRESS_INFO_INBOUND)) + { + GNUNET_break (NULL != session); + } + else + { + GNUNET_break (NULL == session); + } + ai = find_ai (address, session); + if (NULL != ai) + { + GNUNET_break (0); + return; + } + if (NULL == (papi = GST_plugins_find (address->transport_name))) + { + /* we don't have the plugin for this address */ + GNUNET_break(0); + return; + } + if (NULL != session) + { + net = papi->get_network (papi->cls, session); + if (GNUNET_ATS_NET_UNSPECIFIED == net) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Could not obtain a valid network for `%s' %s (%s)\n"), + GNUNET_i2s (&address->peer), + GST_plugins_a2s (address), + address->transport_name); + return; + } + ats2[0].type = htonl (GNUNET_ATS_NETWORK_TYPE); + ats2[0].value = htonl (net); + memcpy (&ats2[1], + ats, + sizeof(struct GNUNET_ATS_Information) * ats_count); + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Notifying ATS about peer `%s''s new address `%s' session %p in network %s\n", + GNUNET_i2s (&address->peer), + (0 == address->address_length) + ? "" + : GST_plugins_a2s (address), + session, + GNUNET_ATS_print_network_type (net)); + ar = GNUNET_ATS_address_add (GST_ats, + address, + session, + (NULL != session) ? ats2 : ats, + (NULL != session) ? ats_count + 1 : ats_count); + ai = GNUNET_new (struct AddressInfo); + ai->address = GNUNET_HELLO_address_copy (address); + ai->session = session; + ai->ar = ar; + (void) GNUNET_CONTAINER_multipeermap_put (p2a, + &ai->address->peer, + ai, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); +} + + +/** + * Notify ATS about a new session now existing for the given + * address. + * + * @param address the address + * @param session the session + */ +void +GST_ats_new_session (const struct GNUNET_HELLO_Address *address, + struct Session *session) +{ + struct AddressInfo *ai; + + ai = find_ai (address, NULL); + if (NULL == ai) + { + GNUNET_break (NULL != (find_ai (address, session))); + return; + } + GNUNET_break (NULL == ai->session); + ai->session = session; + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "transport-ats", + "Telling ATS about new session %p for peer %s\n", + session, + GNUNET_i2s (&address->peer)); + // FIXME: tell ATS API, but not using this call: + GNUNET_ATS_address_update (ai->ar, + session, + NULL, 0); + +} + + +/** + * Notify ATS that the session (but not the address) of + * a given address is no longer relevant. + * + * @param address the address + * @param session the session + */ +void +GST_ats_del_session (const struct GNUNET_HELLO_Address *address, + struct Session *session) +{ + struct AddressInfo *ai; + + if (NULL == session) + { + GNUNET_break (0); + return; + } + ai = find_ai (address, session); + if (NULL == ai) + { + /* We sometimes create sessions just for sending a PING, + and if those are destroyed they were never known to + ATS which means we end up here (however, in this + case, the address must be an outbound address). */ + GNUNET_break (GNUNET_YES != + GNUNET_HELLO_address_check_option (address, + GNUNET_HELLO_ADDRESS_INFO_INBOUND)); + + return; + } + GNUNET_assert (session == ai->session); + ai->session = NULL; + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "transport-ats", + "Telling ATS to destroy session %p from peer %s\n", + session, + GNUNET_i2s (&address->peer)); + /* FIXME: if this was an *inbound* address, destroy it + FULLY here well; but use different API, as looking up + inbound address without session is not great... */ + GNUNET_ATS_address_destroyed (GST_ats, address, session); + if (GNUNET_YES == + GNUNET_HELLO_address_check_option (address, + GNUNET_HELLO_ADDRESS_INFO_INBOUND)) + GST_ats_expire_address (address); +} + + +/** + * Notify ATS about property changes to an address. + * + * @param address our information about the address + * @param session the session + * @param ats performance information + * @param ats_count number of elements in @a ats + */ +void +GST_ats_update_metrics (const struct GNUNET_HELLO_Address *address, + struct Session *session, + const struct GNUNET_ATS_Information *ats, + uint32_t ats_count) +{ + struct GNUNET_ATS_Information *ats_new; + struct AddressInfo *ai; + + ai = find_ai (address, session); + if (NULL == ai) + { + /* We sometimes create sessions just for sending a PING, + and if we get metrics for those, they were never known to + ATS which means we end up here (however, in this + case, the address must be an outbound address). */ + GNUNET_break (GNUNET_YES != + GNUNET_HELLO_address_check_option (address, + GNUNET_HELLO_ADDRESS_INFO_INBOUND)); + + return; + } + /* Call to manipulation to manipulate ATS information */ + GNUNET_assert (NULL != GST_ats); + if ((NULL == ats) || (0 == ats_count)) + return; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Updating metrics for peer `%s' address %s session %p\n", + GNUNET_i2s (&address->peer), + GST_plugins_a2s (address), + session); + ats_new = GST_manipulation_manipulate_metrics (address, + session, + ats, + ats_count); + GNUNET_ATS_address_update (ai->ar, + session, + ats_new, ats_count); + GNUNET_free_non_null (ats_new); +} + + +/** + * Notify ATS that the address has expired and thus cannot + * be used any longer. This function must only be called + * if the corresponding session is already gone. + * + * @param address the address + */ +void +GST_ats_expire_address (const struct GNUNET_HELLO_Address *address) +{ + struct AddressInfo *ai; + + ai = find_ai (address, NULL); + if (NULL == ai) + { + GNUNET_break (0); + return; + } + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multipeermap_remove (p2a, + &address->peer, + ai)); + GNUNET_break (NULL == ai->session); + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "transport-ats", + "Telling ATS to destroy address from peer %s\n", + GNUNET_i2s (&address->peer)); + GNUNET_ATS_address_destroyed (GST_ats, address, NULL); + GNUNET_HELLO_address_free (ai->address); + GNUNET_free (ai); +} + + +/** + * Initialize ATS subsystem. + */ +void +GST_ats_init () +{ + p2a = GNUNET_CONTAINER_multipeermap_create (4, GNUNET_YES); +} + + +/** + * Release memory used by the given address data. + * + * @param cls NULL + * @param key which peer is this about + * @param value the `struct AddressInfo` + * @return #GNUNET_OK (continue to iterate) + */ +static int +destroy_ai (void *cls, + const struct GNUNET_PeerIdentity *key, + void *value) +{ + struct AddressInfo *ai = value; + + GNUNET_HELLO_address_free (ai->address); + GNUNET_free (ai); + return GNUNET_OK; +} + + +/** + * Shutdown ATS subsystem. + */ +void +GST_ats_done () +{ + GNUNET_CONTAINER_multipeermap_iterate (p2a, + &destroy_ai, + NULL); + GNUNET_CONTAINER_multipeermap_destroy (p2a); + p2a = NULL; +} + +/* end of gnunet-service-transport_ats.c */ -- cgit v1.2.3