/* This file is part of GNUnet. Copyright (C) 2011-2014 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 . SPDX-License-Identifier: AGPL3.0-or-later */ /** * @file ats/gnunet-service-ats_plugins.c * @brief ats service plugin management * @author Matthias Wachs * @author Christian Grothoff */ #include "platform.h" #include "gnunet_ats_plugin.h" #include "gnunet-service-ats_connectivity.h" #include "gnunet-service-ats_performance.h" #include "gnunet-service-ats_preferences.h" #include "gnunet-service-ats_plugins.h" #include "gnunet-service-ats_reservations.h" #include "gnunet-service-ats_scheduling.h" #include "gnunet-service-ats_normalization.h" /** * Solver handle. */ static struct GNUNET_ATS_SolverFunctions *sf; /** * Solver environment. */ static struct GNUNET_ATS_PluginEnvironment env; /** * Solver plugin name as string */ static char *plugin; /** * The preference changed for a peer, update solver. * * @param peer the peer * @param kind the ATS kind * @param pref_rel the new relative preference value */ void GAS_plugin_notify_preference_changed (const struct GNUNET_PeerIdentity *peer, enum GNUNET_ATS_PreferenceKind kind, double pref_rel) { sf->s_pref (sf->cls, peer, kind, pref_rel); } /** * The relative value for a property changed. * * @param address the peer for which a property changed */ void GAS_plugin_notify_property_changed (struct ATS_Address *address) { sf->s_address_update_property (sf->cls, address); } /** * Solver information callback * * @param cls the closure * @param op the operation * @param status operation status * @param add additional information */ static void solver_info_cb (void *cls, enum GAS_Solver_Operation op, enum GAS_Solver_Status status, enum GAS_Solver_Additional_Information add) { const char *add_info; switch (add) { case GAS_INFO_NONE: add_info = "GAS_INFO_NONE"; break; case GAS_INFO_FULL: add_info = "GAS_INFO_MLP_FULL"; break; case GAS_INFO_UPDATED: add_info = "GAS_INFO_MLP_UPDATED"; break; case GAS_INFO_PROP_ALL: add_info = "GAS_INFO_PROP_ALL"; break; case GAS_INFO_PROP_SINGLE: add_info = "GAS_INFO_PROP_SINGLE"; break; default: add_info = "INVALID"; break; } switch (op) { case GAS_OP_SOLVE_START: GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Solver notifies `%s' with result `%s' `%s'\n", "GAS_OP_SOLVE_START", (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL", add_info); return; case GAS_OP_SOLVE_STOP: GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Solver notifies `%s' with result `%s'\n", "GAS_OP_SOLVE_STOP", (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL"); return; case GAS_OP_SOLVE_SETUP_START: GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Solver notifies `%s' with result `%s'\n", "GAS_OP_SOLVE_SETUP_START", (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL"); return; case GAS_OP_SOLVE_SETUP_STOP: GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Solver notifies `%s' with result `%s'\n", "GAS_OP_SOLVE_SETUP_STOP", (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL"); return; case GAS_OP_SOLVE_MLP_LP_START: GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Solver notifies `%s' with result `%s'\n", "GAS_OP_SOLVE_LP_START", (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL"); return; case GAS_OP_SOLVE_MLP_LP_STOP: GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Solver notifies `%s' with result `%s'\n", "GAS_OP_SOLVE_LP_STOP", (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL"); return; case GAS_OP_SOLVE_MLP_MLP_START: GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Solver notifies `%s' with result `%s'\n", "GAS_OP_SOLVE_MLP_START", (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL"); return; case GAS_OP_SOLVE_MLP_MLP_STOP: GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Solver notifies `%s' with result `%s'\n", "GAS_OP_SOLVE_MLP_STOP", (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL"); return; case GAS_OP_SOLVE_UPDATE_NOTIFICATION_START: GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Solver notifies `%s' with result `%s'\n", "GAS_OP_SOLVE_UPDATE_NOTIFICATION_START", (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL"); return; case GAS_OP_SOLVE_UPDATE_NOTIFICATION_STOP: GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Solver notifies `%s' with result `%s'\n", "GAS_OP_SOLVE_UPDATE_NOTIFICATION_STOP", (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL"); return; default: GNUNET_break (0); break; } } /** * Callback for solver to notify about assignment changes * * @param cls NULL * @param address the address with changes */ static void bandwidth_changed_cb (void *cls, struct ATS_Address *address) { long long diff_out; long long diff_in; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Bandwidth assignment changed for peer %s to %u/%u\n", GNUNET_i2s (&address->peer), (unsigned int) address->assigned_bw_in, (unsigned int) address->assigned_bw_out); GAS_reservations_set_bandwidth (&address->peer, GNUNET_BANDWIDTH_value_init ( address->assigned_bw_in)); /* Notify performance clients about changes to address */ GAS_performance_notify_all_clients (&address->peer, address->plugin, address->addr, address->addr_len, address->active, &address->properties, address->local_address_info, GNUNET_BANDWIDTH_value_init ( address->assigned_bw_out), GNUNET_BANDWIDTH_value_init ( address->assigned_bw_in)); if ((0 == address->assigned_bw_in) && (0 == address->assigned_bw_out)) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Telling transport to disconnect peer `%s'\n", GNUNET_i2s (&address->peer)); /* Notify scheduling clients about suggestion */ GAS_scheduling_transmit_address_suggestion (&address->peer, address->session_id, GNUNET_BANDWIDTH_ZERO, GNUNET_BANDWIDTH_ZERO); return; } /* Do bandwidth stability check */ diff_out = llabs ((long long) address->assigned_bw_out - (long long) address->last_notified_bw_out); diff_in = llabs ((long long) address->assigned_bw_in - (long long) address->last_notified_bw_in); if ((diff_out < htonl (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__)) && (diff_in < htonl (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__))) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Bandwidth change too small, not notifying client\n"); return; } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending bandwidth update for peer `%s': %u/%u\n", GNUNET_i2s (&address->peer), address->assigned_bw_out, address->assigned_bw_out); /* *Notify scheduling clients about suggestion */ GAS_scheduling_transmit_address_suggestion (&address->peer, address->session_id, GNUNET_BANDWIDTH_value_init ( address->assigned_bw_out), GNUNET_BANDWIDTH_value_init ( address->assigned_bw_in)); address->last_notified_bw_out = address->assigned_bw_out; address->last_notified_bw_in = address->assigned_bw_in; } /** * Convert quota from text to numeric value. * * @param quota_str the value found in the configuration * @param direction direction of the quota * @param network network the quota applies to * @return numeric quota value to use */ static unsigned long long parse_quota (const char *quota_str, const char *direction, enum GNUNET_NetworkType network) { int res; unsigned long long ret; res = GNUNET_NO; if (0 == strcmp (quota_str, GNUNET_ATS_MaxBandwidthString)) { ret = GNUNET_ATS_MaxBandwidth; res = GNUNET_YES; } if ((GNUNET_NO == res) && (GNUNET_OK == GNUNET_STRINGS_fancy_size_to_bytes (quota_str, &ret))) res = GNUNET_YES; if ((GNUNET_NO == res) && (1 == sscanf (quota_str, "%llu", &ret))) res = GNUNET_YES; if (GNUNET_NO == res) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _ ( "Could not load %s quota for network `%s': `%s', assigning default bandwidth %llu\n"), direction, GNUNET_NT_to_string (network), quota_str, (unsigned long long) GNUNET_ATS_DefaultBandwidth); ret = GNUNET_ATS_DefaultBandwidth; } else { GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("%s quota configured for network `%s' is %llu\n"), direction, GNUNET_NT_to_string (network), ret); } return ret; } /** * Load quota value from the configuration @a cfg for the * given network @a type and @a direction. * * @param cfg configuration to parse * @param type network type to parse for * @param direction traffic direction to parse for * @return quota to apply */ static unsigned long long load_quota (const struct GNUNET_CONFIGURATION_Handle *cfg, enum GNUNET_NetworkType type, const char *direction) { char *entry; char *quota_str; unsigned long long ret; GNUNET_asprintf (&entry, "%s_QUOTA_%s", GNUNET_NT_to_string (type), direction); if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg, "ats", entry, "a_str)) { ret = parse_quota (quota_str, direction, type); GNUNET_free (quota_str); } else { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _ ( "No %s-quota configured for network `%s', assigning default bandwidth %llu\n"), direction, GNUNET_NT_to_string (type), (unsigned long long) GNUNET_ATS_DefaultBandwidth); ret = GNUNET_ATS_DefaultBandwidth; } GNUNET_free (entry); return ret; } /** * Load quotas for networks from configuration * * @param cfg configuration handle * @param out_dest where to write outbound quotas * @param in_dest where to write inbound quotas * @param dest_length length of inbound and outbound arrays * @return number of networks loaded */ static unsigned int load_quotas (const struct GNUNET_CONFIGURATION_Handle *cfg, unsigned long long *out_dest, unsigned long long *in_dest, int dest_length) { unsigned int c; for (c = 0; (c < GNUNET_NT_COUNT) && (c < dest_length); c++) { in_dest[c] = load_quota (cfg, c, "out"); out_dest[c] = load_quota (cfg, c, "in"); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Loaded quota for network `%s' (in/out): %llu %llu\n", GNUNET_NT_to_string (c), in_dest[c], out_dest[c]); } return c; } /** * Initialize plugins subsystem. * * @param cfg configuration to use * @return #GNUNET_OK on success, #GNUNET_SYSERR on error (failed to load * solver plugin) */ int GAS_plugin_init (const struct GNUNET_CONFIGURATION_Handle *cfg) { char *mode_str; /* Figure out configured solution method */ if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string (cfg, "ats", "MODE", &mode_str)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "No resource assignment method configured, using proportional approach\n"); mode_str = GNUNET_strdup ("proportional"); } env.cls = NULL; env.info_cb = &solver_info_cb; env.bandwidth_changed_cb = &bandwidth_changed_cb; env.get_preferences = &GAS_preference_get_by_peer; env.get_connectivity = &GAS_connectivity_has_peer; env.cfg = cfg; env.stats = GSA_stats; env.addresses = GSA_addresses; env.network_count = GNUNET_NT_COUNT; load_quotas (cfg, env.out_quota, env.in_quota, GNUNET_NT_COUNT); GNUNET_asprintf (&plugin, "libgnunet_plugin_ats_%s", mode_str); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Initializing solver `%s'\n", mode_str); GNUNET_free (mode_str); if (NULL == (sf = GNUNET_PLUGIN_load (plugin, &env))) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _ ("Failed to initialize solver `%s'!\n"), plugin); return GNUNET_SYSERR; } return GNUNET_OK; } /** * Shutdown address subsystem. */ void GAS_plugin_done () { GNUNET_PLUGIN_unload (plugin, sf); sf = NULL; GNUNET_free (plugin); plugin = NULL; } /** * Tell the solver that the given address can now be used * for talking to the respective peer. * * @param new_address the new address */ void GAS_plugin_new_address (struct ATS_Address *new_address) { sf->s_add (sf->cls, new_address, new_address->properties.scope); /* FIXME: remove 3rd arg here! */ } /** * Tell the solver that the given address is no longer valid * can cannot be used any longer. * * @param address address that was deleted */ void GAS_plugin_delete_address (struct ATS_Address *address) { sf->s_del (sf->cls, address); } /** * Tell the solver that the given client has expressed its * appreciation for the past performance of a given connection. * * @param application client providing the feedback * @param peer peer the feedback is about * @param scope timeframe the feedback applies to * @param kind performance property the feedback relates to * @param score_abs degree of the appreciation */ void GAS_plugin_notify_feedback (struct GNUNET_SERVICE_Client *application, const struct GNUNET_PeerIdentity *peer, const struct GNUNET_TIME_Relative scope, enum GNUNET_ATS_PreferenceKind kind, float score_abs) { sf->s_feedback (sf->cls, application, peer, scope, kind, score_abs); } /** * Stop instant solving, there are many state updates * happening in bulk right now. */ void GAS_plugin_solver_lock () { sf->s_bulk_start (sf->cls); } /** * Resume instant solving, we are done with the bulk state updates. */ void GAS_plugin_solver_unlock () { sf->s_bulk_stop (sf->cls); } /** * Notify the plugin that a request to connect to * a particular peer was given to us. * * @param pid identity of peer we now care about */ void GAS_plugin_request_connect_start (const struct GNUNET_PeerIdentity *pid) { sf->s_get (sf->cls, pid); } /** * Notify the plugin that a request to connect to * a particular peer was dropped. * * @param pid identity of peer we care now less about */ void GAS_plugin_request_connect_stop (const struct GNUNET_PeerIdentity *pid) { sf->s_get_stop (sf->cls, pid); } /* end of gnunet-service-ats_plugins.c */