From: Christian Grothoff Date: Thu, 6 Oct 2011 11:01:23 +0000 (+0000) Subject: hxing X-Git-Tag: initial-import-from-subversion-38251~16751 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=60e84bc16ecf431bd6f7aa22b3adb48df70bf8cf;p=oweals%2Fgnunet.git hxing --- diff --git a/src/core/Makefile.am b/src/core/Makefile.am index ab82ea7b3..2ea90c9ff 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -39,7 +39,11 @@ gnunet_service_core_LDADD = \ $(GN_LIBINTL) -lz gnunet_service_core_new_SOURCES = \ - gnunet-service-core-new.c + gnunet-service-core-new.c gnunet-service-core.h \ + gnunet-service-core_clients.c gnunet-service-core_clients.h \ + gnunet-service-core_neighbours.c gnunet-service-core_neighbours.h \ + gnunet-service-core_kx.c gnunet-service-core_kx.h \ + gnunet-service-core_sessions.c gnunet-service-core_sessions.h gnunet_service_core_new_LDADD = \ $(top_builddir)/src/hello/libgnunethello.la \ $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \ diff --git a/src/core/gnunet-service-core-new.c b/src/core/gnunet-service-core-new.c index cebc5237b..f5f0784f9 100644 --- a/src/core/gnunet-service-core-new.c +++ b/src/core/gnunet-service-core-new.c @@ -177,52 +177,6 @@ enum PeerStateMachine PEER_STATE_KEY_CONFIRMED }; - -/** - * Encapsulation for encrypted messages exchanged between - * peers. Followed by the actual encrypted data. - */ -struct EncryptedMessage -{ - /** - * Message type is either CORE_ENCRYPTED_MESSAGE. - */ - struct GNUNET_MessageHeader header; - - /** - * Random value used for IV generation. - */ - uint32_t iv_seed GNUNET_PACKED; - - /** - * MAC of the encrypted message (starting at 'sequence_number'), - * used to verify message integrity. Everything after this value - * (excluding this value itself) will be encrypted and authenticated. - * ENCRYPTED_HEADER_SIZE must be set to the offset of the *next* field. - */ - GNUNET_HashCode hmac; - - /** - * Sequence number, in network byte order. This field - * must be the first encrypted/decrypted field - */ - uint32_t sequence_number GNUNET_PACKED; - - /** - * Desired bandwidth (how much we should send to this peer / how - * much is the sender willing to receive)? - */ - struct GNUNET_BANDWIDTH_Value32NBO inbound_bw_limit; - - /** - * Timestamp. Used to prevent reply of ancient messages - * (recent messages are caught with the sequence number). - */ - struct GNUNET_TIME_AbsoluteNBO timestamp; - -}; - - /** * Number of bytes (at the beginning) of "struct EncryptedMessage" * that are NOT encrypted. diff --git a/src/core/gnunet-service-core_clients.c b/src/core/gnunet-service-core_clients.c index 8eee8ff4a..e8a713427 100644 --- a/src/core/gnunet-service-core_clients.c +++ b/src/core/gnunet-service-core_clients.c @@ -1,3 +1,35 @@ +/* + 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 core/gnunet-service-core_clients.c + * @brief code for managing interactions with clients of core service + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_transport_service.h" +#include "gnunet_service_core.h" +#include "gnunet_service_core_clients.h" +#include "gnunet_service_core_sessions.h" + /** * Data structure for each client connected to the core service. @@ -923,12 +955,12 @@ send_p2p_message_to_client (struct Neighbour *sender, struct Client *client, /** * Deliver P2P message to interested clients. * - * @param cls always NULL - * @param client who sent us the message (struct Neighbour) + * @param sender peer who sent us the message * @param m the message */ -static void -deliver_message (void *cls, void *client, const struct GNUNET_MessageHeader *m) +void +GSC_CLIENTS_deliver_message (const struct GNUNET_PeerIdentity *sender, + const struct GNUNET_MessageHeader *m) { struct Neighbour *sender = client; size_t msize = ntohs (m->size); diff --git a/src/core/gnunet-service-core_clients.h b/src/core/gnunet-service-core_clients.h new file mode 100644 index 000000000..ed1f6be96 --- /dev/null +++ b/src/core/gnunet-service-core_clients.h @@ -0,0 +1,71 @@ +/* + 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 core/gnunet-service-core_clients.h + * @brief code for managing interactions with clients of core service + * @author Christian Grothoff + */ +#include "gnunet_util_lib.h" +#include "gnunet_service_core_clients.h" + +#ifndef GNUNET_SERVICE_CORE_CLIENTS_H +#define GNUNET_SERVICE_CORE_CLIENTS_H + + +/** + * Notify client about a change to existing connection to one of our neighbours. + * + * @param neighbour identity of the neighbour that changed status + * @param tmap updated type map for the neighbour, NULL for disconnect + */ +void +GDS_CLIENTS_notify_clients_about_neighbour (const struct GNUNET_PeerIdentity *neighbour, + const struct GSC_TypeMap *tmap); + + +/** + * Deliver P2P message to interested clients. + * + * @param sender peer who sent us the message + * @param m the message + */ +void +GSC_CLIENTS_deliver_message (const struct GNUNET_PeerIdentity *sender, + const struct GNUNET_MessageHeader *m); + + +/** + * Initialize clients subsystem. + * + * @param server handle to server clients connect to + */ +void +GSC_CLIENTS_init (struct GNUNET_SERVER_Handle *server); + + +/** + * Shutdown clients subsystem. + */ +void +GSC_CLIENTS_done (void); + +#endif +/* end of gnunet-service-core_clients.h */ diff --git a/src/core/gnunet-service-core_crypto.c b/src/core/gnunet-service-core_crypto.c deleted file mode 100644 index 0df6dabc1..000000000 --- a/src/core/gnunet-service-core_crypto.c +++ /dev/null @@ -1,361 +0,0 @@ - -/** - * Our private key. - */ -static struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key; - -/** - * Our public key. - */ -static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key; - - -/** - * Derive an authentication key from "set key" information - */ -static void -derive_auth_key (struct GNUNET_CRYPTO_AuthKey *akey, - const struct GNUNET_CRYPTO_AesSessionKey *skey, uint32_t seed, - struct GNUNET_TIME_Absolute creation_time) -{ - static const char ctx[] = "authentication key"; - struct GNUNET_TIME_AbsoluteNBO ctbe; - - - ctbe = GNUNET_TIME_absolute_hton (creation_time); - GNUNET_CRYPTO_hmac_derive_key (akey, skey, &seed, sizeof (seed), &skey->key, - sizeof (skey->key), &ctbe, sizeof (ctbe), ctx, - sizeof (ctx), NULL); -} - - -/** - * Derive an IV from packet information - */ -static void -derive_iv (struct GNUNET_CRYPTO_AesInitializationVector *iv, - const struct GNUNET_CRYPTO_AesSessionKey *skey, uint32_t seed, - const struct GNUNET_PeerIdentity *identity) -{ - static const char ctx[] = "initialization vector"; - - GNUNET_CRYPTO_aes_derive_iv (iv, skey, &seed, sizeof (seed), - &identity->hashPubKey.bits, - sizeof (identity->hashPubKey.bits), ctx, - sizeof (ctx), NULL); -} - -/** - * Derive an IV from pong packet information - */ -static void -derive_pong_iv (struct GNUNET_CRYPTO_AesInitializationVector *iv, - const struct GNUNET_CRYPTO_AesSessionKey *skey, uint32_t seed, - uint32_t challenge, const struct GNUNET_PeerIdentity *identity) -{ - static const char ctx[] = "pong initialization vector"; - - GNUNET_CRYPTO_aes_derive_iv (iv, skey, &seed, sizeof (seed), - &identity->hashPubKey.bits, - sizeof (identity->hashPubKey.bits), &challenge, - sizeof (challenge), ctx, sizeof (ctx), NULL); -} - - -/** - * Encrypt size bytes from in and write the result to out. Use the - * key for outbound traffic of the given neighbour. - * - * @param n neighbour we are sending to - * @param iv initialization vector to use - * @param in ciphertext - * @param out plaintext - * @param size size of in/out - * @return GNUNET_OK on success - */ -static int -do_encrypt (struct Neighbour *n, - const struct GNUNET_CRYPTO_AesInitializationVector *iv, - const void *in, void *out, size_t size) -{ - if (size != (uint16_t) size) - { - GNUNET_break (0); - return GNUNET_NO; - } - GNUNET_assert (size == - GNUNET_CRYPTO_aes_encrypt (in, (uint16_t) size, - &n->encrypt_key, iv, out)); - GNUNET_STATISTICS_update (stats, gettext_noop ("# bytes encrypted"), size, - GNUNET_NO); -#if DEBUG_CORE > 2 - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Encrypted %u bytes for `%4s' using key %u, IV %u\n", - (unsigned int) size, GNUNET_i2s (&n->peer), - (unsigned int) n->encrypt_key.crc32, GNUNET_CRYPTO_crc32_n (iv, - sizeof - (iv))); -#endif - return GNUNET_OK; -} - - - - -/** - * Decrypt size bytes from in and write the result to out. Use the - * key for inbound traffic of the given neighbour. This function does - * NOT do any integrity-checks on the result. - * - * @param n neighbour we are receiving from - * @param iv initialization vector to use - * @param in ciphertext - * @param out plaintext - * @param size size of in/out - * @return GNUNET_OK on success - */ -static int -do_decrypt (struct Neighbour *n, - const struct GNUNET_CRYPTO_AesInitializationVector *iv, - const void *in, void *out, size_t size) -{ - if (size != (uint16_t) size) - { - GNUNET_break (0); - return GNUNET_NO; - } - if ((n->status != PEER_STATE_KEY_RECEIVED) && - (n->status != PEER_STATE_KEY_CONFIRMED)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (size != - GNUNET_CRYPTO_aes_decrypt (in, (uint16_t) size, &n->decrypt_key, iv, out)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - GNUNET_STATISTICS_update (stats, gettext_noop ("# bytes decrypted"), size, - GNUNET_NO); -#if DEBUG_CORE > 1 - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Decrypted %u bytes from `%4s' using key %u, IV %u\n", - (unsigned int) size, GNUNET_i2s (&n->peer), - (unsigned int) n->decrypt_key.crc32, GNUNET_CRYPTO_crc32_n (iv, - sizeof - (*iv))); -#endif - return GNUNET_OK; -} - - - -/** - * We received an encrypted message. Decrypt, validate and - * pass on to the appropriate clients. - * - * @param n target of the message - * @param m encrypted message - * @param ats performance data - * @param ats_count number of entries in ats (excluding 0-termination) - */ -static void -handle_encrypted_message (struct Neighbour *n, const struct EncryptedMessage *m, - const struct GNUNET_TRANSPORT_ATS_Information *ats, - uint32_t ats_count) -{ - size_t size = ntohs (m->header.size); - char buf[size]; - struct EncryptedMessage *pt; /* plaintext */ - GNUNET_HashCode ph; - uint32_t snum; - struct GNUNET_TIME_Absolute t; - struct GNUNET_CRYPTO_AesInitializationVector iv; - struct GNUNET_CRYPTO_AuthKey auth_key; - -#if DEBUG_CORE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Core service receives `%s' request from `%4s'.\n", - "ENCRYPTED_MESSAGE", GNUNET_i2s (&n->peer)); -#endif - /* validate hash */ - derive_auth_key (&auth_key, &n->decrypt_key, m->iv_seed, - n->decrypt_key_created); - GNUNET_CRYPTO_hmac (&auth_key, &m->sequence_number, - size - ENCRYPTED_HEADER_SIZE, &ph); -#if DEBUG_HANDSHAKE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Re-Authenticated %u bytes of ciphertext (`%u'): `%s'\n", - (unsigned int) size - ENCRYPTED_HEADER_SIZE, - GNUNET_CRYPTO_crc32_n (&m->sequence_number, - size - ENCRYPTED_HEADER_SIZE), - GNUNET_h2s (&ph)); -#endif - - if (0 != memcmp (&ph, &m->hmac, sizeof (GNUNET_HashCode))) - { - /* checksum failed */ - GNUNET_break_op (0); - return; - } - derive_iv (&iv, &n->decrypt_key, m->iv_seed, &my_identity); - /* decrypt */ - if (GNUNET_OK != - do_decrypt (n, &iv, &m->sequence_number, &buf[ENCRYPTED_HEADER_SIZE], - size - ENCRYPTED_HEADER_SIZE)) - return; - pt = (struct EncryptedMessage *) buf; - - /* validate sequence number */ - snum = ntohl (pt->sequence_number); - if (n->last_sequence_number_received == snum) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Received duplicate message, ignoring.\n"); - /* duplicate, ignore */ - GNUNET_STATISTICS_update (stats, - gettext_noop ("# bytes dropped (duplicates)"), - size, GNUNET_NO); - return; - } - if ((n->last_sequence_number_received > snum) && - (n->last_sequence_number_received - snum > 32)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Received ancient out of sequence message, ignoring.\n"); - /* ancient out of sequence, ignore */ - GNUNET_STATISTICS_update (stats, - gettext_noop - ("# bytes dropped (out of sequence)"), size, - GNUNET_NO); - return; - } - if (n->last_sequence_number_received > snum) - { - unsigned int rotbit = 1 << (n->last_sequence_number_received - snum - 1); - - if ((n->last_packets_bitmap & rotbit) != 0) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Received duplicate message, ignoring.\n"); - GNUNET_STATISTICS_update (stats, - gettext_noop ("# bytes dropped (duplicates)"), - size, GNUNET_NO); - /* duplicate, ignore */ - return; - } - n->last_packets_bitmap |= rotbit; - } - if (n->last_sequence_number_received < snum) - { - int shift = (snum - n->last_sequence_number_received); - - if (shift >= 8 * sizeof (n->last_packets_bitmap)) - n->last_packets_bitmap = 0; - else - n->last_packets_bitmap <<= shift; - n->last_sequence_number_received = snum; - } - - /* check timestamp */ - t = GNUNET_TIME_absolute_ntoh (pt->timestamp); - if (GNUNET_TIME_absolute_get_duration (t).rel_value > - MAX_MESSAGE_AGE.rel_value) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Message received far too old (%llu ms). Content ignored.\n"), - GNUNET_TIME_absolute_get_duration (t).rel_value); - GNUNET_STATISTICS_update (stats, - gettext_noop - ("# bytes dropped (ancient message)"), size, - GNUNET_NO); - return; - } - - /* process decrypted message(s) */ - if (n->bw_out_external_limit.value__ != pt->inbound_bw_limit.value__) - { -#if DEBUG_CORE_SET_QUOTA - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received %u b/s as new inbound limit for peer `%4s'\n", - (unsigned int) ntohl (pt->inbound_bw_limit.value__), - GNUNET_i2s (&n->peer)); -#endif - n->bw_out_external_limit = pt->inbound_bw_limit; - n->bw_out = - GNUNET_BANDWIDTH_value_min (n->bw_out_external_limit, - n->bw_out_internal_limit); - GNUNET_BANDWIDTH_tracker_update_quota (&n->available_send_window, - n->bw_out); - GNUNET_TRANSPORT_set_quota (transport, &n->peer, n->bw_in, n->bw_out); - } - n->last_activity = GNUNET_TIME_absolute_get (); - if (n->keep_alive_task != GNUNET_SCHEDULER_NO_TASK) - GNUNET_SCHEDULER_cancel (n->keep_alive_task); - n->keep_alive_task = - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_divide - (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, - 2), &send_keep_alive, n); - GNUNET_STATISTICS_update (stats, - gettext_noop ("# bytes of payload decrypted"), - size - sizeof (struct EncryptedMessage), GNUNET_NO); - handle_peer_status_change (n); - update_neighbour_performance (n, ats, ats_count); - if (GNUNET_OK != - GNUNET_SERVER_mst_receive (mst, n, &buf[sizeof (struct EncryptedMessage)], - size - sizeof (struct EncryptedMessage), - GNUNET_YES, GNUNET_NO)) - GNUNET_break_op (0); -} - - -/** - * Wrapper around 'free_neighbour'; helper for 'cleaning_task'. - */ -static int -free_neighbour_helper (void *cls, const GNUNET_HashCode * key, void *value) -{ - struct Neighbour *n = value; - - free_neighbour (n); - return GNUNET_OK; -} - - -int -GSC_CRYPTO_init () -{ - char *keyfile; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (GSC_cfg, "GNUNETD", "HOSTKEY", - &keyfile)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ - ("Core service is lacking HOSTKEY configuration setting. Exiting.\n")); - return GNUNET_SYSERR; - } - 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, - _("Core service could not access hostkey. Exiting.\n")); - return GNUNET_SYSERR; - } - GNUNET_CRYPTO_rsa_key_get_public (my_private_key, &my_public_key); - GNUNET_CRYPTO_hash (&my_public_key, sizeof (my_public_key), - &my_identity.hashPubKey); - - return GNUNET_OK; -} - - -void -GSC_CRYPTO_done () -{ - if (my_private_key != NULL) - GNUNET_CRYPTO_rsa_key_free (my_private_key); -} diff --git a/src/core/gnunet-service-core_kx.c b/src/core/gnunet-service-core_kx.c index ac5b08d79..b10e0bc58 100644 --- a/src/core/gnunet-service-core_kx.c +++ b/src/core/gnunet-service-core_kx.c @@ -1,3 +1,32 @@ +/* + 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 core/gnunet-service-core_kx.c + * @brief code for managing the key exchange (SET_KEY, PING, PONG) with other peers + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet-service-core_kx.h" +#include "gnunet-service-core_neighbours.h" + /** * We're sending an (encrypted) PING to the other peer to check if he @@ -29,7 +58,6 @@ struct PingMessage }; - /** * Response to a PING. Includes data from the original PING * plus initial bandwidth quota information. @@ -111,292 +139,294 @@ struct SetKeyMessage }; +/** + * Encapsulation for encrypted messages exchanged between + * peers. Followed by the actual encrypted data. + */ +struct EncryptedMessage +{ + /** + * Message type is either CORE_ENCRYPTED_MESSAGE. + */ + struct GNUNET_MessageHeader header; + + /** + * Random value used for IV generation. + */ + uint32_t iv_seed GNUNET_PACKED; + + /** + * MAC of the encrypted message (starting at 'sequence_number'), + * used to verify message integrity. Everything after this value + * (excluding this value itself) will be encrypted and authenticated. + * ENCRYPTED_HEADER_SIZE must be set to the offset of the *next* field. + */ + GNUNET_HashCode hmac; + + /** + * Sequence number, in network byte order. This field + * must be the first encrypted/decrypted field + */ + uint32_t sequence_number GNUNET_PACKED; + + /** + * Desired bandwidth (how much we should send to this peer / how + * much is the sender willing to receive)? + */ + struct GNUNET_BANDWIDTH_Value32NBO inbound_bw_limit; + + /** + * Timestamp. Used to prevent reply of ancient messages + * (recent messages are caught with the sequence number). + */ + struct GNUNET_TIME_AbsoluteNBO timestamp; + +}; + + /** * Handle to peerinfo service. */ static struct GNUNET_PEERINFO_Handle *peerinfo; +/** + * Our private key. + */ +static struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key; + +/** + * Our public key. + */ +static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key; + + /** - * We received a PING message. Validate and transmit - * PONG. - * - * @param n sender of the PING - * @param m the encrypted PING message itself - * @param ats performance data - * @param ats_count number of entries in ats (excluding 0-termination) + * Derive an authentication key from "set key" information */ static void -handle_ping (struct Neighbour *n, const struct PingMessage *m, - const struct GNUNET_TRANSPORT_ATS_Information *ats, - uint32_t ats_count) +derive_auth_key (struct GNUNET_CRYPTO_AuthKey *akey, + const struct GNUNET_CRYPTO_AesSessionKey *skey, uint32_t seed, + struct GNUNET_TIME_Absolute creation_time) { - struct PingMessage t; - struct PongMessage tx; - struct PongMessage *tp; - struct MessageEntry *me; - struct GNUNET_CRYPTO_AesInitializationVector iv; + static const char ctx[] = "authentication key"; + struct GNUNET_TIME_AbsoluteNBO ctbe; -#if DEBUG_CORE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Core service receives `%s' request from `%4s'.\n", "PING", - GNUNET_i2s (&n->peer)); -#endif - derive_iv (&iv, &n->decrypt_key, m->iv_seed, &my_identity); - if (GNUNET_OK != - do_decrypt (n, &iv, &m->target, &t.target, - sizeof (struct PingMessage) - ((void *) &m->target - - (void *) m))) - return; -#if DEBUG_HANDSHAKE + + ctbe = GNUNET_TIME_absolute_hton (creation_time); + GNUNET_CRYPTO_hmac_derive_key (akey, skey, &seed, sizeof (seed), &skey->key, + sizeof (skey->key), &ctbe, sizeof (ctbe), ctx, + sizeof (ctx), NULL); +} + + +/** + * Derive an IV from packet information + */ +static void +derive_iv (struct GNUNET_CRYPTO_AesInitializationVector *iv, + const struct GNUNET_CRYPTO_AesSessionKey *skey, uint32_t seed, + const struct GNUNET_PeerIdentity *identity) +{ + static const char ctx[] = "initialization vector"; + + GNUNET_CRYPTO_aes_derive_iv (iv, skey, &seed, sizeof (seed), + &identity->hashPubKey.bits, + sizeof (identity->hashPubKey.bits), ctx, + sizeof (ctx), NULL); +} + +/** + * Derive an IV from pong packet information + */ +static void +derive_pong_iv (struct GNUNET_CRYPTO_AesInitializationVector *iv, + const struct GNUNET_CRYPTO_AesSessionKey *skey, uint32_t seed, + uint32_t challenge, const struct GNUNET_PeerIdentity *identity) +{ + static const char ctx[] = "pong initialization vector"; + + GNUNET_CRYPTO_aes_derive_iv (iv, skey, &seed, sizeof (seed), + &identity->hashPubKey.bits, + sizeof (identity->hashPubKey.bits), &challenge, + sizeof (challenge), ctx, sizeof (ctx), NULL); +} + + +/** + * Encrypt size bytes from in and write the result to out. Use the + * key for outbound traffic of the given neighbour. + * + * @param n neighbour we are sending to + * @param iv initialization vector to use + * @param in ciphertext + * @param out plaintext + * @param size size of in/out + * @return GNUNET_OK on success + */ +static int +do_encrypt (struct Neighbour *n, + const struct GNUNET_CRYPTO_AesInitializationVector *iv, + const void *in, void *out, size_t size) +{ + if (size != (uint16_t) size) + { + GNUNET_break (0); + return GNUNET_NO; + } + GNUNET_assert (size == + GNUNET_CRYPTO_aes_encrypt (in, (uint16_t) size, + &n->encrypt_key, iv, out)); + GNUNET_STATISTICS_update (stats, gettext_noop ("# bytes encrypted"), size, + GNUNET_NO); +#if DEBUG_CORE > 2 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Decrypted `%s' to `%4s' with challenge %u decrypted using key %u, IV %u (salt %u)\n", - "PING", GNUNET_i2s (&t.target), (unsigned int) t.challenge, - (unsigned int) n->decrypt_key.crc32, GNUNET_CRYPTO_crc32_n (&iv, + "Encrypted %u bytes for `%4s' using key %u, IV %u\n", + (unsigned int) size, GNUNET_i2s (&n->peer), + (unsigned int) n->encrypt_key.crc32, GNUNET_CRYPTO_crc32_n (iv, sizeof - (iv)), - m->iv_seed); + (iv))); #endif - GNUNET_STATISTICS_update (stats, gettext_noop ("# PING messages decrypted"), - 1, GNUNET_NO); - if (0 != - memcmp (&t.target, &my_identity, sizeof (struct GNUNET_PeerIdentity))) - { - char sender[9]; - char peer[9]; + return GNUNET_OK; +} - GNUNET_snprintf (sender, sizeof (sender), "%8s", GNUNET_i2s (&n->peer)); - GNUNET_snprintf (peer, sizeof (peer), "%8s", GNUNET_i2s (&t.target)); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ - ("Received PING from `%s' for different identity: I am `%s', PONG identity: `%s'\n"), - sender, GNUNET_i2s (&my_identity), peer); + + + +/** + * Decrypt size bytes from in and write the result to out. Use the + * key for inbound traffic of the given neighbour. This function does + * NOT do any integrity-checks on the result. + * + * @param n neighbour we are receiving from + * @param iv initialization vector to use + * @param in ciphertext + * @param out plaintext + * @param size size of in/out + * @return GNUNET_OK on success + */ +static int +do_decrypt (struct Neighbour *n, + const struct GNUNET_CRYPTO_AesInitializationVector *iv, + const void *in, void *out, size_t size) +{ + if (size != (uint16_t) size) + { + GNUNET_break (0); + return GNUNET_NO; + } + if ((n->status != PEER_STATE_KEY_RECEIVED) && + (n->status != PEER_STATE_KEY_CONFIRMED)) + { GNUNET_break_op (0); - return; + return GNUNET_SYSERR; } - update_neighbour_performance (n, ats, ats_count); - me = GNUNET_malloc (sizeof (struct MessageEntry) + - sizeof (struct PongMessage)); - GNUNET_CONTAINER_DLL_insert_after (n->encrypted_head, n->encrypted_tail, - n->encrypted_tail, me); - me->deadline = GNUNET_TIME_relative_to_absolute (MAX_PONG_DELAY); - me->priority = PONG_PRIORITY; - me->size = sizeof (struct PongMessage); - tx.inbound_bw_limit = n->bw_in; - tx.challenge = t.challenge; - tx.target = t.target; - tp = (struct PongMessage *) &me[1]; - tp->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_PONG); - tp->header.size = htons (sizeof (struct PongMessage)); - tp->iv_seed = - GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX); - derive_pong_iv (&iv, &n->encrypt_key, tp->iv_seed, t.challenge, &n->peer); - do_encrypt (n, &iv, &tx.challenge, &tp->challenge, - sizeof (struct PongMessage) - ((void *) &tp->challenge - - (void *) tp)); - GNUNET_STATISTICS_update (stats, gettext_noop ("# PONG messages created"), 1, + if (size != + GNUNET_CRYPTO_aes_decrypt (in, (uint16_t) size, &n->decrypt_key, iv, out)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + GNUNET_STATISTICS_update (stats, gettext_noop ("# bytes decrypted"), size, GNUNET_NO); -#if DEBUG_HANDSHAKE +#if DEBUG_CORE > 1 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Encrypting `%s' with challenge %u using key %u, IV %u (salt %u)\n", - "PONG", (unsigned int) t.challenge, - (unsigned int) n->encrypt_key.crc32, GNUNET_CRYPTO_crc32_n (&iv, + "Decrypted %u bytes from `%4s' using key %u, IV %u\n", + (unsigned int) size, GNUNET_i2s (&n->peer), + (unsigned int) n->decrypt_key.crc32, GNUNET_CRYPTO_crc32_n (iv, sizeof - (iv)), - tp->iv_seed); + (*iv))); #endif - /* trigger queue processing */ - process_encrypted_neighbour_queue (n); + return GNUNET_OK; } /** - * We received a PONG message. Validate and update our status. + * Start the key exchange with the given peer. * - * @param n sender of the PONG - * @param m the encrypted PONG message itself + * @param pid identity of the peer to do a key exchange with + * @return key exchange information context + */ +struct GSC_KeyExchangeInfo * +GSC_KX_start (const struct GNUNET_PeerIdentity *pid) +{ + struct GSC_KeyExchangeInfo *kx; + + kx = NULL; + return kx; +} + + +/** + * Stop key exchange with the given peer. Clean up key material. + * + * @param kx key exchange to stop + */ +void +GSC_KX_stop (struct GSC_KeyExchangeInfo *kx) +{ + if (kx->pitr != NULL) + { + GNUNET_PEERINFO_iterate_cancel (kx->pitr); + kx->pitr = NULL; + } + if (kx->retry_set_key_task != GNUNET_SCHEDULER_NO_TASK) + GNUNET_SCHEDULER_cancel (kx->retry_set_key_task); + GNUNET_free_non_null (kx->public_key); + GNUNET_free (kx); +} + + +/** + * We received a SET_KEY message. Validate and update + * our key material and status. + * + * @param n the neighbour from which we received message m + * @param m the set key message we received * @param ats performance data * @param ats_count number of entries in ats (excluding 0-termination) */ -static void -handle_pong (struct Neighbour *n, const struct PongMessage *m, - const struct GNUNET_TRANSPORT_ATS_Information *ats, - uint32_t ats_count) +void +GSC_KX_handle_set_key (struct GSC_KeyExchangeInfo *n, const struct GNUNET_MessageHandler *msg, + const struct GNUNET_TRANSPORT_ATS_Information *ats, + uint32_t ats_count) { - struct PongMessage t; - struct ConnectNotifyMessage *cnm; - struct GNUNET_CRYPTO_AesInitializationVector iv; - char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1]; - struct GNUNET_TRANSPORT_ATS_Information *mats; - size_t size; + const struct SetKeyMessage *m; + struct SetKeyMessage *m_cpy; + struct GNUNET_TIME_Absolute t; + struct GNUNET_CRYPTO_AesSessionKey k; + struct PingMessage *ping; + struct PongMessage *pong; + enum PeerStateMachine sender_status; + uint16_t size; + + size = ntohs (msg->header); + if (size != sizeof (struct SetKeyMessage)) + { + GNUNET_break_op (0); + return; + } + m = (const struct SetKeyMessage*) msg; + GNUNET_STATISTICS_update (stats, gettext_noop ("# session keys received"), + 1, GNUNET_NO); -#if DEBUG_HANDSHAKE +#if DEBUG_CORE GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Core service receives `%s' response from `%4s'.\n", "PONG", + "Core service receives `%s' request from `%4s'.\n", "SET_KEY", GNUNET_i2s (&n->peer)); #endif - /* mark as garbage, just to be sure */ - memset (&t, 255, sizeof (t)); - derive_pong_iv (&iv, &n->decrypt_key, m->iv_seed, n->ping_challenge, - &my_identity); - if (GNUNET_OK != - do_decrypt (n, &iv, &m->challenge, &t.challenge, - sizeof (struct PongMessage) - ((void *) &m->challenge - - (void *) m))) + if (n->public_key == NULL) { - GNUNET_break_op (0); - return; - } - GNUNET_STATISTICS_update (stats, gettext_noop ("# PONG messages decrypted"), - 1, GNUNET_NO); -#if DEBUG_HANDSHAKE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Decrypted `%s' from `%4s' with challenge %u using key %u, IV %u (salt %u)\n", - "PONG", GNUNET_i2s (&t.target), (unsigned int) t.challenge, - (unsigned int) n->decrypt_key.crc32, GNUNET_CRYPTO_crc32_n (&iv, - sizeof - (iv)), - m->iv_seed); + if (n->pitr != NULL) + { +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Ignoring `%s' message due to lack of public key for peer (still trying to obtain one).\n", + "SET_KEY"); #endif - if ((0 != memcmp (&t.target, &n->peer, sizeof (struct GNUNET_PeerIdentity))) - || (n->ping_challenge != t.challenge)) - { - /* PONG malformed */ -#if DEBUG_CORE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received malformed `%s' wanted sender `%4s' with challenge %u\n", - "PONG", GNUNET_i2s (&n->peer), - (unsigned int) n->ping_challenge); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received malformed `%s' received from `%4s' with challenge %u\n", - "PONG", GNUNET_i2s (&t.target), (unsigned int) t.challenge); -#endif - GNUNET_break_op (n->ping_challenge != t.challenge); - return; - } - switch (n->status) - { - case PEER_STATE_DOWN: - GNUNET_break (0); /* should be impossible */ - return; - case PEER_STATE_KEY_SENT: - GNUNET_break (0); /* should be impossible, how did we decrypt? */ - return; - case PEER_STATE_KEY_RECEIVED: - GNUNET_STATISTICS_update (stats, - gettext_noop - ("# Session keys confirmed via PONG"), 1, - GNUNET_NO); - n->status = PEER_STATE_KEY_CONFIRMED; - { - struct GNUNET_MessageHeader *hdr; - - hdr = compute_type_map_message (); - send_type_map_to_neighbour (hdr, &n->peer.hashPubKey, n); - GNUNET_free (hdr); - } - if (n->bw_out_external_limit.value__ != t.inbound_bw_limit.value__) - { - n->bw_out_external_limit = t.inbound_bw_limit; - n->bw_out = - GNUNET_BANDWIDTH_value_min (n->bw_out_external_limit, - n->bw_out_internal_limit); - GNUNET_BANDWIDTH_tracker_update_quota (&n->available_send_window, - n->bw_out); - GNUNET_TRANSPORT_set_quota (transport, &n->peer, n->bw_in, n->bw_out); - } -#if DEBUG_CORE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Confirmed key via `%s' message for peer `%4s'\n", "PONG", - GNUNET_i2s (&n->peer)); -#endif - if (n->retry_set_key_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (n->retry_set_key_task); - n->retry_set_key_task = GNUNET_SCHEDULER_NO_TASK; - } - update_neighbour_performance (n, ats, ats_count); - size = - sizeof (struct ConnectNotifyMessage) + - (n->ats_count) * sizeof (struct GNUNET_TRANSPORT_ATS_Information); - if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE) - { - GNUNET_break (0); - /* recovery strategy: throw away performance data */ - GNUNET_array_grow (n->ats, n->ats_count, 0); - size = - sizeof (struct PeerStatusNotifyMessage) + - n->ats_count * sizeof (struct GNUNET_TRANSPORT_ATS_Information); - } - cnm = (struct ConnectNotifyMessage *) buf; - cnm->header.size = htons (size); - cnm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT); - cnm->ats_count = htonl (n->ats_count); - cnm->peer = n->peer; - mats = &cnm->ats; - memcpy (mats, n->ats, - n->ats_count * sizeof (struct GNUNET_TRANSPORT_ATS_Information)); - mats[n->ats_count].type = htonl (GNUNET_TRANSPORT_ATS_ARRAY_TERMINATOR); - mats[n->ats_count].value = htonl (0); - send_to_all_clients (&cnm->header, GNUNET_NO, - GNUNET_CORE_OPTION_SEND_CONNECT); - process_encrypted_neighbour_queue (n); - /* fall-through! */ - case PEER_STATE_KEY_CONFIRMED: - n->last_activity = GNUNET_TIME_absolute_get (); - if (n->keep_alive_task != GNUNET_SCHEDULER_NO_TASK) - GNUNET_SCHEDULER_cancel (n->keep_alive_task); - n->keep_alive_task = - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_divide - (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, - 2), &send_keep_alive, n); - handle_peer_status_change (n); - break; - default: - GNUNET_break (0); - break; - } -} - - -/** - * We received a SET_KEY message. Validate and update - * our key material and status. - * - * @param n the neighbour from which we received message m - * @param m the set key message we received - * @param ats performance data - * @param ats_count number of entries in ats (excluding 0-termination) - */ -static void -handle_set_key (struct Neighbour *n, const struct SetKeyMessage *m, - const struct GNUNET_TRANSPORT_ATS_Information *ats, - uint32_t ats_count) -{ - struct SetKeyMessage *m_cpy; - struct GNUNET_TIME_Absolute t; - struct GNUNET_CRYPTO_AesSessionKey k; - struct PingMessage *ping; - struct PongMessage *pong; - enum PeerStateMachine sender_status; - -#if DEBUG_CORE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Core service receives `%s' request from `%4s'.\n", "SET_KEY", - GNUNET_i2s (&n->peer)); -#endif - if (n->public_key == NULL) - { - if (n->pitr != NULL) - { -#if DEBUG_CORE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Ignoring `%s' message due to lack of public key for peer (still trying to obtain one).\n", - "SET_KEY"); -#endif - return; - } + return; + } #if DEBUG_CORE GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Lacking public key for peer, trying to obtain one (handle_set_key).\n"); @@ -467,67 +497,387 @@ handle_set_key (struct Neighbour *n, const struct SetKeyMessage *m, n->decrypt_key = k; if (n->decrypt_key_created.abs_value != t.abs_value) { - /* fresh key, reset sequence numbers */ - n->last_sequence_number_received = 0; - n->last_packets_bitmap = 0; - n->decrypt_key_created = t; + /* fresh key, reset sequence numbers */ + n->last_sequence_number_received = 0; + n->last_packets_bitmap = 0; + n->decrypt_key_created = t; + } + update_neighbour_performance (n, ats, ats_count); + sender_status = (enum PeerStateMachine) ntohl (m->sender_status); + switch (n->status) + { + case PEER_STATE_DOWN: + n->status = PEER_STATE_KEY_RECEIVED; +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Responding to `%s' with my own key.\n", "SET_KEY"); +#endif + send_key (n); + break; + case PEER_STATE_KEY_SENT: + case PEER_STATE_KEY_RECEIVED: + n->status = PEER_STATE_KEY_RECEIVED; + if ((sender_status != PEER_STATE_KEY_RECEIVED) && + (sender_status != PEER_STATE_KEY_CONFIRMED)) + { +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Responding to `%s' with my own key (other peer has status %u).\n", + "SET_KEY", (unsigned int) sender_status); +#endif + send_key (n); + } + break; + case PEER_STATE_KEY_CONFIRMED: + if ((sender_status != PEER_STATE_KEY_RECEIVED) && + (sender_status != PEER_STATE_KEY_CONFIRMED)) + { +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Responding to `%s' with my own key (other peer has status %u), I was already fully up.\n", + "SET_KEY", (unsigned int) sender_status); +#endif + send_key (n); + } + break; + default: + GNUNET_break (0); + break; + } + if (n->pending_ping != NULL) + { + ping = n->pending_ping; + n->pending_ping = NULL; + handle_ping (n, ping, NULL, 0); + GNUNET_free (ping); + } + if (n->pending_pong != NULL) + { + pong = n->pending_pong; + n->pending_pong = NULL; + handle_pong (n, pong, NULL, 0); + GNUNET_free (pong); + } +} + + +/** + * We received a PING message. Validate and transmit + * PONG. + * + * @param n sender of the PING + * @param m the encrypted PING message itself + * @param ats performance data + * @param ats_count number of entries in ats (excluding 0-termination) + */ +void +GSC_KX_handle_ping (struct GSC_KeyExchangeInfo *n, const struct GNUNET_MessageHeader *msg, + const struct GNUNET_TRANSPORT_ATS_Information *ats, + uint32_t ats_count) +{ + const struct PingMessage *m; + struct PingMessage t; + struct PongMessage tx; + struct PongMessage *tp; + struct MessageEntry *me; + struct GNUNET_CRYPTO_AesInitializationVector iv; + size_t size; + + msize = ntohs (msg->size); + if (msize != sizeof (struct PingMessage)) + { + GNUNET_break_op (0); + return; + } + GNUNET_STATISTICS_update (stats, gettext_noop ("# PING messages received"), + 1, GNUNET_NO); + +#if FIXME + if ((n->status != PEER_STATE_KEY_RECEIVED) && + (n->status != PEER_STATE_KEY_CONFIRMED)) + { +#if DEBUG_CORE > 1 + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core service receives `%s' request from `%4s' but have not processed key; marking as pending.\n", + "PING", GNUNET_i2s (&n->peer)); +#endif + GNUNET_free_non_null (n->pending_ping); + n->pending_ping = GNUNET_malloc (sizeof (struct PingMessage)); + memcpy (n->pending_ping, message, sizeof (struct PingMessage)); + return; + } +#endif + m = (const struct PingMessage*) msg; +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core service receives `%s' request from `%4s'.\n", "PING", + GNUNET_i2s (&n->peer)); +#endif + derive_iv (&iv, &n->decrypt_key, m->iv_seed, &my_identity); + if (GNUNET_OK != + do_decrypt (n, &iv, &m->target, &t.target, + sizeof (struct PingMessage) - ((void *) &m->target - + (void *) m))) + return; +#if DEBUG_HANDSHAKE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Decrypted `%s' to `%4s' with challenge %u decrypted using key %u, IV %u (salt %u)\n", + "PING", GNUNET_i2s (&t.target), (unsigned int) t.challenge, + (unsigned int) n->decrypt_key.crc32, GNUNET_CRYPTO_crc32_n (&iv, + sizeof + (iv)), + m->iv_seed); +#endif + GNUNET_STATISTICS_update (stats, gettext_noop ("# PING messages decrypted"), + 1, GNUNET_NO); + if (0 != + memcmp (&t.target, &my_identity, sizeof (struct GNUNET_PeerIdentity))) + { + char sender[9]; + char peer[9]; + + GNUNET_snprintf (sender, sizeof (sender), "%8s", GNUNET_i2s (&n->peer)); + GNUNET_snprintf (peer, sizeof (peer), "%8s", GNUNET_i2s (&t.target)); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("Received PING from `%s' for different identity: I am `%s', PONG identity: `%s'\n"), + sender, GNUNET_i2s (&my_identity), peer); + GNUNET_break_op (0); + return; + } + update_neighbour_performance (n, ats, ats_count); + me = GNUNET_malloc (sizeof (struct MessageEntry) + + sizeof (struct PongMessage)); + GNUNET_CONTAINER_DLL_insert_after (n->encrypted_head, n->encrypted_tail, + n->encrypted_tail, me); + me->deadline = GNUNET_TIME_relative_to_absolute (MAX_PONG_DELAY); + me->priority = PONG_PRIORITY; + me->size = sizeof (struct PongMessage); + tx.inbound_bw_limit = n->bw_in; + tx.challenge = t.challenge; + tx.target = t.target; + tp = (struct PongMessage *) &me[1]; + tp->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_PONG); + tp->header.size = htons (sizeof (struct PongMessage)); + tp->iv_seed = + GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX); + derive_pong_iv (&iv, &n->encrypt_key, tp->iv_seed, t.challenge, &n->peer); + do_encrypt (n, &iv, &tx.challenge, &tp->challenge, + sizeof (struct PongMessage) - ((void *) &tp->challenge - + (void *) tp)); + GNUNET_STATISTICS_update (stats, gettext_noop ("# PONG messages created"), 1, + GNUNET_NO); +#if DEBUG_HANDSHAKE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Encrypting `%s' with challenge %u using key %u, IV %u (salt %u)\n", + "PONG", (unsigned int) t.challenge, + (unsigned int) n->encrypt_key.crc32, GNUNET_CRYPTO_crc32_n (&iv, + sizeof + (iv)), + tp->iv_seed); +#endif + /* trigger queue processing */ + process_encrypted_neighbour_queue (n); +} + + +/** + * We received a PONG message. Validate and update our status. + * + * @param n sender of the PONG + * @param m the encrypted PONG message itself + * @param ats performance data + * @param ats_count number of entries in ats (excluding 0-termination) + */ +void +GSC_KX_handle_pong (struct GSC_KeyExchangeInfo *n, const struct GNUNET_MessageHeader *msg, + const struct GNUNET_TRANSPORT_ATS_Information *ats, + uint32_t ats_count) +{ + const struct PongMessage *m; + struct PongMessage t; + struct ConnectNotifyMessage *cnm; + struct GNUNET_CRYPTO_AesInitializationVector iv; + char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1]; + struct GNUNET_TRANSPORT_ATS_Information *mats; + uint16_t msize; + size_t size; + + msize = ntohs (msg->size); + if (msize != sizeof (struct PongMessage)) + { + GNUNET_break_op (0); + return; + } + GNUNET_STATISTICS_update (stats, gettext_noop ("# PONG messages received"), + 1, GNUNET_NO); + +#if FIXME + if ((n->status != PEER_STATE_KEY_RECEIVED) && + (n->status != PEER_STATE_KEY_CONFIRMED)) + { +#if DEBUG_CORE > 1 + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core service receives `%s' request from `%4s' but have not processed key; marking as pending.\n", + "PONG", GNUNET_i2s (&n->peer)); +#endif + GNUNET_free_non_null (n->pending_pong); + n->pending_pong = GNUNET_malloc (sizeof (struct PongMessage)); + memcpy (n->pending_pong, message, sizeof (struct PongMessage)); + return; + } +#endif + + m = (const struct PongMessage*) msg; +#if DEBUG_HANDSHAKE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core service receives `%s' response from `%4s'.\n", "PONG", + GNUNET_i2s (&n->peer)); +#endif + /* mark as garbage, just to be sure */ + memset (&t, 255, sizeof (t)); + derive_pong_iv (&iv, &n->decrypt_key, m->iv_seed, n->ping_challenge, + &my_identity); + if (GNUNET_OK != + do_decrypt (n, &iv, &m->challenge, &t.challenge, + sizeof (struct PongMessage) - ((void *) &m->challenge - + (void *) m))) + { + GNUNET_break_op (0); + return; + } + GNUNET_STATISTICS_update (stats, gettext_noop ("# PONG messages decrypted"), + 1, GNUNET_NO); +#if DEBUG_HANDSHAKE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Decrypted `%s' from `%4s' with challenge %u using key %u, IV %u (salt %u)\n", + "PONG", GNUNET_i2s (&t.target), (unsigned int) t.challenge, + (unsigned int) n->decrypt_key.crc32, GNUNET_CRYPTO_crc32_n (&iv, + sizeof + (iv)), + m->iv_seed); +#endif + if ((0 != memcmp (&t.target, &n->peer, sizeof (struct GNUNET_PeerIdentity))) + || (n->ping_challenge != t.challenge)) + { + /* PONG malformed */ +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received malformed `%s' wanted sender `%4s' with challenge %u\n", + "PONG", GNUNET_i2s (&n->peer), + (unsigned int) n->ping_challenge); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received malformed `%s' received from `%4s' with challenge %u\n", + "PONG", GNUNET_i2s (&t.target), (unsigned int) t.challenge); +#endif + GNUNET_break_op (n->ping_challenge != t.challenge); + return; } - update_neighbour_performance (n, ats, ats_count); - sender_status = (enum PeerStateMachine) ntohl (m->sender_status); switch (n->status) { case PEER_STATE_DOWN: - n->status = PEER_STATE_KEY_RECEIVED; -#if DEBUG_CORE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Responding to `%s' with my own key.\n", "SET_KEY"); -#endif - send_key (n); - break; + GNUNET_break (0); /* should be impossible */ + return; case PEER_STATE_KEY_SENT: + GNUNET_break (0); /* should be impossible, how did we decrypt? */ + return; case PEER_STATE_KEY_RECEIVED: - n->status = PEER_STATE_KEY_RECEIVED; - if ((sender_status != PEER_STATE_KEY_RECEIVED) && - (sender_status != PEER_STATE_KEY_CONFIRMED)) + GNUNET_STATISTICS_update (stats, + gettext_noop + ("# Session keys confirmed via PONG"), 1, + GNUNET_NO); + n->status = PEER_STATE_KEY_CONFIRMED; { -#if DEBUG_CORE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Responding to `%s' with my own key (other peer has status %u).\n", - "SET_KEY", (unsigned int) sender_status); -#endif - send_key (n); + struct GNUNET_MessageHeader *hdr; + + hdr = compute_type_map_message (); + send_type_map_to_neighbour (hdr, &n->peer.hashPubKey, n); + GNUNET_free (hdr); } - break; - case PEER_STATE_KEY_CONFIRMED: - if ((sender_status != PEER_STATE_KEY_RECEIVED) && - (sender_status != PEER_STATE_KEY_CONFIRMED)) + if (n->bw_out_external_limit.value__ != t.inbound_bw_limit.value__) { + n->bw_out_external_limit = t.inbound_bw_limit; + n->bw_out = + GNUNET_BANDWIDTH_value_min (n->bw_out_external_limit, + n->bw_out_internal_limit); + GNUNET_BANDWIDTH_tracker_update_quota (&n->available_send_window, + n->bw_out); + GNUNET_TRANSPORT_set_quota (transport, &n->peer, n->bw_in, n->bw_out); + } #if DEBUG_CORE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Responding to `%s' with my own key (other peer has status %u), I was already fully up.\n", - "SET_KEY", (unsigned int) sender_status); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Confirmed key via `%s' message for peer `%4s'\n", "PONG", + GNUNET_i2s (&n->peer)); #endif - send_key (n); + if (n->retry_set_key_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (n->retry_set_key_task); + n->retry_set_key_task = GNUNET_SCHEDULER_NO_TASK; + } + update_neighbour_performance (n, ats, ats_count); + size = + sizeof (struct ConnectNotifyMessage) + + (n->ats_count) * sizeof (struct GNUNET_TRANSPORT_ATS_Information); + if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE) + { + GNUNET_break (0); + /* recovery strategy: throw away performance data */ + GNUNET_array_grow (n->ats, n->ats_count, 0); + size = + sizeof (struct PeerStatusNotifyMessage) + + n->ats_count * sizeof (struct GNUNET_TRANSPORT_ATS_Information); } + cnm = (struct ConnectNotifyMessage *) buf; + cnm->header.size = htons (size); + cnm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT); + cnm->ats_count = htonl (n->ats_count); + cnm->peer = n->peer; + mats = &cnm->ats; + memcpy (mats, n->ats, + n->ats_count * sizeof (struct GNUNET_TRANSPORT_ATS_Information)); + mats[n->ats_count].type = htonl (GNUNET_TRANSPORT_ATS_ARRAY_TERMINATOR); + mats[n->ats_count].value = htonl (0); + send_to_all_clients (&cnm->header, GNUNET_NO, + GNUNET_CORE_OPTION_SEND_CONNECT); + process_encrypted_neighbour_queue (n); + /* fall-through! */ + case PEER_STATE_KEY_CONFIRMED: + n->last_activity = GNUNET_TIME_absolute_get (); + if (n->keep_alive_task != GNUNET_SCHEDULER_NO_TASK) + GNUNET_SCHEDULER_cancel (n->keep_alive_task); + n->keep_alive_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_divide + (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, + 2), &send_keep_alive, n); + handle_peer_status_change (n); break; default: GNUNET_break (0); break; } - if (n->pending_ping != NULL) - { - ping = n->pending_ping; - n->pending_ping = NULL; - handle_ping (n, ping, NULL, 0); - GNUNET_free (ping); - } - if (n->pending_pong != NULL) + +#if FIXME + if (n->status == PEER_STATE_KEY_CONFIRMED) { - pong = n->pending_pong; - n->pending_pong = NULL; - handle_pong (n, pong, NULL, 0); - GNUNET_free (pong); + now = GNUNET_TIME_absolute_get (); + n->last_activity = now; + changed = GNUNET_YES; + if (!up) + { + GNUNET_STATISTICS_update (stats, gettext_noop ("# established sessions"), + 1, GNUNET_NO); + n->time_established = now; + } + if (n->keep_alive_task != GNUNET_SCHEDULER_NO_TASK) + GNUNET_SCHEDULER_cancel (n->keep_alive_task); + n->keep_alive_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_divide + (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, + 2), &send_keep_alive, n); } + if (changed) + handle_peer_status_change (n); +#endif } @@ -907,52 +1257,322 @@ set_key_retry_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) } -struct GSC_KeyExchangeInfo * -GSC_KX_start (const struct GNUNET_PeerIdentity *pid) +/** + * Encrypt and transmit a message with the given payload. + * + * @param kx key exchange context + * @param payload payload of the message + * @param payload_size number of bytes in 'payload' + */ +void +GSC_KX_encrypt_and_transmit (struct GSC_KeyExchangeInfo *kx, + const void *payload, + size_t payload_size) { - struct GSC_KeyExchangeInfo *kx; + char pbuf[GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE + sizeof (struct EncryptedMessage)]; /* plaintext */ + size_t used; + struct EncryptedMessage *em; /* encrypted message */ + struct EncryptedMessage *ph; /* plaintext header */ + struct MessageEntry *me; + unsigned int priority; + struct GNUNET_TIME_Absolute deadline; + struct GNUNET_TIME_Relative retry_time; + struct GNUNET_CRYPTO_AesInitializationVector iv; + struct GNUNET_CRYPTO_AuthKey auth_key; - kx = NULL; - return kx; -} +#if DEBUG_CORE_QUOTA + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending %u b/s as new limit to peer `%4s'\n", + (unsigned int) ntohl (n->bw_in.value__), GNUNET_i2s (&n->peer)); +#endif + ph->iv_seed = + htonl (GNUNET_CRYPTO_random_u32 + (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX)); + ph->sequence_number = htonl (++n->last_sequence_number_sent); + ph->inbound_bw_limit = n->bw_in; + ph->timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ()); + + /* setup encryption message header */ + me = GNUNET_malloc (sizeof (struct MessageEntry) + used); + me->deadline = deadline; + me->priority = priority; + me->size = used; + em = (struct EncryptedMessage *) &me[1]; + em->header.size = htons (used); + em->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_ENCRYPTED_MESSAGE); + em->iv_seed = ph->iv_seed; + derive_iv (&iv, &n->encrypt_key, ph->iv_seed, &n->peer); + /* encrypt */ +#if DEBUG_HANDSHAKE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Encrypting %u bytes of plaintext messages for `%4s' for transmission in %llums.\n", + (unsigned int) used - ENCRYPTED_HEADER_SIZE, + GNUNET_i2s (&n->peer), + (unsigned long long) + GNUNET_TIME_absolute_get_remaining (deadline).rel_value); +#endif + GNUNET_assert (GNUNET_OK == + do_encrypt (n, &iv, &ph->sequence_number, &em->sequence_number, + used - ENCRYPTED_HEADER_SIZE)); + derive_auth_key (&auth_key, &n->encrypt_key, ph->iv_seed, + n->encrypt_key_created); + GNUNET_CRYPTO_hmac (&auth_key, &em->sequence_number, + used - ENCRYPTED_HEADER_SIZE, &em->hmac); +#if DEBUG_HANDSHAKE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Authenticated %u bytes of ciphertext %u: `%s'\n", + used - ENCRYPTED_HEADER_SIZE, + GNUNET_CRYPTO_crc32_n (&em->sequence_number, + used - ENCRYPTED_HEADER_SIZE), + GNUNET_h2s (&em->hmac)); +#endif + GDS_NEIGHBOURS_transmit (&kx->peer, + &em->header, + GNUNET_TIME_UNIT_FOREVER_REL); +} +/** + * We received an encrypted message. Decrypt, validate and + * pass on to the appropriate clients. + * + * @param n target of the message + * @param m encrypted message + * @param ats performance data + * @param ats_count number of entries in ats (excluding 0-termination) + */ void -GSC_KX_stop (struct GSC_KeyExchangeInfo *kx) +GSC_KX_handle_encrypted_message (struct GSC_KeyExchangeInfo *n, + const struct GNUNET_MessageHeader *msg, + const struct GNUNET_TRANSPORT_ATS_Information *ats, + uint32_t ats_count) { - if (kx->pitr != NULL) + const struct EncryptedMessage *m; + char buf[size]; + struct EncryptedMessage *pt; /* plaintext */ + GNUNET_HashCode ph; + uint32_t snum; + struct GNUNET_TIME_Absolute t; + struct GNUNET_CRYPTO_AesInitializationVector iv; + struct GNUNET_CRYPTO_AuthKey auth_key; + uint16_t size = ntohs (msg->size); + + if (size < + sizeof (struct EncryptedMessage) + sizeof (struct GNUNET_MessageHeader)) + { + GNUNET_break_op (0); + return; + } + m = (const struct EncryptedMessage*) msg; +#if FIXME + if ((n->status != PEER_STATE_KEY_RECEIVED) && + (n->status != PEER_STATE_KEY_CONFIRMED)) + { + GNUNET_STATISTICS_update (stats, + gettext_noop + ("# failed to decrypt message (no session key)"), + 1, GNUNET_NO); + send_key (n); + return; + } +#endif + +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core service receives `%s' request from `%4s'.\n", + "ENCRYPTED_MESSAGE", GNUNET_i2s (&n->peer)); +#endif + /* validate hash */ + derive_auth_key (&auth_key, &n->decrypt_key, m->iv_seed, + n->decrypt_key_created); + GNUNET_CRYPTO_hmac (&auth_key, &m->sequence_number, + size - ENCRYPTED_HEADER_SIZE, &ph); +#if DEBUG_HANDSHAKE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Re-Authenticated %u bytes of ciphertext (`%u'): `%s'\n", + (unsigned int) size - ENCRYPTED_HEADER_SIZE, + GNUNET_CRYPTO_crc32_n (&m->sequence_number, + size - ENCRYPTED_HEADER_SIZE), + GNUNET_h2s (&ph)); +#endif + + if (0 != memcmp (&ph, &m->hmac, sizeof (GNUNET_HashCode))) { - GNUNET_PEERINFO_iterate_cancel (kx->pitr); - kx->pitr = NULL; + /* checksum failed */ + GNUNET_break_op (0); + return; } - if (kx->retry_set_key_task != GNUNET_SCHEDULER_NO_TASK) - GNUNET_SCHEDULER_cancel (kx->retry_set_key_task); - GNUNET_free_non_null (kx->public_key); - GNUNET_free (kx); + derive_iv (&iv, &n->decrypt_key, m->iv_seed, &my_identity); + /* decrypt */ + if (GNUNET_OK != + do_decrypt (n, &iv, &m->sequence_number, &buf[ENCRYPTED_HEADER_SIZE], + size - ENCRYPTED_HEADER_SIZE)) + return; + pt = (struct EncryptedMessage *) buf; + + /* validate sequence number */ + snum = ntohl (pt->sequence_number); + if (n->last_sequence_number_received == snum) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Received duplicate message, ignoring.\n"); + /* duplicate, ignore */ + GNUNET_STATISTICS_update (stats, + gettext_noop ("# bytes dropped (duplicates)"), + size, GNUNET_NO); + return; + } + if ((n->last_sequence_number_received > snum) && + (n->last_sequence_number_received - snum > 32)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Received ancient out of sequence message, ignoring.\n"); + /* ancient out of sequence, ignore */ + GNUNET_STATISTICS_update (stats, + gettext_noop + ("# bytes dropped (out of sequence)"), size, + GNUNET_NO); + return; + } + if (n->last_sequence_number_received > snum) + { + unsigned int rotbit = 1 << (n->last_sequence_number_received - snum - 1); + + if ((n->last_packets_bitmap & rotbit) != 0) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Received duplicate message, ignoring.\n"); + GNUNET_STATISTICS_update (stats, + gettext_noop ("# bytes dropped (duplicates)"), + size, GNUNET_NO); + /* duplicate, ignore */ + return; + } + n->last_packets_bitmap |= rotbit; + } + if (n->last_sequence_number_received < snum) + { + int shift = (snum - n->last_sequence_number_received); + + if (shift >= 8 * sizeof (n->last_packets_bitmap)) + n->last_packets_bitmap = 0; + else + n->last_packets_bitmap <<= shift; + n->last_sequence_number_received = snum; + } + + /* check timestamp */ + t = GNUNET_TIME_absolute_ntoh (pt->timestamp); + if (GNUNET_TIME_absolute_get_duration (t).rel_value > + MAX_MESSAGE_AGE.rel_value) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Message received far too old (%llu ms). Content ignored.\n"), + GNUNET_TIME_absolute_get_duration (t).rel_value); + GNUNET_STATISTICS_update (stats, + gettext_noop + ("# bytes dropped (ancient message)"), size, + GNUNET_NO); + return; + } + + /* process decrypted message(s) */ + if (n->bw_out_external_limit.value__ != pt->inbound_bw_limit.value__) + { +#if DEBUG_CORE_SET_QUOTA + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received %u b/s as new inbound limit for peer `%4s'\n", + (unsigned int) ntohl (pt->inbound_bw_limit.value__), + GNUNET_i2s (&n->peer)); +#endif + n->bw_out_external_limit = pt->inbound_bw_limit; + n->bw_out = + GNUNET_BANDWIDTH_value_min (n->bw_out_external_limit, + n->bw_out_internal_limit); + GNUNET_BANDWIDTH_tracker_update_quota (&n->available_send_window, + n->bw_out); + GNUNET_TRANSPORT_set_quota (transport, &n->peer, n->bw_in, n->bw_out); + } + n->last_activity = GNUNET_TIME_absolute_get (); + if (n->keep_alive_task != GNUNET_SCHEDULER_NO_TASK) + GNUNET_SCHEDULER_cancel (n->keep_alive_task); + n->keep_alive_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_divide + (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, + 2), &send_keep_alive, n); + GNUNET_STATISTICS_update (stats, + gettext_noop ("# bytes of payload decrypted"), + size - sizeof (struct EncryptedMessage), GNUNET_NO); + handle_peer_status_change (n); + update_neighbour_performance (n, ats, ats_count); + if (GNUNET_OK != + GNUNET_SERVER_mst_receive (mst, n, &buf[sizeof (struct EncryptedMessage)], + size - sizeof (struct EncryptedMessage), + GNUNET_YES, GNUNET_NO)) + GNUNET_break_op (0); } + + +/** + * Initialize KX subsystem. + * + * @return GNUNET_OK on success, GNUNET_SYSERR on failure + */ int GSC_KX_init () { + char *keyfile; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (GSC_cfg, "GNUNETD", "HOSTKEY", + &keyfile)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Core service is lacking HOSTKEY configuration setting. Exiting.\n")); + return GNUNET_SYSERR; + } + 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, + _("Core service could not access hostkey. Exiting.\n")); + return GNUNET_SYSERR; + } + GNUNET_CRYPTO_rsa_key_get_public (my_private_key, &my_public_key); + GNUNET_CRYPTO_hash (&my_public_key, sizeof (my_public_key), + &my_identity.hashPubKey); peerinfo = GNUNET_PEERINFO_connect (cfg); if (NULL == peerinfo) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Could not access PEERINFO service. Exiting.\n")); + GNUNET_CRYPTO_rsa_key_free (my_private_key); + my_private_key = NULL; return GNUNET_SYSERR; } return GNUNET_OK; } +/** + * Shutdown KX subsystem. + */ void GSC_KX_done () { + if (my_private_key != NULL) + { + GNUNET_CRYPTO_rsa_key_free (my_private_key); + my_private_key = NULL; + } if (peerinfo != NULL) { GNUNET_PEERINFO_disconnect (peerinfo); peerinfo = NULL; } - } + +/* end of gnunet-service-core_kx.c */ diff --git a/src/core/gnunet-service-core_kx.h b/src/core/gnunet-service-core_kx.h index f4f1daaeb..b5b9b712e 100644 --- a/src/core/gnunet-service-core_kx.h +++ b/src/core/gnunet-service-core_kx.h @@ -1,3 +1,37 @@ +/* + 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 core/gnunet-service-core_kx.h + * @brief code for managing the key exchange (SET_KEY, PING, PONG) with other peers + * @author Christian Grothoff + */ +#ifndef GNUNET_SERVICE_CORE_KX_H +#define GNUNET_SERVICE_CORE_KX_H + +#include "gnunet_util_lib.h" + + +/** + * Information about the status of a key exchange with another peer. + */ struct GSC_KeyExchangeInfo { @@ -75,3 +109,117 @@ struct GSC_KeyExchangeInfo enum PeerStateMachine status; }; + + +/** + * We received a SET_KEY message. Validate and update + * our key material and status. + * + * @param kx key exchange status for the corresponding peer + * @param msg the set key message we received + * @param ats performance data + * @param ats_count number of entries in ats (excluding 0-termination) + */ +void +GSC_KX_handle_set_key (struct GSC_KeyExchangeInfo *n, + const struct GNUNET_MessageHandler *msg, + const struct GNUNET_TRANSPORT_ATS_Information *ats, + uint32_t ats_count); + + +/** + * We received a PING message. Validate and transmit + * a PONG message. + * + * @param kx key exchange status for the corresponding peer + * @param msg the encrypted PING message itself + * @param ats performance data + * @param ats_count number of entries in ats (excluding 0-termination) + */ +void +GSC_KX_handle_ping (struct GSC_KeyExchangeInfo *kx, + const struct GNUNET_MessageHeader *msg, + const struct GNUNET_TRANSPORT_ATS_Information *ats, + uint32_t ats_count); + + +/** + * We received a PONG message. Validate and update our status. + * + * @param kx key exchange status for the corresponding peer + * @param msg the encrypted PONG message itself + * @param ats performance data + * @param ats_count number of entries in ats (excluding 0-termination) + */ +void +GSC_KX_handle_pong (struct GSC_KeyExchangeInfo *kx, + const struct GNUNET_MessageHeader *msg, + const struct GNUNET_TRANSPORT_ATS_Information *ats, + uint32_t ats_count); + + +/** + * Encrypt and transmit a message with the given payload. + * + * @param kx key exchange context + * @param payload payload of the message + * @param payload_size number of bytes in 'payload' + */ +void +GSC_KX_encrypt_and_transmit (struct GSC_KeyExchangeInfo *kx, + const void *payload, + size_t payload_size); + + +/** + * We received an encrypted message. Decrypt, validate and + * pass on to the appropriate clients. + * + * @param kx key exchange information context + * @param msg encrypted message + * @param ats performance data + * @param ats_count number of entries in ats (excluding 0-termination) + */ +void +GSC_KX_handle_encrypted_message (struct GSC_KeyExchangeInfo *kx, + const struct GNUNET_MessageHeader *msg, + const struct GNUNET_TRANSPORT_ATS_Information *ats, + uint32_t ats_count); + + +/** + * Start the key exchange with the given peer. + * + * @param pid identity of the peer to do a key exchange with + * @return key exchange information context + */ +struct GSC_KeyExchangeInfo * +GSC_KX_start (const struct GNUNET_PeerIdentity *pid); + + +/** + * Stop key exchange with the given peer. Clean up key material. + * + * @param kx key exchange to stop + */ +void +GSC_KX_stop (struct GSC_KeyExchangeInfo *kx); + + +/** + * Initialize KX subsystem. + * + * @return GNUNET_OK on success, GNUNET_SYSERR on failure + */ +int +GSC_KX_init (void); + + +/** + * Shutdown KX subsystem. + */ +void +GSC_KX_done (void); + +#endif +/* end of gnunet-service-core_kx.h */ diff --git a/src/core/gnunet-service-core_neighbours.c b/src/core/gnunet-service-core_neighbours.c index 12d002da8..0e50454d1 100644 --- a/src/core/gnunet-service-core_neighbours.c +++ b/src/core/gnunet-service-core_neighbours.c @@ -27,8 +27,9 @@ #include "gnunet_util_lib.h" #include "gnunet_transport_service.h" #include "gnunet_service_core.h" -#include "gnunet_service_core-neighbours.h" -#include "gnunet_service_core-kx.h" +#include "gnunet_service_core_neighbours.h" +#include "gnunet_service_core_kx.h" +#include "gnunet_service_core_sessions.h" /** @@ -112,7 +113,6 @@ struct Neighbour */ struct GNUNET_BANDWIDTH_Tracker available_recv_window; - }; @@ -127,7 +127,6 @@ static struct GNUNET_CONTAINER_MultiHashMap *neighbours; static struct GNUNET_TRANSPORT_Handle *transport; - /** * Find the entry for the given neighbour. * @@ -167,6 +166,7 @@ free_neighbour (struct Neighbour *n) GNUNET_TRANSPORT_notify_transmit_ready_cancel (n->th); n->th = NULL; } + GSC_SESSION_end (&n->peer); if (NULL != n->kx) { GSC_KX_stop (n->kx); @@ -219,11 +219,7 @@ transmit_ready (void *cls, size_t size, void *buf) m = n->message_head; if (m == NULL) { -#if DEBUG_CORE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Encrypted message queue empty, no messages added to buffer for `%4s'\n", - GNUNET_i2s (&n->peer)); -#endif + GNUNET_break (0); return 0; } GNUNET_CONTAINER_DLL_remove (n->encrypted_head, n->encrypted_tail, m); @@ -278,7 +274,12 @@ process_queue (struct Neighbour *n) return; /* request already pending */ m = n->message_head; if (m == NULL) + { + /* notify sessions that the queue is empty and more messages + could thus be queued now */ + GSC_SESSIONS_solicit (&n->peer); return; + } #if DEBUG_CORE > 1 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Asking transport for transmission of %u bytes to `%4s' in next %llu ms\n", @@ -404,11 +405,7 @@ handle_transport_receive (void *cls, const struct GNUNET_PeerIdentity *peer, uint32_t ats_count) { struct Neighbour *n; - struct GNUNET_TIME_Absolute now; - int up; uint16_t type; - uint16_t size; - int changed; #if DEBUG_CORE > 1 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, @@ -427,89 +424,23 @@ handle_transport_receive (void *cls, const struct GNUNET_PeerIdentity *peer, GNUNET_break (0); return; } - - - changed = GNUNET_NO; - up = (n->status == PEER_STATE_KEY_CONFIRMED); type = ntohs (message->type); - size = ntohs (message->size); switch (type) { case GNUNET_MESSAGE_TYPE_CORE_SET_KEY: - if (size != sizeof (struct SetKeyMessage)) - { - GNUNET_break_op (0); - return; - } - GNUNET_STATISTICS_update (stats, gettext_noop ("# session keys received"), - 1, GNUNET_NO); - handle_set_key (n, (const struct SetKeyMessage *) message, ats, ats_count); - break; - case GNUNET_MESSAGE_TYPE_CORE_ENCRYPTED_MESSAGE: - if (size < - sizeof (struct EncryptedMessage) + sizeof (struct GNUNET_MessageHeader)) - { - GNUNET_break_op (0); - return; - } - if ((n->status != PEER_STATE_KEY_RECEIVED) && - (n->status != PEER_STATE_KEY_CONFIRMED)) - { - GNUNET_STATISTICS_update (stats, - gettext_noop - ("# failed to decrypt message (no session key)"), - 1, GNUNET_NO); - send_key (n); - return; - } - handle_encrypted_message (n, (const struct EncryptedMessage *) message, ats, - ats_count); + GSC_KX_handle_set_key (n->kxinfo, message, ats, ats_count); break; case GNUNET_MESSAGE_TYPE_CORE_PING: - if (size != sizeof (struct PingMessage)) - { - GNUNET_break_op (0); - return; - } - GNUNET_STATISTICS_update (stats, gettext_noop ("# PING messages received"), - 1, GNUNET_NO); - if ((n->status != PEER_STATE_KEY_RECEIVED) && - (n->status != PEER_STATE_KEY_CONFIRMED)) - { -#if DEBUG_CORE > 1 - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Core service receives `%s' request from `%4s' but have not processed key; marking as pending.\n", - "PING", GNUNET_i2s (&n->peer)); -#endif - GNUNET_free_non_null (n->pending_ping); - n->pending_ping = GNUNET_malloc (sizeof (struct PingMessage)); - memcpy (n->pending_ping, message, sizeof (struct PingMessage)); - return; - } - handle_ping (n, (const struct PingMessage *) message, ats, ats_count); + GSC_KX_handle_ping (n->kxinfo, message, ats, ats_count); break; case GNUNET_MESSAGE_TYPE_CORE_PONG: - if (size != sizeof (struct PongMessage)) - { - GNUNET_break_op (0); - return; - } - GNUNET_STATISTICS_update (stats, gettext_noop ("# PONG messages received"), - 1, GNUNET_NO); - if ((n->status != PEER_STATE_KEY_RECEIVED) && - (n->status != PEER_STATE_KEY_CONFIRMED)) - { -#if DEBUG_CORE > 1 - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Core service receives `%s' request from `%4s' but have not processed key; marking as pending.\n", - "PONG", GNUNET_i2s (&n->peer)); -#endif - GNUNET_free_non_null (n->pending_pong); - n->pending_pong = GNUNET_malloc (sizeof (struct PongMessage)); - memcpy (n->pending_pong, message, sizeof (struct PongMessage)); - return; - } - handle_pong (n, (const struct PongMessage *) message, ats, ats_count); + GSC_KX_handle_pong (n->kxinfo, message, ats, ats_count); + break; + case GNUNET_MESSAGE_TYPE_CORE_ENCRYPTED_MESSAGE: + GSC_KX_handle_encrypted_message (peer, + n->kxinfo, + message, ats, + ats_count); break; default: GNUNET_log (GNUNET_ERROR_TYPE_WARNING, @@ -517,26 +448,6 @@ handle_transport_receive (void *cls, const struct GNUNET_PeerIdentity *peer, (unsigned int) type); return; } - if (n->status == PEER_STATE_KEY_CONFIRMED) - { - now = GNUNET_TIME_absolute_get (); - n->last_activity = now; - changed = GNUNET_YES; - if (!up) - { - GNUNET_STATISTICS_update (stats, gettext_noop ("# established sessions"), - 1, GNUNET_NO); - n->time_established = now; - } - if (n->keep_alive_task != GNUNET_SCHEDULER_NO_TASK) - GNUNET_SCHEDULER_cancel (n->keep_alive_task); - n->keep_alive_task = - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_divide - (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, - 2), &send_keep_alive, n); - } - if (changed) - handle_peer_status_change (n); } @@ -552,7 +463,25 @@ GDS_NEIGHBOURS_transmit (const struct GNUNET_PeerIdentity *target, const struct GNUNET_MessageHeader *msg, struct GNUNET_TIME_Relative timeout) { - + struct MessageEntry *me; + struct Neighbour *n; + size_t msize; + + n = find_neighbour (target); + if (NULL == n) + { + GNUNET_break (0); + return; + } + msize = ntohs (msg->size); + me = GNUNET_malloc (sizeof (struct MessageEntry) + msize); + me->deadline = GNUNET_TIME_relative_to_absolute (timeout); + me->size = msize; + memcpy (&me[1], msg, msize); + GNUNET_CONTAINER_DLL_insert (n->message_head, + n->message_tail, + me); + process_queue (n); } diff --git a/src/core/gnunet-service-core_neighbours.h b/src/core/gnunet-service-core_neighbours.h new file mode 100644 index 000000000..8dad1aafe --- /dev/null +++ b/src/core/gnunet-service-core_neighbours.h @@ -0,0 +1,63 @@ +/* + 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 core/gnunet-service-core_neighbours.h + * @brief code for managing low-level 'plaintext' connections with transport (key exchange may or may not be done yet) + * @author Christian Grothoff + */ +#ifndef GNUNET_SERVICE_CORE_NEIGHBOURS_H +#define GNUNET_SERVICE_CORE_NEIGHBOURS_H + +#include "gnunet_util_lib.h" + +/** + * Transmit the given message to the given target. Note that a + * non-control messages should only be transmitted after a + * 'GSC_SESSION_solicit' call was made (that call is always invoked + * when the message queue is empty). Outbound quotas and memory + * bounds will then be enfoced (as GSC_SESSION_solicit is only called + * if sufficient banwdith is available). + * + * @param target peer that should receive the message (must be connected) + * @param msg message to transmit + * @param timeout by when should the transmission be done? + */ +void +GDS_NEIGHBOURS_transmit (const struct GNUNET_PeerIdentity *target, + const struct GNUNET_MessageHeader *msg, + struct GNUNET_TIME_Relative timeout); + + +/** + * Initialize neighbours subsystem. + */ +int +GSC_NEIGHBOURS_init (void); + + +/** + * Shutdown neighbours subsystem. + */ +void +GSC_NEIGHBOURS_done (void); + + +#endif diff --git a/src/core/gnunet-service-core_plan.c b/src/core/gnunet-service-core_plan.c deleted file mode 100644 index 580038e08..000000000 --- a/src/core/gnunet-service-core_plan.c +++ /dev/null @@ -1,563 +0,0 @@ - - - -/** - * Select messages for transmission. This heuristic uses a combination - * of earliest deadline first (EDF) scheduling (with bounded horizon) - * and priority-based discard (in case no feasible schedule exist) and - * speculative optimization (defer any kind of transmission until - * we either create a batch of significant size, 25% of max, or until - * we are close to a deadline). Furthermore, when scheduling the - * heuristic also packs as many messages into the batch as possible, - * starting with those with the earliest deadline. Yes, this is fun. - * - * @param n neighbour to select messages from - * @param size number of bytes to select for transmission - * @param retry_time set to the time when we should try again - * (only valid if this function returns zero) - * @return number of bytes selected, or 0 if we decided to - * defer scheduling overall; in that case, retry_time is set. - */ -static size_t -select_messages (struct Neighbour *n, size_t size, - struct GNUNET_TIME_Relative *retry_time) -{ - struct MessageEntry *pos; - struct MessageEntry *min; - struct MessageEntry *last; - unsigned int min_prio; - struct GNUNET_TIME_Absolute t; - struct GNUNET_TIME_Absolute now; - struct GNUNET_TIME_Relative delta; - uint64_t avail; - struct GNUNET_TIME_Relative slack; /* how long could we wait before missing deadlines? */ - size_t off; - uint64_t tsize; - unsigned int queue_size; - int discard_low_prio; - - GNUNET_assert (NULL != n->messages); - now = GNUNET_TIME_absolute_get (); - /* last entry in linked list of messages processed */ - last = NULL; - /* should we remove the entry with the lowest - * priority from consideration for scheduling at the - * end of the loop? */ - queue_size = 0; - tsize = 0; - pos = n->messages; - while (pos != NULL) - { - queue_size++; - tsize += pos->size; - pos = pos->next; - } - discard_low_prio = GNUNET_YES; - while (GNUNET_YES == discard_low_prio) - { - min = NULL; - min_prio = UINT_MAX; - discard_low_prio = GNUNET_NO; - /* calculate number of bytes available for transmission at time "t" */ - avail = GNUNET_BANDWIDTH_tracker_get_available (&n->available_send_window); - t = now; - /* how many bytes have we (hypothetically) scheduled so far */ - off = 0; - /* maximum time we can wait before transmitting anything - * and still make all of our deadlines */ - slack = GNUNET_TIME_UNIT_FOREVER_REL; - pos = n->messages; - /* note that we use "*2" here because we want to look - * a bit further into the future; much more makes no - * sense since new message might be scheduled in the - * meantime... */ - while ((pos != NULL) && (off < size * 2)) - { - if (pos->do_transmit == GNUNET_YES) - { - /* already removed from consideration */ - pos = pos->next; - continue; - } - if (discard_low_prio == GNUNET_NO) - { - delta = GNUNET_TIME_absolute_get_difference (t, pos->deadline); - if (delta.rel_value > 0) - { - // FIXME: HUH? Check! - t = pos->deadline; - avail += - GNUNET_BANDWIDTH_value_get_available_until (n->bw_out, delta); - } - if (avail < pos->size) - { - // FIXME: HUH? Check! - discard_low_prio = GNUNET_YES; /* we could not schedule this one! */ - } - else - { - avail -= pos->size; - /* update slack, considering both its absolute deadline - * and relative deadlines caused by other messages - * with their respective load */ - slack = - GNUNET_TIME_relative_min (slack, - GNUNET_BANDWIDTH_value_get_delay_for - (n->bw_out, avail)); - if (pos->deadline.abs_value <= now.abs_value) - { - /* now or never */ - slack = GNUNET_TIME_UNIT_ZERO; - } - else if (GNUNET_YES == pos->got_slack) - { - /* should be soon now! */ - slack = - GNUNET_TIME_relative_min (slack, - GNUNET_TIME_absolute_get_remaining - (pos->slack_deadline)); - } - else - { - slack = - GNUNET_TIME_relative_min (slack, - GNUNET_TIME_absolute_get_difference - (now, pos->deadline)); - pos->got_slack = GNUNET_YES; - pos->slack_deadline = - GNUNET_TIME_absolute_min (pos->deadline, - GNUNET_TIME_relative_to_absolute - (GNUNET_CONSTANTS_MAX_CORK_DELAY)); - } - } - } - off += pos->size; - t = GNUNET_TIME_absolute_max (pos->deadline, t); // HUH? Check! - if (pos->priority <= min_prio) - { - /* update min for discard */ - min_prio = pos->priority; - min = pos; - } - pos = pos->next; - } - if (discard_low_prio) - { - GNUNET_assert (min != NULL); - /* remove lowest-priority entry from consideration */ - min->do_transmit = GNUNET_YES; /* means: discard (for now) */ - } - last = pos; - } - /* guard against sending "tiny" messages with large headers without - * urgent deadlines */ - if ((slack.rel_value > GNUNET_CONSTANTS_MAX_CORK_DELAY.rel_value) && - (size > 4 * off) && (queue_size <= MAX_PEER_QUEUE_SIZE - 2)) - { - /* less than 25% of message would be filled with deadlines still - * being met if we delay by one second or more; so just wait for - * more data; but do not wait longer than 1s (since we don't want - * to delay messages for a really long time either). */ - *retry_time = GNUNET_CONSTANTS_MAX_CORK_DELAY; - /* reset do_transmit values for next time */ - while (pos != last) - { - pos->do_transmit = GNUNET_NO; - pos = pos->next; - } - GNUNET_STATISTICS_update (stats, - gettext_noop - ("# transmissions delayed due to corking"), 1, - GNUNET_NO); -#if DEBUG_CORE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Deferring transmission for %llums due to underfull message buffer size (%u/%u)\n", - (unsigned long long) retry_time->rel_value, (unsigned int) off, - (unsigned int) size); -#endif - return 0; - } - /* select marked messages (up to size) for transmission */ - off = 0; - pos = n->messages; - while (pos != last) - { - if ((pos->size <= size) && (pos->do_transmit == GNUNET_NO)) - { - pos->do_transmit = GNUNET_YES; /* mark for transmission */ - off += pos->size; - size -= pos->size; -#if DEBUG_CORE > 1 - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Selecting message of size %u for transmission\n", - (unsigned int) pos->size); -#endif - } - else - { -#if DEBUG_CORE > 1 - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Not selecting message of size %u for transmission at this time (maximum is %u)\n", - (unsigned int) pos->size, size); -#endif - pos->do_transmit = GNUNET_NO; /* mark for not transmitting! */ - } - pos = pos->next; - } -#if DEBUG_CORE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Selected %llu/%llu bytes of %u/%u plaintext messages for transmission to `%4s'.\n", - (unsigned long long) off, (unsigned long long) tsize, queue_size, - (unsigned int) MAX_PEER_QUEUE_SIZE, GNUNET_i2s (&n->peer)); -#endif - return off; -} - - -/** - * Batch multiple messages into a larger buffer. - * - * @param n neighbour to take messages from - * @param buf target buffer - * @param size size of buf - * @param deadline set to transmission deadline for the result - * @param retry_time set to the time when we should try again - * (only valid if this function returns zero) - * @param priority set to the priority of the batch - * @return number of bytes written to buf (can be zero) - */ -static size_t -batch_message (struct Neighbour *n, char *buf, size_t size, - struct GNUNET_TIME_Absolute *deadline, - struct GNUNET_TIME_Relative *retry_time, unsigned int *priority) -{ - char ntmb[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1]; - struct NotifyTrafficMessage *ntm = (struct NotifyTrafficMessage *) ntmb; - struct MessageEntry *pos; - struct MessageEntry *prev; - struct MessageEntry *next; - size_t ret; - - ret = 0; - *priority = 0; - *deadline = GNUNET_TIME_UNIT_FOREVER_ABS; - *retry_time = GNUNET_TIME_UNIT_FOREVER_REL; - if (0 == select_messages (n, size, retry_time)) - { -#if DEBUG_CORE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "No messages selected, will try again in %llu ms\n", - retry_time->rel_value); -#endif - return 0; - } - ntm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_OUTBOUND); - ntm->ats_count = htonl (0); - ntm->ats.type = htonl (0); - ntm->ats.value = htonl (0); - ntm->peer = n->peer; - pos = n->messages; - prev = NULL; - while ((pos != NULL) && (size >= sizeof (struct GNUNET_MessageHeader))) - { - next = pos->next; - if (GNUNET_YES == pos->do_transmit) - { - GNUNET_assert (pos->size <= size); - /* do notifications */ - /* FIXME: track if we have *any* client that wants - * full notifications and only do this if that is - * actually true */ - if (pos->size < - GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct NotifyTrafficMessage)) - { - memcpy (&ntm[1], &pos[1], pos->size); - ntm->header.size = - htons (sizeof (struct NotifyTrafficMessage) + - sizeof (struct GNUNET_MessageHeader)); - send_to_all_clients (&ntm->header, GNUNET_YES, - GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND); - } - else - { - /* message too large for 'full' notifications, we do at - * least the 'hdr' type */ - memcpy (&ntm[1], &pos[1], sizeof (struct GNUNET_MessageHeader)); - } - ntm->header.size = - htons (sizeof (struct NotifyTrafficMessage) + pos->size); - send_to_all_clients (&ntm->header, GNUNET_YES, - GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND); -#if DEBUG_HANDSHAKE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Encrypting %u bytes with message of type %u and size %u\n", - pos->size, - (unsigned int) - ntohs (((const struct GNUNET_MessageHeader *) &pos[1])->type), - (unsigned int) - ntohs (((const struct GNUNET_MessageHeader *) - &pos[1])->size)); -#endif - /* copy for encrypted transmission */ - memcpy (&buf[ret], &pos[1], pos->size); - ret += pos->size; - size -= pos->size; - *priority += pos->priority; -#if DEBUG_CORE > 1 - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Adding plaintext message of size %u with deadline %llu ms to batch\n", - (unsigned int) pos->size, - (unsigned long long) - GNUNET_TIME_absolute_get_remaining (pos->deadline).rel_value); -#endif - deadline->abs_value = - GNUNET_MIN (deadline->abs_value, pos->deadline.abs_value); - GNUNET_free (pos); - if (prev == NULL) - n->messages = next; - else - prev->next = next; - } - else - { - prev = pos; - } - pos = next; - } -#if DEBUG_CORE > 1 - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Deadline for message batch is %llu ms\n", - GNUNET_TIME_absolute_get_remaining (*deadline).rel_value); -#endif - return ret; -} - - -/** - * Remove messages with deadlines that have long expired from - * the queue. - * - * @param n neighbour to inspect - */ -static void -discard_expired_messages (struct Neighbour *n) -{ - struct MessageEntry *prev; - struct MessageEntry *next; - struct MessageEntry *pos; - struct GNUNET_TIME_Absolute now; - struct GNUNET_TIME_Relative delta; - int disc; - unsigned int queue_length; - - disc = GNUNET_NO; - now = GNUNET_TIME_absolute_get (); - prev = NULL; - queue_length = 0; - pos = n->messages; - while (pos != NULL) - { - queue_length++; - next = pos->next; - delta = GNUNET_TIME_absolute_get_difference (pos->deadline, now); - if (delta.rel_value > PAST_EXPIRATION_DISCARD_TIME.rel_value) - { -#if DEBUG_CORE - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Message is %llu ms past due, discarding.\n", - delta.rel_value); -#endif - if (prev == NULL) - n->messages = next; - else - prev->next = next; - GNUNET_STATISTICS_update (stats, - gettext_noop - ("# messages discarded (expired prior to transmission)"), - 1, GNUNET_NO); - disc = GNUNET_YES; - GNUNET_free (pos); - } - else - prev = pos; - pos = next; - } - if ( (GNUNET_YES == disc) && - (queue_length == MAX_PEER_QUEUE_SIZE) ) - schedule_peer_messages (n); -} - - -/** - * Signature of the main function of a task. - * - * @param cls closure - * @param tc context information (why was this task triggered now) - */ -static void -retry_plaintext_processing (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - struct Neighbour *n = cls; - - n->retry_plaintext_task = GNUNET_SCHEDULER_NO_TASK; - process_plaintext_neighbour_queue (n); -} - - -/** - * Check if we have plaintext messages for the specified neighbour - * pending, and if so, consider batching and encrypting them (and - * then trigger processing of the encrypted queue if needed). - * - * @param n neighbour to check. - */ -static void -process_plaintext_neighbour_queue (struct Neighbour *n) -{ - char pbuf[GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE + sizeof (struct EncryptedMessage)]; /* plaintext */ - size_t used; - struct EncryptedMessage *em; /* encrypted message */ - struct EncryptedMessage *ph; /* plaintext header */ - struct MessageEntry *me; - unsigned int priority; - struct GNUNET_TIME_Absolute deadline; - struct GNUNET_TIME_Relative retry_time; - struct GNUNET_CRYPTO_AesInitializationVector iv; - struct GNUNET_CRYPTO_AuthKey auth_key; - - if (n->retry_plaintext_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (n->retry_plaintext_task); - n->retry_plaintext_task = GNUNET_SCHEDULER_NO_TASK; - } - switch (n->status) - { - case PEER_STATE_DOWN: - send_key (n); -#if DEBUG_CORE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Not yet connected to `%4s', deferring processing of plaintext messages.\n", - GNUNET_i2s (&n->peer)); -#endif - return; - case PEER_STATE_KEY_SENT: - if (n->retry_set_key_task == GNUNET_SCHEDULER_NO_TASK) - n->retry_set_key_task = - GNUNET_SCHEDULER_add_delayed (n->set_key_retry_frequency, - &set_key_retry_task, n); -#if DEBUG_CORE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Not yet connected to `%4s', deferring processing of plaintext messages.\n", - GNUNET_i2s (&n->peer)); -#endif - return; - case PEER_STATE_KEY_RECEIVED: - if (n->retry_set_key_task == GNUNET_SCHEDULER_NO_TASK) - n->retry_set_key_task = - GNUNET_SCHEDULER_add_delayed (n->set_key_retry_frequency, - &set_key_retry_task, n); -#if DEBUG_CORE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Not yet connected to `%4s', deferring processing of plaintext messages.\n", - GNUNET_i2s (&n->peer)); -#endif - return; - case PEER_STATE_KEY_CONFIRMED: - /* ready to continue */ - break; - } - discard_expired_messages (n); - if (n->messages == NULL) - { -#if DEBUG_CORE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Plaintext message queue for `%4s' is empty.\n", - GNUNET_i2s (&n->peer)); -#endif - return; /* no pending messages */ - } - if (n->encrypted_head != NULL) - { -#if DEBUG_CORE > 2 - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Encrypted message queue for `%4s' is still full, delaying plaintext processing.\n", - GNUNET_i2s (&n->peer)); -#endif - return; /* wait for messages already encrypted to be - * processed first! */ - } - ph = (struct EncryptedMessage *) pbuf; - deadline = GNUNET_TIME_UNIT_FOREVER_ABS; - priority = 0; - used = sizeof (struct EncryptedMessage); - used += - batch_message (n, &pbuf[used], - GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE, &deadline, - &retry_time, &priority); - if (used == sizeof (struct EncryptedMessage)) - { -#if DEBUG_CORE > 1 - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "No messages selected for transmission to `%4s' at this time, will try again later.\n", - GNUNET_i2s (&n->peer)); -#endif - /* no messages selected for sending, try again later... */ - n->retry_plaintext_task = - GNUNET_SCHEDULER_add_delayed (retry_time, &retry_plaintext_processing, - n); - return; - } -#if DEBUG_CORE_QUOTA - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending %u b/s as new limit to peer `%4s'\n", - (unsigned int) ntohl (n->bw_in.value__), GNUNET_i2s (&n->peer)); -#endif - ph->iv_seed = - htonl (GNUNET_CRYPTO_random_u32 - (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX)); - ph->sequence_number = htonl (++n->last_sequence_number_sent); - ph->inbound_bw_limit = n->bw_in; - ph->timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ()); - - /* setup encryption message header */ - me = GNUNET_malloc (sizeof (struct MessageEntry) + used); - me->deadline = deadline; - me->priority = priority; - me->size = used; - em = (struct EncryptedMessage *) &me[1]; - em->header.size = htons (used); - em->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_ENCRYPTED_MESSAGE); - em->iv_seed = ph->iv_seed; - derive_iv (&iv, &n->encrypt_key, ph->iv_seed, &n->peer); - /* encrypt */ -#if DEBUG_HANDSHAKE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Encrypting %u bytes of plaintext messages for `%4s' for transmission in %llums.\n", - (unsigned int) used - ENCRYPTED_HEADER_SIZE, - GNUNET_i2s (&n->peer), - (unsigned long long) - GNUNET_TIME_absolute_get_remaining (deadline).rel_value); -#endif - GNUNET_assert (GNUNET_OK == - do_encrypt (n, &iv, &ph->sequence_number, &em->sequence_number, - used - ENCRYPTED_HEADER_SIZE)); - derive_auth_key (&auth_key, &n->encrypt_key, ph->iv_seed, - n->encrypt_key_created); - GNUNET_CRYPTO_hmac (&auth_key, &em->sequence_number, - used - ENCRYPTED_HEADER_SIZE, &em->hmac); -#if DEBUG_HANDSHAKE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Authenticated %u bytes of ciphertext %u: `%s'\n", - used - ENCRYPTED_HEADER_SIZE, - GNUNET_CRYPTO_crc32_n (&em->sequence_number, - used - ENCRYPTED_HEADER_SIZE), - GNUNET_h2s (&em->hmac)); -#endif - /* append to transmission list */ - GNUNET_CONTAINER_DLL_insert_after (n->encrypted_head, n->encrypted_tail, - n->encrypted_tail, me); - process_encrypted_neighbour_queue (n); - schedule_peer_messages (n); -} - diff --git a/src/core/gnunet-service-core_sessions.c b/src/core/gnunet-service-core_sessions.c index a9dcb1968..1c1332bd4 100644 --- a/src/core/gnunet-service-core_sessions.c +++ b/src/core/gnunet-service-core_sessions.c @@ -584,6 +584,525 @@ notify_encrypted_transmit_ready (void *cls, size_t size, void *buf) } + + + +/** + * Select messages for transmission. This heuristic uses a combination + * of earliest deadline first (EDF) scheduling (with bounded horizon) + * and priority-based discard (in case no feasible schedule exist) and + * speculative optimization (defer any kind of transmission until + * we either create a batch of significant size, 25% of max, or until + * we are close to a deadline). Furthermore, when scheduling the + * heuristic also packs as many messages into the batch as possible, + * starting with those with the earliest deadline. Yes, this is fun. + * + * @param n neighbour to select messages from + * @param size number of bytes to select for transmission + * @param retry_time set to the time when we should try again + * (only valid if this function returns zero) + * @return number of bytes selected, or 0 if we decided to + * defer scheduling overall; in that case, retry_time is set. + */ +static size_t +select_messages (struct Neighbour *n, size_t size, + struct GNUNET_TIME_Relative *retry_time) +{ + struct MessageEntry *pos; + struct MessageEntry *min; + struct MessageEntry *last; + unsigned int min_prio; + struct GNUNET_TIME_Absolute t; + struct GNUNET_TIME_Absolute now; + struct GNUNET_TIME_Relative delta; + uint64_t avail; + struct GNUNET_TIME_Relative slack; /* how long could we wait before missing deadlines? */ + size_t off; + uint64_t tsize; + unsigned int queue_size; + int discard_low_prio; + + GNUNET_assert (NULL != n->messages); + now = GNUNET_TIME_absolute_get (); + /* last entry in linked list of messages processed */ + last = NULL; + /* should we remove the entry with the lowest + * priority from consideration for scheduling at the + * end of the loop? */ + queue_size = 0; + tsize = 0; + pos = n->messages; + while (pos != NULL) + { + queue_size++; + tsize += pos->size; + pos = pos->next; + } + discard_low_prio = GNUNET_YES; + while (GNUNET_YES == discard_low_prio) + { + min = NULL; + min_prio = UINT_MAX; + discard_low_prio = GNUNET_NO; + /* calculate number of bytes available for transmission at time "t" */ + avail = GNUNET_BANDWIDTH_tracker_get_available (&n->available_send_window); + t = now; + /* how many bytes have we (hypothetically) scheduled so far */ + off = 0; + /* maximum time we can wait before transmitting anything + * and still make all of our deadlines */ + slack = GNUNET_TIME_UNIT_FOREVER_REL; + pos = n->messages; + /* note that we use "*2" here because we want to look + * a bit further into the future; much more makes no + * sense since new message might be scheduled in the + * meantime... */ + while ((pos != NULL) && (off < size * 2)) + { + if (pos->do_transmit == GNUNET_YES) + { + /* already removed from consideration */ + pos = pos->next; + continue; + } + if (discard_low_prio == GNUNET_NO) + { + delta = GNUNET_TIME_absolute_get_difference (t, pos->deadline); + if (delta.rel_value > 0) + { + // FIXME: HUH? Check! + t = pos->deadline; + avail += + GNUNET_BANDWIDTH_value_get_available_until (n->bw_out, delta); + } + if (avail < pos->size) + { + // FIXME: HUH? Check! + discard_low_prio = GNUNET_YES; /* we could not schedule this one! */ + } + else + { + avail -= pos->size; + /* update slack, considering both its absolute deadline + * and relative deadlines caused by other messages + * with their respective load */ + slack = + GNUNET_TIME_relative_min (slack, + GNUNET_BANDWIDTH_value_get_delay_for + (n->bw_out, avail)); + if (pos->deadline.abs_value <= now.abs_value) + { + /* now or never */ + slack = GNUNET_TIME_UNIT_ZERO; + } + else if (GNUNET_YES == pos->got_slack) + { + /* should be soon now! */ + slack = + GNUNET_TIME_relative_min (slack, + GNUNET_TIME_absolute_get_remaining + (pos->slack_deadline)); + } + else + { + slack = + GNUNET_TIME_relative_min (slack, + GNUNET_TIME_absolute_get_difference + (now, pos->deadline)); + pos->got_slack = GNUNET_YES; + pos->slack_deadline = + GNUNET_TIME_absolute_min (pos->deadline, + GNUNET_TIME_relative_to_absolute + (GNUNET_CONSTANTS_MAX_CORK_DELAY)); + } + } + } + off += pos->size; + t = GNUNET_TIME_absolute_max (pos->deadline, t); // HUH? Check! + if (pos->priority <= min_prio) + { + /* update min for discard */ + min_prio = pos->priority; + min = pos; + } + pos = pos->next; + } + if (discard_low_prio) + { + GNUNET_assert (min != NULL); + /* remove lowest-priority entry from consideration */ + min->do_transmit = GNUNET_YES; /* means: discard (for now) */ + } + last = pos; + } + /* guard against sending "tiny" messages with large headers without + * urgent deadlines */ + if ((slack.rel_value > GNUNET_CONSTANTS_MAX_CORK_DELAY.rel_value) && + (size > 4 * off) && (queue_size <= MAX_PEER_QUEUE_SIZE - 2)) + { + /* less than 25% of message would be filled with deadlines still + * being met if we delay by one second or more; so just wait for + * more data; but do not wait longer than 1s (since we don't want + * to delay messages for a really long time either). */ + *retry_time = GNUNET_CONSTANTS_MAX_CORK_DELAY; + /* reset do_transmit values for next time */ + while (pos != last) + { + pos->do_transmit = GNUNET_NO; + pos = pos->next; + } + GNUNET_STATISTICS_update (stats, + gettext_noop + ("# transmissions delayed due to corking"), 1, + GNUNET_NO); +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Deferring transmission for %llums due to underfull message buffer size (%u/%u)\n", + (unsigned long long) retry_time->rel_value, (unsigned int) off, + (unsigned int) size); +#endif + return 0; + } + /* select marked messages (up to size) for transmission */ + off = 0; + pos = n->messages; + while (pos != last) + { + if ((pos->size <= size) && (pos->do_transmit == GNUNET_NO)) + { + pos->do_transmit = GNUNET_YES; /* mark for transmission */ + off += pos->size; + size -= pos->size; +#if DEBUG_CORE > 1 + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Selecting message of size %u for transmission\n", + (unsigned int) pos->size); +#endif + } + else + { +#if DEBUG_CORE > 1 + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Not selecting message of size %u for transmission at this time (maximum is %u)\n", + (unsigned int) pos->size, size); +#endif + pos->do_transmit = GNUNET_NO; /* mark for not transmitting! */ + } + pos = pos->next; + } +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Selected %llu/%llu bytes of %u/%u plaintext messages for transmission to `%4s'.\n", + (unsigned long long) off, (unsigned long long) tsize, queue_size, + (unsigned int) MAX_PEER_QUEUE_SIZE, GNUNET_i2s (&n->peer)); +#endif + return off; +} + + +/** + * Batch multiple messages into a larger buffer. + * + * @param n neighbour to take messages from + * @param buf target buffer + * @param size size of buf + * @param deadline set to transmission deadline for the result + * @param retry_time set to the time when we should try again + * (only valid if this function returns zero) + * @param priority set to the priority of the batch + * @return number of bytes written to buf (can be zero) + */ +static size_t +batch_message (struct Neighbour *n, char *buf, size_t size, + struct GNUNET_TIME_Absolute *deadline, + struct GNUNET_TIME_Relative *retry_time, unsigned int *priority) +{ + char ntmb[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1]; + struct NotifyTrafficMessage *ntm = (struct NotifyTrafficMessage *) ntmb; + struct MessageEntry *pos; + struct MessageEntry *prev; + struct MessageEntry *next; + size_t ret; + + ret = 0; + *priority = 0; + *deadline = GNUNET_TIME_UNIT_FOREVER_ABS; + *retry_time = GNUNET_TIME_UNIT_FOREVER_REL; + if (0 == select_messages (n, size, retry_time)) + { +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "No messages selected, will try again in %llu ms\n", + retry_time->rel_value); +#endif + return 0; + } + ntm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_OUTBOUND); + ntm->ats_count = htonl (0); + ntm->ats.type = htonl (0); + ntm->ats.value = htonl (0); + ntm->peer = n->peer; + pos = n->messages; + prev = NULL; + while ((pos != NULL) && (size >= sizeof (struct GNUNET_MessageHeader))) + { + next = pos->next; + if (GNUNET_YES == pos->do_transmit) + { + GNUNET_assert (pos->size <= size); + /* do notifications */ + /* FIXME: track if we have *any* client that wants + * full notifications and only do this if that is + * actually true */ + if (pos->size < + GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct NotifyTrafficMessage)) + { + memcpy (&ntm[1], &pos[1], pos->size); + ntm->header.size = + htons (sizeof (struct NotifyTrafficMessage) + + sizeof (struct GNUNET_MessageHeader)); + send_to_all_clients (&ntm->header, GNUNET_YES, + GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND); + } + else + { + /* message too large for 'full' notifications, we do at + * least the 'hdr' type */ + memcpy (&ntm[1], &pos[1], sizeof (struct GNUNET_MessageHeader)); + } + ntm->header.size = + htons (sizeof (struct NotifyTrafficMessage) + pos->size); + send_to_all_clients (&ntm->header, GNUNET_YES, + GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND); +#if DEBUG_HANDSHAKE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Encrypting %u bytes with message of type %u and size %u\n", + pos->size, + (unsigned int) + ntohs (((const struct GNUNET_MessageHeader *) &pos[1])->type), + (unsigned int) + ntohs (((const struct GNUNET_MessageHeader *) + &pos[1])->size)); +#endif + /* copy for encrypted transmission */ + memcpy (&buf[ret], &pos[1], pos->size); + ret += pos->size; + size -= pos->size; + *priority += pos->priority; +#if DEBUG_CORE > 1 + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Adding plaintext message of size %u with deadline %llu ms to batch\n", + (unsigned int) pos->size, + (unsigned long long) + GNUNET_TIME_absolute_get_remaining (pos->deadline).rel_value); +#endif + deadline->abs_value = + GNUNET_MIN (deadline->abs_value, pos->deadline.abs_value); + GNUNET_free (pos); + if (prev == NULL) + n->messages = next; + else + prev->next = next; + } + else + { + prev = pos; + } + pos = next; + } +#if DEBUG_CORE > 1 + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Deadline for message batch is %llu ms\n", + GNUNET_TIME_absolute_get_remaining (*deadline).rel_value); +#endif + return ret; +} + + +/** + * Remove messages with deadlines that have long expired from + * the queue. + * + * @param n neighbour to inspect + */ +static void +discard_expired_messages (struct Neighbour *n) +{ + struct MessageEntry *prev; + struct MessageEntry *next; + struct MessageEntry *pos; + struct GNUNET_TIME_Absolute now; + struct GNUNET_TIME_Relative delta; + int disc; + unsigned int queue_length; + + disc = GNUNET_NO; + now = GNUNET_TIME_absolute_get (); + prev = NULL; + queue_length = 0; + pos = n->messages; + while (pos != NULL) + { + queue_length++; + next = pos->next; + delta = GNUNET_TIME_absolute_get_difference (pos->deadline, now); + if (delta.rel_value > PAST_EXPIRATION_DISCARD_TIME.rel_value) + { +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Message is %llu ms past due, discarding.\n", + delta.rel_value); +#endif + if (prev == NULL) + n->messages = next; + else + prev->next = next; + GNUNET_STATISTICS_update (stats, + gettext_noop + ("# messages discarded (expired prior to transmission)"), + 1, GNUNET_NO); + disc = GNUNET_YES; + GNUNET_free (pos); + } + else + prev = pos; + pos = next; + } + if ( (GNUNET_YES == disc) && + (queue_length == MAX_PEER_QUEUE_SIZE) ) + schedule_peer_messages (n); +} + + +/** + * Signature of the main function of a task. + * + * @param cls closure + * @param tc context information (why was this task triggered now) + */ +static void +retry_plaintext_processing (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct Neighbour *n = cls; + + n->retry_plaintext_task = GNUNET_SCHEDULER_NO_TASK; + process_plaintext_neighbour_queue (n); +} + + +/** + * Check if we have plaintext messages for the specified neighbour + * pending, and if so, consider batching and encrypting them (and + * then trigger processing of the encrypted queue if needed). + * + * @param n neighbour to check. + */ +static void +process_plaintext_neighbour_queue (struct Neighbour *n) +{ + char pbuf[GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE + sizeof (struct EncryptedMessage)]; /* plaintext */ + size_t used; + struct EncryptedMessage *em; /* encrypted message */ + struct EncryptedMessage *ph; /* plaintext header */ + struct MessageEntry *me; + unsigned int priority; + struct GNUNET_TIME_Absolute deadline; + struct GNUNET_TIME_Relative retry_time; + struct GNUNET_CRYPTO_AesInitializationVector iv; + struct GNUNET_CRYPTO_AuthKey auth_key; + + if (n->retry_plaintext_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (n->retry_plaintext_task); + n->retry_plaintext_task = GNUNET_SCHEDULER_NO_TASK; + } + switch (n->status) + { + case PEER_STATE_DOWN: + send_key (n); +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Not yet connected to `%4s', deferring processing of plaintext messages.\n", + GNUNET_i2s (&n->peer)); +#endif + return; + case PEER_STATE_KEY_SENT: + if (n->retry_set_key_task == GNUNET_SCHEDULER_NO_TASK) + n->retry_set_key_task = + GNUNET_SCHEDULER_add_delayed (n->set_key_retry_frequency, + &set_key_retry_task, n); +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Not yet connected to `%4s', deferring processing of plaintext messages.\n", + GNUNET_i2s (&n->peer)); +#endif + return; + case PEER_STATE_KEY_RECEIVED: + if (n->retry_set_key_task == GNUNET_SCHEDULER_NO_TASK) + n->retry_set_key_task = + GNUNET_SCHEDULER_add_delayed (n->set_key_retry_frequency, + &set_key_retry_task, n); +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Not yet connected to `%4s', deferring processing of plaintext messages.\n", + GNUNET_i2s (&n->peer)); +#endif + return; + case PEER_STATE_KEY_CONFIRMED: + /* ready to continue */ + break; + } + discard_expired_messages (n); + if (n->messages == NULL) + { +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Plaintext message queue for `%4s' is empty.\n", + GNUNET_i2s (&n->peer)); +#endif + return; /* no pending messages */ + } + if (n->encrypted_head != NULL) + { +#if DEBUG_CORE > 2 + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Encrypted message queue for `%4s' is still full, delaying plaintext processing.\n", + GNUNET_i2s (&n->peer)); +#endif + return; /* wait for messages already encrypted to be + * processed first! */ + } + ph = (struct EncryptedMessage *) pbuf; + deadline = GNUNET_TIME_UNIT_FOREVER_ABS; + priority = 0; + used = sizeof (struct EncryptedMessage); + used += + batch_message (n, &pbuf[used], + GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE, &deadline, + &retry_time, &priority); + if (used == sizeof (struct EncryptedMessage)) + { +#if DEBUG_CORE > 1 + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "No messages selected for transmission to `%4s' at this time, will try again later.\n", + GNUNET_i2s (&n->peer)); +#endif + /* no messages selected for sending, try again later... */ + n->retry_plaintext_task = + GNUNET_SCHEDULER_add_delayed (retry_time, &retry_plaintext_processing, + n); + return; + } + GSC_KX_encrypt_and_transmit (n->kx, + &pbuf[struct EncryptedMessage], + used - sizeof (struct EncryptedMessage)); + schedule_peer_messages (n); +} + + + + /** * Check if we have encrypted messages for the specified neighbour * pending, and if so, check with the transport about sending them diff --git a/src/core/gnunet-service-core_typemap.c b/src/core/gnunet-service-core_typemap.c index 3aa652999..45dcc65c9 100644 --- a/src/core/gnunet-service-core_typemap.c +++ b/src/core/gnunet-service-core_typemap.c @@ -1,4 +1,14 @@ +/** + * A type map describing which messages a given neighbour is able + * to process. + */ +struct GSC_TypeMap +{ + uint32_t bits[(UINT16_MAX + 1) / 32]; +}; + + /** * Bitmap of message types this peer is able to handle. */