From 333ed5b94540b68b9967885c215b57818f22fb79 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 29 Nov 2018 20:50:38 +0100 Subject: [PATCH] draft ATS API for TNG --- src/ats/Makefile.am | 24 +- src/ats/ats2.h | 298 ++++++++ src/ats/ats_api2_application.c | 363 ++++++++++ src/ats/ats_api2_transport.c | 666 ++++++++++++++++++ src/include/gnunet_ats_application_service.h | 27 +- src/include/gnunet_ats_transport_service.h | 62 +- src/include/gnunet_crypto_lib.h | 2 +- src/include/gnunet_protocols.h | 50 +- .../gnunet_transport_communication_service.h | 1 - src/util/plugin.c | 2 +- 10 files changed, 1446 insertions(+), 49 deletions(-) create mode 100644 src/ats/ats2.h create mode 100644 src/ats/ats_api2_application.c create mode 100644 src/ats/ats_api2_transport.c diff --git a/src/ats/Makefile.am b/src/ats/Makefile.am index 6a5c924e0..648849b1a 100644 --- a/src/ats/Makefile.am +++ b/src/ats/Makefile.am @@ -18,7 +18,10 @@ if USE_COVERAGE AM_CFLAGS = -fprofile-arcs -ftest-coverage endif -lib_LTLIBRARIES = libgnunetats.la +lib_LTLIBRARIES = \ + libgnunetats.la \ + libgnunetatsapplication.la \ + libgnunetatstransport.la plugin_LTLIBRARIES = \ libgnunet_plugin_ats_proportional.la @@ -45,6 +48,25 @@ libgnunetats_la_LDFLAGS = \ $(GN_LIB_LDFLAGS) $(WINFLAGS) \ -version-info 4:0:0 + +libgnunetatsapplication_la_SOURCES = \ + ats_api2_application.c +libgnunetatsapplication_la_LIBADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(LTLIBINTL) +libgnunetatsapplication_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) $(WINFLAGS) \ + -version-info 0:0:0 + +libgnunetatstransport_la_SOURCES = \ + ats_api2_transport.c +libgnunetatstransport_la_LIBADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(LTLIBINTL) +libgnunetatstransport_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) $(WINFLAGS) \ + -version-info 0:0:0 + libgnunet_plugin_ats_proportional_la_SOURCES = \ plugin_ats_proportional.c libgnunet_plugin_ats_proportional_la_LIBADD = \ diff --git a/src/ats/ats2.h b/src/ats/ats2.h new file mode 100644 index 000000000..883ac7c38 --- /dev/null +++ b/src/ats/ats2.h @@ -0,0 +1,298 @@ +/* + This file is part of GNUnet. + Copyright (C) 2010-2015 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/ats.h + * @brief automatic transport selection messages + * @author Christian Grothoff + * @author Matthias Wachs + */ +#ifndef ATS_H +#define ATS_H + +#include "gnunet_util_lib.h" +#include "gnunet_ats_transport_service.h" + + +GNUNET_NETWORK_STRUCT_BEGIN + + +/** + * ATS performance characteristics for an address. + */ +struct PropertiesNBO +{ + + /** + * Delay. Time between when the time packet is sent and the packet + * arrives. FOREVER if we did not (successfully) measure yet. + */ + struct GNUNET_TIME_RelativeNBO delay; + + /** + * Confirmed successful payload on this connection from this peer to + * the other peer. In NBO. + * + * Unit: [bytes/second] + */ + uint32_t goodput_out; + + /** + * Confirmed useful payload on this connection to this peer from + * the other peer. In NBO. + * + * Unit: [bytes/second] + */ + uint32_t goodput_in; + + /** + * Actual traffic on this connection from this peer to the other peer. + * Includes transport overhead. In NBO. + * + * Unit: [bytes/second] + */ + uint32_t utilization_out; + + /** + * Actual traffic on this connection from the other peer to this peer. + * Includes transport overhead. In NBO. + * + * Unit: [bytes/second] + */ + uint32_t utilization_in; + + /** + * Distance on network layer (required for distance-vector routing) + * in hops. Zero for direct connections (i.e. plain TCP/UDP). In NBO. + */ + uint32_t distance; + + /** + * MTU of the network layer, UINT32_MAX for no MTU (stream). + * + * Unit: [bytes]. In NBO. + */ + uint32_t mtu; + + /** + * Which network scope does the respective address belong to? + * A `enum GNUNET_NetworkType nt` in NBO. + */ + uint32_t nt; + + /** + * What characteristics does this communicator have? + * A `enum GNUNET_TRANSPORT_CommunicatorCharacteristics` in NBO. + */ + uint32_t cc; + +}; + + +/** + * Application client to ATS service: we would like to have + * address suggestions for this peer. + */ +struct ExpressPreferenceMessage +{ + /** + * Type is #GNUNET_MESSAGE_TYPE_ATS_SUGGEST or + * #GNUNET_MESSAGE_TYPE_ATS_SUGGEST_CANCEL to stop + * suggestions. + */ + struct GNUNET_MessageHeader header; + + /** + * What type of performance preference does the client have? + */ + uint32_t pk GNUNET_PACKED; + + /** + * 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; + +}; + + +/** + * Transport client to ATS service: here is another session you can use. + */ +struct SessionAddMessage +{ + /** + * Type is #GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD or + * #GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD_INBOUND_ONLY + */ + struct GNUNET_MessageHeader header; + + /** + * Internal number this client will henceforth use to + * refer to this session. + */ + uint32_t session_id GNUNET_PACKED; + + /** + * Identity of the peer that this session is for. + */ + struct GNUNET_PeerIdentity peer; + + /** + * Performance properties of the session. + */ + struct PropertiesNBO properties; + + /* followed by: + * - char * address (including '\0'-termination). + */ + +}; + + +/** + * Message used to notify ATS that the performance + * characteristics for an session have changed. + */ +struct SessionUpdateMessage +{ + /** + * Message of type #GNUNET_MESSAGE_TYPE_ATS_SESSION_UPDATE. + */ + struct GNUNET_MessageHeader header; + + /** + * Internal number this client uses to refer to this session. + */ + uint32_t session_id GNUNET_PACKED; + + /** + * Which peer is this about? (Technically redundant, as the + * @e session_id should be sufficient, but enables ATS service + * to find the session faster). + */ + struct GNUNET_PeerIdentity peer; + + /** + * Performance properties of the session. + */ + struct PropertiesNBO properties; + +}; + + +/** + * Message sent by ATS client to ATS service when an session + * was destroyed and must thus henceforth no longer be considered + * for scheduling. + */ +struct SessionDelMessage +{ + /** + * Type is #GNUNET_MESSAGE_TYPE_ATS_SESSION_DEL. + */ + struct GNUNET_MessageHeader header; + + /** + * Internal number this client uses to refer to this session. + */ + uint32_t session_id GNUNET_PACKED; + + /** + * Which peer is this about? (Technically redundant, as the + * @e session_id should be sufficient, but enables ATS service + * to find the session faster). + */ + struct GNUNET_PeerIdentity peer; + +}; + + +/** + * ATS Service allocates resources to an session + * identified by the given @e session_id for the given @e peer with + * the given @e bandwidth_in and @e bandwidth_out limits from now on. + */ +struct SessionAllocationMessage +{ + /** + * A message of type #GNUNET_MESSAGE_TYPE_ATS_SESSION_ALLOCATION. + */ + struct GNUNET_MessageHeader header; + + /** + * Internal number this client uses to refer to the session this + * suggestion is about. + */ + uint32_t session_id GNUNET_PACKED; + + /** + * Which peer is this about? (Technically redundant, as the + * @e session_id should be sufficient, but may enable client + * to find the session faster and/or check consistency). + */ + struct GNUNET_PeerIdentity peer; + + /** + * How much bandwidth we are allowed for sending. + */ + struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out; + + /** + * How much bandwidth we are allowed for receiving. + */ + struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in; + +}; + + +/** + * ATS Service suggests to the transport service to try the address + * for the given @e peer. + */ +struct AddressSuggestionMessage +{ + /** + * A message of type #GNUNET_MESSAGE_TYPE_ATS_ADDRESS_SUGGESTION. + */ + struct GNUNET_MessageHeader header; + + /** + * Zero. + */ + uint32_t reserved GNUNET_PACKED; + + /** + * Which peer is this about? (Technically redundant, as the + * @e session_id should be sufficient, but may enable client + * to find the session faster and/or check consistency). + */ + struct GNUNET_PeerIdentity peer; + + /* Followed by 0-terminated address */ +}; + + +GNUNET_NETWORK_STRUCT_END + + + +#endif diff --git a/src/ats/ats_api2_application.c b/src/ats/ats_api2_application.c new file mode 100644 index 000000000..37198392c --- /dev/null +++ b/src/ats/ats_api2_application.c @@ -0,0 +1,363 @@ +/* + This file is part of GNUnet. + Copyright (C) 2010-2016 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/ats_api2_application.c + * @brief enable clients to ask ATS about establishing connections to peers + * @author Christian Grothoff + * @author Matthias Wachs + */ +#include "platform.h" +#include "gnunet_ats_application_service.h" +#include "ats2.h" + + +#define LOG(kind,...) GNUNET_log_from(kind, "ats-application-api", __VA_ARGS__) + + +/** + * Handle for ATS address suggestion requests. + */ +struct GNUNET_ATS_ApplicationSuggestHandle +{ + /** + * ID of the peer for which address suggestion was requested. + */ + struct GNUNET_PeerIdentity id; + + /** + * Connecitivity handle this suggestion handle belongs to. + */ + struct GNUNET_ATS_ApplicationHandle *ch; + + /** + * What preference is being expressed? + */ + enum GNUNET_MQ_PreferenceKind pk; + + /** + * How much bandwidth does the client expect? + */ + struct GNUNET_BANDWIDTH_Value32NBO bw; +}; + + +/** + * Handle to the ATS subsystem for application management. + */ +struct GNUNET_ATS_ApplicationHandle +{ + + /** + * Our configuration. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Map with the identities of all the peers for which we would + * like to have address suggestions. The key is the PID, the + * value is currently the `struct GNUNET_ATS_ApplicationSuggestHandle` + */ + struct GNUNET_CONTAINER_MultiPeerMap *sug_requests; + + /** + * Message queue for sending requests to the ATS service. + */ + struct GNUNET_MQ_Handle *mq; + + /** + * Task to trigger reconnect. + */ + struct GNUNET_SCHEDULER_Task *task; + + /** + * Reconnect backoff delay. + */ + struct GNUNET_TIME_Relative backoff; +}; + + +/** + * Re-establish the connection to the ATS service. + * + * @param ch handle to use to re-connect. + */ +static void +reconnect (struct GNUNET_ATS_ApplicationHandle *ch); + + +/** + * Re-establish the connection to the ATS service. + * + * @param cls handle to use to re-connect. + */ +static void +reconnect_task (void *cls) +{ + struct GNUNET_ATS_ApplicationHandle *ch = cls; + + ch->task = NULL; + reconnect (ch); +} + + +/** + * Disconnect from ATS and then reconnect. + * + * @param ch our handle + */ +static void +force_reconnect (struct GNUNET_ATS_ApplicationHandle *ch) +{ + if (NULL != ch->mq) + { + GNUNET_MQ_destroy (ch->mq); + ch->mq = NULL; + } + ch->backoff = GNUNET_TIME_STD_BACKOFF (ch->backoff); + ch->task = GNUNET_SCHEDULER_add_delayed (ch->backoff, + &reconnect_task, + ch); +} + + +/** + * We encountered an error handling the MQ to the + * ATS service. Reconnect. + * + * @param cls the `struct GNUNET_ATS_ApplicationHandle` + * @param error details about the error + */ +static void +error_handler (void *cls, + enum GNUNET_MQ_Error error) +{ + struct GNUNET_ATS_ApplicationHandle *ch = cls; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "ATS connection died (code %d), reconnecting\n", + (int) error); + force_reconnect (ch); +} + + +/** + * Transmit request for an address suggestion. + * + * @param cls the `struct GNUNET_ATS_ApplicationHandle` + * @param peer peer to ask for an address suggestion for + * @param value the `struct GNUNET_ATS_SuggestHandle` + * @return #GNUNET_OK (continue to iterate), #GNUNET_SYSERR on + * failure (message queue no longer exists) + */ +static int +transmit_suggestion (void *cls, + const struct GNUNET_PeerIdentity *peer, + void *value) +{ + struct GNUNET_ATS_ApplicationHandle *ch = cls; + struct GNUNET_ATS_ApplicationSuggestHandle *sh = value; + struct GNUNET_MQ_Envelope *ev; + struct ExpressPreferenceMessage *m; + + if (NULL == ch->mq) + return GNUNET_SYSERR; + ev = GNUNET_MQ_msg (m, + GNUNET_MESSAGE_TYPE_ATS_SUGGEST); + m->pk = htonl ((uint32_t) sh->pk); + m->bw = sh->bw; + m->peer = *peer; + GNUNET_MQ_send (ch->mq, ev); + return GNUNET_OK; +} + + +/** + * Re-establish the connection to the ATS service. + * + * @param ch handle to use to re-connect. + */ +static void +reconnect (struct GNUNET_ATS_ApplicationHandle *ch) +{ + static const struct GNUNET_MQ_MessageHandler handlers[] = { + { NULL, 0, 0 } + }; + + GNUNET_assert (NULL == ch->mq); + ch->mq = GNUNET_CLIENT_connect (ch->cfg, + "ats", + handlers, + &error_handler, + ch); + if (NULL == ch->mq) + { + force_reconnect (ch); + return; + } + GNUNET_CONTAINER_multipeermap_iterate (ch->sug_requests, + &transmit_suggestion, + ch); +} + + +/** + * Initialize the ATS application suggestion client handle. + * + * @param cfg configuration to use + * @return ats application handle, NULL on error + */ +struct GNUNET_ATS_ApplicationHandle * +GNUNET_ATS_application_init (const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct GNUNET_ATS_ApplicationHandle *ch; + + ch = GNUNET_new (struct GNUNET_ATS_ApplicationHandle); + ch->cfg = cfg; + ch->sug_requests = GNUNET_CONTAINER_multipeermap_create (32, + GNUNET_YES); + reconnect (ch); + return ch; +} + + +/** + * Function called to free all `struct GNUNET_ATS_ApplicationSuggestHandle`s + * in the map. + * + * @param cls NULL + * @param key the key + * @param value the value to free + * @return #GNUNET_OK (continue to iterate) + */ +static int +free_sug_handle (void *cls, + const struct GNUNET_PeerIdentity *key, + void *value) +{ + struct GNUNET_ATS_ApplicationSuggestHandle *cur = value; + + GNUNET_free (cur); + return GNUNET_OK; +} + + +/** + * Client is done with ATS application management, release resources. + * + * @param ch handle to release + */ +void +GNUNET_ATS_application_done (struct GNUNET_ATS_ApplicationHandle *ch) +{ + if (NULL != ch->mq) + { + GNUNET_MQ_destroy (ch->mq); + ch->mq = NULL; + } + if (NULL != ch->task) + { + GNUNET_SCHEDULER_cancel (ch->task); + ch->task = NULL; + } + GNUNET_CONTAINER_multipeermap_iterate (ch->sug_requests, + &free_sug_handle, + NULL); + GNUNET_CONTAINER_multipeermap_destroy (ch->sug_requests); + GNUNET_free (ch); +} + + +/** + * We would like to receive address suggestions for a peer. ATS will + * respond with a call to the continuation immediately containing an address or + * no address if none is available. ATS can suggest more addresses until we call + * #GNUNET_ATS_application_suggest_cancel(). + * + * @param ch handle + * @param peer identity of the peer we need an address for + * @param pk what kind of application will the application require (can be + * #GNUNET_MQ_PREFERENCE_NONE, we will still try to connect) + * @param bw desired bandwith, can be zero (we will still try to connect) + * @return suggest handle, NULL if a request is already pending + */ +struct GNUNET_ATS_ApplicationSuggestHandle * +GNUNET_ATS_application_suggest (struct GNUNET_ATS_ApplicationHandle *ch, + const struct GNUNET_PeerIdentity *peer, + enum GNUNET_MQ_PreferenceKind pk, + struct GNUNET_BANDWIDTH_Value32NBO bw) +{ + struct GNUNET_ATS_ApplicationSuggestHandle *s; + + s = GNUNET_new (struct GNUNET_ATS_ApplicationSuggestHandle); + s->ch = ch; + s->id = *peer; + s->pk = pk; + s->bw = bw; + (void) GNUNET_CONTAINER_multipeermap_put (ch->sug_requests, + &s->id, + s, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Requesting ATS to suggest address for `%s'\n", + GNUNET_i2s (peer)); + if (NULL == ch->mq) + return s; + GNUNET_assert (GNUNET_OK == + transmit_suggestion (ch, + &s->id, + s)); + return s; +} + + +/** + * We no longer care about being connected to a peer. + * + * @param sh handle to stop + */ +void +GNUNET_ATS_application_suggest_cancel (struct GNUNET_ATS_ApplicationSuggestHandle *sh) +{ + struct GNUNET_ATS_ApplicationHandle *ch = sh->ch; + struct GNUNET_MQ_Envelope *ev; + struct ExpressPreferenceMessage *m; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Telling ATS we no longer care for an address for `%s'\n", + GNUNET_i2s (&sh->id)); + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_multipeermap_remove (ch->sug_requests, + &sh->id, + sh)); + if (NULL == ch->mq) + { + GNUNET_free (sh); + return; + } + ev = GNUNET_MQ_msg (m, + GNUNET_MESSAGE_TYPE_ATS_SUGGEST_CANCEL); + m->pk = htonl ((uint32_t) sh->pk); + m->bw = sh->bw; + m->peer = sh->id; + GNUNET_MQ_send (ch->mq, + ev); + GNUNET_free (sh); +} + + +/* end of ats_api2_application.c */ diff --git a/src/ats/ats_api2_transport.c b/src/ats/ats_api2_transport.c new file mode 100644 index 000000000..b8133beea --- /dev/null +++ b/src/ats/ats_api2_transport.c @@ -0,0 +1,666 @@ +/* + This file is part of GNUnet. + Copyright (C) 2010-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 . +*/ +/** + * @file ats/ats_api2_transport.c + * @brief address suggestions and bandwidth allocation + * @author Christian Grothoff + * @author Matthias Wachs + */ +#include "platform.h" +#include "gnunet_ats_transport_service.h" +#include "ats2.h" + +#define LOG(kind,...) GNUNET_log_from(kind, "ats-transport-api", __VA_ARGS__) + + +/** + * Information we track per session, incoming or outgoing. It also + * doesn't matter if we have a session, any session that ATS is + * allowed to suggest right now should be tracked. + */ +struct GNUNET_ATS_SessionRecord +{ + + /** + * Transport handle this session record belongs to. + */ + struct GNUNET_ATS_TransportHandle *ath; + + /** + * Address data. + */ + const char *address; + + /** + * Session handle, NULL if inbound-only (also implies we cannot + * actually control inbound traffic via transport!). So if + * @e session is NULL, the @e properties are informative for + * ATS (connection exists, utilization) but ATS cannot directly + * influence it (and should thus not call the + * #GNUNET_ATS_AllocationCallback for this @e session, which is + * obvious as NULL is not a meaningful session to allocation + * resources to). + */ + struct GNUNET_ATS_Session *session; + + /** + * Identity of the peer reached at @e address. + */ + struct GNUNET_PeerIdentity pid; + + /** + * Performance data about the @e session. + */ + struct GNUNET_ATS_Properties properties; + + /** + * Unique ID to identify this session at this @a pid in IPC + * messages. + */ + uint32_t slot; + +}; + + +/** + * Handle to the ATS subsystem for bandwidth/transport transport information. + */ +struct GNUNET_ATS_TransportHandle +{ + + /** + * Our configuration. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Callback to invoke on suggestions. + */ + GNUNET_ATS_SuggestionCallback suggest_cb; + + /** + * Closure for @e suggest_cb. + */ + void *suggest_cb_cls; + + /** + * Callback to invoke on allocations. + */ + GNUNET_ATS_AllocationCallback alloc_cb; + + /** + * Closure for @e alloc_cb. + */ + void *alloc_cb_cls; + + /** + * Message queue for sending requests to the ATS service. + */ + struct GNUNET_MQ_Handle *mq; + + /** + * Task to trigger reconnect. + */ + struct GNUNET_SCHEDULER_Task *task; + + /** + * Hash map mapping PIDs to session records. + */ + struct GNUNET_CONTAINER_MultiPeerMap *records; + + /** + * Reconnect backoff delay. + */ + struct GNUNET_TIME_Relative backoff; + +}; + + +/** + * Re-establish the connection to the ATS service. + * + * @param sh handle to use to re-connect. + */ +static void +reconnect (struct GNUNET_ATS_TransportHandle *ath); + + +/** + * Re-establish the connection to the ATS service. + * + * @param cls handle to use to re-connect. + */ +static void +reconnect_task (void *cls) +{ + struct GNUNET_ATS_TransportHandle *ath = cls; + + ath->task = NULL; + reconnect (ath); +} + + +/** + * Disconnect from ATS and then reconnect. + * + * @param ath our handle + */ +static void +force_reconnect (struct GNUNET_ATS_TransportHandle *ath) +{ + if (NULL != ath->mq) + { + GNUNET_MQ_destroy (ath->mq); + ath->mq = NULL; + } + /* FIXME: do we tell transport service about disconnect events? CON: + initially ATS will have a really screwed picture of the world and + the rapid change would be bad. PRO: if we don't, ATS and + transport may disagree about the allocation for a while... + For now: lazy: do nothing. */ + ath->backoff = GNUNET_TIME_STD_BACKOFF (ath->backoff); + ath->task = GNUNET_SCHEDULER_add_delayed (ath->backoff, + &reconnect_task, + ath); +} + + +/** + * Check format of address suggestion message from the service. + * + * @param cls the `struct GNUNET_ATS_TransportHandle` + * @param m message received + */ +static int +check_ats_address_suggestion (void *cls, + const struct AddressSuggestionMessage *m) +{ + // FIXME: check 0-termination! + // FIXME: MQ API should really have a macro for this! + return GNUNET_SYSERR; +} + + +/** + * We received an address suggestion message from the service. + * + * @param cls the `struct GNUNET_ATS_TransportHandle` + * @param m message received + */ +static void +handle_ats_address_suggestion (void *cls, + const struct AddressSuggestionMessage *m) +{ + struct GNUNET_ATS_TransportHandle *ath = cls; + const char *address = (const char *) &m[1]; + + ath->suggest_cb (ath->suggest_cb_cls, + &m->peer, + address); +} + + +/** + * Closure for #match_session_cb. + */ +struct FindContext +{ + /** + * Key to look for. + */ + uint32_t session_id; + + /** + * Where to store the result. + */ + struct GNUNET_ATS_SessionRecord *sr; +}; + + +/** + * Finds matching session record. + * + * @param cls a `struct FindContext` + * @param pid peer identity (unused) + * @param value a `struct GNUNET_ATS_SessionRecord` + * @return #GNUNET_NO if match found, #GNUNET_YES to continue searching + */ +static int +match_session_cb (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) +{ + struct FindContext *fc = cls; + struct GNUNET_ATS_SessionRecord *sr = value; + + (void) pid; + if (fc->session_id == sr->slot) + { + fc->sr = sr; + return GNUNET_NO; + } + return GNUNET_YES; +} + + + +/** + * Find session record for peer @a pid and session @a session_id + * + * @param ath transport handle to search + * @param session_id session ID to match + * @param pid peer to search under + * @return NULL if no such record exists + */ +static struct GNUNET_ATS_SessionRecord * +find_session (struct GNUNET_ATS_TransportHandle *ath, + uint32_t session_id, + const struct GNUNET_PeerIdentity *pid) +{ + struct FindContext fc = { + .session_id = session_id, + .sr = NULL + }; + GNUNET_CONTAINER_multipeermap_get_multiple (ath->records, + pid, + &match_session_cb, + &fc); + return fc.sr; +} + + +/** + * We received a session allocation message from the service. + * + * @param cls the `struct GNUNET_ATS_TransportHandle` + * @param m message received + */ +static void +handle_ats_session_allocation (void *cls, + const struct SessionAllocationMessage *m) +{ + struct GNUNET_ATS_TransportHandle *ath = cls; + struct GNUNET_ATS_SessionRecord *ar; + uint32_t session_id; + + session_id = ntohl (m->session_id); + ar = find_session (ath, + session_id, + &m->peer); + if (NULL == ar) + { + /* this can (rarely) happen if ATS changes an sessiones allocation + just when the transport service deleted it */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Allocation ignored, session unknown\n"); + return; + } + ath->backoff = GNUNET_TIME_UNIT_ZERO; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "ATS allocates bandwidth for peer `%s' using address %s\n", + GNUNET_i2s (&ar->pid), + ar->address); + ath->alloc_cb (ath->alloc_cb_cls, + ar->session, + m->bandwidth_out, + m->bandwidth_in); +} + + +/** + * We encountered an error handling the MQ to the ATS service. + * Reconnect. + * + * @param cls the `struct GNUNET_ATS_TransportHandle` + * @param error details about the error + */ +static void +error_handler (void *cls, + enum GNUNET_MQ_Error error) +{ + struct GNUNET_ATS_TransportHandle *ath = cls; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "ATS connection died (code %d), reconnecting\n", + (int) error); + force_reconnect (ath); +} + + +/** + * Generate and transmit the `struct SessionAddMessage` for the given + * session record. + * + * @param ar the session to inform the ATS service about + */ +static void +send_add_session_message (const struct GNUNET_ATS_SessionRecord *ar) +{ + struct GNUNET_ATS_TransportHandle *ath = ar->ath; + struct GNUNET_MQ_Envelope *ev; + struct SessionAddMessage *m; + size_t alen; + + if (NULL == ath->mq) + return; /* disconnected, skip for now */ + alen = strlen (ar->address) + 1; + ev = GNUNET_MQ_msg_extra (m, + alen, + (NULL == ar->session) + ? GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD_INBOUND_ONLY + : GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD); + m->peer = ar->pid; + m->session_id = htonl (ar->slot); + // FIXME: convert endianess here! + // m->properties = ar->properties; + GNUNET_memcpy (&m[1], + ar->address, + alen); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Adding address `%s' for peer `%s'\n", + ar->address, + GNUNET_i2s (&ar->pid)); + GNUNET_MQ_send (ath->mq, + ev); +} + + +/** + * Send ATS information about the session record. + * + * @param cls our `struct GNUNET_ATS_TransportHandle *`, unused + * @param pid unused + * @param value the `struct GNUNET_ATS_SessionRecord *` to add + * @return #GNUNET_OK + */ +static int +send_add_session_cb (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) +{ + struct GNUNET_ATS_SessionRecord *ar = value; + + (void) cls; + (void) pid; + send_add_session_message (ar); + return GNUNET_OK; +} + + +/** + * Re-establish the connection to the ATS service. + * + * @param ath handle to use to re-connect. + */ +static void +reconnect (struct GNUNET_ATS_TransportHandle *ath) +{ + struct GNUNET_MQ_MessageHandler handlers[] = { + GNUNET_MQ_hd_var_size (ats_address_suggestion, + GNUNET_MESSAGE_TYPE_ATS_ADDRESS_SUGGESTION, + struct AddressSuggestionMessage, + ath), + GNUNET_MQ_hd_fixed_size (ats_session_allocation, + GNUNET_MESSAGE_TYPE_ATS_SESSION_ALLOCATION, + struct SessionAllocationMessage, + ath), + GNUNET_MQ_handler_end () + }; + struct GNUNET_MQ_Envelope *ev; + struct GNUNET_MessageHeader *init; + + GNUNET_assert (NULL == ath->mq); + ath->mq = GNUNET_CLIENT_connect (ath->cfg, + "ats", + handlers, + &error_handler, + ath); + if (NULL == ath->mq) + { + GNUNET_break (0); + force_reconnect (ath); + return; + } + ev = GNUNET_MQ_msg (init, + GNUNET_MESSAGE_TYPE_ATS_START); + GNUNET_MQ_send (ath->mq, + ev); + if (NULL == ath->mq) + return; + GNUNET_CONTAINER_multipeermap_iterate (ath->records, + &send_add_session_cb, + ath); +} + + +/** + * Initialize the ATS subsystem. + * + * @param cfg configuration to use + * @param alloc_cb notification to call whenever the allocation changed + * @param alloc_cb_cls closure for @a alloc_cb + * @param suggest_cb notification to call whenever the suggestation is made + * @param suggest_cb_cls closure for @a suggest_cb + * @return ats context + */ +struct GNUNET_ATS_TransportHandle * +GNUNET_ATS_transport_init (const struct GNUNET_CONFIGURATION_Handle *cfg, + GNUNET_ATS_AllocationCallback alloc_cb, + void *alloc_cb_cls, + GNUNET_ATS_SuggestionCallback suggest_cb, + void *suggest_cb_cls) +{ + struct GNUNET_ATS_TransportHandle *ath; + + ath = GNUNET_new (struct GNUNET_ATS_TransportHandle); + ath->cfg = cfg; + ath->suggest_cb = suggest_cb; + ath->suggest_cb_cls = suggest_cb_cls; + ath->alloc_cb = alloc_cb; + ath->alloc_cb_cls = alloc_cb_cls; + ath->records = GNUNET_CONTAINER_multipeermap_create (128, + GNUNET_YES); + reconnect (ath); + return ath; +} + + +/** + * Release memory associated with the session record. + * + * @param cls NULL + * @param pid unused + * @param value a `struct GNUNET_ATS_SessionRecord` + * @return #GNUNET_OK + */ +static int +free_record (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) +{ + struct GNUNET_ATS_SessionRecord *ar = value; + + (void) cls; + (void) pid; + GNUNET_free (ar); + return GNUNET_OK; +} + + +/** + * Client is done with ATS transport, release resources. + * + * @param ath handle to release + */ +void +GNUNET_ATS_transport_done (struct GNUNET_ATS_TransportHandle *ath) +{ + if (NULL != ath->mq) + { + GNUNET_MQ_destroy (ath->mq); + ath->mq = NULL; + } + if (NULL != ath->task) + { + GNUNET_SCHEDULER_cancel (ath->task); + ath->task = NULL; + } + GNUNET_CONTAINER_multipeermap_iterate (ath->records, + &free_record, + NULL); + GNUNET_CONTAINER_multipeermap_destroy (ath->records); + GNUNET_free (ath); +} + + +/** + * We have a new session ATS should know. Sessiones have to be added + * with this function before they can be: updated, set in use and + * destroyed. + * + * @param ath handle + * @param pid peer we connected to + * @param address the address (human readable version) + * @param session transport-internal handle for the session/queue, NULL if + * the session is inbound-only + * @param prop performance data for the session + * @return handle to the session representation inside ATS, NULL + * on error (i.e. ATS knows this exact session already) + */ +struct GNUNET_ATS_SessionRecord * +GNUNET_ATS_session_add (struct GNUNET_ATS_TransportHandle *ath, + const struct GNUNET_PeerIdentity *pid, + const char *address, + struct GNUNET_ATS_Session *session, + const struct GNUNET_ATS_Properties *prop) +{ + struct GNUNET_ATS_SessionRecord *ar; + uint32_t s; + size_t alen; + + if (NULL == address) + { + /* we need a valid address */ + GNUNET_break (0); + return NULL; + } + alen = strlen (address) + 1; + if ( (alen + sizeof (struct SessionAddMessage) >= GNUNET_MAX_MESSAGE_SIZE) || + (alen >= GNUNET_MAX_MESSAGE_SIZE) ) + { + /* address too large for us, this should not happen */ + GNUNET_break (0); + return NULL; + } + + /* Spin 's' until we find an unused session ID for this pid */ + for (s = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, + UINT32_MAX); + NULL != find_session (ath, + s, + pid); + s++) ; + + alen = strlen (address) + 1; + ar = GNUNET_malloc (sizeof (struct GNUNET_ATS_SessionRecord) + alen); + ar->ath = ath; + ar->slot = 42; // FIXME: make up unique number! + ar->session = session; + ar->address = (const char *) &ar[1]; + ar->pid = *pid; + ar->properties = *prop; + memcpy (&ar[1], + address, + alen); + (void) GNUNET_CONTAINER_multipeermap_put (ath->records, + &ar->pid, + ar, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); + send_add_session_message (ar); + return ar; +} + + +/** + * We have updated performance statistics for a given session. Note + * that this function can be called for sessiones that are currently + * in use as well as sessiones that are valid but not actively in use. + * Furthermore, the peer may not even be connected to us right now (in + * which case the call may be ignored or the information may be stored + * for later use). Update bandwidth assignments. + * + * @param ar session record to update information for + * @param prop performance data for the session + */ +void +GNUNET_ATS_session_update (struct GNUNET_ATS_SessionRecord *ar, + const struct GNUNET_ATS_Properties *prop) +{ + struct GNUNET_ATS_TransportHandle *ath = ar->ath; + struct GNUNET_MQ_Envelope *ev; + struct SessionUpdateMessage *m; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Updating address `%s' for peer `%s'\n", + ar->address, + GNUNET_i2s (&ar->pid)); + ar->properties = *prop; + if (NULL == ath->mq) + return; /* disconnected, skip for now */ + ev = GNUNET_MQ_msg (m, + GNUNET_MESSAGE_TYPE_ATS_SESSION_UPDATE); + m->session_id = htonl (ar->slot); + m->peer = ar->pid; + // FIXME: convert endianess here! + // m->properties = ar->properties; + GNUNET_MQ_send (ath->mq, + ev); +} + + +/** + * A session was destroyed, ATS should now schedule and + * allocate under the assumption that this @a ar is no + * longer in use. + * + * @param ar session record to drop + */ +void +GNUNET_ATS_session_del (struct GNUNET_ATS_SessionRecord *ar) +{ + struct GNUNET_ATS_TransportHandle *ath = ar->ath; + struct GNUNET_MQ_Envelope *ev; + struct SessionDelMessage *m; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Deleting address `%s' for peer `%s'\n", + ar->address, + GNUNET_i2s (&ar->pid)); + if (NULL == ath->mq) + return; + ev = GNUNET_MQ_msg (m, + GNUNET_MESSAGE_TYPE_ATS_SESSION_DEL); + m->session_id = htonl (ar->slot); + m->peer = ar->pid; + GNUNET_MQ_send (ath->mq, + ev); +} + + +/* end of ats_api2_transport.c */ diff --git a/src/include/gnunet_ats_application_service.h b/src/include/gnunet_ats_application_service.h index af92d5639..60560790a 100644 --- a/src/include/gnunet_ats_application_service.h +++ b/src/include/gnunet_ats_application_service.h @@ -39,32 +39,32 @@ * Handle to the ATS subsystem for making suggestions about * connections the peer would like to have. */ -struct GNUNET_ATS_ConnectivityHandle; +struct GNUNET_ATS_ApplicationHandle; /** - * Initialize the ATS connectivity suggestion client handle. + * Initialize the ATS application client handle. * * @param cfg configuration to use - * @return ats connectivity handle, NULL on error + * @return ats application handle, NULL on error */ -struct GNUNET_ATS_ConnectivityHandle * -GNUNET_ATS_connectivity_init (const struct GNUNET_CONFIGURATION_Handle *cfg); +struct GNUNET_ATS_ApplicationHandle * +GNUNET_ATS_application_init (const struct GNUNET_CONFIGURATION_Handle *cfg); /** - * Shutdown ATS connectivity suggestion client. + * Shutdown ATS application client. * * @param ch handle to destroy */ void -GNUNET_ATS_connectivity_done (struct GNUNET_ATS_ConnectivityHandle *ch); +GNUNET_ATS_application_done (struct GNUNET_ATS_ApplicationHandle *ch); /** - * Handle for address suggestion requests. + * Handle for suggestion requests. */ -struct GNUNET_ATS_ConnectivitySuggestHandle; +struct GNUNET_ATS_ApplicationSuggestHandle; /** @@ -74,13 +74,13 @@ struct GNUNET_ATS_ConnectivitySuggestHandle; * * @param ch handle * @param peer identity of the peer we need an address for - * @param pk what kind of connectivity will the application require (can be + * @param pk what kind of application will the application require (can be * #GNUNET_MQ_PREFERENCE_NONE, we will still try to connect) * @param bw desired bandwith, can be zero (we will still try to connect) * @return suggestion handle, NULL if request is already pending */ -struct GNUNET_ATS_ConnectivitySuggestHandle * -GNUNET_ATS_connectivity_suggest (struct GNUNET_ATS_ConnectivityHandle *ch, +struct GNUNET_ATS_ApplicationSuggestHandle * +GNUNET_ATS_application_suggest (struct GNUNET_ATS_ApplicationHandle *ch, const struct GNUNET_PeerIdentity *peer, enum GNUNET_MQ_PreferenceKind pk, struct GNUNET_BANDWIDTH_Value32NBO bw); @@ -92,8 +92,9 @@ GNUNET_ATS_connectivity_suggest (struct GNUNET_ATS_ConnectivityHandle *ch, * @param sh handle */ void -GNUNET_ATS_connectivity_suggest_cancel (struct GNUNET_ATS_ConnectivitySuggestHandle *sh); +GNUNET_ATS_application_suggest_cancel (struct GNUNET_ATS_ApplicationSuggestHandle *sh); /** @} */ /* end of group */ +#endif /* end of file gnunet_ats_application_service.h */ diff --git a/src/include/gnunet_ats_transport_service.h b/src/include/gnunet_ats_transport_service.h index b069f8b24..b55c6a374 100644 --- a/src/include/gnunet_ats_transport_service.h +++ b/src/include/gnunet_ats_transport_service.h @@ -108,12 +108,12 @@ struct GNUNET_ATS_Properties }; -/* ******************************** Scheduling API ***************************** */ +/* ******************************** Transport API ***************************** */ /** - * Handle to the ATS subsystem for bandwidth/transport scheduling information. + * Handle to the ATS subsystem for bandwidth/transport transport information. */ -struct GNUNET_ATS_SchedulingHandle; +struct GNUNET_ATS_TransportHandle; /** * Opaque session handle, to be defined by transport. Contents not known to ATS. @@ -154,7 +154,7 @@ typedef void /** - * Initialize the ATS scheduling subsystem. + * Initialize the ATS transport subsystem. * * @param cfg configuration to use * @param alloc_cb notification to call whenever the allocation changed @@ -163,45 +163,45 @@ typedef void * @param suggest_cb_cls closure for @a suggest_cb * @return ats context */ -struct GNUNET_ATS_SchedulingHandle * -GNUNET_ATS_scheduling_init (const struct GNUNET_CONFIGURATION_Handle *cfg, - GNUNET_ATS_AllocationCallback alloc_cb, - void *alloc_cb_cls); - GNUNET_ATS_SuggestionCallback suggest_cb, - void *suggest_cb_cls); +struct GNUNET_ATS_TransportHandle * +GNUNET_ATS_transport_init (const struct GNUNET_CONFIGURATION_Handle *cfg, + GNUNET_ATS_AllocationCallback alloc_cb, + void *alloc_cb_cls, + GNUNET_ATS_SuggestionCallback suggest_cb, + void *suggest_cb_cls); /** - * Client is done with ATS scheduling, release resources. + * Client is done with ATS transport, release resources. * - * @param sh handle to release + * @param ath handle to release */ void -GNUNET_ATS_scheduling_done (struct GNUNET_ATS_SchedulingHandle *sh); +GNUNET_ATS_transport_done (struct GNUNET_ATS_TransportHandle *ath); /** - * Handle used within ATS to track an address. + * Handle used within ATS to track an session. */ -struct GNUNET_ATS_AddressRecord; +struct GNUNET_ATS_SessionRecord; /** - * We have a new address ATS should know. Addresses have to be added with this + * We have a new session ATS should know. Sessiones have to be added with this * function before they can be: updated, set in use and destroyed * - * @param sh handle + * @param ath handle * @param pid peer we connected to - * @param address the address (human readable version), NULL if + * @param address the address (human readable version), + * @param session transport-internal handle for the session/queue, NULL if * the session is inbound-only - * @param session transport-internal handle for the address/queue - * @param prop performance data for the address - * @return handle to the address representation inside ATS, NULL - * on error (i.e. ATS knows this exact address already, or - * address is invalid) + * @param prop performance data for the session + * @return handle to the session representation inside ATS, NULL + * on error (i.e. ATS knows this exact session already, or + * session is invalid) */ -struct GNUNET_ATS_AddressRecord * -GNUNET_ATS_address_add (struct GNUNET_ATS_SchedulingHandle *sh, +struct GNUNET_ATS_SessionRecord * +GNUNET_ATS_session_add (struct GNUNET_ATS_TransportHandle *ath, const struct GNUNET_PeerIdentity *pid, const char *address, struct GNUNET_ATS_Session *session, @@ -209,14 +209,14 @@ GNUNET_ATS_address_add (struct GNUNET_ATS_SchedulingHandle *sh, /** - * We have updated performance statistics for a given address. Based + * We have updated performance statistics for a given session. Based * on the information provided, ATS may update bandwidth assignments. * - * @param ar address record to update information for - * @param prop performance data for the address + * @param ar session record to update information for + * @param prop performance data for the session */ void -GNUNET_ATS_address_update (struct GNUNET_ATS_AddressRecord *ar, +GNUNET_ATS_session_update (struct GNUNET_ATS_SessionRecord *ar, const struct GNUNET_ATS_Properties *prop); @@ -225,10 +225,10 @@ GNUNET_ATS_address_update (struct GNUNET_ATS_AddressRecord *ar, * allocate under the assumption that this @a ar is no * longer in use. * - * @param ar address record to drop + * @param ar session record to drop */ void -GNUNET_ATS_address_del (struct GNUNET_ATS_AddressRecord *ar); +GNUNET_ATS_session_del (struct GNUNET_ATS_SessionRecord *ar); #endif diff --git a/src/include/gnunet_crypto_lib.h b/src/include/gnunet_crypto_lib.h index 8a591fa09..1c69646f3 100644 --- a/src/include/gnunet_crypto_lib.h +++ b/src/include/gnunet_crypto_lib.h @@ -11,7 +11,7 @@ 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 . */ diff --git a/src/include/gnunet_protocols.h b/src/include/gnunet_protocols.h index 6f3b886d8..c82b11762 100644 --- a/src/include/gnunet_protocols.h +++ b/src/include/gnunet_protocols.h @@ -3095,9 +3095,57 @@ extern "C" #define GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_END 1252 +/* ************** NEW (NG) ATS Messages ************* */ + +/** + * Type of the 'struct ExpressPreferenceMessage' send by clients to ATS + * to establish bandwidth preference. + */ +#define GNUNET_MESSAGE_TYPE_ATS_SUGGEST 1400 + /** - * Next available: 1400 + * Type of the 'struct ExpressPreferenceMessage' send by clients to ATS + * to abandon bandwidth preference. */ +#define GNUNET_MESSAGE_TYPE_ATS_SUGGEST_CANCEL 1401 + + +/** + * Type of the 'struct SessionAddMessage' send by transport clients to ATS + * to ask ATS to allocate resources to a session. + */ +#define GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD 1402 + +/** + * Type of the 'struct SessionAddMessage' send by transport clients to ATS + * to inform ATS about a session where resources are consumed but allocation + * is impossible (unidirectional). + */ +#define GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD_INBOUND_ONLY 1403 + +/** + * Type of the 'struct SessionUpdateMessage' send by transport clients to ATS + * to inform ATS about property changes of a session. + */ +#define GNUNET_MESSAGE_TYPE_ATS_SESSION_UPDATE 1404 + +/** + * Type of the 'struct SessionDelMessage' send by transport clients to ATS + * to tell ATS that a session is no longer available. + */ +#define GNUNET_MESSAGE_TYPE_ATS_SESSION_DEL 1405 + +/** + * Type of the 'struct SessionAllocationMessage' send by ATS to the + * transport to tell it about resources to allocate to the session. + */ +#define GNUNET_MESSAGE_TYPE_ATS_SESSION_ALLOCATION 1406 + + +/** + * Next available: 1450 + */ + /** diff --git a/src/include/gnunet_transport_communication_service.h b/src/include/gnunet_transport_communication_service.h index 1d0add99f..8a0f0fcd5 100644 --- a/src/include/gnunet_transport_communication_service.h +++ b/src/include/gnunet_transport_communication_service.h @@ -42,7 +42,6 @@ extern "C" #endif #include "gnunet_util_lib.h" -#include "gnunet_ats_service.h" /** * Version number of the transport communication API. diff --git a/src/util/plugin.c b/src/util/plugin.c index b88dc4012..4a6e8577e 100644 --- a/src/util/plugin.c +++ b/src/util/plugin.c @@ -11,7 +11,7 @@ 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 . */ -- 2.25.1