From: Christian Grothoff Date: Sat, 23 Jul 2011 13:23:40 +0000 (+0000) Subject: nse hacking X-Git-Tag: initial-import-from-subversion-38251~17693 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=f53e991a59f4befa47b0ce90d8b68318dcd23864;p=oweals%2Fgnunet.git nse hacking --- diff --git a/configure.ac b/configure.ac index 8f7bbac4f..fe414b31e 100644 --- a/configure.ac +++ b/configure.ac @@ -755,6 +755,7 @@ src/include/gnunet_directories.h src/hostlist/Makefile src/mesh/Makefile src/nat/Makefile +src/nse/Makefile src/peerinfo/Makefile src/peerinfo-tool/Makefile src/statistics/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index 1442854ff..c3cf0836a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -23,6 +23,7 @@ SUBDIRS = \ peerinfo-tool \ core \ testing \ + nse \ dv \ dht \ hostlist \ diff --git a/src/include/gnunet_nse_service.h b/src/include/gnunet_nse_service.h index 27cce9bd0..7a670a584 100644 --- a/src/include/gnunet_nse_service.h +++ b/src/include/gnunet_nse_service.h @@ -1,6 +1,6 @@ /* This file is part of GNUnet - (C) 2009, 2010 Christian Grothoff (and other contributing authors) + (C) 2011 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 @@ -47,56 +47,32 @@ extern "C" */ #define GNUNET_NSE_VERSION 0x00000000 -/** - * Interval for sending network size estimation flood requests. - * Number is in milliseconds. - * This needs to be a factor of the number milliseconds in - * a day, as the base time used is midnight each day offset - * by this amount. - * - * There are 86400000 milliseconds in a day. - */ -#if 0 -#define GNUNET_NSE_INTERVAL 60000 /* Every minute */ -#define GNUNET_NSE_INTERVAL 180000 /* Every three minutes */ -#define GNUNET_NSE_INTERVAL 360000 /* Every six minutes */ -#define GNUNET_NSE_INTERVAL 600000 /* Every ten minutes */ -#define GNUNET_NSE_INTERVAL 1200000 /* Every twenty minutes */ -#endif -#define GNUNET_NSE_INTERVAL 360000 /* Every ten minutes */ -/** - * How much clock skew (in milliseconds) will we allow - * for received messages. We check our current time - * with the timestamp received as part of the message - * and if the difference is greater than this tolerance - * we will discard the message as invalid. - * - * There are 86400000 milliseconds in a day. - */ -#define GNUNET_NSE_DRIFT_TOLERANCE 600000 /* Ten minutes. */ - -/** - * Number of bits - */ -#define GNUNET_NSE_BITS - /** * Handle for the network size estimation service. */ struct GNUNET_NSE_Handle; - /** * Callback to call when network size estimate is updated. * * @param cls closure - * @param estimate the value of the current network size estimate - * @param std_dev standard deviation (rounded down to nearest integer) - * of the size estimation values seen + * @param logestimate the log(Base 2) value of the current network size estimate + * @param std_dev standard deviation for the estimate + * + */ +typedef void (*GNUNET_NSE_Callback) (void *cls, + double logestimate, + double std_dev); + + +/** + * Convert the logarithmic estimated returned to the 'GNUNET_NSE_Callback' + * into an absolute estimate in terms of the number of peers in the network. * + * @param loge logarithmic estimate + * @return absolute number of peers in the network (estimated) */ -typedef void -(*GNUNET_NSE_Callback) (void *cls, double estimate, double std_dev); +#define GNUNET_NSE_log_estimate_to_n(loge) pow(2.0, (loge)) /** * Connect to the network size estimation service. diff --git a/src/nse/Makefile.am b/src/nse/Makefile.am index 0718a9d51..a58dd3692 100644 --- a/src/nse/Makefile.am +++ b/src/nse/Makefile.am @@ -31,7 +31,7 @@ nse_profiler_SOURCES = \ nse_profiler_LDADD = \ $(top_builddir)/src/nse/libgnunetnse.la \ $(top_builddir)/src/util/libgnunetutil.la \ - $(top_builddir)/src/testing/libgnunettesting.la + $(top_builddir)/src/testing/libgnunettesting.la \ $(GN_LIBINTL) nse_profiler_DEPENDENCIES = \ libgnunetnse.la diff --git a/src/nse/gnunet-service-nse.c b/src/nse/gnunet-service-nse.c index 25d020caa..6128300dd 100644 --- a/src/nse/gnunet-service-nse.c +++ b/src/nse/gnunet-service-nse.c @@ -1,40 +1,43 @@ /* - This file is part of GNUnet. - (C) 2009, 2010, 2011 Christian Grothoff (and other contributing authors) - - GNUnet is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 3, or (at your - option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. + This file is part of GNUnet. + (C) 2009, 2010, 2011 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ /** * @file nse/gnunet-service-nse.c * @brief network size estimation service * @author Nathan Evans + * @author Christian Grothoff * * The purpose of this service is to estimate the size of the network. * Given a specified interval, each peer hashes the most recent - * timestamp which is evenly divisible by that interval. This hash - * is compared in distance to the peer identity to choose an offset. - * The closer the peer identity to the hashed timestamp, the earlier - * the peer sends out a "nearest peer" message. The closest peer's + * timestamp which is evenly divisible by that interval. This hash is + * compared in distance to the peer identity to choose an offset. The + * closer the peer identity to the hashed timestamp, the earlier the + * peer sends out a "nearest peer" message. The closest peer's * message should thus be received before any others, which stops * those peer from sending their messages at a later duration. So - * every peer should receive the same nearest peer message, and - * from this can calculate the expected number of peers in the - * network. + * every peer should receive the same nearest peer message, and from + * this can calculate the expected number of peers in the network. * + * TODO: + * - generate proof-of-work asynchronously, store it on disk & load it back + * - handle messages for future round (one into the future, see FIXME) */ #include "platform.h" #include "gnunet_client_lib.h" @@ -50,68 +53,118 @@ #include "gnunet_nse_service.h" #include "nse.h" -#define DEFAULT_HISTORY_SIZE 20 +/** + * Over how many values do we calculate the weighted average? + */ +#define HISTORY_SIZE 8 -#define DEFAULT_CORE_QUEUE_SIZE 32 +/** + * Size of the queue to core. + */ +#define CORE_QUEUE_SIZE 2 -#define DEFAULT_NSE_PRIORITY 5 +/** + * Message priority to use. + */ +#define NSE_PRIORITY 5 -#define DO_FORWARD GNUNET_YES +/** + * Amount of work required (W-bit collisions) for NSE proofs, in collision-bits. + */ +#define NSE_WORK_REQUIRED 0 /** - * Entry in the list of clients which - * should be notified upon a new network - * size estimate calculation. + * Interval for sending network size estimation flood requests. */ -struct ClientListEntry +#define GNUNET_NSE_INTERVAL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60) + + +/** + * Per-peer information. + */ +struct NSEPeerEntry { + /** - * Pointer to previous entry + * Pending message for this peer. */ - struct ClientListEntry *prev; + struct GNUNET_MessageHeader *pending_message; /** - * Pointer to next entry + * Core handle for sending messages to this peer. */ - struct ClientListEntry *next; + struct GNUNET_CORE_TransmitHandle *th; /** - * Client to notify. + * What is the identity of the peer? */ - struct GNUNET_SERVER_Client *client; + struct GNUNET_PeerIdentity id; + + /** + * Task scheduled to send message to this peer. + */ + GNUNET_SCHEDULER_TaskIdentifier transmit_task; + + /** + * Did we receive or send a message about the previous round + * to this peer yet? + */ + int previous_round; }; + /** - * Per-peer information. + * Network size estimate reply; sent when "this" + * peer's timer has run out before receiving a + * valid reply from another peer. */ -struct NSEPeerEntry +struct GNUNET_NSE_FloodMessage { /** - * Next peer entry (DLL) + * Type: GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD */ - struct NSEPeerEntry *next; + struct GNUNET_MessageHeader header; /** - * Prev peer entry (DLL) + * Number of hops this message has taken so far. */ - struct NSEPeerEntry *prev; + uint32_t hop_count; /** - * Pending message for this peer. + * Purpose. */ - struct GNUNET_MessageHeader *pending_message; + struct GNUNET_CRYPTO_RsaSignaturePurpose purpose; /** - * Core handle for sending messages to this peer. + * The current timestamp value (which all + * peers should agree on). */ - struct GNUNET_CORE_TransmitHandle *th; + struct GNUNET_TIME_AbsoluteNBO timestamp; /** - * What is the identity of the peer? + * Number of matching bits between the hash + * of timestamp and the initiator's public + * key. */ - struct GNUNET_PeerIdentity id; + uint32_t matching_bits; + + /** + * Public key of the originator. + */ + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey; + + /** + * Proof of work, causing leading zeros when hashed with pkey. + */ + uint64_t proof_of_work; + + /** + * Signature (over range specified in purpose). + */ + struct GNUNET_CRYPTO_RsaSignature signature; }; + /** * Handle to our current configuration. */ @@ -128,49 +181,31 @@ static struct GNUNET_STATISTICS_Handle *stats; static struct GNUNET_CORE_Handle *coreAPI; /** - * Head of global list of peers. - */ -static struct NSEPeerEntry *peers_head; - -/** - * Head of global list of clients. - */ -static struct NSEPeerEntry *peers_tail; - -/** - * Head of global list of clients. - */ -static struct ClientListEntry *cle_head; - -/** - * Tail of global list of clients. + * Map of all connected peers. */ -static struct ClientListEntry *cle_tail; +static struct GNUNET_CONTAINER_MultiHashMap *peers; /** - * The current network size estimate. - * Number of bits matching on average - * thus far. + * The current network size estimate. Number of bits matching on + * average thus far. */ static double current_size_estimate; /** - * The standard deviation of the last - * DEFAULT_HISTORY_SIZE network size estimates. + * The standard deviation of the last HISTORY_SIZE network + * size estimates. */ -static double current_std_dev; +static double current_std_dev = NAN; /** - * Array of the last DEFAULT_HISTORY_SIZE - * network size estimates (matching bits, actually). + * Current hop counter estimate (estimate for network diameter). */ -static unsigned int size_estimates[DEFAULT_HISTORY_SIZE]; +static uint32_t hop_count_max; /** - * Array of size estimate messages. + * Array of recent size estimate messages. */ -static struct GNUNET_NSE_FloodMessage - size_estimate_messages[DEFAULT_HISTORY_SIZE]; +static struct GNUNET_NSE_FloodMessage size_estimate_messages[HISTORY_SIZE]; /** * Index of most recent estimate. @@ -178,39 +213,24 @@ static struct GNUNET_NSE_FloodMessage static unsigned int estimate_index; /** - * Task scheduled to send flood message. + * Task scheduled to update our flood message for the next round. */ static GNUNET_SCHEDULER_TaskIdentifier flood_task; -/** - * Task to schedule flood message and update state. - */ -static GNUNET_SCHEDULER_TaskIdentifier schedule_flood_task; - /** * Notification context, simplifies client broadcasts. */ static struct GNUNET_SERVER_NotificationContext *nc; -/** - * The previous major time. - */ -static struct GNUNET_TIME_Absolute previous_timestamp; - /** * The next major time. */ static struct GNUNET_TIME_Absolute next_timestamp; /** - * Base increment of time to add to send time. + * The current major time. */ -static struct GNUNET_TIME_Relative increment; - -/** - * The current network size estimate message. - */ -static struct GNUNET_NSE_ClientMessage current_estimate_message; +static struct GNUNET_TIME_Absolute current_timestamp; /** * The public key of this peer. @@ -228,9 +248,66 @@ static struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key; static struct GNUNET_PeerIdentity my_identity; /** - * Our flood message, updated whenever a flood is sent. + * Proof of work for this peer. */ -static struct GNUNET_NSE_FloodMessage flood_message; +static uint64_t my_proof; + + +/** + * Initialize a message to clients with the current network + * size estimate. + * + * @param em message to fill in + */ +static void +setup_estimate_message (struct GNUNET_NSE_ClientMessage *em) +{ + unsigned int i; + double mean; + double sum; + double std_dev; + double variance; + double val; + double weight; + double sumweight; + double q; + double r; + double temp; + + /* Weighted incremental algorithm for stddev according to West (1979) */ + mean = 0.0; + sum = 0.0; + sumweight = 0.0; + for (i=0; i= 0); + std_dev = sqrt (variance); + current_std_dev = std_dev; + current_size_estimate = mean; + + em->header.size + = htons (sizeof(struct GNUNET_NSE_ClientMessage)); + em->header.type + = htons (GNUNET_MESSAGE_TYPE_NSE_ESTIMATE); + em->reserved = htonl (0); + em->size_estimate = mean - 0.5; + em->std_deviation = std_dev; + GNUNET_STATISTICS_set (stats, + "Current network size estimate", + (uint64_t) pow (2, mean - 0.5), GNUNET_NO); +} + /** * Handler for START message from client, triggers an @@ -246,18 +323,126 @@ static void handle_start_message(void *cls, struct GNUNET_SERVER_Client *client, const struct GNUNET_MessageHeader *message) { - if ((ntohs (message->size) != sizeof(struct GNUNET_MessageHeader)) - || (ntohs (message->type) != GNUNET_MESSAGE_TYPE_NSE_START)) - return; - + struct GNUNET_NSE_ClientMessage em; #if DEBUG_NSE - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "NSE", - "Received START message from client\n"); + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, + "Received START message from client\n"); #endif GNUNET_SERVER_notification_context_add (nc, client); + setup_estimate_message (&em); + GNUNET_SERVER_notification_context_unicast (nc, client, &em.header, GNUNET_YES); GNUNET_SERVER_receive_done (client, GNUNET_OK); } + +/** + * How long should we delay a message to go the given number of + * matching bits? + * + * @param matching_bits number of matching bits to consider + */ +static double +get_matching_bits_delay (uint32_t matching_bits) +{ + /* Calculated as: S + f/2 - (f / pi) * (atan(x - p'))*/ + // S is next_timestamp + // f is frequency (GNUNET_NSE_INTERVAL) + // x is matching_bits + // p' is current_size_estimate + return ((double) GNUNET_NSE_INTERVAL.rel_value / (double) 2) + - ((GNUNET_NSE_INTERVAL.rel_value / M_PI) * atan (matching_bits - current_size_estimate)); +} + + +/** + * What delay randomization should we apply for a given number of matching bits? + * + * @param matching_bits number of matching bits + * @return random delay to apply + */ +static struct GNUNET_TIME_Relative +get_delay_randomization (uint32_t matching_bits) +{ + struct GNUNET_TIME_Relative ret; + + if (matching_bits == 0) + return GNUNET_TIME_UNIT_ZERO; + ret.rel_value = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, + (uint32_t) (get_matching_bits_delay (matching_bits - 1) / (double) (hop_count_max + 1))); + return ret; +} + + +/** + * Get the number of matching bits that the given timestamp has to the given peer ID. + * + * @param timestamp time to generate key + * @param id peer identity to compare with + * @return number of matching bits + */ +static uint32_t +get_matching_bits (struct GNUNET_TIME_Absolute timestamp, + const struct GNUNET_PeerIdentity *id) +{ + GNUNET_HashCode timestamp_hash; + + GNUNET_CRYPTO_hash (×tamp.abs_value, + sizeof(timestamp.abs_value), + ×tamp_hash); + return GNUNET_CRYPTO_hash_matching_bits (×tamp_hash, + &id->hashPubKey); +} + + +/** + * Get the transmission delay that should be applied for a + * particular round. + * + * @param round_offset -1 for the previous round (random delay between 0 and 50ms) + * 0 for the current round (based on our proximity to time key) + * @return delay that should be applied + */ +static struct GNUNET_TIME_Relative +get_transmit_delay (int round_offset) +{ + struct GNUNET_TIME_Relative ret; + struct GNUNET_TIME_Absolute tgt; + double dist_delay; + uint32_t matching_bits; + + switch (round_offset) + { + case -1: + /* previous round is randomized between 0 and 50 ms */ + ret.rel_value = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, + 50); + return ret; + case 0: + /* current round is based on best-known matching_bits */ + matching_bits = ntohl (size_estimate_messages[estimate_index].matching_bits); + dist_delay = get_matching_bits_delay (matching_bits); + dist_delay += get_delay_randomization (matching_bits).rel_value; + ret.rel_value = (uint64_t) dist_delay; + /* now consider round start time and add delay to it */ + tgt = GNUNET_TIME_absolute_add (current_timestamp, ret); + return GNUNET_TIME_absolute_get_remaining (tgt); + } + GNUNET_break (0); + return GNUNET_TIME_UNIT_FOREVER_REL; +} + + +/** + * Task that triggers a NSE P2P transmission. + * + * @param cls the 'struct NSEPeerEntry' + * @param tc scheduler context + */ +static void +transmit_task (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc); + + /** * Called when core is ready to send a message we asked for * out to the destination. @@ -268,313 +453,272 @@ handle_start_message(void *cls, struct GNUNET_SERVER_Client *client, * @return number of bytes written to buf */ static size_t -transmit_ready(void *cls, size_t size, void *buf) +transmit_ready (void *cls, size_t size, void *buf) { struct NSEPeerEntry *peer_entry = cls; - char *cbuf = buf; + unsigned int idx; - size_t msize; peer_entry->th = NULL; -#if DEBUG_NSE > 1 - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "%s: transmit_ready called\n", - GNUNET_i2s (&my_identity)); -#endif - if (buf == NULL) /* client disconnected */ + if (buf == NULL) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "%s: transmit_ready called (disconnect)\n", - GNUNET_i2s (&my_identity)); + /* client disconnected */ return 0; } - - if (peer_entry->pending_message == NULL) + GNUNET_assert (size >= sizeof (struct GNUNET_NSE_FloodMessage)); +#if DEBUG_NSE > 1 + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Sending size estimate to `%s'\n", + GNUNET_i2s (&peer_entry->id)); +#endif + idx = estimate_index; + if (peer_entry->previous_round == GNUNET_YES) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "%s: transmit_ready called (no message)\n", - GNUNET_i2s (&my_identity)); - return 0; + idx = (idx + HISTORY_SIZE -1) % HISTORY_SIZE; + peer_entry->previous_round = GNUNET_NO; + peer_entry->transmit_task = GNUNET_SCHEDULER_add_delayed (get_transmit_delay (0), + &transmit_task, + peer_entry); } + memcpy (buf, + &size_estimate_messages[idx], + sizeof (struct GNUNET_NSE_FloodMessage)); + GNUNET_STATISTICS_update (stats, + "# flood messages sent", + 1, + GNUNET_NO); + return sizeof (struct GNUNET_NSE_FloodMessage); +} - msize = ntohs (peer_entry->pending_message->size); - if (msize <= size) - memcpy (cbuf, peer_entry->pending_message, msize); -#if DEBUG_NSE > 1 - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "%s: transmit_ready called (transmit %d bytes)\n", - GNUNET_i2s (&my_identity), msize); -#endif - return msize; + +/** + * Task that triggers a NSE P2P transmission. + * + * @param cls the 'struct NSEPeerEntry' + * @param tc scheduler context + */ +static void +transmit_task (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct NSEPeerEntry *peer_entry = cls; + + peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK; + peer_entry->th + = GNUNET_CORE_notify_transmit_ready (coreAPI, + GNUNET_NO, + NSE_PRIORITY, + GNUNET_TIME_UNIT_FOREVER_REL, + &peer_entry->id, + sizeof (struct GNUNET_NSE_FloodMessage), + &transmit_ready, peer_entry); } + /** - * We sent on our flood message or one that we received - * which was validated and closer than ours. Update the - * global list of recent messages and the average. Also - * re-broadcast the message to any clients. + * We've sent on our flood message or one that we received which was + * validated and closer than ours. Update the global list of recent + * messages and the average. Also re-broadcast the message to any + * clients. + */ +static void +update_network_size_estimate () +{ + struct GNUNET_NSE_ClientMessage em; + + setup_estimate_message (&em); + GNUNET_SERVER_notification_context_broadcast (nc, + &em.header, + GNUNET_YES); +} + + +/** + * Setup a flood message in our history array at the given + * slot offset for the given timestamp. * - * @param message the network flood message + * @param slot index to use + * @param ts timestamp to use */ static void -update_network_size_estimate(struct GNUNET_NSE_FloodMessage *message) +setup_flood_message (unsigned int slot, + struct GNUNET_TIME_Absolute ts) { - unsigned int i; - unsigned int count; - double average; - double std_dev; - double diff; + struct GNUNET_NSE_FloodMessage *fm; + uint32_t matching_bits; + + matching_bits = get_matching_bits (ts, &my_identity); + fm = &size_estimate_messages[slot]; + fm->header.size = htons (sizeof(struct GNUNET_NSE_FloodMessage)); + fm->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD); + fm->hop_count = htonl (0); + fm->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_NSE_SEND); + fm->purpose.size = htonl (sizeof(struct GNUNET_NSE_FloodMessage) + - sizeof (struct GNUNET_MessageHeader) + - sizeof (uint32_t) + - sizeof (struct GNUNET_CRYPTO_RsaSignature)); + fm->matching_bits = htonl (matching_bits); + fm->timestamp = GNUNET_TIME_absolute_hton (ts); + fm->pkey = my_public_key; + fm->proof_of_work = my_proof; + GNUNET_CRYPTO_rsa_sign (my_private_key, + &fm->purpose, + &fm->signature); +} + - size_estimates[estimate_index] = htonl (message->distance); - memcpy (&size_estimate_messages[estimate_index], message, - sizeof(struct GNUNET_NSE_FloodMessage)); +/** + * Schedule transmission for the given peer for the current round based + * on what we know about the desired delay. + * + * @param cls unused + * @param key hash of peer identity + * @param value the 'struct NSEPeerEntry' + * @return GNUNET_OK (continue to iterate) + */ +static int +schedule_current_round (void *cls, + const GNUNET_HashCode *key, + void *value) +{ + struct NSEPeerEntry *peer_entry = value; + struct GNUNET_TIME_Relative delay; - count = 0; - std_dev = 0.0; - average = 0.0; - for (i = 0; i < DEFAULT_HISTORY_SIZE; i++) + if (peer_entry->th != NULL) { - if (size_estimate_messages[i].distance != 0) - { -#if AVERAGE_SQUARE - average += (1 << htonl (size_estimate_messages[i].distance)); -#else - average += htonl (size_estimate_messages[i].distance); -#endif - count++; - } + peer_entry->previous_round = GNUNET_NO; + return GNUNET_OK; } - - if (count > 0) + if (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK) { - average /= (double) count; - for (i = 0; i < DEFAULT_HISTORY_SIZE; i++) - { - if (size_estimate_messages[i].distance != 0) - { -#if DEBUG_NSE - GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "%s: estimate %d %d\n", GNUNET_i2s(&my_identity), i, (1 << htonl(size_estimate_messages[i].distance))); -#endif -#if AVERAGE_SQUARE - diff = average - - (1 << htonl (size_estimate_messages[i].distance)); -#else - diff = average - htonl (size_estimate_messages[i].distance); -#endif - std_dev += diff * diff; - } - } - std_dev /= count; - std_dev = sqrt (std_dev); - current_estimate_message.header.size - = htons (sizeof(struct GNUNET_NSE_ClientMessage)); - current_estimate_message.header.type - = htons (GNUNET_MESSAGE_TYPE_NSE_ESTIMATE); -#if AVERAGE_SQUARE - current_estimate_message.size_estimate = average; - current_estimate_message.std_deviation = std_dev; -#else - current_estimate_message.size_estimate = pow(2, average); - current_estimate_message.std_deviation = pow(2, std_dev); -#endif - /* Finally, broadcast the current estimate to all clients */ -#if DEBUG_NSE - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "%s: sending estimate %f -- %f to client\n", - GNUNET_i2s (&my_identity), - average, - std_dev); -#endif - GNUNET_SERVER_notification_context_broadcast ( - nc, - ¤t_estimate_message.header, - GNUNET_NO); - - GNUNET_STATISTICS_set (stats, "Current network size estimate", - (uint64_t) average, GNUNET_NO); + GNUNET_SCHEDULER_cancel (peer_entry->transmit_task); + peer_entry->previous_round = GNUNET_NO; } + delay = get_transmit_delay ((peer_entry->previous_round == GNUNET_NO) ? -1 : 0); + peer_entry->transmit_task = GNUNET_SCHEDULER_add_delayed (delay, + &transmit_task, + peer_entry); + return GNUNET_OK; } -static void -send_flood_message(void *cls, const struct GNUNET_SCHEDULER_TaskContext * tc); /** - * Schedule a flood message to be sent. + * Update our flood message to be sent (and our timestamps). * * @param cls unused * @param tc context for this message - * - * This should be called on startup, - * when a valid flood message is received (and - * the next send flood message hasn't been - * scheduled yet) and when this peer sends - * a valid flood message. As such, there should - * always be a message scheduled to be sent. */ static void -schedule_flood_message(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +update_flood_message(void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) { - GNUNET_HashCode timestamp_hash; - struct GNUNET_TIME_Absolute curr_time; struct GNUNET_TIME_Relative offset; - unsigned int matching_bits; - double millisecond_offset; - - schedule_flood_task = GNUNET_SCHEDULER_NO_TASK; - if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) - return; - - GNUNET_assert(flood_task == GNUNET_SCHEDULER_NO_TASK); + unsigned int i; - if (0 != GNUNET_TIME_absolute_get_remaining (next_timestamp).rel_value) + flood_task = GNUNET_SCHEDULER_NO_TASK; + offset = GNUNET_TIME_absolute_get_remaining (next_timestamp); + if (0 != offset.rel_value) { - GNUNET_break(0); /* Shouldn't ever happen! */ - schedule_flood_task - = GNUNET_SCHEDULER_add_delayed ( - GNUNET_TIME_absolute_get_remaining ( - next_timestamp), - &schedule_flood_message, NULL); + /* somehow run early, delay more */ + flood_task + = GNUNET_SCHEDULER_add_delayed (offset, + &update_flood_message, + NULL); + return; } + current_timestamp = next_timestamp; + next_timestamp = GNUNET_TIME_absolute_add (current_timestamp, + GNUNET_NSE_INTERVAL); + estimate_index = (estimate_index + 1) % HISTORY_SIZE; + setup_flood_message (estimate_index, current_timestamp); + hop_count_max = 0; + for (i=0;i 1 - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "%s: curr_time %lu, prev timestamp %lu, next timestamp %lu\n", - GNUNET_i2s (&my_identity), curr_time.abs_value, - previous_timestamp.abs_value, next_timestamp.abs_value); -#endif - GNUNET_CRYPTO_hash (&next_timestamp.abs_value, - sizeof(next_timestamp.abs_value), ×tamp_hash); - matching_bits = GNUNET_CRYPTO_hash_matching_bits (×tamp_hash, - &my_identity.hashPubKey); - - flood_message.header.size = htons (sizeof(struct GNUNET_NSE_FloodMessage)); - flood_message.header.type = htons (GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD); - flood_message.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_NSE_SEND); - flood_message.purpose.size = htonl (sizeof(struct GNUNET_NSE_FloodMessage) - - sizeof(struct GNUNET_MessageHeader) - sizeof(flood_message.signature)); - flood_message.distance = htonl (matching_bits); - flood_message.timestamp = GNUNET_TIME_absolute_hton (next_timestamp); - memcpy (&flood_message.pkey, &my_public_key, - sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)); - flood_message.proof_of_work = htonl (0); - GNUNET_CRYPTO_rsa_sign (my_private_key, &flood_message.purpose, - &flood_message.signature); - - /*S + f/2 - (f / pi) * (atan(x - p'))*/ - - // S is next_timestamp - // f is frequency (GNUNET_NSE_INTERVAL) - // x is matching_bits - // p' is current_size_estimate - millisecond_offset = ((double) GNUNET_NSE_INTERVAL / (double) 2) - - ((GNUNET_NSE_INTERVAL / M_PI) * atan (matching_bits - - current_size_estimate)); -#if DEBUG_NSE > 1 - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "%s: id matches %d bits, offset is %lu\n\n", - GNUNET_i2s (&my_identity), matching_bits, - (uint64_t) millisecond_offset); -#endif - /* Stop initial call from incrementing */ - if (size_estimate_messages[estimate_index].distance != 0) - estimate_index += 1; - - if (estimate_index >= DEFAULT_HISTORY_SIZE) - estimate_index = 0; - - if (millisecond_offset < curr_time.abs_value - previous_timestamp.abs_value) - offset.rel_value = 0; - else - offset.rel_value = (uint64_t) millisecond_offset + curr_time.abs_value - - previous_timestamp.abs_value; -#if DEBUG_NSE - GNUNET_log ( - GNUNET_ERROR_TYPE_WARNING, - "%s: %u bits match, %lu milliseconds to timestamp , sending flood in %lu\n", - GNUNET_i2s (&my_identity), matching_bits, - GNUNET_TIME_absolute_get_remaining (next_timestamp).rel_value, - offset.rel_value); -#endif - flood_task = GNUNET_SCHEDULER_add_delayed (offset, &send_flood_message, NULL); +/** + * Count the trailing zeroes in hash. + * + * @param hash + * @return the number of trailing zero bits. + */ +static unsigned int +count_trailing_zeroes(const GNUNET_HashCode *hash) +{ + unsigned int hash_count; + + hash_count = sizeof(GNUNET_HashCode) * 8; + while ((0 == GNUNET_CRYPTO_hash_get_bit(hash, hash_count))) + hash_count--; + return (sizeof(GNUNET_HashCode) * 8) - hash_count; } -#if VERIFY_CRYPTO + /** * Check whether the given public key * and integer are a valid proof of work. * * @param pkey the public key * @param val the integer - * @param want the number of trailing zeroes * * @return GNUNET_YES if valid, GNUNET_NO if not */ -static int check_proof_of_work(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey, uint64_t val, unsigned int want) - { - - return GNUNET_YES; - } - -/** - * Count the trailing zeroes in hash. - * - * @param hash - * - * @return the number of trailing zero bits. - */ -static unsigned int count_trailing_zeroes(GNUNET_HashCode *hash) - { - unsigned int hash_count; +static int +check_proof_of_work(const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *pkey, + uint64_t val) +{ + char buf[sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) + sizeof(val)]; + GNUNET_HashCode result; + + memcpy (buf, + &val, + sizeof (val)); + memcpy (&buf[sizeof(val)], + pkey, + sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)); + GNUNET_CRYPTO_hash (buf, sizeof (buf), &result); + return (count_trailing_zeroes (&result) >= NSE_WORK_REQUIRED) ? GNUNET_YES : GNUNET_NO; +} - hash_count = sizeof(GNUNET_HashCode) * 8; - while ((0 == GNUNET_CRYPTO_hash_get_bit(hash, hash_count))) - hash_count--; - return (sizeof(GNUNET_HashCode) * 8) - hash_count; - } /** - * Given a public key, find an integer such that - * the hash of the key concatenated with the integer - * has want trailing 0 bits. + * Given a public key, find an integer such that the hash of the key + * concatenated with the integer has NSE_WORK_REQUIRED trailing 0 + * bits. FIXME: this is a synchronous function... bad * * @param pkey the public key - * @param want the number of trailing 0 bits - * - * @return 64 bit number that satisfies the - * requirements - * - * FIXME: use pointer and return GNUNET_YES or - * GNUNET_NO in case no such number works? + * @return 64 bit number that satisfies the requirements */ -static uint64_t find_proof_of_work(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *pkey, unsigned int want) - { - uint64_t counter; - static char buf[sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) + sizeof(uint64_t)]; - unsigned int data_size; - static GNUNET_HashCode result; - - data_size = sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) + sizeof(uint64_t); - memcpy(buf, pkey, sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)); - counter = 0; - while (counter != (uint64_t)-1) - { - memcpy(&buf[sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)], &counter, sizeof(uint64_t)); - GNUNET_CRYPTO_hash(buf, data_size, &result); - if (want == count_trailing_zeroes(&result)) /* Found good proof of work! */ +static uint64_t +find_proof_of_work(const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *pkey) +{ + uint64_t counter; + char buf[sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) + sizeof(uint64_t)]; + GNUNET_HashCode result; + + memcpy (&buf[sizeof(uint64_t)], + pkey, + sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)); + counter = 0; + while (counter != UINT64_MAX) + { + memcpy (buf, + &counter, + sizeof(uint64_t)); + GNUNET_CRYPTO_hash (buf, sizeof (buf), &result); + if (NSE_WORK_REQUIRED <= count_trailing_zeroes(&result)) break; - counter++; - } - if (counter < (uint64_t)-1) - return counter; /* Found valid proof of work */ - else - return 0; /* Did not find valid proof of work */ - } + counter++; + } + return counter; +} + /** * An incoming flood message has been received which claims @@ -586,19 +730,70 @@ static uint64_t find_proof_of_work(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncode * @return GNUNET_YES if the message is verified * GNUNET_NO if the key/signature don't verify */ -static int verify_message_crypto(struct GNUNET_NSE_FloodMessage *incoming_flood) - { - int ret; - if (GNUNET_OK == (ret - = GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_NSE_SEND, - &incoming_flood->purpose, - &incoming_flood->signature, - &incoming_flood->pkey))) - return GNUNET_YES; - - return GNUNET_NO; - } -#endif +static int +verify_message_crypto(const struct GNUNET_NSE_FloodMessage *incoming_flood) +{ + if (GNUNET_YES != + check_proof_of_work (&incoming_flood->pkey, + incoming_flood->proof_of_work)) + { + GNUNET_break_op (0); + return GNUNET_NO; + } + if (GNUNET_OK != + GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_NSE_SEND, + &incoming_flood->purpose, + &incoming_flood->signature, + &incoming_flood->pkey)) + { + GNUNET_break_op (0); + return GNUNET_NO; + } + return GNUNET_YES; +} + + +/** + * Update transmissions for the given peer for the current round based + * on updated proximity information. + * + * @param cls peer entry to exclude from updates + * @param key hash of peer identity + * @param value the 'struct NSEPeerEntry' + * @return GNUNET_OK (continue to iterate) + */ +static int +update_flood_times (void *cls, + const GNUNET_HashCode *key, + void *value) +{ + struct NSEPeerEntry *exclude = cls; + struct NSEPeerEntry *peer_entry = value; + struct GNUNET_TIME_Relative delay; + + if (peer_entry->th != NULL) + return GNUNET_OK; /* already active */ + if (peer_entry == exclude) + return GNUNET_OK; /* trigger of the update */ + if (peer_entry->previous_round == GNUNET_YES) + { + /* still stuck in previous round, no point to update, check that + we are active here though... */ + GNUNET_break (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK); + return GNUNET_OK; + } + if (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (peer_entry->transmit_task); + peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK; + } + delay = get_transmit_delay (0); + peer_entry->transmit_task = GNUNET_SCHEDULER_add_delayed (delay, + &transmit_task, + peer_entry); + return GNUNET_OK; +} + /** * Core handler for size estimate flooding messages. @@ -610,176 +805,122 @@ static int verify_message_crypto(struct GNUNET_NSE_FloodMessage *incoming_flood) * */ static int -handle_p2p_size_estimate(void *cls, const struct GNUNET_PeerIdentity *peer, +handle_p2p_size_estimate(void *cls, + const struct GNUNET_PeerIdentity *peer, const struct GNUNET_MessageHeader *message, const struct GNUNET_TRANSPORT_ATS_Information *atsi) { - struct GNUNET_NSE_FloodMessage *incoming_flood; - struct GNUNET_TIME_Absolute curr_time; - uint64_t drift; - -#if DEBUG_NSE > 1 - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "%s: received flood message!\n", - GNUNET_i2s (&my_identity)); -#endif - if (ntohs (message->size) != sizeof(struct GNUNET_NSE_FloodMessage)) + const struct GNUNET_NSE_FloodMessage *incoming_flood; + struct GNUNET_TIME_Absolute ts; + struct NSEPeerEntry *peer_entry; + uint32_t matching_bits; + unsigned int idx; + + incoming_flood = (const struct GNUNET_NSE_FloodMessage *) message; + GNUNET_STATISTICS_update (stats, + "# flood messages received", + 1, + GNUNET_NO); + matching_bits = ntohl (incoming_flood->matching_bits); + peer_entry = GNUNET_CONTAINER_multihashmap_get (peers, &peer->hashPubKey); + if (NULL == peer_entry) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "%s: bad message size!\n", - GNUNET_i2s (&my_identity)); - return GNUNET_NO; + GNUNET_break (0); + return GNUNET_OK; } - - GNUNET_STATISTICS_update (stats, "# flood messages received", 1, GNUNET_NO); - incoming_flood = (struct GNUNET_NSE_FloodMessage *) message; - if (ntohl (incoming_flood->distance) - <= ntohl (size_estimate_messages[estimate_index].distance)) /* Not closer than our most recent message */ + ts = GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp); + if (ts.abs_value == current_timestamp.abs_value) + idx = estimate_index; + else if (ts.abs_value == current_timestamp.abs_value - GNUNET_NSE_INTERVAL.rel_value) + idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE; + else if (ts.abs_value == next_timestamp.abs_value - GNUNET_NSE_INTERVAL.rel_value) { -#if DEBUG_NSE - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "%s: distance %d not greater than %d, discarding\n", - GNUNET_i2s (&my_identity), ntohl (incoming_flood->distance), - ntohl (size_estimate_messages[estimate_index].distance)); -#endif - GNUNET_STATISTICS_update (stats, - "# flood messages discarded (had closer)", 1, - GNUNET_NO); + if (GNUNET_YES != + verify_message_crypto (incoming_flood)) + { + GNUNET_break_op (0); + return GNUNET_OK; + } + /* FIXME: keep in special 'future' buffer until next round starts for us! */ + GNUNET_break (0); /* not implemented */ return GNUNET_OK; } - - curr_time = GNUNET_TIME_absolute_get (); - if (curr_time.abs_value - > GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp).abs_value) - drift = curr_time.abs_value - - GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp).abs_value; else - drift = GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp).abs_value - - curr_time.abs_value; - - if (drift > GNUNET_NSE_DRIFT_TOLERANCE) { - GNUNET_STATISTICS_update ( - stats, - "# flood messages discarded (clock skew too high)", - 1, GNUNET_NO); + GNUNET_STATISTICS_update (stats, + "# flood messages discarded (clock skew too large)", + 1, + GNUNET_NO); + GNUNET_break_op (0); return GNUNET_OK; } - -#if VERIFY_CRYPTO - if (GNUNET_YES != verify_message_crypto(incoming_flood)) + if (0 == (memcmp (peer, &my_identity, sizeof(struct GNUNET_PeerIdentity)))) + { + /* send to self, update our own estimate IF this also comes from us! */ + if (0 == memcmp (&incoming_flood->pkey, + &my_public_key, + sizeof (my_public_key))) + update_network_size_estimate (); + return GNUNET_OK; + } + if (matching_bits >= ntohl (size_estimate_messages[idx].matching_bits)) + { + /* cancel transmission from us to this peer for this round */ + if (idx == estimate_index) + { + if (peer_entry->previous_round == GNUNET_NO) + { + /* cancel any activity for current round */ + if (peer_entry->transmit_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (peer_entry->transmit_task); + peer_entry->previous_round = GNUNET_NO; + } + if (peer_entry->th != NULL) + { + GNUNET_CORE_notify_transmit_ready_cancel (peer_entry->th); + peer_entry->th = NULL; + } + } + } + else + { + /* cancel previous round only */ + peer_entry->previous_round = GNUNET_NO; + } + + } + if (matching_bits <= ntohl (size_estimate_messages[idx].matching_bits)) { + /* Not closer than our most recent message, no need to do work here */ GNUNET_STATISTICS_update (stats, - "# flood messages discarded (bad crypto)", - 1, GNUNET_NO); + "# flood messages ignored (had closer already)", + 1, + GNUNET_NO); return GNUNET_OK; } -#endif - - /* Have a new, better size estimate! */ - update_network_size_estimate (incoming_flood); - - if (flood_task != GNUNET_SCHEDULER_NO_TASK) + if (GNUNET_YES != + verify_message_crypto (incoming_flood)) { -#if DEBUG_NSE - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "%s: received closer message, canceling my flood task!\n", GNUNET_i2s(&my_identity)); -#endif - GNUNET_SCHEDULER_cancel (flood_task); - flood_task = GNUNET_SCHEDULER_NO_TASK; + GNUNET_break_op (0); + return GNUNET_OK; } - - /** Commenting out prevents forwarding of messages */ -#if DO_FORWARD - GNUNET_SCHEDULER_add_now(&send_flood_message, &size_estimate_messages[estimate_index]); -#endif - if (schedule_flood_task != GNUNET_SCHEDULER_NO_TASK) - GNUNET_SCHEDULER_cancel (schedule_flood_task); - - schedule_flood_task - = GNUNET_SCHEDULER_add_delayed ( - GNUNET_TIME_absolute_get_remaining ( - next_timestamp), - &schedule_flood_message, NULL); - + size_estimate_messages[idx] = *incoming_flood; + size_estimate_messages[idx].hop_count = htonl (ntohl (incoming_flood->hop_count) + 1); + hop_count_max = GNUNET_MAX (ntohl (incoming_flood->hop_count) + 1, + hop_count_max); + + /* have a new, better size estimate, inform clients */ + update_network_size_estimate (); + + /* flood to rest */ + GNUNET_CONTAINER_multihashmap_iterate (peers, + &update_flood_times, + peer_entry); return GNUNET_OK; } -/** - * Send a flood message. - * - * If we've gotten here, it means either we haven't received - * a network size estimate message closer than ours, or - * we need to forward a message we received which was closer - * than ours. - */ -static void -send_flood_message(void *cls, const struct GNUNET_SCHEDULER_TaskContext * tc) -{ - struct NSEPeerEntry *peer_entry; - struct GNUNET_NSE_FloodMessage *to_send; - - if (cls == NULL) /* Means we are sending our OWN flood message */ - to_send = &flood_message; - else - /* Received a message from another peer that should be forwarded */ - to_send = (struct GNUNET_NSE_FloodMessage *) cls; - flood_task = GNUNET_SCHEDULER_NO_TASK; - if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) - return; -#if DEBUG_NSE - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "%s: my time has come, sending flood message of size %d!\n", - GNUNET_i2s (&my_identity), ntohs (to_send->header.size)); -#endif - peer_entry = peers_head; - - while (peer_entry != NULL) - { - peer_entry->pending_message = &to_send->header; - peer_entry->th - = GNUNET_CORE_notify_transmit_ready ( - coreAPI, - GNUNET_NO, - DEFAULT_NSE_PRIORITY, - GNUNET_TIME_absolute_get_remaining ( - next_timestamp), - &peer_entry->id, - ntohs (to_send->header.size), - &transmit_ready, peer_entry); - if (peer_entry->th == NULL) - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "%s: transmit handle is null!\n", GNUNET_i2s (&my_identity)); -#if DEBUG_NSE > 1 - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "%s: Sending flood message (distance %d) to %s!\n", - GNUNET_i2s (&my_identity), ntohl (to_send->distance), - GNUNET_h2s (&peer_entry->id.hashPubKey)); -#endif - peer_entry = peer_entry->next; - } - - if (cls == NULL) /* Need to update our size estimate */ - { - update_network_size_estimate (to_send); - GNUNET_STATISTICS_update (stats, "# flood messages sent", 1, GNUNET_NO); - } - else - GNUNET_STATISTICS_update (stats, "# flood messages forwarded", 1, GNUNET_NO); - -#if DEBUG_NSE - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "%s: scheduling schedule_flood_message in %lu\n", - GNUNET_i2s (&my_identity), - GNUNET_TIME_absolute_get_remaining (next_timestamp).rel_value); -#endif - if (schedule_flood_task != GNUNET_SCHEDULER_NO_TASK) - GNUNET_SCHEDULER_cancel (schedule_flood_task); - - schedule_flood_task - = GNUNET_SCHEDULER_add_delayed ( - GNUNET_TIME_absolute_get_remaining ( - next_timestamp), - &schedule_flood_message, NULL); -} /** * Method called whenever a peer connects. @@ -794,14 +935,18 @@ handle_core_connect(void *cls, const struct GNUNET_PeerIdentity *peer, { struct NSEPeerEntry *peer_entry; - if (0 == (memcmp (peer, &my_identity, sizeof(struct GNUNET_PeerIdentity)))) - return; /* Do not connect to self... */ - peer_entry = GNUNET_malloc(sizeof(struct NSEPeerEntry)); - memcpy (&peer_entry->id, peer, sizeof(struct GNUNET_PeerIdentity)); - GNUNET_CONTAINER_DLL_insert(peers_head, peers_tail, peer_entry); + peer_entry->id = *peer; + GNUNET_CONTAINER_multihashmap_put (peers, + &peer->hashPubKey, + peer_entry, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); + peer_entry->transmit_task = GNUNET_SCHEDULER_add_delayed (get_transmit_delay (-1), + &transmit_task, + peer_entry); } + /** * Method called whenever a peer disconnects. * @@ -813,57 +958,24 @@ handle_core_disconnect(void *cls, const struct GNUNET_PeerIdentity *peer) { struct NSEPeerEntry *pos; - if (0 == (memcmp (peer, &my_identity, sizeof(struct GNUNET_PeerIdentity)))) - return; /* Ignore disconnect from self... */ - - pos = peers_head; - while ((NULL != pos) && (0 != memcmp (&pos->id, peer, - sizeof(struct GNUNET_PeerIdentity)))) - pos = pos->next; - - if (pos == NULL) + pos = GNUNET_CONTAINER_multihashmap_get (peers, + &peer->hashPubKey); + if (NULL == pos) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Received disconnect before connect!\n"); - GNUNET_break(0); /* Should never receive a disconnect message for a peer we don't know about... */ + GNUNET_break (0); return; } - + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (peers, + &peer->hashPubKey, + pos)); + if (pos->transmit_task != GNUNET_SCHEDULER_NO_TASK) + GNUNET_SCHEDULER_cancel (pos->transmit_task); if (pos->th != NULL) GNUNET_CORE_notify_transmit_ready_cancel (pos->th); - GNUNET_CONTAINER_DLL_remove(peers_head, peers_tail, pos); GNUNET_free(pos); } -/** - * A client disconnected. Remove it from the - * global DLL of clients. - * - * @param cls closure, NULL - * @param client identification of the client - */ -static void -handle_client_disconnect(void *cls, struct GNUNET_SERVER_Client* client) -{ - struct ClientListEntry *cle; - - while (NULL != (cle = cle_head)) - cle = cle->next; - - if (cle != NULL) - { - GNUNET_SERVER_client_drop (cle->client); - GNUNET_CONTAINER_DLL_remove(cle_head, - cle_tail, - cle); - GNUNET_free(cle); - } - if (coreAPI != NULL) - { - GNUNET_CORE_disconnect (coreAPI); - coreAPI = NULL; - } -} /** * Task run during shutdown. @@ -872,34 +984,29 @@ handle_client_disconnect(void *cls, struct GNUNET_SERVER_Client* client) * @param tc unused */ static void -shutdown_task(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +shutdown_task(void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) { - struct ClientListEntry *cle; - if (flood_task != GNUNET_SCHEDULER_NO_TASK) - GNUNET_SCHEDULER_cancel (flood_task); - GNUNET_SERVER_notification_context_destroy (nc); - nc = NULL; - while (NULL != (cle = cle_head)) { - GNUNET_SERVER_client_drop (cle->client); - GNUNET_CONTAINER_DLL_remove (cle_head, - cle_tail, - cle); - GNUNET_free (cle); + GNUNET_SCHEDULER_cancel (flood_task); + flood_task = GNUNET_SCHEDULER_NO_TASK; } - + GNUNET_SERVER_notification_context_destroy (nc); + nc = NULL; if (coreAPI != NULL) { GNUNET_CORE_disconnect (coreAPI); coreAPI = NULL; } - if (stats != NULL) - GNUNET_STATISTICS_destroy (stats, GNUNET_NO); - + { + GNUNET_STATISTICS_destroy (stats, GNUNET_NO); + stats = NULL; + } } + /** * Called on core init/fail. * @@ -913,54 +1020,39 @@ core_init(void *cls, struct GNUNET_CORE_Handle *server, const struct GNUNET_PeerIdentity *identity, const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey) { - struct GNUNET_TIME_Absolute curr_time; + struct GNUNET_TIME_Absolute now; + struct GNUNET_TIME_Absolute prev_time; + unsigned int i; + if (server == NULL) { #if DEBUG_NSE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%s: Connection to core FAILED!\n", - "nse", GNUNET_i2s (identity)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Connection to core FAILED!\n"); #endif GNUNET_SCHEDULER_add_now (&shutdown_task, NULL); return; } - - /* Copy our identity so we can use it */ - memcpy (&my_identity, identity, sizeof(struct GNUNET_PeerIdentity)); - /* Copy our public key for inclusion in flood messages */ - memcpy (&my_public_key, publicKey, - sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)); - - if (flood_task != GNUNET_SCHEDULER_NO_TASK) - GNUNET_SCHEDULER_cancel (flood_task); - - /* Get the current UTC time */ - curr_time = GNUNET_TIME_absolute_get (); - /* Find the previous interval start time */ - previous_timestamp.abs_value = (curr_time.abs_value / GNUNET_NSE_INTERVAL) - * GNUNET_NSE_INTERVAL; - /* Find the next interval start time */ - next_timestamp.abs_value = previous_timestamp.abs_value + GNUNET_NSE_INTERVAL; - -#if DEBUG_NSE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "%s: Core connection initialized, I am peer: %s, scheduling flood task in %lu\n", "nse", - GNUNET_i2s (identity), GNUNET_TIME_absolute_get_remaining(next_timestamp)); -#endif - /* FIXME: In production, we'd likely want to do this immediately, but in test-beds it causes stupid behavior */ - if (schedule_flood_task != GNUNET_SCHEDULER_NO_TASK) - GNUNET_SCHEDULER_cancel (schedule_flood_task); - schedule_flood_task - = GNUNET_SCHEDULER_add_delayed ( - GNUNET_TIME_absolute_get_remaining ( - next_timestamp), - &schedule_flood_message, NULL); - - GNUNET_SERVER_notification_context_broadcast ( - nc, - ¤t_estimate_message.header, - GNUNET_NO); + my_identity = *identity; + my_public_key = *publicKey; + + now = GNUNET_TIME_absolute_get (); + current_timestamp.abs_value = (now.abs_value / GNUNET_NSE_INTERVAL.rel_value) * GNUNET_NSE_INTERVAL.rel_value; + next_timestamp.abs_value = current_timestamp.abs_value + GNUNET_NSE_INTERVAL.rel_value; + + for (i=0;isize) < sizeof(struct GNUNET_NSE_ClientMessage)) - || (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_NSE_ESTIMATE)) + if (msg == NULL) { -#if DEBUG_NSE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "%s: received incorrect message (size %d < %d) from service!", - "NSE API", ntohs (msg->size), - sizeof(struct GNUNET_NSE_ClientMessage)); -#endif + /* Error, timeout, death */ + GNUNET_CLIENT_disconnect (h->client, GNUNET_NO); + h->client = NULL; + h->reconnect_task = GNUNET_SCHEDULER_add_delayed (h->reconnect_delay, + &reconnect, + h); return; } - - client_msg = (struct GNUNET_NSE_ClientMessage *)msg; - - h->recv_cb (h->recv_cb_cls, client_msg->size_estimate, + if ( (ntohs (msg->size) != sizeof(struct GNUNET_NSE_ClientMessage)) || + (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_NSE_ESTIMATE) ) + { + GNUNET_break (0); + return; + } + client_msg = (const struct GNUNET_NSE_ClientMessage *)msg; + h->recv_cb (h->recv_cb_cls, + client_msg->size_estimate, client_msg->std_deviation); - GNUNET_CLIENT_receive (h->client, - &message_handler, h, GNUNET_TIME_UNIT_FOREVER_REL); + &message_handler, + h, + GNUNET_TIME_UNIT_FOREVER_REL); } -static void -reconnect (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc); + /** * Reschedule a connect attempt to the service. @@ -174,6 +173,7 @@ reschedule_connect (struct GNUNET_NSE_Handle *h) } } + /** * Transmit START message to service. * @@ -214,6 +214,7 @@ send_start (void *cls, size_t size, void *buf) return sizeof (struct GNUNET_MessageHeader); } + /** * Try again to connect to network size estimation service. * @@ -250,6 +251,7 @@ reconnect (void *cls, GNUNET_assert(h->th != NULL); } + /** * Connect to the network size estimation service. * @@ -265,11 +267,8 @@ GNUNET_NSE_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, { struct GNUNET_NSE_Handle *ret; + GNUNET_assert (func != NULL); ret = GNUNET_malloc (sizeof (struct GNUNET_NSE_Handle)); - - if (func == NULL) - return NULL; - ret->cfg = cfg; ret->recv_cb = func; ret->recv_cb_cls = func_cls; @@ -278,11 +277,11 @@ GNUNET_NSE_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, return ret; } + /** * Disconnect from network size estimation service * * @param h handle to destroy - * */ void GNUNET_NSE_disconnect (struct GNUNET_NSE_Handle *h) @@ -294,9 +293,16 @@ GNUNET_NSE_disconnect (struct GNUNET_NSE_Handle *h) h->reconnect_task = GNUNET_SCHEDULER_NO_TASK; } if (h->th != NULL) - GNUNET_CLIENT_notify_transmit_ready_cancel(h->th); + { + GNUNET_CLIENT_notify_transmit_ready_cancel(h->th); + h->th = NULL; + } if (h->client != NULL) - GNUNET_CLIENT_disconnect(h->client, GNUNET_NO); - + { + GNUNET_CLIENT_disconnect(h->client, GNUNET_NO); + h->client = NULL; + } GNUNET_free(h); } + +/* end of nse_api.c */ diff --git a/src/nse/test_nse_api.c b/src/nse/test_nse_api.c index 4f83a6d78..273cff294 100644 --- a/src/nse/test_nse_api.c +++ b/src/nse/test_nse_api.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - (C) 2009 Christian Grothoff (and other contributing authors) + (C) 2011 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 @@ -35,7 +35,7 @@ static struct GNUNET_NSE_Handle *h; -GNUNET_SCHEDULER_TaskIdentifier die_task; +static GNUNET_SCHEDULER_TaskIdentifier die_task; struct PeerContext { @@ -83,18 +83,19 @@ check_nse_message (void *cls, double estimate, double std_dev) /* Fantastic check below. Expect NaN, the only thing not equal to itself. */ if ((estimate != estimate) && (std_dev != std_dev)) (*ok) = 0; - if (die_task != GNUNET_SCHEDULER_NO_TASK) GNUNET_SCHEDULER_cancel(die_task); - GNUNET_SCHEDULER_add_now(&end_test, NULL); + die_task = GNUNET_SCHEDULER_add_now(&end_test, NULL); } + static void setup_peer (struct PeerContext *p, const char *cfgname) { p->cfg = GNUNET_CONFIGURATION_create (); #if START_ARM - p->arm_proc = GNUNET_OS_start_process (NULL, NULL, "gnunet-service-arm", + p->arm_proc = GNUNET_OS_start_process (NULL, NULL, + "gnunet-service-arm", "gnunet-service-arm", #if VERBOSE_ARM "-L", "DEBUG", @@ -118,6 +119,7 @@ stop_arm (struct PeerContext *p) GNUNET_CONFIGURATION_destroy (p->cfg); } + static void run (void *cls, char *const *args, @@ -135,6 +137,7 @@ run (void *cls, GNUNET_assert (h != NULL); } + static int check () { @@ -161,6 +164,7 @@ check () return ok; } + int main (int argc, char *argv[]) {