From e298fa7e3eeafea48a8abf6c5b8bf449b279e7a7 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 6 Dec 2018 17:03:35 +0100 Subject: high-level new ATS service implementation --- src/ats/Makefile.am | 11 +- src/ats/ats2.h | 7 +- src/ats/gnunet-service-ats-new.c | 653 +++++++++++++++++++++++++++++ src/include/gnunet_ats_plugin_new.h | 169 ++++++++ src/include/gnunet_ats_transport_service.h | 2 +- src/include/gnunet_container_lib.h | 10 +- 6 files changed, 842 insertions(+), 10 deletions(-) create mode 100644 src/ats/gnunet-service-ats-new.c create mode 100644 src/include/gnunet_ats_plugin_new.h (limited to 'src') diff --git a/src/ats/Makefile.am b/src/ats/Makefile.am index 648849b1a..52a1e7d11 100644 --- a/src/ats/Makefile.am +++ b/src/ats/Makefile.am @@ -99,7 +99,8 @@ libgnunet_plugin_ats_ril_la_LDFLAGS = \ $(GN_PLUGIN_LDFLAGS) libexec_PROGRAMS = \ - gnunet-service-ats + gnunet-service-ats \ + gnunet-service-ats-new gnunet_service_ats_SOURCES = \ gnunet-service-ats.c gnunet-service-ats.h \ @@ -118,6 +119,14 @@ gnunet_service_ats_LDADD = \ libgnunetats.la \ $(GN_LIBINTL) +gnunet_service_ats_new_SOURCES = \ + gnunet-service-ats-new.c +gnunet_service_ats_new_LDADD = \ + $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) + + if HAVE_TESTING TESTING_TESTS = \ test_ats_api_proportional \ diff --git a/src/ats/ats2.h b/src/ats/ats2.h index 883ac7c38..0882c5f20 100644 --- a/src/ats/ats2.h +++ b/src/ats/ats2.h @@ -16,13 +16,13 @@ along with this program. If not, see . */ /** - * @file ats/ats.h + * @file ats/ats2.h * @brief automatic transport selection messages * @author Christian Grothoff * @author Matthias Wachs */ -#ifndef ATS_H -#define ATS_H +#ifndef ATS2_H +#define ATS2_H #include "gnunet_util_lib.h" #include "gnunet_ats_transport_service.h" @@ -118,6 +118,7 @@ struct ExpressPreferenceMessage /** * What type of performance preference does the client have? + * A `enum GNUNET_MQ_PreferenceKind` in NBO. */ uint32_t pk GNUNET_PACKED; diff --git a/src/ats/gnunet-service-ats-new.c b/src/ats/gnunet-service-ats-new.c new file mode 100644 index 000000000..57398e641 --- /dev/null +++ b/src/ats/gnunet-service-ats-new.c @@ -0,0 +1,653 @@ +/* + This file is part of GNUnet. + Copyright (C) 2011, 2018 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +/** + * @file ats/gnunet-service-ats-new.c + * @brief ats service + * @author Matthias Wachs + * @author Christian Grothoff + * + * TODO: + * - implement messages ATS -> transport + * - implement loading / unloading of ATS plugins + * - expose plugin the API to send messages ATS -> transport + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_statistics_service.h" +#include "gnunet_ats_plugin_new.h" +#include "ats2.h" + + +/** + * What type of client is this client? + */ +enum ClientType { + /** + * We don't know yet. + */ + CT_NONE = 0, + + /** + * Transport service. + */ + CT_TRANSPORT, + + /** + * Application. + */ + CT_APPLICATION +}; + + +/** + * Information we track per client. + */ +struct Client; + +/** + * Preferences expressed by a client are kept in a DLL per client. + */ +struct ClientPreference +{ + /** + * DLL pointer. + */ + struct ClientPreference *next; + + /** + * DLL pointer. + */ + struct ClientPreference *prev; + + /** + * Which client expressed the preference? + */ + struct Client *client; + + /** + * Plugin's representation of the preference. + */ + struct GNUNET_ATS_PreferenceHandle *ph; + + /** + * Details about the preference. + */ + struct GNUNET_ATS_Preference pref; +}; + + +/** + * Information about ongoing sessions of the transport client. + */ +struct GNUNET_ATS_Session +{ + + /** + * Session data exposed to the plugin. + */ + struct GNUNET_ATS_SessionData data; + + /** + * The transport client that provided the session. + */ + struct Client *client; + + /** + * Session state in the plugin. + */ + struct GNUNET_ATS_SessionHandle *sh; + + /** + * Unique ID for the session when talking with the client. + */ + uint32_t session_id; + +}; + + +/** + * Information we track per client. + */ +struct Client +{ + /** + * Type of the client, initially #CT_NONE. + */ + enum ClientType type; + + /** + * Service handle of the client. + */ + struct GNUNET_SERVICE_Client *client; + + /** + * Message queue to talk to the client. + */ + struct GNUNET_MQ_Handle *mq; + + /** + * Details depending on @e type. + */ + union { + + struct { + + /** + * Head of DLL of preferences expressed by this client. + */ + struct ClientPreference *cp_head; + + /** + * Tail of DLL of preferences expressed by this client. + */ + struct ClientPreference *cp_tail; + + } application; + + struct { + + /** + * Map from session IDs to `struct GNUNET_ATS_Session` objects. + */ + struct GNUNET_CONTAINER_MultiHashMap32 *sessions; + + } transport; + + } details; + +}; + + +/** + * Handle for statistics. + */ +static struct GNUNET_STATISTICS_Handle *GSA_stats; + +/** + * Our solver. + */ +static struct GNUNET_ATS_SolverFunctions *plugin; + +/** + * The transport client (there can only be one at a time). + */ +static struct Client *transport_client; + + +/** + * Convert @a properties to @a prop + * + * @param properties in NBO + * @param prop[out] in HBO + */ +static void +prop_ntoh (const struct PropertiesNBO *properties, + struct GNUNET_ATS_Properties *prop) +{ + prop->delay = GNUNET_TIME_relative_ntoh (properties->delay); + prop->goodput_out = ntohl (properties->goodput_out); + prop->goodput_in = ntohl (properties->goodput_in); + prop->utilization_out = ntohl (properties->utilization_out); + prop->utilization_in = ntohl (properties->utilization_in); + prop->distance = ntohl (properties->distance); + prop->mtu = ntohl (properties->mtu); + prop->nt = (enum GNUNET_NetworkType) ntohl (properties->nt); + prop->cc = (enum GNUNET_TRANSPORT_CommunicatorCharacteristics) ntohl (properties->cc); +} + + +/** + * We have received a `struct ExpressPreferenceMessage` from an application client. + * + * @param cls handle to the client + * @param msg the start message + */ +static void +handle_suggest (void *cls, + const struct ExpressPreferenceMessage *msg) +{ + struct Client *c = cls; + struct ClientPreference *cp; + + if (CT_NONE == c->type) + c->type = CT_APPLICATION; + if (CT_APPLICATION != c->type) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (c->client); + return; + } + cp = GNUNET_new (struct ClientPreference); + cp->client = c; + cp->pref.peer = msg->peer; + cp->pref.bw = msg->bw; + cp->pref.pk = (enum GNUNET_MQ_PreferenceKind) ntohl (msg->pk); + cp->ph = plugin->preference_add (plugin->cls, + &cp->pref); + GNUNET_CONTAINER_DLL_insert (c->details.application.cp_head, + c->details.application.cp_tail, + cp); + GNUNET_SERVICE_client_continue (c->client); +} + + +/** + * We have received a `struct ExpressPreferenceMessage` from an application client. + * + * @param cls handle to the client + * @param msg the start message + */ +static void +handle_suggest_cancel (void *cls, + const struct ExpressPreferenceMessage *msg) +{ + struct Client *c = cls; + struct ClientPreference *cp; + + if (CT_NONE == c->type) + c->type = CT_APPLICATION; + if (CT_APPLICATION != c->type) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (c->client); + return; + } + for (cp = c->details.application.cp_head; + NULL != cp; + cp = cp->next) + if ( (cp->pref.pk == (enum GNUNET_MQ_PreferenceKind) ntohl (msg->pk)) && + (cp->pref.bw.value__ == msg->bw.value__) && + (0 == memcmp (&cp->pref.peer, + &msg->peer, + sizeof (struct GNUNET_PeerIdentity))) ) + break; + if (NULL == cp) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (c->client); + return; + } + plugin->preference_del (plugin->cls, + cp->ph, + &cp->pref); + GNUNET_CONTAINER_DLL_remove (c->details.application.cp_head, + c->details.application.cp_tail, + cp); + GNUNET_free (cp); + GNUNET_SERVICE_client_continue (c->client); +} + + +/** + * Handle 'start' messages from transport clients. + * + * @param cls client that sent the request + * @param message the request message + */ +static void +handle_start (void *cls, + const struct GNUNET_MessageHeader *hdr) +{ + struct Client *c = cls; + + if (CT_NONE != c->type) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (c->client); + return; + } + c->type = CT_TRANSPORT; + c->details.transport.sessions + = GNUNET_CONTAINER_multihashmap32_create (128); + if (NULL != transport_client) + { + GNUNET_SERVICE_client_drop (transport_client->client); + transport_client = NULL; + } + transport_client = c; + GNUNET_SERVICE_client_continue (c->client); +} + + +/** + * Check 'session_add' message is well-formed and comes from a + * transport client. + * + * @param cls client that sent the request + * @param message the request message + * @return #GNUNET_OK if @a message is well-formed + */ +static int +check_session_add (void *cls, + const struct SessionAddMessage *message) +{ + struct Client *c = cls; + + GNUNET_MQ_check_zero_termination (message); + if (CT_TRANSPORT != c->type) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Handle 'session add' messages from transport clients. + * + * @param cls client that sent the request + * @param message the request message + */ +static void +handle_session_add (void *cls, + const struct SessionAddMessage *message) +{ + struct Client *c = cls; + struct GNUNET_ATS_Session *session; + int inbound_only = (GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD_INBOUND_ONLY == + ntohs (message->header.type)); + + session = GNUNET_CONTAINER_multihashmap32_get (c->details.transport.sessions, + message->session_id); + if (NULL != session) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (c->client); + return; + } + session = GNUNET_new (struct GNUNET_ATS_Session); + session->data.session = session; + session->client = c; + session->session_id = message->session_id; + session->data.peer = message->peer; + prop_ntoh (&message->properties, + &session->data.prop); + session->data.inbound_only = inbound_only; + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap32_put (c->details.transport.sessions, + message->session_id, + session, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + session->sh = plugin->session_add (plugin->cls, + &session->data); + GNUNET_SERVICE_client_continue (c->client); +} + + + +/** + * Handle 'session update' messages from transport clients. + * + * @param cls client that sent the request + * @param msg the request message + */ +static void +handle_session_update (void *cls, + const struct SessionUpdateMessage *msg) +{ + struct Client *c = cls; + struct GNUNET_ATS_Session *session; + + if (CT_TRANSPORT != c->type) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (c->client); + return; + } + session = GNUNET_CONTAINER_multihashmap32_get (c->details.transport.sessions, + msg->session_id); + if (NULL == session) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (c->client); + return; + } + prop_ntoh (&msg->properties, + &session->data.prop); + plugin->session_update (plugin->cls, + session->sh, + &session->data); + GNUNET_SERVICE_client_continue (c->client); +} + + +/** + * Handle 'session delete' messages from transport clients. + * + * @param cls client that sent the request + * @param message the request message + */ +static void +handle_session_del (void *cls, + const struct SessionDelMessage *message) +{ + struct Client *c = cls; + struct GNUNET_ATS_Session *session; + + if (CT_TRANSPORT != c->type) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (c->client); + return; + } + session = GNUNET_CONTAINER_multihashmap32_get (c->details.transport.sessions, + message->session_id); + if (NULL == session) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (c->client); + return; + } + plugin->session_del (plugin->cls, + session->sh, + &session->data); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap32_remove (c->details.transport.sessions, + session->session_id, + session)); + GNUNET_free (session); + GNUNET_SERVICE_client_continue (c->client); +} + + +/** + * A client connected to us. Setup the local client + * record. + * + * @param cls unused + * @param client handle of the client + * @param mq message queue to talk to @a client + * @return @a client + */ +static void * +client_connect_cb (void *cls, + struct GNUNET_SERVICE_Client *client, + struct GNUNET_MQ_Handle *mq) +{ + struct Client *c = GNUNET_new (struct Client); + + c->client = client; + c->mq = mq; + return c; +} + + +/** + * Function called on each session to release associated state + * on transport disconnect. + * + * @param cls the `struct Client` + * @param key unused (session_id) + * @param value a `struct GNUNET_ATS_Session` + */ +static int +free_session (void *cls, + uint32_t key, + void *value) +{ + struct Client *c = cls; + struct GNUNET_ATS_Session *session = value; + + (void) key; + GNUNET_assert (c == session->client); + plugin->session_del (plugin->cls, + session->sh, + &session->data); + GNUNET_free (session); + return GNUNET_OK; +} + + +/** + * A client disconnected from us. Tear down the local client + * record. + * + * @param cls unused + * @param client handle of the client + * @param app_ctx our `struct Client` + */ +static void +client_disconnect_cb (void *cls, + struct GNUNET_SERVICE_Client *client, + void *app_ctx) +{ + struct Client *c = app_ctx; + + (void) cls; + GNUNET_assert (c->client == client); + switch (c->type) + { + case CT_NONE: + break; + case CT_APPLICATION: + for (struct ClientPreference *cp = c->details.application.cp_head; + NULL != cp; + cp = c->details.application.cp_head) + { + plugin->preference_del (plugin->cls, + cp->ph, + &cp->pref); + GNUNET_CONTAINER_DLL_remove (c->details.application.cp_head, + c->details.application.cp_tail, + cp); + GNUNET_free (cp); + } + break; + case CT_TRANSPORT: + if (transport_client == c) + transport_client = NULL; + GNUNET_CONTAINER_multihashmap32_iterate (c->details.transport.sessions, + &free_session, + c); + GNUNET_CONTAINER_multihashmap32_destroy (c->details.transport.sessions); + break; + } + GNUNET_free (c); +} + + +/** + * Task run during shutdown. + * + * @param cls unused + */ +static void +cleanup_task (void *cls) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "ATS shutdown initiated\n"); + if (NULL != GSA_stats) + { + GNUNET_STATISTICS_destroy (GSA_stats, + GNUNET_NO); + GSA_stats = NULL; + } +} + + +/** + * Process template requests. + * + * @param cls closure + * @param cfg configuration to use + * @param service the initialized service + */ +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_SERVICE_Handle *service) +{ + GSA_stats = GNUNET_STATISTICS_create ("ats", + cfg); + GNUNET_SCHEDULER_add_shutdown (&cleanup_task, + NULL); +#if 0 + if (GNUNET_OK != + GAS_plugin_init (cfg)) + { + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } +#endif +} + + +/** + * Define "main" method using service macro. + */ +GNUNET_SERVICE_MAIN +("ats", + GNUNET_SERVICE_OPTION_NONE, + &run, + &client_connect_cb, + &client_disconnect_cb, + NULL, + GNUNET_MQ_hd_fixed_size (suggest, + GNUNET_MESSAGE_TYPE_ATS_SUGGEST, + struct ExpressPreferenceMessage, + NULL), + GNUNET_MQ_hd_fixed_size (suggest_cancel, + GNUNET_MESSAGE_TYPE_ATS_SUGGEST_CANCEL, + struct ExpressPreferenceMessage, + NULL), + GNUNET_MQ_hd_fixed_size (start, + GNUNET_MESSAGE_TYPE_ATS_START, + struct GNUNET_MessageHeader, + NULL), + GNUNET_MQ_hd_var_size (session_add, + GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD, + struct SessionAddMessage, + NULL), + GNUNET_MQ_hd_var_size (session_add, + GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD_INBOUND_ONLY, + struct SessionAddMessage, + NULL), + GNUNET_MQ_hd_fixed_size (session_update, + GNUNET_MESSAGE_TYPE_ATS_SESSION_UPDATE, + struct SessionUpdateMessage, + NULL), + GNUNET_MQ_hd_fixed_size (session_del, + GNUNET_MESSAGE_TYPE_ATS_SESSION_DEL, + struct SessionDelMessage, + NULL), + GNUNET_MQ_handler_end ()); + + +/* end of gnunet-service-ats.c */ diff --git a/src/include/gnunet_ats_plugin_new.h b/src/include/gnunet_ats_plugin_new.h new file mode 100644 index 000000000..267477871 --- /dev/null +++ b/src/include/gnunet_ats_plugin_new.h @@ -0,0 +1,169 @@ +/* + This file is part of GNUnet + Copyright (C) 2009-2015, 2018 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +/** + * @author Christian Grothoff + * + * @file + * API for the ATS solvers. + * + * @defgroup ats-plugin ATS service plugin API + * Plugin API for the ATS service. + * + * Specifies the struct that is given to the plugin's entry method and the other + * struct that must be returned. Note that the destructors of ATS plugins will + * be given the value returned by the constructor and is expected to return a + * NULL pointer. + * + * @{ + */ +#ifndef PLUGIN_ATS_H +#define PLUGIN_ATS_H + +#include "gnunet_mq_lib.h" +#include "gnunet_bandwidth_lib.h" +#include "gnunet_ats_application_service.h" +#include "gnunet_ats_transport_service.h" +#include "gnunet_statistics_service.h" + + +/** + * Preference being expressed by an application client. + */ +struct GNUNET_ATS_Preference { + + /** + * Peer to get address suggestions for. + */ + struct GNUNET_PeerIdentity peer; + + /** + * How much bandwidth in bytes/second does the application expect? + */ + struct GNUNET_BANDWIDTH_Value32NBO bw; + + /** + * What type of performance preference does the client have? + */ + enum GNUNET_MQ_PreferenceKind pk; +}; + + +/** + * Opaque representation of a session the plugin can allocate bandwidth for. + */ +struct GNUNET_ATS_Session; + +/** + * Plugin-relevant information about a session. + */ +struct GNUNET_ATS_SessionData { + + /** + * Peer the session is with. + */ + struct GNUNET_PeerIdentity peer; + + /** + * ATS performance characteristics for a session. + */ + struct GNUNET_ATS_Properties prop; + + /** + * Handle to the session that has the given properties. + */ + struct GNUNET_ATS_Session *session; + + /** + * Is the session inbound only? + */ + int inbound_only; + +}; + +/** + * Internal representation of a preference by the plugin. + * (If desired, plugin may just use NULL.) + */ +struct GNUNET_ATS_PreferenceHandle; + +/** + * Internal representation of a session by the plugin. + * (If desired, plugin may just use NULL.) + */ +struct GNUNET_ATS_SessionHandle; + + +/** + * Solver functions. + * + * Each solver is required to set up and return an instance + * of this struct during initialization. + */ +struct GNUNET_ATS_SolverFunctions +{ + + /** + * Closure to pass to all solver functions in this struct. + */ + void *cls; + + /** + * The plugin should begin to respect a new preference. + * + * @param cls the closure + * @param pref the preference to add + * @return plugin's internal representation, or NULL + */ + struct GNUNET_ATS_PreferenceHandle * + (*preference_add)(void *cls, + const struct GNUNET_ATS_Preference *pref); + + /** + * The plugin should end respecting a preference. + * + * @param cls the closure + * @param ph whatever @e preference_add returned + * @param pref the preference to delete + * @return plugin's internal representation, or NULL + */ + void + (*preference_del)(void *cls, + struct GNUNET_ATS_PreferenceHandle *ph, + const struct GNUNET_ATS_Preference *pref); + + + struct GNUNET_ATS_SessionHandle * + (*session_add)(void *cls, + const struct GNUNET_ATS_SessionData *data); + + void + (*session_update)(void *cls, + struct GNUNET_ATS_SessionHandle *sh, + const struct GNUNET_ATS_SessionData *data); + + void + (*session_del)(void *cls, + struct GNUNET_ATS_SessionHandle *sh, + const struct GNUNET_ATS_SessionData *data); + +}; + +#endif + +/** @} */ /* end of group */ diff --git a/src/include/gnunet_ats_transport_service.h b/src/include/gnunet_ats_transport_service.h index b55c6a374..0df703712 100644 --- a/src/include/gnunet_ats_transport_service.h +++ b/src/include/gnunet_ats_transport_service.h @@ -39,7 +39,7 @@ /** - * ATS performance characteristics for an address. + * ATS performance characteristics for a session. */ struct GNUNET_ATS_Properties { diff --git a/src/include/gnunet_container_lib.h b/src/include/gnunet_container_lib.h index bd9ce7bb2..a06d697f7 100644 --- a/src/include/gnunet_container_lib.h +++ b/src/include/gnunet_container_lib.h @@ -1580,9 +1580,10 @@ struct GNUNET_CONTAINER_MultiHashMap32Iterator; * iterate, * #GNUNET_NO if not. */ -typedef int (*GNUNET_CONTAINER_HashMapIterator32) (void *cls, - uint32_t key, - void *value); +typedef int +(*GNUNET_CONTAINER_HashMapIterator32) (void *cls, + uint32_t key, + void *value); /** @@ -1604,8 +1605,7 @@ GNUNET_CONTAINER_multihashmap32_create (unsigned int len); * @param map the map */ void -GNUNET_CONTAINER_multihashmap32_destroy (struct GNUNET_CONTAINER_MultiHashMap32 - *map); +GNUNET_CONTAINER_multihashmap32_destroy (struct GNUNET_CONTAINER_MultiHashMap32 *map); /** -- cgit v1.2.3