X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Ftransport%2Fgnunet-service-transport_validation.c;h=314eefaec2fb7d25dd7a80ab0b8d9373ea7b4f55;hb=f0a8d26b26cda04518e3555373172f6b4940e489;hp=d80818369e621f195ce4a9386494d57e6d8719ef;hpb=0f08bc7705282c34fc0d14a6a88d87369ecb1614;p=oweals%2Fgnunet.git diff --git a/src/transport/gnunet-service-transport_validation.c b/src/transport/gnunet-service-transport_validation.c index d80818369..314eefaec 100644 --- a/src/transport/gnunet-service-transport_validation.c +++ b/src/transport/gnunet-service-transport_validation.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - (C) 2010,2011 Christian Grothoff (and other contributing authors) + Copyright (C) 2010-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 @@ -14,8 +14,8 @@ 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. + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ /** @@ -24,10 +24,13 @@ * @author Christian Grothoff */ #include "platform.h" -#include "gnunet-service-transport_validation.h" -#include "gnunet-service-transport_plugins.h" -#include "gnunet-service-transport_hello.h" +#include "gnunet-service-transport_ats.h" #include "gnunet-service-transport_blacklist.h" +#include "gnunet-service-transport_clients.h" +#include "gnunet-service-transport_hello.h" +#include "gnunet-service-transport_neighbours.h" +#include "gnunet-service-transport_plugins.h" +#include "gnunet-service-transport_validation.h" #include "gnunet-service-transport.h" #include "gnunet_hello_lib.h" #include "gnunet_ats_service.h" @@ -104,7 +107,7 @@ struct TransportPingMessage { /** - * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_PING + * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_PING */ struct GNUNET_MessageHeader header; @@ -135,7 +138,7 @@ struct TransportPongMessage { /** - * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_PONG + * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_PONG */ struct GNUNET_MessageHeader header; @@ -148,10 +151,10 @@ struct TransportPongMessage /** * Signature. */ - struct GNUNET_CRYPTO_EccSignature signature; + struct GNUNET_CRYPTO_EddsaSignature signature; /** - * GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN to confirm that this is a + * #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN to confirm that this is a * plausible address for the signing peer. */ struct GNUNET_CRYPTO_EccSignaturePurpose purpose; @@ -186,36 +189,31 @@ struct ValidationEntry */ struct GST_BlacklistCheck *bc; - /** - * Public key of the peer. - */ - struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded public_key; - - /** - * The identity of the peer. FIXME: duplicated (also in 'address') - */ - struct GNUNET_PeerIdentity pid; - /** * Cached PONG signature */ - struct GNUNET_CRYPTO_EccSignature pong_sig_cache; + struct GNUNET_CRYPTO_EddsaSignature pong_sig_cache; /** * ID of task that will clean up this entry if nothing happens. */ - GNUNET_SCHEDULER_TaskIdentifier timeout_task; + struct GNUNET_SCHEDULER_Task *timeout_task; /** * ID of task that will trigger address revalidation. */ - GNUNET_SCHEDULER_TaskIdentifier revalidation_task; + struct GNUNET_SCHEDULER_Task *revalidation_task; /** * At what time did we send the latest validation request (PING)? */ struct GNUNET_TIME_Absolute send_time; + /** + * At what time do we send the next validation request (PING)? + */ + struct GNUNET_TIME_Absolute next_validation; + /** * Until when is this address valid? * ZERO if it is not currently considered valid. @@ -242,13 +240,18 @@ struct ValidationEntry */ struct GNUNET_TIME_Relative latency; + /** + * Current state of this validation entry + */ + enum GNUNET_TRANSPORT_ValidationState state; + /** * Challenge number we used. */ uint32_t challenge; /** - * When passing the address in 'add_valid_peer_address', did we + * When passing the address in #add_valid_peer_address(), did we * copy the address to the HELLO yet? */ int copied; @@ -263,67 +266,39 @@ struct ValidationEntry */ int expecting_pong; - /* FIXME: DEBUGGING */ - int last_line_set_to_no; - int last_line_set_to_yes; -}; - - -/** - * Context of currently active requests to peerinfo - * for validation of HELLOs. - */ -struct CheckHelloValidatedContext -{ - - /** - * This is a doubly-linked list. - */ - struct CheckHelloValidatedContext *next; - /** - * This is a doubly-linked list. + * Is this address known to ATS as valid right now? */ - struct CheckHelloValidatedContext *prev; + int known_to_ats; /** - * Hello that we are validating. + * Which network type does our address belong to? */ - const struct GNUNET_HELLO_Message *hello; - + enum GNUNET_ATS_Network_Type network; }; -/** - * Head of linked list of HELLOs awaiting validation. - */ -static struct CheckHelloValidatedContext *chvc_head; - -/** - * Tail of linked list of HELLOs awaiting validation - */ -static struct CheckHelloValidatedContext *chvc_tail; - /** * Map of PeerIdentities to 'struct ValidationEntry*'s (addresses * of the given peer that we are currently validating, have validated * or are blocked from re-validation for a while). */ -static struct GNUNET_CONTAINER_MultiHashMap *validation_map; +static struct GNUNET_CONTAINER_MultiPeerMap *validation_map; /** * Context for peerinfo iteration. */ static struct GNUNET_PEERINFO_NotifyContext *pnc; - /** * Minimum delay between to validations */ static struct GNUNET_TIME_Relative validation_delay; /** - * Number of validations running + * Number of validations running; any PING that was not yet + * matched by a PONG and for which we have not yet hit the + * timeout is considered a running 'validation'. */ static unsigned int validations_running; @@ -337,6 +312,7 @@ static unsigned int validations_fast_start_threshold; */ static struct GNUNET_TIME_Absolute validation_next; + /** * Context for the validation entry match function. */ @@ -355,22 +331,40 @@ struct ValidationEntryMatchContext }; +/** + * Provide an update on the `validation_map` map size to statistics. + * This function should be called whenever the `validation_map` + * is changed. + */ +static void +publish_ve_stat_update () +{ + GNUNET_STATISTICS_set (GST_stats, + gettext_noop ("# Addresses in validation map"), + GNUNET_CONTAINER_multipeermap_size (validation_map), + GNUNET_NO); +} + + /** * Iterate over validation entries until a matching one is found. * - * @param cls the 'struct ValidationEntryMatchContext' + * @param cls the `struct ValidationEntryMatchContext *` * @param key peer identity (unused) - * @param value a 'struct ValidationEntry' to match - * @return GNUNET_YES if the entry does not match, - * GNUNET_NO if the entry does match + * @param value a `struct ValidationEntry *` to match + * @return #GNUNET_YES if the entry does not match, + * #GNUNET_NO if the entry does match */ static int -validation_entry_match (void *cls, const struct GNUNET_HashCode * key, void *value) +validation_entry_match (void *cls, + const struct GNUNET_PeerIdentity *key, + void *value) { struct ValidationEntryMatchContext *vemc = cls; struct ValidationEntry *ve = value; - if (0 == GNUNET_HELLO_address_cmp (ve->address, vemc->address)) + if (0 == GNUNET_HELLO_address_cmp (ve->address, + vemc->address)) { vemc->ve = ve; return GNUNET_NO; @@ -379,45 +373,83 @@ validation_entry_match (void *cls, const struct GNUNET_HashCode * key, void *val } +/** + * A validation entry changed. Update the state and notify + * monitors. + * + * @param ve validation entry that changed + * @param state new state + */ +static void +validation_entry_changed (struct ValidationEntry *ve, + enum GNUNET_TRANSPORT_ValidationState state) +{ + ve->state = state; + GST_clients_broadcast_validation_notification (&ve->address->peer, + ve->address, + ve->send_time, + ve->valid_until, + ve->next_validation, + state); +} + + /** * Iterate over validation entries and free them. * * @param cls (unused) * @param key peer identity (unused) - * @param value a 'struct ValidationEntry' to clean up - * @return GNUNET_YES (continue to iterate) + * @param value a `struct ValidationEntry *` to clean up + * @return #GNUNET_YES (continue to iterate) */ static int -cleanup_validation_entry (void *cls, const struct GNUNET_HashCode * key, void *value) +cleanup_validation_entry (void *cls, + const struct GNUNET_PeerIdentity *key, + void *value) { struct ValidationEntry *ve = value; + ve->next_validation = GNUNET_TIME_UNIT_ZERO_ABS; + ve->valid_until = GNUNET_TIME_UNIT_ZERO_ABS; + + /* Notify about deleted entry */ + validation_entry_changed (ve, + GNUNET_TRANSPORT_VS_REMOVE); + if (NULL != ve->bc) { GST_blacklist_test_cancel (ve->bc); ve->bc = NULL; } GNUNET_break (GNUNET_OK == - GNUNET_CONTAINER_multihashmap_remove (validation_map, - &ve->pid.hashPubKey, ve)); + GNUNET_CONTAINER_multipeermap_remove (validation_map, + &ve->address->peer, + ve)); + publish_ve_stat_update (); + if (GNUNET_YES == ve->known_to_ats) + { + GST_ats_expire_address (ve->address); + ve->known_to_ats = GNUNET_NO; + } GNUNET_HELLO_address_free (ve->address); - if (GNUNET_SCHEDULER_NO_TASK != ve->timeout_task) + if (NULL != ve->timeout_task) { GNUNET_SCHEDULER_cancel (ve->timeout_task); - ve->timeout_task = GNUNET_SCHEDULER_NO_TASK; + ve->timeout_task = NULL; } - if (GNUNET_SCHEDULER_NO_TASK != ve->revalidation_task) + if (NULL != ve->revalidation_task) { GNUNET_SCHEDULER_cancel (ve->revalidation_task); - ve->revalidation_task = GNUNET_SCHEDULER_NO_TASK; + ve->revalidation_task = NULL; } - if ((GNUNET_YES == ve->expecting_pong) && - (validations_running > 0)) + if ( (GNUNET_YES == ve->expecting_pong) && + (validations_running > 0) ) { - validations_running --; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Validation finished, %u validation processes running\n", - validations_running); + validations_running--; + GNUNET_STATISTICS_set (GST_stats, + gettext_noop ("# validations running"), + validations_running, + GNUNET_NO); } GNUNET_free (ve); return GNUNET_OK; @@ -428,7 +460,7 @@ cleanup_validation_entry (void *cls, const struct GNUNET_HashCode * key, void *v * Address validation cleanup task. Assesses if the record is no * longer valid and then possibly triggers its removal. * - * @param cls the 'struct ValidationEntry' + * @param cls the `struct ValidationEntry` * @param tc scheduler context (unused) */ static void @@ -439,20 +471,31 @@ timeout_hello_validation (void *cls, struct GNUNET_TIME_Absolute max; struct GNUNET_TIME_Relative left; - ve->timeout_task = GNUNET_SCHEDULER_NO_TASK; - max = GNUNET_TIME_absolute_max (ve->valid_until, ve->revalidation_block); + ve->timeout_task = NULL; + /* For valid addresses, we want to wait until the expire; + for addresses under PING validation, we want to wait + until we give up on the PING */ + max = GNUNET_TIME_absolute_max (ve->valid_until, + ve->revalidation_block); left = GNUNET_TIME_absolute_get_remaining (max); - if (left.rel_value > 0) + if (left.rel_value_us > 0) { - /* should wait a bit longer */ + /* We should wait a bit longer. This happens when + address lifetimes are extended due to successful + validations. */ ve->timeout_task = - GNUNET_SCHEDULER_add_delayed (left, &timeout_hello_validation, ve); + GNUNET_SCHEDULER_add_delayed (left, + &timeout_hello_validation, + ve); return; } GNUNET_STATISTICS_update (GST_stats, - gettext_noop ("# address records discarded"), 1, + gettext_noop ("# address records discarded (timeout)"), + 1, GNUNET_NO); - cleanup_validation_entry (NULL, &ve->pid.hashPubKey, ve); + cleanup_validation_entry (NULL, + &ve->address->peer, + ve); } @@ -460,12 +503,19 @@ timeout_hello_validation (void *cls, * Function called with the result from blacklisting. * Send a PING to the other peer if a communication is allowed. * - * @param cls our 'struct ValidationEntry' + * @param cls our `struct ValidationEntry` * @param pid identity of the other peer - * @param result GNUNET_OK if the connection is allowed, GNUNET_NO if not + * @param address_null address associated with the request, always NULL + * @param session_null session associated with the request, always NULL + * @param result #GNUNET_OK if the connection is allowed, + * #GNUNET_NO if not, + * #GNUNET_SYSERR if operation was aborted */ static void -transmit_ping_if_allowed (void *cls, const struct GNUNET_PeerIdentity *pid, +transmit_ping_if_allowed (void *cls, + const struct GNUNET_PeerIdentity *pid, + const struct GNUNET_HELLO_Address *address_null, + struct Session *session_null, int result) { struct ValidationEntry *ve = cls; @@ -477,29 +527,31 @@ transmit_ping_if_allowed (void *cls, const struct GNUNET_PeerIdentity *pid, size_t tsize; size_t slen; uint16_t hsize; + struct Session *session; ve->bc = NULL; - - if (GNUNET_NO == result) + if (GNUNET_OK != result) { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Blacklist denies to send PING to `%s' %s %s\n", - GNUNET_i2s (pid), GST_plugins_a2s (ve->address), ve->address->transport_name); - return; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Blacklist denies sending PING to `%s' `%s' `%s'\n", + GNUNET_i2s (pid), + GST_plugins_a2s (ve->address), + ve->address->transport_name); + GNUNET_STATISTICS_update (GST_stats, + gettext_noop ("# address records discarded (blacklist)"), + 1, + GNUNET_NO); + cleanup_validation_entry (NULL, + pid, + ve); + return; } - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitting plain PING to `%s' %s %s\n", - GNUNET_i2s (pid), GST_plugins_a2s (ve->address), ve->address->transport_name); - - next = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get(), validation_delay); - if (next.abs_value > validation_next.abs_value) - validation_next = next; /* We're going to send a PING so delay next validation */ - - slen = strlen (ve->address->transport_name) + 1; hello = GST_hello_get (); + GNUNET_assert (NULL != hello); + slen = strlen (ve->address->transport_name) + 1; hsize = ntohs (hello->size); - tsize = - sizeof (struct TransportPingMessage) + ve->address->address_length + - slen + hsize; + tsize = sizeof (struct TransportPingMessage) + + ve->address->address_length + slen + hsize; ping.header.size = htons (sizeof (struct TransportPingMessage) + @@ -510,64 +562,86 @@ transmit_ping_if_allowed (void *cls, const struct GNUNET_PeerIdentity *pid, if (tsize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ - ("Not transmitting `%s' with `%s', message too big (%u bytes!). This should not happen.\n"), - "HELLO", "PING", (unsigned int) tsize); - /* message too big (!?), get rid of HELLO */ + GNUNET_break (0); hsize = 0; tsize = sizeof (struct TransportPingMessage) + ve->address->address_length + slen + hsize; } { - char message_buf[tsize]; - - /* build message with structure: - * [HELLO][TransportPingMessage][Transport name][Address] */ - memcpy (message_buf, hello, hsize); - memcpy (&message_buf[hsize], &ping, sizeof (struct TransportPingMessage)); + char message_buf[tsize] GNUNET_ALIGN; + + memcpy (message_buf, + hello, + hsize); + memcpy (&message_buf[hsize], + &ping, + sizeof (struct TransportPingMessage)); memcpy (&message_buf[sizeof (struct TransportPingMessage) + hsize], - ve->address->transport_name, slen); + ve->address->transport_name, + slen); memcpy (&message_buf[sizeof (struct TransportPingMessage) + slen + hsize], - ve->address->address, ve->address->address_length); + ve->address->address, + ve->address->address_length); papi = GST_plugins_find (ve->address->transport_name); - if (papi == NULL) - ret = -1; - else + GNUNET_assert (NULL != papi); + session = papi->get_session (papi->cls, + ve->address); + if (NULL == session) { - GNUNET_assert (papi->send != NULL); - GNUNET_assert (papi->get_session != NULL); - struct Session * session = papi->get_session(papi->cls, ve->address); - - if (session != NULL) - { - ret = papi->send (papi->cls, session, - message_buf, tsize, - PING_PRIORITY, ACCEPTABLE_PING_DELAY, - NULL, NULL); - } - else - { - /* Could not get a valid session */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Could not get a valid session for `%s' %s\n", - GNUNET_i2s (pid), GST_plugins_a2s (ve->address)); - ret = -1; - } + /* Could not get a valid session */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Failed to get session to send PING to `%s' at `%s'\n", + GNUNET_i2s (pid), + GST_plugins_a2s (ve->address)); + return; } - } - if (-1 != ret) - { + + ret = papi->send (papi->cls, session, + message_buf, tsize, + PING_PRIORITY, + ACCEPTABLE_PING_DELAY, + NULL, NULL); + if (-1 == ret) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to send PING to `%s' at `%s'\n", + GNUNET_i2s (pid), + GST_plugins_a2s (ve->address)); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmitted plain PING to `%s' `%s' `%s'\n", + GNUNET_i2s (pid), + GST_plugins_a2s (ve->address), + ve->address->transport_name); + ve->network = papi->get_network (papi->cls, + session); + GNUNET_break (GNUNET_ATS_NET_UNSPECIFIED != ve->network); + GST_neighbours_notify_data_sent (ve->address, + session, + tsize); + next = GNUNET_TIME_relative_to_absolute (validation_delay); + validation_next = GNUNET_TIME_absolute_max (next, + validation_next); ve->send_time = GNUNET_TIME_absolute_get (); GNUNET_STATISTICS_update (GST_stats, - gettext_noop - ("# PING without HELLO messages sent"), 1, + gettext_noop ("# PINGs for address validation sent"), + 1, GNUNET_NO); ve->expecting_pong = GNUNET_YES; - validations_running ++; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Validation started, %u validation processes running\n", - validations_running); + validations_running++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Validation started, %u validation processes running\n", + validations_running); + GNUNET_STATISTICS_set (GST_stats, + gettext_noop ("# validations running"), + validations_running, + GNUNET_NO); + /* Notify about PING sent */ + validation_entry_changed (ve, + GNUNET_TRANSPORT_VS_UPDATE); + } } @@ -575,11 +649,12 @@ transmit_ping_if_allowed (void *cls, const struct GNUNET_PeerIdentity *pid, /** * Do address validation again to keep address valid. * - * @param cls the 'struct ValidationEntry' + * @param cls the `struct ValidationEntry` * @param tc scheduler context (unused) */ static void -revalidate_address (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +revalidate_address (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) { struct ValidationEntry *ve = cls; struct GNUNET_TIME_Relative canonical_delay; @@ -588,70 +663,96 @@ revalidate_address (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) struct GST_BlacklistCheck *bc; uint32_t rdelay; - ve->revalidation_task = GNUNET_SCHEDULER_NO_TASK; + ve->revalidation_task = NULL; delay = GNUNET_TIME_absolute_get_remaining (ve->revalidation_block); - /* How long until we can possibly permit the next PING? */ - canonical_delay = - (ve->in_use == - GNUNET_YES) ? CONNECTED_PING_FREQUENCY - : ((GNUNET_TIME_absolute_get_remaining (ve->valid_until).rel_value > - 0) ? VALIDATED_PING_FREQUENCY : UNVALIDATED_PING_KEEPALIVE); - if (delay.rel_value > canonical_delay.rel_value * 2) - { - /* situation changed, recalculate delay */ - delay = canonical_delay; - ve->revalidation_block = GNUNET_TIME_relative_to_absolute (delay); - } - if (delay.rel_value > 0) + /* Considering current connectivity situation, what is the maximum + block period permitted? */ + if (GNUNET_YES == ve->in_use) + canonical_delay = CONNECTED_PING_FREQUENCY; + else if (GNUNET_TIME_absolute_get_remaining (ve->valid_until).rel_value_us > 0) + canonical_delay = VALIDATED_PING_FREQUENCY; + else + canonical_delay = UNVALIDATED_PING_KEEPALIVE; + /* Use delay that is MIN of original delay and possibly adjusted + new maximum delay (which may be lower); the real delay + is originally randomized between "canonical_delay" and "2 * canonical_delay", + so continue to permit that window for the operation. */ + delay = GNUNET_TIME_relative_min (delay, + GNUNET_TIME_relative_multiply (canonical_delay, + 2)); + ve->revalidation_block = GNUNET_TIME_relative_to_absolute (delay); + if (delay.rel_value_us > 0) { /* should wait a bit longer */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Waiting for %s longer before (re)validating address `%s'\n", + GNUNET_STRINGS_relative_time_to_string (delay, + GNUNET_YES), + GST_plugins_a2s (ve->address)); ve->revalidation_task = - GNUNET_SCHEDULER_add_delayed (delay, &revalidate_address, ve); + GNUNET_SCHEDULER_add_delayed (delay, + &revalidate_address, ve); + ve->next_validation = GNUNET_TIME_relative_to_absolute (delay); return; } - blocked_for = GNUNET_TIME_absolute_get_remaining(validation_next); - if ((validations_running > validations_fast_start_threshold) && - (blocked_for.rel_value > 0)) + /* check if globally we have too many active validations at a + too high rate, if so, delay ours */ + blocked_for = GNUNET_TIME_absolute_get_remaining (validation_next); + if ( (validations_running > validations_fast_start_threshold) && + (blocked_for.rel_value_us > 0) ) { /* Validations are blocked, have to wait for blocked_for time */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Validations blocked for another %s, delaying validating address `%s'\n", + GNUNET_STRINGS_relative_time_to_string (blocked_for, + GNUNET_YES), + GST_plugins_a2s (ve->address)); + GNUNET_STATISTICS_update (GST_stats, + gettext_noop ("# validations delayed by global throttle"), + 1, + GNUNET_NO); ve->revalidation_task = - GNUNET_SCHEDULER_add_delayed (blocked_for, &revalidate_address, ve); + GNUNET_SCHEDULER_add_delayed (blocked_for, + &revalidate_address, + ve); + ve->next_validation = GNUNET_TIME_relative_to_absolute (blocked_for); return; } - ve->revalidation_block = GNUNET_TIME_relative_to_absolute (canonical_delay); + /* We are good to go; remember to not go again for `canonical_delay` time; + add up to `canonical_delay` to randomize start time */ + ve->revalidation_block = GNUNET_TIME_relative_to_absolute (canonical_delay); /* schedule next PINGing with some extra random delay to avoid synchronous re-validations */ rdelay = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, - canonical_delay.rel_value); + canonical_delay.rel_value_us); - /* Debug code for mantis 0002726*/ - if (GNUNET_TIME_UNIT_FOREVER_REL.rel_value == - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, rdelay).rel_value) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Revalidation interval for peer `%s' for is FOREVER (debug: rdelay: %llu, canonical delay %llu)\n", - GNUNET_i2s (&ve->pid), - (unsigned long long) delay.rel_value, - (unsigned long long) canonical_delay.rel_value); - delay = canonical_delay; - } - else - { - delay = GNUNET_TIME_relative_add (canonical_delay, - GNUNET_TIME_relative_multiply - (GNUNET_TIME_UNIT_MILLISECONDS, rdelay)); - } - /* End debug code for mantis 0002726*/ + delay = GNUNET_TIME_relative_add (canonical_delay, + GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MICROSECONDS, + rdelay)); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Validating now, next scheduled for %s, now validating address `%s'\n", + GNUNET_STRINGS_relative_time_to_string (blocked_for, + GNUNET_YES), + GST_plugins_a2s (ve->address)); ve->revalidation_task = - GNUNET_SCHEDULER_add_delayed (delay, &revalidate_address, ve); + GNUNET_SCHEDULER_add_delayed (delay, + &revalidate_address, + ve); + ve->next_validation = GNUNET_TIME_relative_to_absolute (delay); /* start PINGing by checking blacklist */ GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# address revalidations started"), 1, GNUNET_NO); - bc = GST_blacklist_test_allowed (&ve->pid, ve->address->transport_name, - &transmit_ping_if_allowed, ve); + bc = GST_blacklist_test_allowed (&ve->address->peer, + ve->address->transport_name, + &transmit_ping_if_allowed, + ve, + NULL, + NULL); if (NULL != bc) ve->bc = bc; /* only set 'bc' if 'transmit_ping_if_allowed' was not already * called... */ @@ -663,46 +764,42 @@ revalidate_address (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) * the given address and transport. If none exists, create one (but * without starting any validation). * - * @param public_key public key of the peer, NULL for unknown * @param address address to find * @return validation entry matching the given specifications, NULL * if we don't have an existing entry and no public key was given */ static struct ValidationEntry * -find_validation_entry (const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded - *public_key, const struct GNUNET_HELLO_Address *address) +find_validation_entry (const struct GNUNET_HELLO_Address *address) { struct ValidationEntryMatchContext vemc; struct ValidationEntry *ve; vemc.ve = NULL; vemc.address = address; - GNUNET_CONTAINER_multihashmap_get_multiple (validation_map, - &address->peer.hashPubKey, + GNUNET_CONTAINER_multipeermap_get_multiple (validation_map, + &address->peer, &validation_entry_match, &vemc); if (NULL != (ve = vemc.ve)) return ve; - if (public_key == NULL) - return NULL; - ve = GNUNET_malloc (sizeof (struct ValidationEntry)); + ve = GNUNET_new (struct ValidationEntry); ve->in_use = GNUNET_SYSERR; /* not defined */ - ve->last_line_set_to_no = 0; - ve->last_line_set_to_yes = 0; ve->address = GNUNET_HELLO_address_copy (address); - ve->public_key = *public_key; - ve->pid = address->peer; - ve->pong_sig_valid_until = GNUNET_TIME_absolute_get_zero_(); - memset (&ve->pong_sig_cache, '\0', sizeof (struct GNUNET_CRYPTO_EccSignature)); + ve->pong_sig_valid_until = GNUNET_TIME_UNIT_ZERO_ABS; + memset (&ve->pong_sig_cache, '\0', sizeof (struct GNUNET_CRYPTO_EddsaSignature)); ve->latency = GNUNET_TIME_UNIT_FOREVER_REL; ve->challenge = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX); ve->timeout_task = GNUNET_SCHEDULER_add_delayed (UNVALIDATED_PING_KEEPALIVE, - &timeout_hello_validation, ve); - GNUNET_CONTAINER_multihashmap_put (validation_map, &address->peer.hashPubKey, + &timeout_hello_validation, + ve); + GNUNET_CONTAINER_multipeermap_put (validation_map, + &address->peer, ve, GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); - ve->expecting_pong = GNUNET_NO; + publish_ve_stat_update (); + validation_entry_changed (ve, + GNUNET_TRANSPORT_VS_NEW); return ve; } @@ -714,37 +811,55 @@ find_validation_entry (const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded * @param cls original HELLO message * @param address the address * @param expiration expiration time - * @return GNUNET_OK (keep the address) + * @return #GNUNET_OK (keep the address), could return + * #GNUNET_NO (delete address, but this is ignored); + * #GNUNET_SYSERR would abort iteration (but we always iterate all) */ static int -add_valid_address (void *cls, const struct GNUNET_HELLO_Address *address, +add_valid_address (void *cls, + const struct GNUNET_HELLO_Address *address, struct GNUNET_TIME_Absolute expiration) { const struct GNUNET_HELLO_Message *hello = cls; struct ValidationEntry *ve; struct GNUNET_PeerIdentity pid; - struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded public_key; + struct GNUNET_ATS_Properties prop; - if (GNUNET_TIME_absolute_get_remaining (expiration).rel_value == 0) + if (0 == GNUNET_TIME_absolute_get_remaining (expiration).rel_value_us) return GNUNET_OK; /* expired */ - if ((GNUNET_OK != GNUNET_HELLO_get_id (hello, &pid)) || - (GNUNET_OK != GNUNET_HELLO_get_key (hello, &public_key))) + if (GNUNET_OK != GNUNET_HELLO_get_id (hello, &pid)) { GNUNET_break (0); return GNUNET_OK; /* invalid HELLO !? */ } - if (0 == memcmp (&GST_my_identity, &pid, sizeof (struct GNUNET_PeerIdentity))) + if (NULL == GST_plugins_find (address->transport_name)) { - /* Peerinfo returned own identity, skip validation */ + /* might have been valid in the past, but we don't have that + plugin loaded right now */ return GNUNET_OK; } - ve = find_validation_entry (&public_key, address); - ve->valid_until = GNUNET_TIME_absolute_max (ve->valid_until, expiration); - - if (GNUNET_SCHEDULER_NO_TASK == ve->revalidation_task) + ve = find_validation_entry (address); + ve->valid_until = GNUNET_TIME_absolute_max (ve->valid_until, + expiration); + if (NULL == ve->revalidation_task) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting revalidations for valid address `%s'\n", + GST_plugins_a2s (ve->address)); + ve->next_validation = GNUNET_TIME_absolute_get(); ve->revalidation_task = GNUNET_SCHEDULER_add_now (&revalidate_address, ve); - GNUNET_ATS_address_add (GST_ats, address, NULL, NULL, 0); + } + validation_entry_changed (ve, + GNUNET_TRANSPORT_VS_UPDATE); + memset (&prop, 0, sizeof (prop)); + prop.scope = ve->network; + prop.delay = GNUNET_TIME_relative_divide (ve->latency, 2); + if (GNUNET_YES != ve->known_to_ats) + { + ve->known_to_ats = GNUNET_YES; + GST_ats_add_address (address, &prop); + } return GNUNET_OK; } @@ -752,19 +867,31 @@ add_valid_address (void *cls, const struct GNUNET_HELLO_Address *address, /** * Function called for any HELLO known to PEERINFO. * - * @param cls unused - * @param peer id of the peer, NULL for last call + * @param cls unused (NULL) + * @param peer id of the peer, NULL for last call (during iteration, + * as we are monitoring, this should never happen) * @param hello hello message for the peer (can be NULL) * @param err_msg error message */ static void -process_peerinfo_hello (void *cls, const struct GNUNET_PeerIdentity *peer, +process_peerinfo_hello (void *cls, + const struct GNUNET_PeerIdentity *peer, const struct GNUNET_HELLO_Message *hello, const char *err_msg) { GNUNET_assert (NULL != peer); if (NULL == hello) return; + if (0 == memcmp (&GST_my_identity, + peer, + sizeof (struct GNUNET_PeerIdentity))) + { + /* Peerinfo returned own identity, skip validation */ + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Handling HELLO for peer `%s'\n", + GNUNET_i2s (peer)); GNUNET_assert (NULL == GNUNET_HELLO_iterate_addresses (hello, GNUNET_NO, &add_valid_address, @@ -780,27 +907,35 @@ process_peerinfo_hello (void *cls, const struct GNUNET_PeerIdentity *peer, void GST_validation_start (unsigned int max_fds) { - /** - * Initialization for validation throttling - * - * We have a maximum number max_fds of connections we can use for validation - * We monitor the number of validations in parallel and start to throttle it - * when doing to many validations in parallel: - * if (running validations < (max_fds / 2)) - * - "fast start": run validation immediately - * - have delay of (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value) / (max_fds / 2) - * (300 sec / ~150 == ~2 sec.) between two validations - */ - - validation_next = GNUNET_TIME_absolute_get(); - validation_delay.rel_value = (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value) / (max_fds / 2); - validations_fast_start_threshold = (max_fds / 2); - validations_running = 0; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Validation uses a fast start threshold of %u connections and a delay between of %u ms\n ", - validations_fast_start_threshold, validation_delay.rel_value); - validation_map = GNUNET_CONTAINER_multihashmap_create (VALIDATION_MAP_SIZE, + /** + * Initialization for validation throttling + * + * We have a maximum number max_fds of connections we can use for validation + * We monitor the number of validations in parallel and start to throttle it + * when doing to many validations in parallel: + * if (running validations < (max_fds / 2)) + * - "fast start": run validation immediately + * - have delay of (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value_us) / (max_fds / 2) + * (300 sec / ~150 == ~2 sec.) between two validations + */ + + validation_next = GNUNET_TIME_absolute_get(); + validation_delay.rel_value_us = (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value_us) / (max_fds / 2); + validations_fast_start_threshold = (max_fds / 2); + validations_running = 0; + GNUNET_STATISTICS_set (GST_stats, + gettext_noop ("# validations running"), + validations_running, + GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Validation uses a fast start threshold of %u connections and a delay of %s\n", + validations_fast_start_threshold, + GNUNET_STRINGS_relative_time_to_string (validation_delay, + GNUNET_YES)); + validation_map = GNUNET_CONTAINER_multipeermap_create (VALIDATION_MAP_SIZE, GNUNET_NO); - pnc = GNUNET_PEERINFO_notify (GST_cfg, GNUNET_YES, &process_peerinfo_hello, NULL); + pnc = GNUNET_PEERINFO_notify (GST_cfg, GNUNET_YES, + &process_peerinfo_hello, NULL); } @@ -810,17 +945,11 @@ GST_validation_start (unsigned int max_fds) void GST_validation_stop () { - struct CheckHelloValidatedContext *chvc; - - GNUNET_CONTAINER_multihashmap_iterate (validation_map, - &cleanup_validation_entry, NULL); - GNUNET_CONTAINER_multihashmap_destroy (validation_map); + GNUNET_CONTAINER_multipeermap_iterate (validation_map, + &cleanup_validation_entry, + NULL); + GNUNET_CONTAINER_multipeermap_destroy (validation_map); validation_map = NULL; - while (NULL != (chvc = chvc_head)) - { - GNUNET_CONTAINER_DLL_remove (chvc_head, chvc_tail, chvc); - GNUNET_free (chvc); - } GNUNET_PEERINFO_notify_cancel (pnc); } @@ -829,7 +958,6 @@ GST_validation_stop () * Send the given PONG to the given address. * * @param cls the PONG message - * @param public_key public key for the peer, never NULL * @param valid_until is ZERO if we never validated the address, * otherwise a time up to when we consider it (or was) valid * @param validation_block is FOREVER if the address is for an unsupported plugin (from PEERINFO) @@ -839,32 +967,40 @@ GST_validation_stop () */ static void multicast_pong (void *cls, - const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded - *public_key, struct GNUNET_TIME_Absolute valid_until, + struct GNUNET_TIME_Absolute valid_until, struct GNUNET_TIME_Absolute validation_block, const struct GNUNET_HELLO_Address *address) { struct TransportPongMessage *pong = cls; struct GNUNET_TRANSPORT_PluginFunctions *papi; + struct Session *session; papi = GST_plugins_find (address->transport_name); - if (papi == NULL) + if (NULL == papi) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Plugin %s not supported, cannot send PONG\n", + address->transport_name); return; - - GNUNET_assert (papi->send != NULL); - GNUNET_assert (papi->get_session != NULL); - - struct Session * session = papi->get_session(papi->cls, address); - if (session == NULL) + } + GNUNET_assert (NULL != papi->send); + GNUNET_assert (NULL != papi->get_session); + session = papi->get_session(papi->cls, address); + if (NULL == session) { GNUNET_break (0); return; } - + GST_ats_new_session (address, session); papi->send (papi->cls, session, - (const char *) pong, ntohs (pong->header.size), - PONG_PRIORITY, ACCEPTABLE_PING_DELAY, + (const char *) pong, + ntohs (pong->header.size), + PONG_PRIORITY, + ACCEPTABLE_PING_DELAY, NULL, NULL); + GST_neighbours_notify_data_sent (address, + session, + pong->header.size); } @@ -875,8 +1011,9 @@ multicast_pong (void *cls, * @param hdr the PING * @param sender_address the sender address as we got it * @param session session we got the PING from + * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error */ -void +int GST_validation_handle_ping (const struct GNUNET_PeerIdentity *sender, const struct GNUNET_MessageHeader *hdr, const struct GNUNET_HELLO_Address *sender_address, @@ -885,20 +1022,21 @@ GST_validation_handle_ping (const struct GNUNET_PeerIdentity *sender, const struct TransportPingMessage *ping; struct TransportPongMessage *pong; struct GNUNET_TRANSPORT_PluginFunctions *papi; - struct GNUNET_CRYPTO_EccSignature *sig_cache; + struct GNUNET_CRYPTO_EddsaSignature *sig_cache; struct GNUNET_TIME_Absolute *sig_cache_exp; const char *addr; const char *addrend; - size_t alen; - size_t slen; + char *plugin_name; + char *pos; + size_t len_address; + size_t len_plugin; ssize_t ret; - int buggy = GNUNET_NO; struct GNUNET_HELLO_Address address; if (ntohs (hdr->size) < sizeof (struct TransportPingMessage)) { GNUNET_break_op (0); - return; + return GNUNET_SYSERR; } ping = (const struct TransportPingMessage *) hdr; if (0 != @@ -909,59 +1047,103 @@ GST_validation_handle_ping (const struct GNUNET_PeerIdentity *sender, gettext_noop ("# PING message for different peer received"), 1, GNUNET_NO); - return; + return GNUNET_SYSERR; } GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# PING messages received"), 1, GNUNET_NO); addr = (const char *) &ping[1]; - alen = ntohs (hdr->size) - sizeof (struct TransportPingMessage); + len_address = ntohs (hdr->size) - sizeof (struct TransportPingMessage); /* peer wants to confirm that this is one of our addresses, this is what is * used for address validation */ sig_cache = NULL; sig_cache_exp = NULL; - - if (alen > 0) + papi = NULL; + if (len_address > 0) { - addrend = memchr (addr, '\0', alen); + addrend = memchr (addr, '\0', len_address); if (NULL == addrend) { GNUNET_break_op (0); - return; + return GNUNET_SYSERR; } addrend++; - slen = strlen (addr) + 1; - alen -= slen; + len_plugin = strlen (addr) + 1; + len_address -= len_plugin; + address.local_info = GNUNET_HELLO_ADDRESS_INFO_NONE; address.address = addrend; - address.address_length = alen; + address.address_length = len_address; address.transport_name = addr; address.peer = GST_my_identity; + if (NULL == address.transport_name) + { + GNUNET_break (0); + } + + if (0 != strstr (address.transport_name, "_client")) + { + plugin_name = GNUNET_strdup (address.transport_name); + pos = strstr (plugin_name, "_client"); + GNUNET_assert (NULL != pos); + GNUNET_snprintf (pos, strlen ("_server") + 1, "%s", "_server"); + } + else + plugin_name = GNUNET_strdup (address.transport_name); - if (GNUNET_YES != GST_hello_test_address (&address, &sig_cache, &sig_cache_exp)) + if (NULL == (papi = GST_plugins_find (plugin_name))) + { + /* we don't have the plugin for this address */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Plugin `%s' not available, cannot confirm having this address\n"), + plugin_name); + GNUNET_free (plugin_name); + return GNUNET_SYSERR; + } + GNUNET_free (plugin_name); + if (GNUNET_OK != + papi->check_address (papi->cls, + addrend, + len_address)) { - if (GNUNET_NO == buggy) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Not confirming PING from peer `%s' with address `%s' since I cannot confirm having this address.\n", - GNUNET_i2s (sender), - GST_plugins_a2s (&address)); - return; - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - _("Received a PING message with validation bug from `%s'\n"), - GNUNET_i2s (sender)); - } + GNUNET_STATISTICS_update (GST_stats, + gettext_noop + ("# failed address checks during validation"), 1, + GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Address `%s' is not one of my addresses, not confirming PING\n"), + GST_plugins_a2s (&address)); + return GNUNET_SYSERR; + } + else + { + GNUNET_STATISTICS_update (GST_stats, + gettext_noop + ("# successful address checks during validation"), 1, + GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Address `%s' is one of my addresses, confirming PING\n", + GST_plugins_a2s (&address)); + } + + if (GNUNET_YES != + GST_hello_test_address (&address, + &sig_cache, + &sig_cache_exp)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Not confirming PING from peer `%s' with address `%s' since I cannot confirm having this address.\n"), + GNUNET_i2s (sender), + GST_plugins_a2s (&address)); + return GNUNET_SYSERR; } } else { addrend = NULL; /* make gcc happy */ - slen = 0; - static struct GNUNET_CRYPTO_EccSignature no_address_signature; + len_plugin = 0; + static struct GNUNET_CRYPTO_EddsaSignature no_address_signature; static struct GNUNET_TIME_Absolute no_address_signature_expiration; sig_cache = &no_address_signature; @@ -970,64 +1152,68 @@ GST_validation_handle_ping (const struct GNUNET_PeerIdentity *sender, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "I am `%s', sending PONG to peer `%s'\n", - GNUNET_h2s (&GST_my_identity.hashPubKey), + GNUNET_i2s_full (&GST_my_identity), GNUNET_i2s (sender)); /* message with structure: * [TransportPongMessage][Transport name][Address] */ - pong = GNUNET_malloc (sizeof (struct TransportPongMessage) + alen + slen); + pong = GNUNET_malloc (sizeof (struct TransportPongMessage) + len_address + len_plugin); pong->header.size = - htons (sizeof (struct TransportPongMessage) + alen + slen); + htons (sizeof (struct TransportPongMessage) + len_address + len_plugin); pong->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PONG); pong->purpose.size = htonl (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) + sizeof (uint32_t) + sizeof (struct GNUNET_TIME_AbsoluteNBO) + - alen + slen); + len_address + len_plugin); pong->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN); memcpy (&pong->challenge, &ping->challenge, sizeof (ping->challenge)); - pong->addrlen = htonl (alen + slen); - memcpy (&pong[1], addr, slen); /* Copy transport plugin */ - if (alen > 0) + pong->addrlen = htonl (len_address + len_plugin); + memcpy (&pong[1], addr, len_plugin); /* Copy transport plugin */ + if (len_address > 0) { GNUNET_assert (NULL != addrend); - memcpy (&((char *) &pong[1])[slen], addrend, alen); + memcpy (&((char *) &pong[1])[len_plugin], addrend, len_address); } - if (GNUNET_TIME_absolute_get_remaining (*sig_cache_exp).rel_value < - PONG_SIGNATURE_LIFETIME.rel_value / 4) + if (GNUNET_TIME_absolute_get_remaining (*sig_cache_exp).rel_value_us < + PONG_SIGNATURE_LIFETIME.rel_value_us / 4) { /* create / update cached sig */ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating PONG signature to indicate ownership.\n"); *sig_cache_exp = GNUNET_TIME_relative_to_absolute (PONG_SIGNATURE_LIFETIME); pong->expiration = GNUNET_TIME_absolute_hton (*sig_cache_exp); - GNUNET_assert (GNUNET_OK == - GNUNET_CRYPTO_ecc_sign (GST_my_private_key, &pong->purpose, - sig_cache)); + if (GNUNET_OK != + GNUNET_CRYPTO_eddsa_sign (GST_my_private_key, &pong->purpose, + sig_cache)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Failed to create PONG signature for peer `%s'\n"), GNUNET_i2s (sender)); + } } else { pong->expiration = GNUNET_TIME_absolute_hton (*sig_cache_exp); } pong->signature = *sig_cache; - - GNUNET_assert (sender_address != NULL); + + GNUNET_assert (NULL != sender_address); /* first see if the session we got this PING from can be used to transmit * a response reliably */ - papi = GST_plugins_find (sender_address->transport_name); - if (papi == NULL) + if (NULL == papi) + { ret = -1; + } else { - GNUNET_assert (papi->send != NULL); - GNUNET_assert (papi->get_session != NULL); - - if (session == NULL) + GNUNET_assert (NULL != papi->send); + GNUNET_assert (NULL != papi->get_session); + if (NULL == session) { session = papi->get_session (papi->cls, sender_address); } - if (session == NULL) + if (NULL == session) { GNUNET_break (0); ret = -1; @@ -1035,12 +1221,17 @@ GST_validation_handle_ping (const struct GNUNET_PeerIdentity *sender, else { ret = papi->send (papi->cls, session, - (const char *) pong, ntohs (pong->header.size), + (const char *) pong, + ntohs (pong->header.size), PONG_PRIORITY, ACCEPTABLE_PING_DELAY, NULL, NULL); + if (-1 != ret) + GST_neighbours_notify_data_sent (sender_address, + session, + pong->header.size); } } - if (ret != -1) + if (-1 != ret) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitted PONG to `%s' via reliable mechanism\n", @@ -1051,7 +1242,7 @@ GST_validation_handle_ping (const struct GNUNET_PeerIdentity *sender, ("# PONGs unicast via reliable transport"), 1, GNUNET_NO); GNUNET_free (pong); - return; + return GNUNET_OK; } /* no reliable method found, try transmission via all known addresses */ @@ -1059,50 +1250,63 @@ GST_validation_handle_ping (const struct GNUNET_PeerIdentity *sender, gettext_noop ("# PONGs multicast to all available addresses"), 1, GNUNET_NO); - GST_validation_get_addresses (sender, &multicast_pong, pong); + GST_validation_get_addresses (sender, + &multicast_pong, pong); GNUNET_free (pong); + return GNUNET_OK; } /** - * Context for the 'validate_address' function + * Validate an individual address. + * + * @param address address we should try to validate */ -struct ValidateAddressContext +void +GST_validation_handle_address (const struct GNUNET_HELLO_Address *address) { - /** - * Hash of the public key of the peer whose address is being validated. - */ - struct GNUNET_PeerIdentity pid; + struct GNUNET_TRANSPORT_PluginFunctions *papi; + struct ValidationEntry *ve; - /** - * Public key of the peer whose address is being validated. - */ - struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded public_key; -}; + papi = GST_plugins_find (address->transport_name); + if (NULL == papi) + { + /* This plugin is currently unvailable ... ignore */ + return; + } + ve = find_validation_entry (address); + if (NULL == ve->revalidation_task) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Validation process started for fresh address `%s' of %s\n", + GST_plugins_a2s (ve->address), + GNUNET_i2s (&ve->address->peer)); + ve->revalidation_task = GNUNET_SCHEDULER_add_now (&revalidate_address, ve); + } +} /** * Iterator callback to go over all addresses and try to validate them * (unless blocked or already validated). * - * @param cls pointer to a 'struct ValidateAddressContext' + * @param cls NULL * @param address the address * @param expiration expiration time - * @return GNUNET_OK (keep the address) + * @return #GNUNET_OK (keep the address) */ static int validate_address_iterator (void *cls, const struct GNUNET_HELLO_Address *address, struct GNUNET_TIME_Absolute expiration) { - const struct ValidateAddressContext *vac = cls; - struct ValidationEntry *ve; - - if (GNUNET_TIME_absolute_get_remaining (expiration).rel_value == 0) + if (0 == GNUNET_TIME_absolute_get_remaining (expiration).rel_value_us) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Skipping expired address from HELLO\n"); return GNUNET_OK; /* expired */ - ve = find_validation_entry (&vac->public_key, address); - if (GNUNET_SCHEDULER_NO_TASK == ve->revalidation_task) - ve->revalidation_task = GNUNET_SCHEDULER_add_now (&revalidate_address, ve); + } + GST_validation_handle_address (address); return GNUNET_OK; } @@ -1110,21 +1314,26 @@ validate_address_iterator (void *cls, /** * Add the validated peer address to the HELLO. * - * @param cls the 'struct ValidationEntry' with the validated address - * @param max space in buf + * @param cls the `struct ValidationEntry *` with the validated address + * @param max space in @a buf * @param buf where to add the address - * @return number of bytes written, 0 to signal the + * @return number of bytes written, #GNUNET_SYSERR to signal the * end of the iteration. */ -static size_t -add_valid_peer_address (void *cls, size_t max, void *buf) +static ssize_t +add_valid_peer_address (void *cls, + size_t max, + void *buf) { struct ValidationEntry *ve = cls; if (GNUNET_YES == ve->copied) - return 0; /* terminate */ + return GNUNET_SYSERR; /* Done */ ve->copied = GNUNET_YES; - return GNUNET_HELLO_add_address (ve->address, ve->valid_until, buf, max); + return GNUNET_HELLO_add_address (ve->address, + ve->valid_until, + buf, + max); } @@ -1134,8 +1343,9 @@ add_valid_peer_address (void *cls, size_t max, void *buf) * * @param sender peer sending the PONG * @param hdr the PONG + * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error */ -void +int GST_validation_handle_pong (const struct GNUNET_PeerIdentity *sender, const struct GNUNET_MessageHeader *hdr) { @@ -1154,7 +1364,7 @@ GST_validation_handle_pong (const struct GNUNET_PeerIdentity *sender, if (ntohs (hdr->size) < sizeof (struct TransportPongMessage)) { GNUNET_break_op (0); - return; + return GNUNET_SYSERR; } GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# PONG messages received"), 1, @@ -1170,104 +1380,162 @@ GST_validation_handle_pong (const struct GNUNET_PeerIdentity *sender, if (NULL == addr) { GNUNET_break_op (0); - return; + return GNUNET_SYSERR; } addr++; slen = strlen (tname) + 1; addrlen = size - slen; + + if (NULL == GST_plugins_find (tname)) + { + /* we got the PONG, but the transport plugin specified in it + is not supported by this peer, so this cannot be a good + PONG for us. */ + GNUNET_break_op (0); + return GNUNET_OK; + } + address.peer = *sender; address.address = addr; address.address_length = addrlen; address.transport_name = tname; - ve = find_validation_entry (NULL, &address); + address.local_info = GNUNET_HELLO_ADDRESS_INFO_NONE; + ve = find_validation_entry (&address); if ((NULL == ve) || (GNUNET_NO == ve->expecting_pong)) { GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# PONGs dropped, no matching pending validation"), 1, GNUNET_NO); - return; + return GNUNET_OK; } /* now check that PONG is well-formed */ - if (0 != memcmp (&ve->pid, sender, sizeof (struct GNUNET_PeerIdentity))) + if (0 != memcmp (&ve->address->peer, + sender, + sizeof (struct GNUNET_PeerIdentity))) { GNUNET_break_op (0); - return; + return GNUNET_SYSERR; } - if (GNUNET_TIME_absolute_get_remaining - (GNUNET_TIME_absolute_ntoh (pong->expiration)).rel_value == 0) + if (0 == + GNUNET_TIME_absolute_get_remaining + (GNUNET_TIME_absolute_ntoh (pong->expiration)).rel_value_us) { GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# PONGs dropped, signature expired"), 1, GNUNET_NO); - return; + return GNUNET_SYSERR; } sig_res = GNUNET_SYSERR; do_verify = GNUNET_YES; - if (0 != GNUNET_TIME_absolute_get_remaining(ve->pong_sig_valid_until).rel_value) + if (0 != GNUNET_TIME_absolute_get_remaining (ve->pong_sig_valid_until).rel_value_us) { - /* We have a cached and valid signature for this peer, - * try to compare instead of verify */ - if (0 == memcmp (&ve->pong_sig_cache, &pong->signature, sizeof (struct GNUNET_CRYPTO_EccSignature))) - { - /* signatures are identical, we can skip verification */ - sig_res = GNUNET_OK; - do_verify = GNUNET_NO; - } - else - { - sig_res = GNUNET_SYSERR; - /* signatures do not match, we have to verify */ - } + /* We have a cached and valid signature for this peer, + * try to compare instead of verify */ + if (0 == memcmp (&ve->pong_sig_cache, + &pong->signature, + sizeof (struct GNUNET_CRYPTO_EddsaSignature))) + { + /* signatures are identical, we can skip verification */ + sig_res = GNUNET_OK; + do_verify = GNUNET_NO; + } + else + { + sig_res = GNUNET_SYSERR; + /* signatures do not match, we have to verify */ + } } if (GNUNET_YES == do_verify) { - /* Do expensive verification */ - sig_res = GNUNET_CRYPTO_ecc_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN, - &pong->purpose, &pong->signature, - &ve->public_key); - if (sig_res == GNUNET_SYSERR) - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to verify: invalid signature on address %s:%s from peer `%s'\n", - tname, GST_plugins_a2s (ve->address),GNUNET_i2s (sender)); + /* Do expensive verification */ + sig_res = GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN, + &pong->purpose, + &pong->signature, + &ve->address->peer.public_key); + if (sig_res == GNUNET_SYSERR) + { + GNUNET_break_op (0); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to verify: invalid signature on address `%s':%s from peer `%s'\n", + tname, + GST_plugins_a2s (ve->address), + GNUNET_i2s (sender)); + } } - if (sig_res == GNUNET_SYSERR) - return; + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Address validated for peer `%s' with plugin `%s': `%s'\n", - GNUNET_i2s (sender), tname, GST_plugins_a2s (ve->address)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Validation process successful for peer `%s' with plugin `%s' address `%s'\n", + GNUNET_i2s (sender), + tname, + GST_plugins_a2s (ve->address)); + GNUNET_STATISTICS_update (GST_stats, + gettext_noop ("# validations succeeded"), + 1, + GNUNET_NO); /* validity achieved, remember it! */ ve->expecting_pong = GNUNET_NO; ve->valid_until = GNUNET_TIME_relative_to_absolute (HELLO_ADDRESS_EXPIRATION); ve->pong_sig_cache = pong->signature; - ve->pong_sig_valid_until = GNUNET_TIME_absolute_ntoh (pong->expiration); + ve->pong_sig_valid_until = GNUNET_TIME_absolute_ntoh (pong->expiration); ve->latency = GNUNET_TIME_absolute_get_duration (ve->send_time); { - struct GNUNET_ATS_Information ats; - ats.type = htonl (GNUNET_ATS_QUALITY_NET_DELAY); - ats.value = htonl ((uint32_t) ve->latency.rel_value); - GNUNET_ATS_address_add (GST_ats, ve->address, NULL, &ats, 1); + if (GNUNET_YES == ve->known_to_ats) + { + GST_ats_update_delay (ve->address, + GNUNET_TIME_relative_divide (ve->latency, 2)); + } + else + { + struct GNUNET_ATS_Properties prop; + + memset (&prop, 0, sizeof (prop)); + prop.scope = ve->network; + prop.delay = GNUNET_TIME_relative_divide (ve->latency, 2); + ve->known_to_ats = GNUNET_YES; + GST_ats_add_address (ve->address, &prop); + } } if (validations_running > 0) { - validations_running --; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Validation finished, %u validation processes running\n", - validations_running); + validations_running--; + GNUNET_STATISTICS_set (GST_stats, + gettext_noop ("# validations running"), + validations_running, + GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Validation finished, %u validation processes running\n", + validations_running); } else - GNUNET_break (0); + { + GNUNET_break (0); + } + + /* Notify about new validity */ + validation_entry_changed (ve, + GNUNET_TRANSPORT_VS_UPDATE); /* build HELLO to store in PEERINFO */ ve->copied = GNUNET_NO; - hello = GNUNET_HELLO_create (&ve->public_key, &add_valid_peer_address, ve, GNUNET_NO); - GNUNET_PEERINFO_add_peer (GST_peerinfo, hello, NULL, NULL); + hello = GNUNET_HELLO_create (&ve->address->peer.public_key, + &add_valid_peer_address, + ve, + GNUNET_NO); + GNUNET_PEERINFO_add_peer (GST_peerinfo, + hello, + NULL, + NULL); GNUNET_free (hello); + return GNUNET_OK; } @@ -1276,46 +1544,48 @@ GST_validation_handle_pong (const struct GNUNET_PeerIdentity *sender, * validation. * * @param hello the HELLO we received + * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error */ -void +int GST_validation_handle_hello (const struct GNUNET_MessageHeader *hello) { const struct GNUNET_HELLO_Message *hm = (const struct GNUNET_HELLO_Message *) hello; - struct ValidateAddressContext vac; - struct GNUNET_HELLO_Message *h; + struct GNUNET_PeerIdentity pid; int friend; friend = GNUNET_HELLO_is_friend_only (hm); - if (((GNUNET_YES != friend) && (GNUNET_NO != friend)) || - (GNUNET_OK != GNUNET_HELLO_get_id (hm, &vac.pid)) || - (GNUNET_OK != GNUNET_HELLO_get_key (hm, &vac.public_key))) + if ( ( (GNUNET_YES != friend) && + (GNUNET_NO != friend) ) || + (GNUNET_OK != GNUNET_HELLO_get_id (hm, &pid)) ) { /* malformed HELLO */ - GNUNET_break (0); - return; + GNUNET_break_op (0); + return GNUNET_SYSERR; } if (0 == - memcmp (&GST_my_identity, &vac.pid, sizeof (struct GNUNET_PeerIdentity))) - return; - /* Add peer identity without addresses to peerinfo service */ - h = GNUNET_HELLO_create (&vac.public_key, NULL, NULL, friend); - GNUNET_PEERINFO_add_peer (GST_peerinfo, h, NULL, NULL); - + memcmp (&GST_my_identity, + &pid, + sizeof (struct GNUNET_PeerIdentity))) + { + /* got our own HELLO, how boring */ + return GNUNET_OK; + } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - _("Adding `%s' without addresses for peer `%s'\n"), "HELLO", - GNUNET_i2s (&vac.pid)); - - GNUNET_free (h); + "Validation received HELLO message for peer `%s' with size %u, checking for new addresses\n", + GNUNET_i2s (&pid), + ntohs (hello->size)); GNUNET_assert (NULL == - GNUNET_HELLO_iterate_addresses (hm, GNUNET_NO, + GNUNET_HELLO_iterate_addresses (hm, + GNUNET_NO, &validate_address_iterator, - &vac)); + NULL)); + return GNUNET_OK; } /** - * Closure for 'iterate_addresses' + * Closure for #iterate_addresses(). */ struct IteratorContext { @@ -1325,7 +1595,7 @@ struct IteratorContext GST_ValidationAddressCallback cb; /** - * Closure for 'cb'. + * Closure for @e cb. */ void *cb_cls; @@ -1335,18 +1605,22 @@ struct IteratorContext /** * Call the callback in the closure for each validation entry. * - * @param cls the 'struct GST_ValidationIteratorContext' + * @param cls the `struct IteratorContext` * @param key the peer's identity - * @param value the 'struct ValidationEntry' - * @return GNUNET_OK (continue to iterate) + * @param value the `struct ValidationEntry` + * @return #GNUNET_OK (continue to iterate) */ static int -iterate_addresses (void *cls, const struct GNUNET_HashCode * key, void *value) +iterate_addresses (void *cls, + const struct GNUNET_PeerIdentity *key, + void *value) { struct IteratorContext *ic = cls; struct ValidationEntry *ve = value; - ic->cb (ic->cb_cls, &ve->public_key, ve->valid_until, ve->revalidation_block, + ic->cb (ic->cb_cls, + ve->valid_until, + ve->revalidation_block, ve->address); return GNUNET_OK; } @@ -1358,18 +1632,19 @@ iterate_addresses (void *cls, const struct GNUNET_HashCode * key, void *value) * * @param target peer information is requested for * @param cb function to call; will not be called after this function returns - * @param cb_cls closure for 'cb' + * @param cb_cls closure for @a cb */ void GST_validation_get_addresses (const struct GNUNET_PeerIdentity *target, - GST_ValidationAddressCallback cb, void *cb_cls) + GST_ValidationAddressCallback cb, + void *cb_cls) { struct IteratorContext ic; ic.cb = cb; ic.cb_cls = cb_cls; - GNUNET_CONTAINER_multihashmap_get_multiple (validation_map, - &target->hashPubKey, + GNUNET_CONTAINER_multipeermap_get_multiple (validation_map, + target, &iterate_addresses, &ic); } @@ -1379,95 +1654,113 @@ GST_validation_get_addresses (const struct GNUNET_PeerIdentity *target, * Based on this, the validation module will measure latency for the * address more or less often. * - * @param address the address - * @param session the session - * @param in_use GNUNET_YES if we are now using the address for a connection, - * GNUNET_NO if we are no longer using the address for a connection - * @param line line of caller just for DEBUGGING! + * @param address the address that we are now using (or not) + * @param in_use #GNUNET_YES if we are now using the address for a connection, + * #GNUNET_NO if we are no longer using the address for a connection */ void GST_validation_set_address_use (const struct GNUNET_HELLO_Address *address, - struct Session *session, - int in_use, - int line) + int in_use) { struct ValidationEntry *ve; - if (NULL != address) - ve = find_validation_entry (NULL, address); - else - ve = NULL; /* FIXME: lookup based on session... */ - if (NULL == ve) + if (GNUNET_HELLO_address_check_option (address, + GNUNET_HELLO_ADDRESS_INFO_INBOUND)) + return; /* ignore inbound for validation */ + if (NULL == GST_plugins_find (address->transport_name)) { - /* this can happen for inbound connections (sender_address_len == 0); */ + /* How can we use an address for which we don't have the plugin? */ + GNUNET_break (0); return; } - if (ve->in_use == in_use) - { - - if (GNUNET_YES == in_use) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Error setting address in use for peer `%s' `%s' to USED: set last time by %i, called now by %i\n", - GNUNET_i2s (&address->peer), GST_plugins_a2s (address), - ve->last_line_set_to_yes, line); - } - if (GNUNET_NO == in_use) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Error setting address in use for peer `%s' `%s' to NOT_USED: set last time by %i, called now by %i\n", - GNUNET_i2s (&address->peer), GST_plugins_a2s (address), - ve->last_line_set_to_no, line); - } - } - - if (GNUNET_YES == in_use) - { - ve->last_line_set_to_yes = line; - } - if (GNUNET_NO == in_use) + ve = find_validation_entry (address); + if (NULL == ve) { - ve->last_line_set_to_no = line; + GNUNET_break (0); + return; } - - GNUNET_break (ve->in_use != in_use); /* should be different... */ + if (in_use == ve->in_use) + return; ve->in_use = in_use; - if (in_use == GNUNET_YES) + if (GNUNET_YES == in_use) { /* from now on, higher frequeny, so reschedule now */ - GNUNET_SCHEDULER_cancel (ve->revalidation_task); - ve->revalidation_task = GNUNET_SCHEDULER_add_now (&revalidate_address, ve); + if (NULL != ve->revalidation_task) + GNUNET_SCHEDULER_cancel (ve->revalidation_task); + ve->revalidation_task = GNUNET_SCHEDULER_add_now (&revalidate_address, + ve); } } /** - * Query validation about the latest observed latency on a given - * address. + * Closure for the validation_entries_iterate function. + */ +struct ValidationIteratorContext +{ + /** + * Function to call on each validation entry + */ + GST_ValidationChangedCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; +}; + + +/** + * Function called on each entry in the validation map. + * Passes the information from the validation entry to + * the callback given in the closure. * - * @param sender peer - * @param address the address - * @param session session - * @return observed latency of the address, FOREVER if the address was - * never successfully validated + * @param cls the `struct ValidationIteratorContext` + * @param key peer this is about + * @param value the `struct ValidationEntry` + * @return #GNUNET_OK (continue to iterate) */ -struct GNUNET_TIME_Relative -GST_validation_get_address_latency (const struct GNUNET_PeerIdentity *sender, - const struct GNUNET_HELLO_Address *address, - struct Session *session) +static int +validation_entries_iterate (void *cls, + const struct GNUNET_PeerIdentity *key, + void *value) { - struct ValidationEntry *ve; + struct ValidationIteratorContext *ic = cls; + struct ValidationEntry *ve = value; - if (NULL == address) - { - GNUNET_break (0); // FIXME: support having latency only with session... - return GNUNET_TIME_UNIT_FOREVER_REL; - } - ve = find_validation_entry (NULL, address); - if (NULL == ve) - return GNUNET_TIME_UNIT_FOREVER_REL; - return ve->latency; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Notifying about validation entry for peer `%s' address `%s' \n", + GNUNET_i2s (&ve->address->peer), + GST_plugins_a2s (ve->address)); + ic->cb (ic->cb_cls, + ve->address, + ve->send_time, + ve->valid_until, + ve->next_validation, + ve->state); + return GNUNET_OK; } +/** + * Iterate over all iteration entries + * + * @param cb function to call + * @param cb_cls closure for @a cb + */ +void +GST_validation_iterate (GST_ValidationChangedCallback cb, + void *cb_cls) +{ + struct ValidationIteratorContext ic; + + if (NULL == validation_map) + return; /* can happen during shutdown */ + ic.cb = cb; + ic.cb_cls = cb_cls; + GNUNET_CONTAINER_multipeermap_iterate (validation_map, + &validation_entries_iterate, + &ic); +} + /* end of file gnunet-service-transport_validation.c */