From: Nathan S. Evans Date: Thu, 14 Jul 2011 10:51:11 +0000 (+0000) Subject: nse changes X-Git-Tag: initial-import-from-subversion-38251~17884 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=4db7c5604fe7fa1609dea044b0c3850d15c02138;p=oweals%2Fgnunet.git nse changes --- diff --git a/src/nse/Makefile.am b/src/nse/Makefile.am index bb8e5f509..fa9d43f4d 100644 --- a/src/nse/Makefile.am +++ b/src/nse/Makefile.am @@ -40,6 +40,7 @@ gnunet_service_nse_LDADD = \ $(top_builddir)/src/nse/libgnunetnse.la \ $(top_builddir)/src/util/libgnunetutil.la \ $(top_builddir)/src/core/libgnunetcore.la \ + -lm \ $(GN_LIBINTL) gnunet_service_nse_DEPENDENCIES = \ libgnunetnse.la diff --git a/src/nse/gnunet-service-nse.c b/src/nse/gnunet-service-nse.c index 6c4466bc7..50ec9a013 100644 --- a/src/nse/gnunet-service-nse.c +++ b/src/nse/gnunet-service-nse.c @@ -41,6 +41,7 @@ #include "gnunet_constants.h" #include "gnunet_container_lib.h" #include "gnunet_protocols.h" +#include "gnunet_signatures.h" #include "gnunet_service_lib.h" #include "gnunet_server_lib.h" #include "gnunet_core_service.h" @@ -52,7 +53,7 @@ #define DEFAULT_CORE_QUEUE_SIZE 32 -#define MILLISECONDS_PER_DAY 86400000 +#define DEFAULT_NSE_PRIORITY 0 /** * Entry in the list of clients which @@ -77,6 +78,37 @@ struct ClientListEntry struct GNUNET_SERVER_Client *client; }; +/** + * Per-peer information. + */ +struct NSEPeerEntry +{ + /** + * Next peer entry (DLL) + */ + struct NSEPeerEntry *next; + + /** + * Prev peer entry (DLL) + */ + struct NSEPeerEntry *prev; + + /** + * Pending message for this peer. + */ + struct GNUNET_MessageHeader *pending_message; + + /** + * Core handle for sending messages to this peer. + */ + struct GNUNET_CORE_TransmitHandle *th; + + /** + * What is the identity of the peer? + */ + struct GNUNET_PeerIdentity id; +}; + /** * Handle to our current configuration. */ @@ -85,12 +117,17 @@ static const struct GNUNET_CONFIGURATION_Handle *cfg; /** * Handle to the core service. */ -struct GNUNET_CORE_Handle *coreAPI; +static struct GNUNET_CORE_Handle *coreAPI; /** - * Copy of this peer's identity. + * Head of global list of peers. */ -static struct GNUNET_PeerIdentity my_identity; +static struct NSEPeerEntry *peers_head; + +/** + * Head of global list of clients. + */ +static struct NSEPeerEntry *peers_tail; /** * Head of global list of clients. @@ -104,6 +141,8 @@ static struct ClientListEntry *cle_tail; /** * The current network size estimate. + * Number of bits matching on average + * thus far. */ static double current_size_estimate; @@ -115,15 +154,30 @@ static double current_std_dev; /** * Array of the last DEFAULT_HISTORY_SIZE - * network size estimates. + * network size estimates (matching bits, actually). + */ +static unsigned int size_estimates[DEFAULT_HISTORY_SIZE]; + +/** + * Array of size estimate messages. + */ +static struct GNUNET_NSE_FloodMessage size_estimate_messages[DEFAULT_HISTORY_SIZE]; + +/** + * Index of most recent estimate. */ -//static double *size_estimates[DEFAULT_HISTORY_SIZE]; +static unsigned int estimate_index; /** * Task scheduled to send flood message. */ 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. */ @@ -132,7 +186,7 @@ static struct GNUNET_SERVER_NotificationContext *nc; /** * The previous major time. */ -struct GNUNET_TIME_Absolute previous_timestamp; +static struct GNUNET_TIME_Absolute previous_timestamp; /** * The next major time. @@ -149,6 +203,26 @@ static struct GNUNET_TIME_Relative increment; */ static struct GNUNET_NSE_ClientMessage current_estimate_message; +/** + * The public key of this peer. + */ +static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key; + +/** + * The private key of this peer. + */ +static struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key; + +/** + * The peer identity of this peer. + */ +static struct GNUNET_PeerIdentity my_identity; + +/** + * Our flood message, updated whenever a flood is sent. + */ +static struct GNUNET_NSE_FloodMessage flood_message; + /** * Handler for START message from client, triggers an * immediate current network estimate notification. @@ -172,45 +246,275 @@ handle_start_message (void *cls, GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "NSE", "Received START message from client\n"); #endif GNUNET_SERVER_notification_context_add (nc, client); - GNUNET_SERVER_notification_context_unicast (nc, client, - ¤t_estimate_message.header, - GNUNET_NO); GNUNET_SERVER_receive_done(client, GNUNET_OK); } + +/** + * Called when core is ready to send a message we asked for + * out to the destination. + * + * @param cls closure (NULL) + * @param size number of bytes available in buf + * @param buf where the callee should write the message + * @return number of bytes written to buf + */ +static size_t +transmit_ready (void *cls, size_t size, void *buf) +{ + struct NSEPeerEntry *peer_entry = cls; + char *cbuf = buf; + + size_t msize; + peer_entry->th = NULL; + if (buf == NULL) /* client disconnected */ + return 0; + + if (peer_entry->pending_message == NULL) + return 0; + + msize = ntohs(peer_entry->pending_message->size); + if (msize <= size) + memcpy(cbuf, peer_entry->pending_message, msize); + + return msize; +} + +/** + * 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. + * + * @param message the network flood message + */ +static void update_network_size_estimate(struct GNUNET_NSE_FloodMessage *message) +{ + unsigned int i; + unsigned int count; + double average; + + size_estimates[estimate_index] = htons(message->distance); + memcpy(&size_estimate_messages[estimate_index], message, sizeof(struct GNUNET_NSE_FloodMessage)); + + count = 0; + for (i = 0; i < DEFAULT_HISTORY_SIZE; i++) + { + if (size_estimate_messages[i].distance != 0) + { + average += 1 << htons(size_estimate_messages[i].distance); + count++; + } + } + average /= (double)count; + current_estimate_message.size_estimate = average; + /* Finally, broadcast the current estimate to all clients */ + GNUNET_SERVER_notification_context_broadcast (nc, + ¤t_estimate_message.header, + GNUNET_NO); +} + +static void +send_flood_message (void *cls, + const struct GNUNET_SCHEDULER_TaskContext * tc); + +/** + * Schedule a flood message to be sent. + * + * @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) +{ + GNUNET_HashCode timestamp_hash; + struct GNUNET_TIME_Absolute curr_time; + struct GNUNET_TIME_Relative offset; + struct GNUNET_CRYPTO_RsaSignaturePurpose purpose; + 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); + + if (0 != GNUNET_TIME_absolute_get_remaining(next_timestamp).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); + } + + /* 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 = (curr_time.abs_value / GNUNET_NSE_INTERVAL) * (GNUNET_NSE_INTERVAL + 1); + + GNUNET_CRYPTO_hash(&next_timestamp.abs_value, sizeof(uint64_t), ×tamp_hash); + matching_bits = GNUNET_CRYPTO_hash_matching_bits(×tamp_hash, &my_identity.hashPubKey); + + flood_message.timestamp = GNUNET_TIME_absolute_hton(next_timestamp); + flood_message.distance = htons(matching_bits); + flood_message.enc_type = htons(0); + flood_message.proof_of_work = htonl(0); + purpose.purpose = GNUNET_SIGNATURE_PURPOSE_NSE_SEND; + purpose.size = sizeof(struct GNUNET_NSE_FloodMessage) - sizeof(struct GNUNET_MessageHeader) - sizeof(flood_message.proof_of_work) - sizeof(flood_message.signature); + GNUNET_CRYPTO_rsa_sign(my_private_key, &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)); + + fprintf(stderr, "my id matches %d bits, offset is %lu\n", matching_bits, (uint64_t)millisecond_offset); + + estimate_index += 1; + + if (estimate_index >= DEFAULT_HISTORY_SIZE) + estimate_index = 0; + + offset.rel_value = (uint64_t)millisecond_offset + GNUNET_TIME_absolute_get_remaining (next_timestamp).rel_value; + flood_task = GNUNET_SCHEDULER_add_delayed (offset, + &send_flood_message, NULL); + +} + /** * Core handler for size estimate flooding messages. * - * @param cls closure + * @param cls closure unused * @param message message - * @param peer peer identity this notification is about - * @param atsi performance data + * @param peer peer identity this message is from (ignored) + * @param atsi performance data (ignored) * */ static int -handle_p2p_size_estimate (void *cls, - const struct GNUNET_PeerIdentity *peer, - const struct GNUNET_MessageHeader *message, - const struct GNUNET_TRANSPORT_ATS_Information - *atsi) +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; + + if (ntohs(message->size) != sizeof(struct GNUNET_NSE_FloodMessage)) + return GNUNET_NO; + + incoming_flood = (struct GNUNET_NSE_FloodMessage *)message; + if (ntohs(incoming_flood->distance) <= ntohs(size_estimate_messages[estimate_index].distance)) /* Not closer than our most recent message */ + return GNUNET_OK; + + /* Have a new, better size estimate! */ + update_network_size_estimate(incoming_flood); + + 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); return GNUNET_OK; } - /** - * Send a flood message containing our peer's public key - * and the hashed current timestamp. + * Send a flood message. + * + * If we've gotten here, it means we haven't received + * a network size estimate message closer than ours. */ static void send_flood_message (void *cls, const struct GNUNET_SCHEDULER_TaskContext * tc) { + struct NSEPeerEntry *peer_entry; + + flood_task = GNUNET_SCHEDULER_NO_TASK; if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) return; + + peer_entry = peers_head; + + while (peer_entry != NULL) + { + peer_entry->th + = GNUNET_CORE_notify_transmit_ready (coreAPI, + GNUNET_YES, + DEFAULT_NSE_PRIORITY, + GNUNET_TIME_absolute_get_remaining ( + next_timestamp), + &peer_entry->id, + ntohs (flood_message.header.size), + &transmit_ready, peer_entry); + peer_entry = peer_entry->next; + } + + update_network_size_estimate(&flood_message); + 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. + * + * @param cls closure + * @param peer peer identity this notification is about + * @param atsi performance data + */ +static void +handle_core_connect (void *cls, + const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_TRANSPORT_ATS_Information *atsi) +{ + struct NSEPeerEntry *peer_entry; + + 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); +} + + +/** + * Method called whenever a peer disconnects. + * + * @param cls closure + * @param peer peer identity this notification is about + */ +static void +handle_core_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer) +{ + struct NSEPeerEntry *pos; + + pos = peers_head; + while ((NULL != pos) && (0 != memcmp(&pos->id, peer, sizeof(struct GNUNET_PeerIdentity)))) + pos = pos->next; + if (pos == NULL) + { + 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... */ + return; + } + + if (pos->pending_message != NULL) + GNUNET_free(pos->pending_message); + + 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. @@ -254,6 +558,8 @@ shutdown_task (void *cls, { 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)) @@ -274,40 +580,6 @@ shutdown_task (void *cls, } -/** - * Task to schedule a flood message to be sent. - * - * @param cls closure - * @param tc context information (why was this task triggered now) - */ -static void schedule_flood_message (void *cls, - const struct - GNUNET_SCHEDULER_TaskContext * tc) -{ - GNUNET_HashCode timestamp_hash; - struct GNUNET_TIME_Absolute curr_time; - unsigned int matching_bits; - - /* 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 = (curr_time.abs_value / GNUNET_NSE_INTERVAL) * (GNUNET_NSE_INTERVAL + 1); - - GNUNET_CRYPTO_hash(&next_timestamp.abs_value, sizeof(uint64_t), ×tamp_hash); - matching_bits = GNUNET_CRYPTO_hash_matching_bits(×tamp_hash, &my_identity.hashPubKey); - - GNUNET_SCHEDULER_add_delayed ( - GNUNET_TIME_relative_add ( - GNUNET_TIME_relative_multiply ( - increment, - matching_bits), - GNUNET_TIME_absolute_get_remaining ( - next_timestamp)), - &send_flood_message, NULL); -} - /** * Called on core init/fail. * @@ -340,8 +612,21 @@ core_init (void *cls, /* 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)); + + flood_message.header.size = htons(sizeof(struct GNUNET_NSE_FloodMessage)); + flood_message.header.type = htons(GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD); + memcpy(&flood_message.pkey, &my_public_key, sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)); + + if (flood_task != GNUNET_SCHEDULER_NO_TASK) + GNUNET_SCHEDULER_cancel(flood_task); - flood_task = GNUNET_SCHEDULER_add_now(&schedule_flood_message, NULL); + schedule_flood_task = GNUNET_SCHEDULER_add_now(&schedule_flood_message, NULL); + + GNUNET_SERVER_notification_context_broadcast (nc, + ¤t_estimate_message.header, + GNUNET_NO); } /** @@ -356,6 +641,7 @@ run (void *cls, struct GNUNET_SERVER_Handle *server, const struct GNUNET_CONFIGURATION_Handle *c) { + char *keyfile; static const struct GNUNET_SERVER_MessageHandler handlers[] = { {&handle_start_message, NULL, GNUNET_MESSAGE_TYPE_NSE_START, 0}, {NULL, NULL, 0, 0} @@ -367,19 +653,43 @@ run (void *cls, }; cfg = c; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (c, + "GNUNETD", + "HOSTKEY", &keyfile)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("NSE service is lacking key configuration settings. Exiting.\n")); + GNUNET_SCHEDULER_shutdown (); + return; + } + + my_private_key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile); + GNUNET_free (keyfile); + if (my_private_key == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("NSE Service could not access hostkey. Exiting.\n")); + GNUNET_SCHEDULER_shutdown (); + return; + } + GNUNET_SERVER_add_handlers (server, handlers); nc = GNUNET_SERVER_notification_context_create (server, 16); GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL); + flood_task = GNUNET_SCHEDULER_NO_TASK; /** Connect to core service and register core handlers */ coreAPI = GNUNET_CORE_connect (cfg, /* Main configuration */ DEFAULT_CORE_QUEUE_SIZE, /* queue size */ NULL, /* Closure passed to functions */ &core_init, /* Call core_init once connected */ - NULL, /* Handle connects */ - NULL, /* Handle disconnects */ + &handle_core_connect, /* Handle connects */ + &handle_core_disconnect, /* Handle disconnects */ NULL, /* Do we care about "status" updates? */ NULL, /* Don't want notified about all incoming messages */ GNUNET_NO, /* For header only inbound notification */ @@ -399,9 +709,9 @@ run (void *cls, / (sizeof(GNUNET_HashCode) * 8)); /* Set we have no idea defaults for network size estimate */ - current_size_estimate = NAN; + current_size_estimate = 0.0; current_std_dev = NAN; - + size_estimates[estimate_index] = 0; current_estimate_message.header.size = htons(sizeof(struct GNUNET_NSE_ClientMessage)); current_estimate_message.header.type = htons(GNUNET_MESSAGE_TYPE_NSE_ESTIMATE); current_estimate_message.size_estimate = current_size_estimate; diff --git a/src/nse/nse.h b/src/nse/nse.h index ee9832dc6..59dd6bc1b 100644 --- a/src/nse/nse.h +++ b/src/nse/nse.h @@ -32,14 +32,6 @@ #define DEBUG_NSE GNUNET_YES -#define SIGNED_TIMESTAMP_SIZE sizeof(struct GNUNET_TIME_Absolute) - -/** FIXME: define NSE message types here. */ - -struct GNUNET_Signed_Timestamp -{ - char data[SIGNED_TIMESTAMP_SIZE]; -}; /** * Network size estimate sent from the service @@ -95,25 +87,45 @@ struct GNUNET_NSE_ClientMessage * doesn't allow us to verify that the * public/private key pair were generated, right? */ -struct GNUNET_NSE_ReplyMessage +struct GNUNET_NSE_FloodMessage { /** - * Type: GNUNET_MESSAGE_TYPE_NSE_REPLY + * Type: GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD */ struct GNUNET_MessageHeader header; + /** + * Magic header code(?) + */ + uint16_t enc_type; + + /** + * Number of matching bits between the hash + * of timestamp and the initiator's public + * key. + */ + uint16_t distance; + /** * The current timestamp value (which all - * peers should agree on) signed by the - * private key of the initiating peer. + * peers should agree on). */ - struct GNUNET_Signed_Timestamp timestamp; + struct GNUNET_TIME_AbsoluteNBO timestamp; /** - * Public key of the originator, signed timestamp - * is decrypted by this. + * Public key of the originator. */ struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey; + + /** + * FIXME: use, document. + */ + uint32_t proof_of_work; + + /** + * FIXME: use, document. + */ + struct GNUNET_CRYPTO_RsaSignature signature; }; #endif