/*
This file is part of GNUnet.
- (C) 2009 Christian Grothoff (and other contributing authors)
+ (C) 2009, 2010 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 2, or (at your
+ 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
*/
enum PeerStateMachine
{
+ /**
+ * No handshake yet.
+ */
PEER_STATE_DOWN,
+
+ /**
+ * We've sent our session key.
+ */
PEER_STATE_KEY_SENT,
+
+ /**
+ * We've received the other peers session key.
+ */
PEER_STATE_KEY_RECEIVED,
+
+ /**
+ * The other peer has confirmed our session key with a message
+ * encrypted with his session key (which we got). Session is now fully up.
+ */
PEER_STATE_KEY_CONFIRMED
};
-/**
- * Number of bytes (at the beginning) of "struct EncryptedMessage"
- * that are NOT encrypted.
- */
-#define ENCRYPTED_HEADER_SIZE (sizeof(struct GNUNET_MessageHeader) + sizeof(uint32_t))
-
-
/**
* Encapsulation for encrypted messages exchanged between
* peers. Followed by the actual encrypted data.
struct GNUNET_MessageHeader header;
/**
- * Random value used for IV generation. ENCRYPTED_HEADER_SIZE must
- * be set to the offset of the *next* field.
+ * Random value used for IV generation.
*/
uint32_t iv_seed GNUNET_PACKED;
/**
- * Hash of the plaintext (starting at 'sequence_number'), used to
- * verify message integrity. Everything after this hash (including
- * this hash itself) will be encrypted.
+ * 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 plaintext_hash;
+ GNUNET_HashCode hmac;
/**
* Sequence number, in network byte order. This field
- * must be the first encrypted/decrypted field and the
- * first byte that is hashed for the plaintext hash.
+ * must be the first encrypted/decrypted field
*/
uint32_t sequence_number GNUNET_PACKED;
};
+/**
+ * Number of bytes (at the beginning) of "struct EncryptedMessage"
+ * that are NOT encrypted.
+ */
+#define ENCRYPTED_HEADER_SIZE (offsetof(struct EncryptedMessage, sequence_number))
+
+
/**
* We're sending an (encrypted) PING to the other peer to check if he
* can decrypt. The other peer should respond with a PONG with the
* Message type is CORE_PING.
*/
struct GNUNET_MessageHeader header;
-
+
/**
- * Random number chosen to make reply harder.
+ * Seed for the IV
*/
- uint32_t challenge GNUNET_PACKED;
+ uint32_t iv_seed GNUNET_PACKED;
/**
* Intended target of the PING, used primarily to check
* that decryption actually worked.
*/
struct GNUNET_PeerIdentity target;
+
+ /**
+ * Random number chosen to make reply harder.
+ */
+ uint32_t challenge GNUNET_PACKED;
};
* Message type is CORE_PONG.
*/
struct GNUNET_MessageHeader header;
-
+
/**
- * Random number proochosen to make reply harder. Must be
- * first field after header (this is where we start to encrypt!).
+ * Seed for the IV
*/
- uint32_t challenge GNUNET_PACKED;
+ uint32_t iv_seed GNUNET_PACKED;
/**
- * Must be zero.
+ * Random number to make faking the reply harder. Must be
+ * first field after header (this is where we start to encrypt!).
*/
- uint32_t reserved GNUNET_PACKED;
+ uint32_t challenge GNUNET_PACKED;
/**
* Desired bandwidth (how much we should send to this
};
+/**
+ * Record kept for each request for transmission issued by a
+ * client that is still pending.
+ */
+struct ClientActiveRequest;
+
+/**
+ * Data kept per neighbouring peer.
+ */
struct Neighbour
{
- /**
- * We keep neighbours in a linked list (for now).
- */
- struct Neighbour *next;
/**
* Unencrypted messages destined for this peer.
*/
struct MessageEntry *encrypted_tail;
+ /**
+ * Head of list of requests from clients for transmission to
+ * this peer.
+ */
+ struct ClientActiveRequest *active_client_request_head;
+
+ /**
+ * Tail of list of requests from clients for transmission to
+ * this peer.
+ */
+ struct ClientActiveRequest *active_client_request_tail;
+
/**
* Handle for pending requests for transmission to this peer
* with the transport service. NULL if no request is pending.
*/
struct SetKeyMessage *skm;
+ /**
+ * Performance data for the peer.
+ */
+ struct GNUNET_TRANSPORT_ATS_Information *ats;
+
/**
* Identity of the neighbour.
*/
*/
struct GNUNET_TIME_Absolute last_activity;
- /**
- * Last latency observed from this peer.
- */
- struct GNUNET_TIME_Relative last_latency;
-
/**
* At what frequency are we currently re-trying SET_KEY messages?
*/
*/
unsigned long long current_preference;
+ /**
+ * Number of entries in 'ats'.
+ */
+ unsigned int ats_count;
+
/**
* Bit map indicating which of the 32 sequence numbers before the last
* were received (good for accepting out-of-order packets and
*/
uint32_t ping_challenge;
- /**
- * What was the last distance to this peer as reported by the transports?
- */
- uint32_t last_distance;
-
/**
* What is our connection status?
*/
*/
const uint16_t *types;
+ /**
+ * Map of peer identities to active transmission requests of this
+ * client to the peer (of type 'struct ClientActiveRequest').
+ */
+ struct GNUNET_CONTAINER_MultiHashMap *requests;
+
/**
* Options for messages this client cares about,
* see GNUNET_CORE_OPTION_ values.
};
+/**
+ * Record kept for each request for transmission issued by a
+ * client that is still pending.
+ */
+struct ClientActiveRequest
+{
+
+ /**
+ * Active requests are kept in a doubly-linked list of
+ * the respective target peer.
+ */
+ struct ClientActiveRequest *next;
+
+ /**
+ * Active requests are kept in a doubly-linked list of
+ * the respective target peer.
+ */
+ struct ClientActiveRequest *prev;
+
+ /**
+ * Handle to the client.
+ */
+ struct Client *client;
+
+ /**
+ * By what time would the client want to see this message out?
+ */
+ struct GNUNET_TIME_Absolute deadline;
+
+ /**
+ * How important is this request.
+ */
+ uint32_t priority;
+
+ /**
+ * How many more requests does this client have?
+ */
+ uint32_t queue_size;
+
+ /**
+ * How many bytes does the client intend to send?
+ */
+ uint16_t msize;
+
+ /**
+ * Unique request ID (in big endian).
+ */
+ uint16_t smr_id;
+
+};
+
+
+
/**
* Our public key.
*/
*/
static struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key;
-/**
- * Our scheduler.
- */
-struct GNUNET_SCHEDULER_Handle *sched;
/**
* Handle to peerinfo service.
static struct GNUNET_PEERINFO_Handle *peerinfo;
/**
- * Our configuration.
+ * Our message stream tokenizer (for encrypted payload).
*/
-const struct GNUNET_CONFIGURATION_Handle *cfg;
+static struct GNUNET_SERVER_MessageStreamTokenizer *mst;
/**
- * Our server.
+ * Our configuration.
*/
-static struct GNUNET_SERVER_Handle *server;
+const struct GNUNET_CONFIGURATION_Handle *cfg;
/**
* Transport service.
static struct GNUNET_SERVER_NotificationContext *notifier;
/**
- * We keep neighbours in a linked list (for now).
+ * Map of peer identities to 'struct Neighbour'.
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *neighbours;
+
+/**
+ * Neighbour entry for "this" peer.
*/
-static struct Neighbour *neighbours;
+static struct Neighbour self;
/**
* For creating statistics.
*/
static unsigned long long preference_sum;
-/**
- * Total number of neighbours we have.
- */
-static unsigned int neighbour_count;
-
/**
* How much inbound bandwidth are we supposed to be using per second?
- * FIXME: this value is not used!
*/
static unsigned long long bandwidth_target_in_bps;
*/
static unsigned long long bandwidth_target_out_bps;
+/**
+ * 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);
+}
+
+
+/**
+ * At what time should the connection to the given neighbour
+ * time out (given no further activity?)
+ *
+ * @param n neighbour in question
+ * @return absolute timeout
+ */
+static struct GNUNET_TIME_Absolute
+get_neighbour_timeout (struct Neighbour *n)
+{
+ return GNUNET_TIME_absolute_add (n->last_activity,
+ GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
+}
+
+
+/**
+ * Helper function for update_preference_sum.
+ */
+static int
+update_preference (void *cls,
+ const GNUNET_HashCode *key,
+ void *value)
+{
+ unsigned long long *ps = cls;
+ struct Neighbour *n = value;
+
+ n->current_preference /= 2;
+ *ps += n->current_preference;
+ return GNUNET_OK;
+}
/**
static void
update_preference_sum (unsigned long long inc)
{
- struct Neighbour *n;
unsigned long long os;
os = preference_sum;
return; /* done! */
/* overflow! compensate by cutting all values in half! */
preference_sum = 0;
- n = neighbours;
- while (n != NULL)
- {
- n->current_preference /= 2;
- preference_sum += n->current_preference;
- n = n->next;
- }
+ GNUNET_CONTAINER_multihashmap_iterate (neighbours,
+ &update_preference,
+ &preference_sum);
GNUNET_STATISTICS_set (stats, gettext_noop ("# total peer preference"), preference_sum, GNUNET_NO);
}
static struct Neighbour *
find_neighbour (const struct GNUNET_PeerIdentity *peer)
{
- struct Neighbour *ret;
-
- ret = neighbours;
- while ((ret != NULL) &&
- (0 != memcmp (&ret->peer,
- peer, sizeof (struct GNUNET_PeerIdentity))))
- ret = ret->next;
- return ret;
+ return GNUNET_CONTAINER_multihashmap_get (neighbours, &peer->hashPubKey);
}
}
+/**
+ * Function called by transport telling us that a peer
+ * changed status.
+ *
+ * @param n the peer that changed status
+ */
+static void
+handle_peer_status_change (struct Neighbour *n)
+{
+ struct PeerStatusNotifyMessage *psnm;
+ char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
+ struct GNUNET_TRANSPORT_ATS_Information *ats;
+ size_t size;
+
+ if ( (! n->is_connected) ||
+ (n->status != PEER_STATE_KEY_CONFIRMED) )
+ return;
+#if DEBUG_CORE
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Peer `%4s' changed status\n",
+ GNUNET_i2s (&n->peer));
+#endif
+ size = sizeof (struct PeerStatusNotifyMessage) +
+ 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);
+ }
+ psnm = (struct PeerStatusNotifyMessage*) buf;
+ psnm->header.size = htons (size);
+ psnm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_STATUS_CHANGE);
+ psnm->timeout = GNUNET_TIME_absolute_hton (get_neighbour_timeout (n));
+ psnm->bandwidth_in = n->bw_in;
+ psnm->bandwidth_out = n->bw_out;
+ psnm->peer = n->peer;
+ psnm->ats_count = htonl (n->ats_count);
+ ats = &psnm->ats;
+ memcpy (ats,
+ n->ats,
+ n->ats_count * sizeof (struct GNUNET_TRANSPORT_ATS_Information));
+ ats[n->ats_count].type = htonl (0);
+ ats[n->ats_count].value = htonl (0);
+ send_to_all_clients (&psnm->header,
+ GNUNET_YES,
+ GNUNET_CORE_OPTION_SEND_STATUS_CHANGE);
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# peer status changes"),
+ 1,
+ GNUNET_NO);
+}
+
+
+/**
+ * Go over our message queue and if it is not too long, go
+ * over the pending requests from clients for this
+ * neighbour and send some clients a 'READY' notification.
+ *
+ * @param n which peer to process
+ */
+static void
+schedule_peer_messages (struct Neighbour *n)
+{
+ struct SendMessageReady smr;
+ struct ClientActiveRequest *car;
+ struct ClientActiveRequest *pos;
+ struct Client *c;
+ struct MessageEntry *mqe;
+ unsigned int queue_size;
+
+ /* check if neighbour queue is empty enough! */
+ if (n != &self)
+ {
+ queue_size = 0;
+ mqe = n->messages;
+ while (mqe != NULL)
+ {
+ queue_size++;
+ mqe = mqe->next;
+ }
+ if (queue_size >= MAX_PEER_QUEUE_SIZE)
+ {
+#if DEBUG_CORE_CLIENT
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Not considering client transmission requests: queue full\n");
+#endif
+ return; /* queue still full */
+ }
+ /* find highest priority request */
+ pos = n->active_client_request_head;
+ car = NULL;
+ while (pos != NULL)
+ {
+ if ( (car == NULL) ||
+ (pos->priority > car->priority) )
+ car = pos;
+ pos = pos->next;
+ }
+ }
+ else
+ {
+ car = n->active_client_request_head;
+ }
+ if (car == NULL)
+ return; /* no pending requests */
+#if DEBUG_CORE_CLIENT
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Permitting client transmission request to `%s'\n",
+ GNUNET_i2s (&n->peer));
+#endif
+ c = car->client;
+ GNUNET_CONTAINER_DLL_remove (n->active_client_request_head,
+ n->active_client_request_tail,
+ car);
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multihashmap_remove (c->requests,
+ &n->peer.hashPubKey,
+ car));
+ smr.header.size = htons (sizeof (struct SendMessageReady));
+ smr.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_SEND_READY);
+ smr.size = htons (car->msize);
+ smr.smr_id = car->smr_id;
+ smr.peer = n->peer;
+ send_to_client (c, &smr.header, GNUNET_NO);
+ GNUNET_free (car);
+}
+
+
+/**
+ * Handle CORE_SEND_REQUEST message.
+ */
+static void
+handle_client_send_request (void *cls,
+ struct GNUNET_SERVER_Client *client,
+ const struct GNUNET_MessageHeader *message)
+{
+ const struct SendMessageRequest *req;
+ struct Neighbour *n;
+ struct Client *c;
+ struct ClientActiveRequest *car;
+
+ req = (const struct SendMessageRequest*) message;
+ if (0 == memcmp (&req->peer,
+ &my_identity,
+ sizeof (struct GNUNET_PeerIdentity)))
+ n = &self;
+ else
+ n = find_neighbour (&req->peer);
+ if ( (n == NULL) ||
+ (GNUNET_YES != n->is_connected) ||
+ (n->status != PEER_STATE_KEY_CONFIRMED) )
+ {
+ /* neighbour must have disconnected since request was issued,
+ ignore (client will realize it once it processes the
+ disconnect notification) */
+#if DEBUG_CORE_CLIENT
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Dropped client request for transmission (am disconnected)\n");
+#endif
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# send requests dropped (disconnected)"),
+ 1,
+ GNUNET_NO);
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
+ return;
+ }
+ c = clients;
+ while ( (c != NULL) &&
+ (c->client_handle != client) )
+ c = c->next;
+ if (c == NULL)
+ {
+ /* client did not send INIT first! */
+ GNUNET_break (0);
+ GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+ return;
+ }
+ if (c->requests == NULL)
+ c->requests = GNUNET_CONTAINER_multihashmap_create (16);
+#if DEBUG_CORE_CLIENT
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received client transmission request. queueing\n");
+#endif
+ car = GNUNET_CONTAINER_multihashmap_get (c->requests,
+ &req->peer.hashPubKey);
+ if (car == NULL)
+ {
+ /* create new entry */
+ car = GNUNET_malloc (sizeof (struct ClientActiveRequest));
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_put (c->requests,
+ &req->peer.hashPubKey,
+ car,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
+ GNUNET_CONTAINER_DLL_insert (n->active_client_request_head,
+ n->active_client_request_tail,
+ car);
+ car->client = c;
+ }
+ car->deadline = GNUNET_TIME_absolute_ntoh (req->deadline);
+ car->priority = ntohl (req->priority);
+ car->queue_size = ntohl (req->queue_size);
+ car->msize = ntohs (req->size);
+ car->smr_id = req->smr_id;
+ schedule_peer_messages (n);
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
+}
+
+
+/**
+ * Notify client about an existing connection to one of our neighbours.
+ */
+static int
+notify_client_about_neighbour (void *cls,
+ const GNUNET_HashCode *key,
+ void *value)
+{
+ struct Client *c = cls;
+ struct Neighbour *n = value;
+ size_t size;
+ char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
+ struct GNUNET_TRANSPORT_ATS_Information *ats;
+ struct ConnectNotifyMessage *cnm;
+
+ 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 ConnectNotifyMessage) +
+ (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);
+ ats = &cnm->ats;
+ memcpy (ats,
+ n->ats,
+ sizeof (struct GNUNET_TRANSPORT_ATS_Information) * n->ats_count);
+ ats[n->ats_count].type = htonl (GNUNET_TRANSPORT_ATS_ARRAY_TERMINATOR);
+ ats[n->ats_count].value = htonl (0);
+ if (n->status == PEER_STATE_KEY_CONFIRMED)
+ {
+#if DEBUG_CORE_CLIENT
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Sending `%s' message to client.\n", "NOTIFY_CONNECT");
+#endif
+ cnm->peer = n->peer;
+ send_to_client (c, &cnm->header, GNUNET_NO);
+ }
+ return GNUNET_OK;
+}
+
+
+
/**
* Handle CORE_INIT request.
*/
uint16_t msize;
const uint16_t *types;
uint16_t *wtypes;
- struct Neighbour *n;
- struct ConnectNotifyMessage cnm;
unsigned int i;
#if DEBUG_CORE_CLIENT
if (0 != (c->options & GNUNET_CORE_OPTION_SEND_CONNECT))
{
/* notify new client about existing neighbours */
- cnm.header.size = htons (sizeof (struct ConnectNotifyMessage));
- cnm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT);
- n = neighbours;
- while (n != NULL)
- {
- if (n->status == PEER_STATE_KEY_CONFIRMED)
- {
-#if DEBUG_CORE_CLIENT
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Sending `%s' message to client.\n", "NOTIFY_CONNECT");
-#endif
- cnm.distance = htonl (n->last_distance);
- cnm.latency = GNUNET_TIME_relative_hton (n->last_latency);
- cnm.peer = n->peer;
- send_to_client (c, &cnm.header, GNUNET_NO);
- }
- n = n->next;
- }
+ GNUNET_CONTAINER_multihashmap_iterate (neighbours,
+ ¬ify_client_about_neighbour,
+ c);
}
GNUNET_SERVER_receive_done (client, GNUNET_OK);
}
+/**
+ * Free client request records.
+ *
+ * @param cls NULL
+ * @param key identity of peer for which this is an active request
+ * @param value the 'struct ClientActiveRequest' to free
+ * @return GNUNET_YES (continue iteration)
+ */
+static int
+destroy_active_client_request (void *cls,
+ const GNUNET_HashCode *key,
+ void *value)
+{
+ struct ClientActiveRequest *car = value;
+ struct Neighbour *n;
+ struct GNUNET_PeerIdentity peer;
+
+ peer.hashPubKey = *key;
+ n = find_neighbour (&peer);
+ GNUNET_assert (NULL != n);
+ GNUNET_CONTAINER_DLL_remove (n->active_client_request_head,
+ n->active_client_request_tail,
+ car);
+ GNUNET_free (car);
+ return GNUNET_YES;
+}
+
+
/**
* A client disconnected, clean up.
*
while (pos != NULL)
{
if (client == pos->client_handle)
- {
- if (prev == NULL)
- clients = pos->next;
- else
- prev->next = pos->next;
- GNUNET_free (pos);
- return;
- }
+ break;
prev = pos;
pos = pos->next;
}
- /* client never sent INIT */
+ if (pos == NULL)
+ {
+ /* client never sent INIT */
+ return;
+ }
+ if (prev == NULL)
+ clients = pos->next;
+ else
+ prev->next = pos->next;
+ if (pos->requests != NULL)
+ {
+ GNUNET_CONTAINER_multihashmap_iterate (pos->requests,
+ &destroy_active_client_request,
+ NULL);
+ GNUNET_CONTAINER_multihashmap_destroy (pos->requests);
+ }
+ GNUNET_free (pos);
+}
+
+
+/**
+ * Helper function for handle_client_iterate_peers.
+ *
+ * @param cls the 'struct GNUNET_SERVER_TransmitContext' to queue replies
+ * @param key identity of the connected peer
+ * @param value the 'struct Neighbour' for the peer
+ * @return GNUNET_OK (continue to iterate)
+ */
+static int
+queue_connect_message (void *cls,
+ const GNUNET_HashCode *key,
+ void *value)
+{
+ struct GNUNET_SERVER_TransmitContext *tc = cls;
+ struct Neighbour *n = value;
+ char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
+ struct GNUNET_TRANSPORT_ATS_Information *ats;
+ size_t size;
+ struct ConnectNotifyMessage *cnm;
+
+ cnm = (struct ConnectNotifyMessage*) buf;
+ if (n->status != PEER_STATE_KEY_CONFIRMED)
+ return GNUNET_OK;
+ 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);
+ ats = &cnm->ats;
+ memcpy (ats,
+ n->ats,
+ n->ats_count * sizeof (struct GNUNET_TRANSPORT_ATS_Information));
+ ats[n->ats_count].type = htonl (GNUNET_TRANSPORT_ATS_ARRAY_TERMINATOR);
+ ats[n->ats_count].value = htonl (0);
+#if DEBUG_CORE_CLIENT
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Sending `%s' message to client.\n",
+ "NOTIFY_CONNECT");
+#endif
+ cnm->peer = n->peer;
+ GNUNET_SERVER_transmit_context_append_message (tc,
+ &cnm->header);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Handle CORE_ITERATE_PEERS request.
+ *
+ * @param cls unused
+ * @param client client sending the iteration request
+ * @param message iteration request message
+ */
+static void
+handle_client_iterate_peers (void *cls,
+ struct GNUNET_SERVER_Client *client,
+ const struct GNUNET_MessageHeader *message)
+
+{
+ struct GNUNET_MessageHeader done_msg;
+ struct GNUNET_SERVER_TransmitContext *tc;
+ int msize;
+ /* notify new client about existing neighbours */
+
+ msize = ntohs(message->size);
+ tc = GNUNET_SERVER_transmit_context_create (client);
+ if (msize == sizeof(struct GNUNET_MessageHeader))
+ GNUNET_CONTAINER_multihashmap_iterate (neighbours, &queue_connect_message, tc);
+ else
+ GNUNET_break(0);
+
+ done_msg.size = htons (sizeof (struct GNUNET_MessageHeader));
+ done_msg.type = htons (GNUNET_MESSAGE_TYPE_CORE_ITERATE_PEERS_END);
+ GNUNET_SERVER_transmit_context_append_message (tc, &done_msg);
+ GNUNET_SERVER_transmit_context_run (tc,
+ GNUNET_TIME_UNIT_FOREVER_REL);
+}
+
+/**
+ * Handle CORE_ITERATE_PEERS request. Notify client about existing neighbours.
+ *
+ * @param cls unused
+ * @param client client sending the iteration request
+ * @param message iteration request message
+ */
+static void
+handle_client_have_peer (void *cls,
+ struct GNUNET_SERVER_Client *client,
+ const struct GNUNET_MessageHeader *message)
+
+{
+ struct GNUNET_MessageHeader done_msg;
+ struct GNUNET_SERVER_TransmitContext *tc;
+ struct GNUNET_PeerIdentity *peer;
+
+ tc = GNUNET_SERVER_transmit_context_create (client);
+ peer = (struct GNUNET_PeerIdentity *) &message[1];
+ GNUNET_CONTAINER_multihashmap_get_multiple(neighbours,
+ &peer->hashPubKey,
+ &queue_connect_message,
+ tc);
+ done_msg.size = htons (sizeof (struct GNUNET_MessageHeader));
+ done_msg.type = htons (GNUNET_MESSAGE_TYPE_CORE_ITERATE_PEERS_END);
+ GNUNET_SERVER_transmit_context_append_message (tc, &done_msg);
+ GNUNET_SERVER_transmit_context_run (tc,
+ GNUNET_TIME_UNIT_FOREVER_REL);
}
/**
* Handle REQUEST_INFO request.
+ *
+ * @param cls unused
+ * @param client client sending the request
+ * @param message iteration request message
*/
static void
handle_client_request_info (void *cls,
const struct GNUNET_MessageHeader *message)
{
const struct RequestInfoMessage *rcm;
+ struct Client *pos;
struct Neighbour *n;
struct ConfigurationInfoMessage cim;
int32_t want_reserv;
int32_t got_reserv;
unsigned long long old_preference;
- struct GNUNET_SERVER_TransmitContext *tc;
+ struct GNUNET_TIME_Relative rdelay;
+ rdelay = GNUNET_TIME_relative_get_zero();
#if DEBUG_CORE_CLIENT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Core service receives `%s' request.\n", "REQUEST_INFO");
#endif
+ pos = clients;
+ while (pos != NULL)
+ {
+ if (client == pos->client_handle)
+ break;
+ pos = pos->next;
+ }
+ if (pos == NULL)
+ {
+ GNUNET_break (0);
+ GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+ return;
+ }
+
rcm = (const struct RequestInfoMessage *) message;
n = find_neighbour (&rcm->peer);
memset (&cim, 0, sizeof (cim));
if (n->bw_out_internal_limit.value__ != rcm->limit_outbound.value__)
{
n->bw_out_internal_limit = rcm->limit_outbound;
- n->bw_out = GNUNET_BANDWIDTH_value_min (n->bw_out_internal_limit,
- n->bw_out_external_limit);
- GNUNET_BANDWIDTH_tracker_update_quota (&n->available_recv_window,
- n->bw_out);
- GNUNET_TRANSPORT_set_quota (transport,
- &n->peer,
- n->bw_in,
- n->bw_out,
- GNUNET_TIME_UNIT_FOREVER_REL,
- NULL, NULL);
+ if (n->bw_out.value__ != GNUNET_BANDWIDTH_value_min (n->bw_out_internal_limit,
+ n->bw_out_external_limit).value__)
+ {
+ n->bw_out = GNUNET_BANDWIDTH_value_min (n->bw_out_internal_limit,
+ n->bw_out_external_limit);
+ GNUNET_BANDWIDTH_tracker_update_quota (&n->available_recv_window,
+ n->bw_out);
+ GNUNET_TRANSPORT_set_quota (transport,
+ &n->peer,
+ n->bw_in,
+ n->bw_out,
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ NULL, NULL);
+ handle_peer_status_change (n);
+ }
}
if (want_reserv < 0)
{
}
else if (want_reserv > 0)
{
- if (GNUNET_BANDWIDTH_tracker_get_delay (&n->available_recv_window,
- want_reserv).value == 0)
+ rdelay = GNUNET_BANDWIDTH_tracker_get_delay (&n->available_recv_window,
+ want_reserv);
+ if (rdelay.rel_value == 0)
got_reserv = want_reserv;
else
got_reserv = 0; /* all or nothing */
(int) got_reserv);
#endif
cim.reserved_amount = htonl (got_reserv);
- cim.bw_in = n->bw_in;
+ cim.reserve_delay = GNUNET_TIME_relative_hton (rdelay);
+ cim.rim_id = rcm->rim_id;
cim.bw_out = n->bw_out;
cim.preference = n->current_preference;
}
cim.header.size = htons (sizeof (struct ConfigurationInfoMessage));
cim.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_CONFIGURATION_INFO);
cim.peer = rcm->peer;
-
#if DEBUG_CORE_CLIENT
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Sending `%s' message to client.\n", "CONFIGURATION_INFO");
#endif
- tc = GNUNET_SERVER_transmit_context_create (client);
- GNUNET_SERVER_transmit_context_append_message (tc, &cim.header);
- GNUNET_SERVER_transmit_context_run (tc,
- GNUNET_TIME_UNIT_FOREVER_REL);
+ send_to_client (pos, &cim.header, GNUNET_NO);
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
}
free_neighbour (struct Neighbour *n)
{
struct MessageEntry *m;
+ struct ClientActiveRequest *car;
#if DEBUG_CORE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
m);
GNUNET_free (m);
}
+ while (NULL != (car = n->active_client_request_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (n->active_client_request_head,
+ n->active_client_request_tail,
+ car);
+ GNUNET_CONTAINER_multihashmap_remove (car->client->requests,
+ &n->peer.hashPubKey,
+ car);
+ GNUNET_free (car);
+ }
if (NULL != n->th)
{
GNUNET_TRANSPORT_notify_transmit_ready_cancel (n->th);
n->th = NULL;
}
if (n->retry_plaintext_task != GNUNET_SCHEDULER_NO_TASK)
- GNUNET_SCHEDULER_cancel (sched, n->retry_plaintext_task);
+ GNUNET_SCHEDULER_cancel (n->retry_plaintext_task);
if (n->retry_set_key_task != GNUNET_SCHEDULER_NO_TASK)
- GNUNET_SCHEDULER_cancel (sched, n->retry_set_key_task);
+ GNUNET_SCHEDULER_cancel (n->retry_set_key_task);
if (n->quota_update_task != GNUNET_SCHEDULER_NO_TASK)
- GNUNET_SCHEDULER_cancel (sched, n->quota_update_task);
+ GNUNET_SCHEDULER_cancel (n->quota_update_task);
if (n->dead_clean_task != GNUNET_SCHEDULER_NO_TASK)
- GNUNET_SCHEDULER_cancel (sched, n->dead_clean_task);
+ GNUNET_SCHEDULER_cancel (n->dead_clean_task);
if (n->keep_alive_task != GNUNET_SCHEDULER_NO_TASK)
- GNUNET_SCHEDULER_cancel (sched, n->keep_alive_task);
+ GNUNET_SCHEDULER_cancel (n->keep_alive_task);
if (n->status == PEER_STATE_KEY_CONFIRMED)
- GNUNET_STATISTICS_update (stats, gettext_noop ("# established sessions"), -1, GNUNET_NO);
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# established sessions"),
+ -1,
+ GNUNET_NO);
+ GNUNET_array_grow (n->ats, n->ats_count, 0);
GNUNET_free_non_null (n->public_key);
GNUNET_free_non_null (n->pending_ping);
GNUNET_free_non_null (n->pending_pong);
*/
static int
do_encrypt (struct Neighbour *n,
- const GNUNET_HashCode * iv,
+ const struct GNUNET_CRYPTO_AesInitializationVector * iv,
const void *in, void *out, size_t size)
{
if (size != (uint16_t) size)
GNUNET_CRYPTO_aes_encrypt (in,
(uint16_t) size,
&n->encrypt_key,
- (const struct
- GNUNET_CRYPTO_AesInitializationVector
- *) iv, out));
+ iv, out));
GNUNET_STATISTICS_update (stats, gettext_noop ("# bytes encrypted"), size, GNUNET_NO);
#if DEBUG_CORE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Encrypted %u bytes for `%4s' using key %u\n",
+ "Encrypted %u bytes for `%4s' using key %u, IV %u\n",
(unsigned int) size,
GNUNET_i2s (&n->peer),
- (unsigned int) n->encrypt_key.crc32);
+ (unsigned int) n->encrypt_key.crc32,
+ GNUNET_CRYPTO_crc32_n (iv, sizeof(iv)));
#endif
return GNUNET_OK;
}
struct MessageEntry *me;
struct PingMessage pp;
struct PingMessage *pm;
+ struct GNUNET_CRYPTO_AesInitializationVector iv;
n->keep_alive_task = GNUNET_SCHEDULER_NO_TASK;
/* send PING */
pm = (struct PingMessage *) &me[1];
pm->header.size = htons (sizeof (struct PingMessage));
pm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_PING);
- pp.challenge = htonl (n->ping_challenge);
+ pm->iv_seed = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
+ UINT32_MAX);
+ derive_iv (&iv, &n->encrypt_key, pm->iv_seed, &n->peer);
+ pp.challenge = n->ping_challenge;
pp.target = n->peer;
#if DEBUG_HANDSHAKE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Encrypting `%s' message with challenge %u for `%4s' using key %u.\n",
+ "Encrypting `%s' message with challenge %u for `%4s' using key %u, IV %u (salt %u).\n",
"PING",
(unsigned int) n->ping_challenge,
GNUNET_i2s (&n->peer),
- (unsigned int) n->encrypt_key.crc32);
+ (unsigned int) n->encrypt_key.crc32,
+ GNUNET_CRYPTO_crc32_n (&iv, sizeof(iv)),
+ pm->iv_seed);
#endif
do_encrypt (n,
- &n->peer.hashPubKey,
- &pp.challenge,
- &pm->challenge,
+ &iv,
+ &pp.target,
+ &pm->target,
sizeof (struct PingMessage) -
- sizeof (struct GNUNET_MessageHeader));
+ ((void *) &pm->target - (void *) pm));
process_encrypted_neighbour_queue (n);
/* reschedule PING job */
- left = GNUNET_TIME_absolute_get_remaining (GNUNET_TIME_absolute_add (n->last_activity,
- GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
+ left = GNUNET_TIME_absolute_get_remaining (get_neighbour_timeout (n));
retry = GNUNET_TIME_relative_max (GNUNET_TIME_relative_divide (left, 2),
MIN_PING_FREQUENCY);
n->keep_alive_task
- = GNUNET_SCHEDULER_add_delayed (sched,
- retry,
+ = GNUNET_SCHEDULER_add_delayed (retry,
&send_keep_alive,
n);
static void
consider_free_neighbour (struct Neighbour *n)
{
- struct Neighbour *pos;
- struct Neighbour *prev;
struct GNUNET_TIME_Relative left;
if ( (n->th != NULL) ||
(n->pitr != NULL) ||
- (n->status == PEER_STATE_KEY_CONFIRMED) ||
(GNUNET_YES == n->is_connected) )
return; /* no chance */
-
- left = GNUNET_TIME_absolute_get_remaining (GNUNET_TIME_absolute_add (n->last_activity,
- GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
- if (left.value > 0)
+
+ left = GNUNET_TIME_absolute_get_remaining (get_neighbour_timeout (n));
+ if (left.rel_value > 0)
{
if (n->dead_clean_task != GNUNET_SCHEDULER_NO_TASK)
- GNUNET_SCHEDULER_cancel (sched, n->dead_clean_task);
- n->dead_clean_task = GNUNET_SCHEDULER_add_delayed (sched,
- left,
+ GNUNET_SCHEDULER_cancel (n->dead_clean_task);
+ n->dead_clean_task = GNUNET_SCHEDULER_add_delayed (left,
&consider_free_task,
n);
return;
}
/* actually free the neighbour... */
- prev = NULL;
- pos = neighbours;
- while (pos != n)
- {
- prev = pos;
- pos = pos->next;
- }
- if (prev == NULL)
- neighbours = n->next;
- else
- prev->next = n->next;
- GNUNET_assert (neighbour_count > 0);
- neighbour_count--;
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_remove (neighbours,
+ &n->peer.hashPubKey,
+ n));
GNUNET_STATISTICS_set (stats,
gettext_noop ("# neighbour entries allocated"),
- neighbour_count,
+ GNUNET_CONTAINER_multihashmap_size (neighbours),
GNUNET_NO);
free_neighbour (n);
}
* @return number of bytes transmitted
*/
static size_t
-notify_encrypted_transmit_ready (void *cls, size_t size, void *buf)
+notify_encrypted_transmit_ready (void *cls,
+ size_t size,
+ void *buf)
{
struct Neighbour *n = cls;
struct MessageEntry *m;
}
GNUNET_free (m);
consider_free_neighbour (n);
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# encrypted bytes given to transport"),
+ ret,
+ GNUNET_NO);
return ret;
}
"Asking transport for transmission of %u bytes to `%4s' in next %llu ms\n",
(unsigned int) m->size,
GNUNET_i2s (&n->peer),
- (unsigned long long) GNUNET_TIME_absolute_get_remaining (m->deadline).
- value);
+ (unsigned long long) GNUNET_TIME_absolute_get_remaining (m->deadline).rel_value);
#endif
n->th =
GNUNET_TRANSPORT_notify_transmit_ready (transport, &n->peer,
*/
static int
do_decrypt (struct Neighbour *n,
- const GNUNET_HashCode * iv,
+ const struct GNUNET_CRYPTO_AesInitializationVector * iv,
const void *in, void *out, size_t size)
{
if (size != (uint16_t) size)
GNUNET_CRYPTO_aes_decrypt (in,
(uint16_t) size,
&n->decrypt_key,
- (const struct
- GNUNET_CRYPTO_AesInitializationVector *) iv,
+ iv,
out))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
- GNUNET_STATISTICS_update (stats, gettext_noop ("# bytes decrypted"), size, GNUNET_NO);
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# bytes decrypted"),
+ size,
+ GNUNET_NO);
#if DEBUG_CORE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Decrypted %u bytes from `%4s' using key %u\n",
+ "Decrypted %u bytes from `%4s' using key %u, IV %u\n",
(unsigned int) size,
GNUNET_i2s (&n->peer),
- (unsigned int) n->decrypt_key.crc32);
+ (unsigned int) n->decrypt_key.crc32,
+ GNUNET_CRYPTO_crc32_n (iv, sizeof(*iv)));
#endif
return GNUNET_OK;
}
if (discard_low_prio == GNUNET_NO)
{
delta = GNUNET_TIME_absolute_get_difference (t, pos->deadline);
- if (delta.value > 0)
+ if (delta.rel_value > 0)
{
// FIXME: HUH? Check!
t = pos->deadline;
slack = GNUNET_TIME_relative_min (slack,
GNUNET_BANDWIDTH_value_get_delay_for (n->bw_out,
avail));
- if (pos->deadline.value <= now.value)
+ if (pos->deadline.abs_value <= now.abs_value)
{
/* now or never */
slack = GNUNET_TIME_UNIT_ZERO;
}
/* guard against sending "tiny" messages with large headers without
urgent deadlines */
- if ( (slack.value > GNUNET_CONSTANTS_MAX_CORK_DELAY.value) &&
+ if ( (slack.rel_value > GNUNET_CONSTANTS_MAX_CORK_DELAY.rel_value) &&
(size > 4 * off) &&
(queue_size <= MAX_PEER_QUEUE_SIZE - 2) )
{
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->value,
+ (unsigned long long) retry_time->rel_value,
(unsigned int) off,
(unsigned int) size);
#endif
struct GNUNET_TIME_Relative *retry_time,
unsigned int *priority)
{
- char ntmb[GNUNET_SERVER_MAX_MESSAGE_SIZE];
+ char ntmb[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
struct NotifyTrafficMessage *ntm = (struct NotifyTrafficMessage*) ntmb;
struct MessageEntry *pos;
struct MessageEntry *prev;
#if DEBUG_CORE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"No messages selected, will try again in %llu ms\n",
- retry_time->value);
+ retry_time->rel_value);
#endif
return 0;
}
ntm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_OUTBOUND);
- ntm->distance = htonl (n->last_distance);
- ntm->latency = GNUNET_TIME_relative_hton (n->last_latency);
+ ntm->ats_count = htonl (0);
+ ntm->ats.type = htonl (0);
+ ntm->ats.value = htonl (0);
ntm->peer = n->peer;
pos = n->messages;
prev = NULL;
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).value);
+ (unsigned long long) GNUNET_TIME_absolute_get_remaining (pos->deadline).rel_value);
#endif
- deadline->value = GNUNET_MIN (deadline->value, pos->deadline.value);
+ deadline->abs_value = GNUNET_MIN (deadline->abs_value, pos->deadline.abs_value);
GNUNET_free (pos);
if (prev == NULL)
n->messages = next;
#if DEBUG_CORE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Deadline for message batch is %llu ms\n",
- GNUNET_TIME_absolute_get_remaining (*deadline).value);
+ GNUNET_TIME_absolute_get_remaining (*deadline).rel_value);
#endif
return ret;
}
struct MessageEntry *pos;
struct GNUNET_TIME_Absolute now;
struct GNUNET_TIME_Relative delta;
+ int disc;
+ disc = GNUNET_NO;
now = GNUNET_TIME_absolute_get ();
prev = NULL;
pos = n->messages;
{
next = pos->next;
delta = GNUNET_TIME_absolute_get_difference (pos->deadline, now);
- if (delta.value > PAST_EXPIRATION_DISCARD_TIME.value)
+ 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.value);
+ 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)
+ schedule_peer_messages (n);
}
{
char pbuf[GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE + sizeof (struct EncryptedMessage)]; /* plaintext */
size_t used;
- size_t esize;
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;
- GNUNET_HashCode iv;
+ struct GNUNET_CRYPTO_AesInitializationVector iv;
+ struct GNUNET_CRYPTO_AuthKey auth_key;
if (n->retry_plaintext_task != GNUNET_SCHEDULER_NO_TASK)
{
- GNUNET_SCHEDULER_cancel (sched, n->retry_plaintext_task);
+ GNUNET_SCHEDULER_cancel (n->retry_plaintext_task);
n->retry_plaintext_task = GNUNET_SCHEDULER_NO_TASK;
}
switch (n->status)
case PEER_STATE_KEY_SENT:
if (n->retry_set_key_task == GNUNET_SCHEDULER_NO_TASK)
n->retry_set_key_task
- = GNUNET_SCHEDULER_add_delayed (sched,
- n->set_key_retry_frequency,
+ = GNUNET_SCHEDULER_add_delayed (n->set_key_retry_frequency,
&set_key_retry_task, n);
#if DEBUG_CORE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
case PEER_STATE_KEY_RECEIVED:
if (n->retry_set_key_task == GNUNET_SCHEDULER_NO_TASK)
n->retry_set_key_task
- = GNUNET_SCHEDULER_add_delayed (sched,
- n->set_key_retry_frequency,
+ = GNUNET_SCHEDULER_add_delayed (n->set_key_retry_frequency,
&set_key_retry_task, n);
#if DEBUG_CORE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
#endif
/* no messages selected for sending, try again later... */
n->retry_plaintext_task =
- GNUNET_SCHEDULER_add_delayed (sched,
- retry_time,
+ GNUNET_SCHEDULER_add_delayed (retry_time,
&retry_plaintext_processing, n);
return;
}
(unsigned int) ntohl (n->bw_in.value__),
GNUNET_i2s (&n->peer));
#endif
- ph->iv_seed = htonl (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX));
+ 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 ());
em->header.size = htons (used);
em->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_ENCRYPTED_MESSAGE);
em->iv_seed = ph->iv_seed;
- esize = used - ENCRYPTED_HEADER_SIZE;
- GNUNET_CRYPTO_hash (&ph->sequence_number,
- esize - sizeof (GNUNET_HashCode),
- &ph->plaintext_hash);
- GNUNET_CRYPTO_hash (&ph->iv_seed, sizeof (uint32_t), &iv);
-#if DEBUG_HANDSHAKE
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Hashed %u bytes of plaintext (`%s') using IV `%d'\n",
- (unsigned int) (esize - sizeof (GNUNET_HashCode)),
- GNUNET_h2s (&ph->plaintext_hash),
- (int) ph->iv_seed);
-#endif
+ 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) esize,
+ (unsigned int) used - ENCRYPTED_HEADER_SIZE,
GNUNET_i2s(&n->peer),
- (unsigned long long) GNUNET_TIME_absolute_get_remaining (deadline).value);
+ (unsigned long long) GNUNET_TIME_absolute_get_remaining (deadline).rel_value);
#endif
GNUNET_assert (GNUNET_OK ==
do_encrypt (n,
&iv,
- &ph->plaintext_hash,
- &em->plaintext_hash, esize));
+ &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);
}
GNUNET_assert (n->quota_update_task ==
GNUNET_SCHEDULER_NO_TASK);
n->quota_update_task
- = GNUNET_SCHEDULER_add_delayed (sched,
- QUOTA_UPDATE_FREQUENCY,
+ = GNUNET_SCHEDULER_add_delayed (QUOTA_UPDATE_FREQUENCY,
&neighbour_quota_update,
n);
}
GNUNET_i2s (pid));
#endif
n = GNUNET_malloc (sizeof (struct Neighbour));
- n->next = neighbours;
- neighbours = n;
- neighbour_count++;
- GNUNET_STATISTICS_set (stats, gettext_noop ("# neighbour entries allocated"), neighbour_count, GNUNET_NO);
n->peer = *pid;
GNUNET_CRYPTO_aes_create_session_key (&n->encrypt_key);
now = GNUNET_TIME_absolute_get ();
n->bw_out = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
n->bw_out_internal_limit = GNUNET_BANDWIDTH_value_init (UINT32_MAX);
n->bw_out_external_limit = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
- n->ping_challenge = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
+ n->ping_challenge = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
UINT32_MAX);
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_put (neighbours,
+ &n->peer.hashPubKey,
+ n,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+ GNUNET_STATISTICS_set (stats, gettext_noop ("# neighbour entries allocated"),
+ GNUNET_CONTAINER_multihashmap_size (neighbours), GNUNET_NO);
neighbour_quota_update (n, NULL);
consider_free_neighbour (n);
return n;
if (msize <
sizeof (struct SendMessage) + sizeof (struct GNUNET_MessageHeader))
{
+ GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "about to assert fail, msize is %d, should be at least %d\n", msize, sizeof (struct SendMessage) + sizeof (struct GNUNET_MessageHeader));
GNUNET_break (0);
if (client != NULL)
GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
msize -= sizeof (struct SendMessage);
if (0 == memcmp (&sm->peer, &my_identity, sizeof (struct GNUNET_PeerIdentity)))
{
- /* FIXME: should we not allow loopback-injection here? */
- GNUNET_break (0);
+ /* loopback */
+ GNUNET_SERVER_mst_receive (mst,
+ &self,
+ (const char*) &sm[1],
+ msize,
+ GNUNET_YES,
+ GNUNET_NO);
if (client != NULL)
- GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
return;
}
n = find_neighbour (&sm->peer);
(unsigned int) msize,
GNUNET_i2s (&sm->peer));
#endif
- /* bound queue size */
discard_expired_messages (n);
+ /* bound queue size */
+ /* NOTE: this entire block to bound the queue size should be
+ obsolete with the new client-request code and the
+ 'schedule_peer_messages' mechanism; we still have this code in
+ here for now as a sanity check for the new mechanmism;
+ ultimately, we should probably simply reject SEND messages that
+ are not 'approved' (or provide a new core API for very unreliable
+ delivery that always sends with priority 0). Food for thought. */
min_prio = UINT32_MAX;
min_prio_entry = NULL;
min_prio_prev = NULL;
pos = n->messages;
while (pos != NULL)
{
- if (pos->priority < min_prio)
+ if (pos->priority <= min_prio)
{
min_prio_entry = pos;
min_prio_prev = prev;
/* queue full */
if (ntohl(sm->priority) <= min_prio)
{
- /* discard new entry */
+ /* discard new entry; this should no longer happen! */
+ GNUNET_break (0);
#if DEBUG_CORE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Queue full (%u/%u), discarding new request (%u bytes of type %u)\n",
(unsigned int) msize,
(unsigned int) ntohs (message->type));
#endif
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# discarded CORE_SEND requests"),
+ 1, GNUNET_NO);
+
if (client != NULL)
GNUNET_SERVER_receive_done (client, GNUNET_OK);
return;
}
+ GNUNET_assert (min_prio_entry != NULL);
/* discard "min_prio_entry" */
#if DEBUG_CORE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Queue full, discarding existing older request\n");
#endif
+ GNUNET_STATISTICS_update (stats, gettext_noop ("# discarded lower priority CORE_SEND requests"), 1, GNUNET_NO);
if (min_prio_prev == NULL)
n->messages = min_prio_entry->next;
else
GNUNET_i2s (&sm->peer),
(unsigned int) msize);
#endif
+ GNUNET_break (0 == ntohl (sm->reserved));
e = GNUNET_malloc (sizeof (struct MessageEntry) + msize);
e->deadline = GNUNET_TIME_absolute_ntoh (sm->deadline);
e->priority = ntohl (sm->priority);
e->size = msize;
+ if (GNUNET_YES != (int) ntohl (sm->cork))
+ e->got_slack = GNUNET_YES;
memcpy (&e[1], &sm[1], msize);
/* insert, keep list sorted by deadline */
prev = NULL;
pos = n->messages;
- while ((pos != NULL) && (pos->deadline.value < e->deadline.value))
+ while ((pos != NULL) && (pos->deadline.abs_value < e->deadline.abs_value))
{
prev = pos;
pos = pos->next;
* @return number of bytes transmitted
*/
static size_t
-notify_transport_connect_done (void *cls, size_t size, void *buf)
+notify_transport_connect_done (void *cls,
+ size_t size,
+ void *buf)
{
struct Neighbour *n = cls;
n->th = NULL;
+ if (GNUNET_YES != n->is_connected)
+ {
+ /* transport should only call us to transmit a message after
+ * telling us about a successful connection to the respective peer */
+#if DEBUG_CORE
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Timeout on notify connect!\n");
+#endif
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# connection requests timed out in transport"),
+ 1,
+ GNUNET_NO);
+ return 0;
+ }
if (buf == NULL)
{
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# connection requests timed out in transport"),
+ 1,
+ GNUNET_NO);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
_("Failed to connect to `%4s': transport failed to connect\n"),
GNUNET_i2s (&n->peer));
_("TRANSPORT connection to peer `%4s' is up, trying to establish CORE connection\n"),
GNUNET_i2s (&n->peer));
if (n->retry_set_key_task != GNUNET_SCHEDULER_NO_TASK)
- GNUNET_SCHEDULER_cancel (sched,
- n->retry_set_key_task);
- n->retry_set_key_task = GNUNET_SCHEDULER_add_now (sched,
- &set_key_retry_task,
+ GNUNET_SCHEDULER_cancel (n->retry_set_key_task);
+ n->retry_set_key_task = GNUNET_SCHEDULER_add_now (&set_key_retry_task,
n);
return 0;
}
struct Neighbour *n;
struct GNUNET_TIME_Relative timeout;
- if (0 == memcmp (&cm->peer, &my_identity, sizeof (struct GNUNET_PeerIdentity)))
+ if (0 == memcmp (&cm->peer,
+ &my_identity,
+ sizeof (struct GNUNET_PeerIdentity)))
{
- GNUNET_break (0);
- GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+ /* In this case a client has asked us to connect to ourselves, not really an error! */
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
return;
}
+ timeout = GNUNET_TIME_relative_ntoh (cm->timeout);
+ GNUNET_break (ntohl (cm->reserved) == 0);
GNUNET_SERVER_receive_done (client, GNUNET_OK);
n = find_neighbour (&cm->peer);
if (n == NULL)
n = create_neighbour (&cm->peer);
if ( (GNUNET_YES == n->is_connected) ||
(n->th != NULL) )
- return; /* already connected, or at least trying */
- GNUNET_STATISTICS_update (stats, gettext_noop ("# connection requests received"), 1, GNUNET_NO);
+ {
+ if (GNUNET_YES == n->is_connected)
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# connection requests ignored (already connected)"),
+ 1,
+ GNUNET_NO);
+ else
+ {
+ GNUNET_TRANSPORT_notify_transmit_ready_cancel(n->th);
+ n->th = GNUNET_TRANSPORT_notify_transmit_ready (transport,
+ &cm->peer,
+ sizeof (struct GNUNET_MessageHeader), 0,
+ timeout,
+ ¬ify_transport_connect_done,
+ n);
+ GNUNET_break (NULL != n->th);
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# connection requests retried (due to repeat request connect)"),
+ 1,
+ GNUNET_NO);
+ }
+ return; /* already connected, or at least trying */
+ }
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# connection requests received"),
+ 1,
+ GNUNET_NO);
+
#if DEBUG_CORE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Core received `%s' request for `%4s', will try to establish connection\n",
"REQUEST_CONNECT",
GNUNET_i2s (&cm->peer));
#endif
- timeout = GNUNET_TIME_relative_ntoh (cm->timeout);
+
/* ask transport to connect to the peer */
n->th = GNUNET_TRANSPORT_notify_transmit_ready (transport,
&cm->peer,
}
-/**
- * List of handlers for the messages understood by this
- * service.
- */
-static struct GNUNET_SERVER_MessageHandler handlers[] = {
- {&handle_client_init, NULL,
- GNUNET_MESSAGE_TYPE_CORE_INIT, 0},
- {&handle_client_request_info, NULL,
- GNUNET_MESSAGE_TYPE_CORE_REQUEST_INFO,
- sizeof (struct RequestInfoMessage)},
- {&handle_client_send, NULL,
- GNUNET_MESSAGE_TYPE_CORE_SEND, 0},
- {&handle_client_request_connect, NULL,
- GNUNET_MESSAGE_TYPE_CORE_REQUEST_CONNECT,
- sizeof (struct ConnectMessage)},
- {NULL, NULL, 0, 0}
-};
-
-
/**
* PEERINFO is giving us a HELLO for a peer. Add the public key to
* the neighbour's struct and retry send_key. Or, if we did not get a
* @param cls the 'struct Neighbour' to retry sending the key for
* @param peer the peer for which this is the HELLO
* @param hello HELLO message of that peer
- * @param trust amount of trust we currently have in that peer
+ * @param err_msg NULL if successful, otherwise contains error message
*/
static void
process_hello_retry_send_key (void *cls,
const struct GNUNET_PeerIdentity *peer,
const struct GNUNET_HELLO_Message *hello,
- uint32_t trust)
+ const char *err_msg)
{
struct Neighbour *n = cls;
+ if (err_msg != NULL)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ _("Error in communication with PEERINFO service\n"));
+ /* return; */
+ }
+
if (peer == NULL)
{
#if DEBUG_CORE
{
if (n->retry_set_key_task != GNUNET_SCHEDULER_NO_TASK)
{
- GNUNET_SCHEDULER_cancel (sched, n->retry_set_key_task);
+ GNUNET_SCHEDULER_cancel (n->retry_set_key_task);
n->retry_set_key_task = GNUNET_SCHEDULER_NO_TASK;
}
GNUNET_STATISTICS_update (stats,
}
else
{
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- _("Failed to obtain public key for peer `%4s', delaying processing of SET_KEY\n"),
+#if DEBUG_CORE
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Failed to obtain public key for peer `%4s', delaying processing of SET_KEY\n",
GNUNET_i2s (&n->peer));
+#endif
GNUNET_STATISTICS_update (stats,
gettext_noop ("# Delayed connecting due to lack of public key"),
1,
GNUNET_NO);
if (GNUNET_SCHEDULER_NO_TASK == n->retry_set_key_task)
n->retry_set_key_task
- = GNUNET_SCHEDULER_add_delayed (sched,
- n->set_key_retry_frequency,
+ = GNUNET_SCHEDULER_add_delayed (n->set_key_retry_frequency,
&set_key_retry_task, n);
}
return;
struct MessageEntry *me;
struct PingMessage pp;
struct PingMessage *pm;
+ struct GNUNET_CRYPTO_AesInitializationVector iv;
if (n->retry_set_key_task != GNUNET_SCHEDULER_NO_TASK)
{
- GNUNET_SCHEDULER_cancel (sched, n->retry_set_key_task);
+ GNUNET_SCHEDULER_cancel (n->retry_set_key_task);
n->retry_set_key_task = GNUNET_SCHEDULER_NO_TASK;
}
if (n->pitr != NULL)
GNUNET_assert (n->pitr == NULL);
n->pitr = GNUNET_PEERINFO_iterate (peerinfo,
&n->peer,
- 0,
GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 20),
&process_hello_retry_send_key, n);
return;
pm = (struct PingMessage *) &sm[1];
pm->header.size = htons (sizeof (struct PingMessage));
pm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_PING);
- pp.challenge = htonl (n->ping_challenge);
+ pm->iv_seed = GNUNET_CRYPTO_random_u32(GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX);
+ derive_iv (&iv, &n->encrypt_key, pm->iv_seed, &n->peer);
+ pp.challenge = n->ping_challenge;
pp.target = n->peer;
#if DEBUG_HANDSHAKE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Encrypting `%s' and `%s' messages with challenge %u for `%4s' using key %u.\n",
+ "Encrypting `%s' and `%s' messages with challenge %u for `%4s' using key %u, IV %u (salt %u).\n",
"SET_KEY", "PING",
(unsigned int) n->ping_challenge,
GNUNET_i2s (&n->peer),
- (unsigned int) n->encrypt_key.crc32);
+ (unsigned int) n->encrypt_key.crc32,
+ GNUNET_CRYPTO_crc32_n (&iv, sizeof(iv)),
+ pm->iv_seed);
#endif
do_encrypt (n,
- &n->peer.hashPubKey,
- &pp.challenge,
- &pm->challenge,
+ &iv,
+ &pp.target,
+ &pm->target,
sizeof (struct PingMessage) -
- sizeof (struct GNUNET_MessageHeader));
+ ((void *) &pm->target - (void *) pm));
GNUNET_STATISTICS_update (stats,
gettext_noop ("# SET_KEY and PING messages created"),
1,
#if DEBUG_CORE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Have %llu ms left for `%s' transmission.\n",
- (unsigned long long) GNUNET_TIME_absolute_get_remaining (me->deadline).value,
+ (unsigned long long) GNUNET_TIME_absolute_get_remaining (me->deadline).rel_value,
"SET_KEY");
#endif
trigger_processing:
if ( (n->status != PEER_STATE_KEY_CONFIRMED) &&
(GNUNET_SCHEDULER_NO_TASK == n->retry_set_key_task) )
n->retry_set_key_task
- = GNUNET_SCHEDULER_add_delayed (sched,
- n->set_key_retry_frequency,
+ = GNUNET_SCHEDULER_add_delayed (n->set_key_retry_frequency,
&set_key_retry_task, n);
}
*
* @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 SetKeyMessage *m,
+ const struct GNUNET_TRANSPORT_ATS_Information *ats,
+ uint32_t ats_count);
+
/**
* @param cls pointer to the set key message
* @param peer the peer for which this is the HELLO
* @param hello HELLO message of that peer
- * @param trust amount of trust we currently have in that peer
+ * @param err_msg NULL if successful, otherwise contains error message
*/
static void
process_hello_retry_handle_set_key (void *cls,
const struct GNUNET_PeerIdentity *peer,
const struct GNUNET_HELLO_Message *hello,
- uint32_t trust)
+ const char *err_msg)
{
struct Neighbour *n = cls;
struct SetKeyMessage *sm = n->skm;
+ if (err_msg != NULL)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ _("Error in communication with PEERINFO service\n"));
+ /* return; */
+ }
+
if (peer == NULL)
{
n->skm = NULL;
GNUNET_i2s (&n->peer),
"SET_KEY");
#endif
- handle_set_key (n, sm);
+ handle_set_key (n, sm, NULL, 0);
}
else
{
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
_("Ignoring `%s' message due to lack of public key for peer `%4s' (failed to obtain one).\n"),
"SET_KEY",
GNUNET_i2s (&n->peer));
}
+/**
+ * Merge the given performance data with the data we currently
+ * track for the given neighbour.
+ *
+ * @param n neighbour
+ * @param ats new performance data
+ * @param ats_count number of records in ats
+ */
+static void
+update_neighbour_performance (struct Neighbour *n,
+ const struct GNUNET_TRANSPORT_ATS_Information *ats,
+ uint32_t ats_count)
+{
+ uint32_t i;
+ unsigned int j;
+
+ if (ats_count == 0)
+ return;
+ for (i = 0; i < ats_count; i++)
+ {
+ for (j=0;j < n->ats_count; j++)
+ {
+ if (n->ats[j].type == ats[i].type)
+ {
+ n->ats[j].value = ats[i].value;
+ break;
+ }
+ }
+ if (j == n->ats_count)
+ {
+ GNUNET_array_append (n->ats,
+ n->ats_count,
+ ats[i]);
+ }
+ }
+}
+
+
/**
* 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)
*/
static void
-handle_ping (struct Neighbour *n, const struct PingMessage *m)
+handle_ping (struct Neighbour *n,
+ const struct PingMessage *m,
+ const struct GNUNET_TRANSPORT_ATS_Information *ats,
+ uint32_t ats_count)
{
struct PingMessage t;
struct PongMessage tx;
struct PongMessage *tp;
struct MessageEntry *me;
+ struct GNUNET_CRYPTO_AesInitializationVector iv;
#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,
- &my_identity.hashPubKey,
- &m->challenge,
- &t.challenge,
+ &iv,
+ &m->target,
+ &t.target,
sizeof (struct PingMessage) -
- sizeof (struct GNUNET_MessageHeader)))
+ ((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\n",
+ "Decrypted `%s' to `%4s' with challenge %u decrypted using key %u, IV %u (salt %u)\n",
"PING",
GNUNET_i2s (&t.target),
- (unsigned int) ntohl (t.challenge),
- (unsigned int) n->decrypt_key.crc32);
+ (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"),
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,
me->deadline = GNUNET_TIME_relative_to_absolute (MAX_PONG_DELAY);
me->priority = PONG_PRIORITY;
me->size = sizeof (struct PongMessage);
- tx.reserved = htonl (0);
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,
- &my_identity.hashPubKey,
+ &iv,
&tx.challenge,
&tp->challenge,
sizeof (struct PongMessage) -
- sizeof (struct GNUNET_MessageHeader));
+ ((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\n",
+ "Encrypting `%s' with challenge %u using key %u, IV %u (salt %u)\n",
"PONG",
- (unsigned int) ntohl (t.challenge),
- (unsigned int) n->encrypt_key.crc32);
+ (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);
*
* @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)
*/
static void
handle_pong (struct Neighbour *n,
- const struct PongMessage *m)
+ const struct PongMessage *m,
+ const struct GNUNET_TRANSPORT_ATS_Information *ats,
+ uint32_t ats_count)
{
struct PongMessage t;
- struct ConnectNotifyMessage cnm;
+ 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;
#if DEBUG_CORE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
#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,
- &n->peer.hashPubKey,
+ &iv,
&m->challenge,
&t.challenge,
sizeof (struct PongMessage) -
- sizeof (struct GNUNET_MessageHeader)))
+ ((void *) &m->challenge - (void *) m)))
{
GNUNET_break_op (0);
return;
gettext_noop ("# PONG messages decrypted"),
1,
GNUNET_NO);
- if (0 != ntohl (t.reserved))
- {
- GNUNET_break_op (0);
- return;
- }
#if DEBUG_HANDSHAKE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Decrypted `%s' from `%4s' with challenge %u using key %u\n",
+ "Decrypted `%s' from `%4s' with challenge %u using key %u, IV %u (salt %u)\n",
"PONG",
GNUNET_i2s (&t.target),
- (unsigned int) ntohl (t.challenge),
- (unsigned int) n->decrypt_key.crc32);
+ (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 != ntohl (t.challenge)))
+ (n->ping_challenge != t.challenge))
{
/* PONG malformed */
#if DEBUG_CORE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Received malformed `%s' received from `%4s' with challenge %u\n",
"PONG", GNUNET_i2s (&t.target),
- (unsigned int) ntohl (t.challenge));
+ (unsigned int) t.challenge);
#endif
- GNUNET_break_op (0);
+ GNUNET_break_op (n->ping_challenge != t.challenge);
return;
}
switch (n->status)
#endif
if (n->retry_set_key_task != GNUNET_SCHEDULER_NO_TASK)
{
- GNUNET_SCHEDULER_cancel (sched, n->retry_set_key_task);
+ GNUNET_SCHEDULER_cancel (n->retry_set_key_task);
n->retry_set_key_task = GNUNET_SCHEDULER_NO_TASK;
}
- cnm.header.size = htons (sizeof (struct ConnectNotifyMessage));
- cnm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT);
- cnm.distance = htonl (n->last_distance);
- cnm.latency = GNUNET_TIME_relative_hton (n->last_latency);
- cnm.peer = n->peer;
- send_to_all_clients (&cnm.header, GNUNET_YES, GNUNET_CORE_OPTION_SEND_CONNECT);
+ 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 (sched, n->keep_alive_task);
+ GNUNET_SCHEDULER_cancel (n->keep_alive_task);
n->keep_alive_task
- = GNUNET_SCHEDULER_add_delayed (sched,
- GNUNET_TIME_relative_divide (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, 2),
+ = 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);
*
* @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)
+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;
n->skm = m_cpy;
n->pitr = GNUNET_PEERINFO_iterate (peerinfo,
&n->peer,
- 0,
GNUNET_TIME_UNIT_MINUTES,
&process_hello_retry_handle_set_key, n);
GNUNET_STATISTICS_update (stats,
&my_identity,
sizeof (struct GNUNET_PeerIdentity)))
{
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
_("Received `%s' message that was for `%s', not for me. Ignoring.\n"),
"SET_KEY",
GNUNET_i2s (&m->target));
t = GNUNET_TIME_absolute_ntoh (m->creation_time);
if (((n->status == PEER_STATE_KEY_RECEIVED) ||
(n->status == PEER_STATE_KEY_CONFIRMED)) &&
- (t.value < n->decrypt_key_created.value))
+ (t.abs_value < n->decrypt_key_created.abs_value))
{
/* this could rarely happen due to massive re-ordering of
messages on the network level, but is most likely either
1,
GNUNET_NO);
n->decrypt_key = k;
- if (n->decrypt_key_created.value != t.value)
+ 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;
}
+ update_neighbour_performance (n, ats, ats_count);
sender_status = (enum PeerStateMachine) ntohl (m->sender_status);
switch (n->status)
{
{
ping = n->pending_ping;
n->pending_ping = NULL;
- handle_ping (n, ping);
+ 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);
+ handle_pong (n, pong, NULL, 0);
GNUNET_free (pong);
}
}
struct Client *client,
const void *m, size_t msize)
{
- char buf[msize + sizeof (struct NotifyTrafficMessage)];
+ size_t size = msize + sizeof (struct NotifyTrafficMessage) +
+ (sender->ats_count) * sizeof (struct GNUNET_TRANSPORT_ATS_Information);
+ char buf[size];
struct NotifyTrafficMessage *ntm;
+ struct GNUNET_TRANSPORT_ATS_Information *ats;
+ if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
+ {
+ GNUNET_break (0);
+ /* recovery strategy: throw performance data away... */
+ GNUNET_array_grow (sender->ats,
+ sender->ats_count,
+ 0);
+ size = msize + sizeof (struct NotifyTrafficMessage) +
+ (sender->ats_count) * sizeof (struct GNUNET_TRANSPORT_ATS_Information);
+ }
#if DEBUG_CORE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Core service passes message from `%4s' of type %u to client.\n",
(unsigned int) ntohs (((const struct GNUNET_MessageHeader *) m)->type));
#endif
ntm = (struct NotifyTrafficMessage *) buf;
- ntm->header.size = htons (msize + sizeof (struct NotifyTrafficMessage));
+ ntm->header.size = htons (size);
ntm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND);
- ntm->distance = htonl (sender->last_distance);
- ntm->latency = GNUNET_TIME_relative_hton (sender->last_latency);
+ ntm->ats_count = htonl (sender->ats_count);
ntm->peer = sender->peer;
- memcpy (&ntm[1], m, msize);
+ ats = &ntm->ats;
+ memcpy (ats,
+ sender->ats,
+ sizeof (struct GNUNET_TRANSPORT_ATS_Information) * sender->ats_count);
+ ats[sender->ats_count].type = htonl (GNUNET_TRANSPORT_ATS_ARRAY_TERMINATOR);
+ ats[sender->ats_count].value = htonl (0);
+ memcpy (&ats[sender->ats_count+1],
+ m,
+ msize);
send_to_client (client, &ntm->header, GNUNET_YES);
}
/**
* Deliver P2P message to interested clients.
*
- * @param sender who sent us the message?
+ * @param cls always NULL
+ * @param client who sent us the message (struct Neighbour)
* @param m the message
- * @param msize size of the message (including header)
*/
static void
-deliver_message (struct Neighbour *sender,
- const struct GNUNET_MessageHeader *m, size_t msize)
+deliver_message (void *cls,
+ void *client,
+ const struct GNUNET_MessageHeader *m)
{
+ struct Neighbour *sender = client;
+ size_t msize = ntohs (m->size);
char buf[256];
struct Client *cpos;
uint16_t type;
(unsigned int) type,
GNUNET_i2s (&sender->peer));
#endif
- /* FIXME: stats... */
- }
-}
-
-
-/**
- * Align P2P message and then deliver to interested clients.
- *
- * @param sender who sent us the message?
- * @param buffer unaligned (!) buffer containing message
- * @param msize size of the message (including header)
- */
-static void
-align_and_deliver (struct Neighbour *sender, const char *buffer, size_t msize)
-{
- char abuf[msize];
-
- /* TODO: call to statistics? */
- memcpy (abuf, buffer, msize);
- deliver_message (sender, (const struct GNUNET_MessageHeader *) abuf, msize);
-}
-
-
-/**
- * Deliver P2P messages to interested clients.
- *
- * @param sender who sent us the message?
- * @param buffer buffer containing messages, can be modified
- * @param buffer_size size of the buffer (overall)
- * @param offset offset where messages in the buffer start
- */
-static void
-deliver_messages (struct Neighbour *sender,
- const char *buffer, size_t buffer_size, size_t offset)
-{
- struct GNUNET_MessageHeader *mhp;
- struct GNUNET_MessageHeader mh;
- uint16_t msize;
- int need_align;
-
- while (offset + sizeof (struct GNUNET_MessageHeader) <= buffer_size)
- {
- if (0 != offset % sizeof (uint16_t))
- {
- /* outch, need to copy to access header */
- memcpy (&mh, &buffer[offset], sizeof (struct GNUNET_MessageHeader));
- mhp = &mh;
- }
- else
- {
- /* can access header directly */
- mhp = (struct GNUNET_MessageHeader *) &buffer[offset];
- }
- msize = ntohs (mhp->size);
- if (msize + offset > buffer_size)
- {
- /* malformed message, header says it is larger than what
- would fit into the overall buffer */
- GNUNET_break_op (0);
- break;
- }
-#if HAVE_UNALIGNED_64_ACCESS
- need_align = (0 != offset % 4) ? GNUNET_YES : GNUNET_NO;
-#else
- need_align = (0 != offset % 8) ? GNUNET_YES : GNUNET_NO;
-#endif
-#if DEBUG_CORE
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Delivering %u bytes of message at offset %u/%u to clients.\n",
- (unsigned int) msize,
- (unsigned int) offset,
- (unsigned int) buffer_size);
-#endif
-
- if (GNUNET_YES == need_align)
- align_and_deliver (sender, &buffer[offset], msize);
- else
- deliver_message (sender,
- (const struct GNUNET_MessageHeader *)
- &buffer[offset], msize);
- offset += msize;
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# messages not delivered to any client"),
+ 1, GNUNET_NO);
}
}
/**
* 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 EncryptedMessage *m,
+ const struct GNUNET_TRANSPORT_ATS_Information *ats,
+ uint32_t ats_count)
{
size_t size = ntohs (m->header.size);
char buf[size];
GNUNET_HashCode ph;
uint32_t snum;
struct GNUNET_TIME_Absolute t;
- GNUNET_HashCode iv;
+ 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
- GNUNET_CRYPTO_hash (&m->iv_seed, sizeof (uint32_t), &iv);
- /* decrypt */
- if (GNUNET_OK !=
- do_decrypt (n,
- &iv,
- &m->plaintext_hash,
- &buf[ENCRYPTED_HEADER_SIZE],
- size - ENCRYPTED_HEADER_SIZE))
- return;
- pt = (struct EncryptedMessage *) buf;
/* validate hash */
- GNUNET_CRYPTO_hash (&pt->sequence_number,
- size - ENCRYPTED_HEADER_SIZE - sizeof (GNUNET_HashCode), &ph);
-#if DEBUG_HANDSHAKE
+ 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,
- "V-Hashed %u bytes of plaintext (`%s') using IV `%d'\n",
- (unsigned int) (size - ENCRYPTED_HEADER_SIZE - sizeof (GNUNET_HashCode)),
- GNUNET_h2s (&ph),
- (int) m->iv_seed);
+ "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,
- &pt->plaintext_hash,
+
+ 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)
{
- n->last_packets_bitmap <<= (snum - n->last_sequence_number_received);
+ 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).value > MAX_MESSAGE_AGE.value)
+ 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).value);
+ GNUNET_TIME_absolute_get_duration (t).rel_value);
GNUNET_STATISTICS_set (stats,
gettext_noop ("# bytes dropped (ancient message)"),
size,
}
n->last_activity = GNUNET_TIME_absolute_get ();
if (n->keep_alive_task != GNUNET_SCHEDULER_NO_TASK)
- GNUNET_SCHEDULER_cancel (sched, n->keep_alive_task);
+ GNUNET_SCHEDULER_cancel (n->keep_alive_task);
n->keep_alive_task
- = GNUNET_SCHEDULER_add_delayed (sched,
- GNUNET_TIME_relative_divide (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, 2),
+ = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_divide (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, 2),
&send_keep_alive,
n);
GNUNET_STATISTICS_set (stats,
gettext_noop ("# bytes of payload decrypted"),
size - sizeof (struct EncryptedMessage),
- GNUNET_NO);
- deliver_messages (n, buf, 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);
}
* @param cls closure
* @param peer (claimed) identity of the other peer
* @param message the message
- * @param latency estimated latency for communicating with the
- * given peer (round-trip)
- * @param distance in overlay hops, as given by transport plugin
+ * @param ats performance data
+ * @param ats_count number of entries in ats (excluding 0-termination)
*/
static void
handle_transport_receive (void *cls,
const struct GNUNET_PeerIdentity *peer,
const struct GNUNET_MessageHeader *message,
- struct GNUNET_TIME_Relative latency,
- unsigned int distance)
+ const struct GNUNET_TRANSPORT_ATS_Information *ats,
+ 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
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
n = find_neighbour (peer);
if (n == NULL)
n = create_neighbour (peer);
- n->last_latency = latency;
- n->last_distance = distance;
+ changed = GNUNET_NO;
up = (n->status == PEER_STATE_KEY_CONFIRMED);
type = ntohs (message->type);
size = ntohs (message->size);
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);
+ 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) +
if ((n->status != PEER_STATE_KEY_RECEIVED) &&
(n->status != PEER_STATE_KEY_CONFIRMED))
{
- GNUNET_break_op (0);
+ 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);
+ handle_encrypted_message (n,
+ (const struct EncryptedMessage *) message,
+ ats, ats_count);
break;
case GNUNET_MESSAGE_TYPE_CORE_PING:
if (size != sizeof (struct PingMessage))
memcpy (n->pending_ping, message, sizeof (struct PingMessage));
return;
}
- handle_ping (n, (const struct PingMessage *) message);
+ handle_ping (n, (const struct PingMessage *) message,
+ ats, ats_count);
break;
case GNUNET_MESSAGE_TYPE_CORE_PONG:
if (size != sizeof (struct PongMessage))
memcpy (n->pending_pong, message, sizeof (struct PongMessage));
return;
}
- handle_pong (n, (const struct PongMessage *) message);
+ handle_pong (n, (const struct PongMessage *) message,
+ ats, ats_count);
break;
default:
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
{
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);
+ 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 (sched, n->keep_alive_task);
+ GNUNET_SCHEDULER_cancel (n->keep_alive_task);
n->keep_alive_task
- = GNUNET_SCHEDULER_add_delayed (sched,
- GNUNET_TIME_relative_divide (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, 2),
+ = 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);
}
{
struct Neighbour *n = cls;
struct GNUNET_BANDWIDTH_Value32NBO q_in;
+ struct GNUNET_BANDWIDTH_Value32NBO q_out;
+ struct GNUNET_BANDWIDTH_Value32NBO q_out_min;
double pref_rel;
double share;
unsigned long long distributable;
uint64_t need_per_peer;
uint64_t need_per_second;
+ unsigned int neighbour_count;
#if DEBUG_CORE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
divides by a bit more to avoid division by zero AND to
account for possibility of new neighbours joining any time
AND to convert to double... */
+ neighbour_count = GNUNET_CONTAINER_multihashmap_size (neighbours);
+ if (neighbour_count == 0)
+ return;
if (preference_sum == 0)
{
pref_rel = 1.0 / (double) neighbour_count;
need_per_peer = GNUNET_BANDWIDTH_value_get_available_until (MIN_BANDWIDTH_PER_PEER,
GNUNET_TIME_UNIT_SECONDS);
need_per_second = need_per_peer * neighbour_count;
+
+ /* calculate inbound bandwidth per peer */
distributable = 0;
- if (bandwidth_target_out_bps > need_per_second)
- distributable = bandwidth_target_out_bps - need_per_second;
+ if (bandwidth_target_in_bps > need_per_second)
+ distributable = bandwidth_target_in_bps - need_per_second;
share = distributable * pref_rel;
if (share + need_per_peer > UINT32_MAX)
q_in = GNUNET_BANDWIDTH_value_init (UINT32_MAX);
else
q_in = GNUNET_BANDWIDTH_value_init (need_per_peer + (uint32_t) share);
+
+ /* calculate outbound bandwidth per peer */
+ distributable = 0;
+ if (bandwidth_target_out_bps > need_per_second)
+ distributable = bandwidth_target_out_bps - need_per_second;
+ share = distributable * pref_rel;
+ if (share + need_per_peer > UINT32_MAX)
+ q_out = GNUNET_BANDWIDTH_value_init (UINT32_MAX);
+ else
+ q_out = GNUNET_BANDWIDTH_value_init (need_per_peer + (uint32_t) share);
+ n->bw_out_internal_limit = q_out;
+
+ q_out_min = 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);
+
/* check if we want to disconnect for good due to inactivity */
- if ( (GNUNET_TIME_absolute_get_duration (n->last_activity).value > GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.value) &&
- (GNUNET_TIME_absolute_get_duration (n->time_established).value > GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.value) )
+ if ( (GNUNET_TIME_absolute_get_duration (get_neighbour_timeout (n)).rel_value > 0) &&
+ (GNUNET_TIME_absolute_get_duration (n->time_established).rel_value > GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value) )
{
#if DEBUG_CORE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
(unsigned int) ntohl (n->bw_in.value__),
(unsigned int) ntohl (n->bw_out.value__),
(unsigned int) ntohl (n->bw_out_internal_limit.value__));
-#endif
- if (n->bw_in.value__ != q_in.value__)
+ #endif
+ if ((n->bw_in.value__ != q_in.value__) || (n->bw_out.value__ != q_out_min.value__))
{
- n->bw_in = q_in;
+ if (n->bw_in.value__ != q_in.value__)
+ n->bw_in = q_in;
+ if (n->bw_out.value__ != q_out_min.value__)
+ n->bw_out = q_out_min;
if (GNUNET_YES == n->is_connected)
GNUNET_TRANSPORT_set_quota (transport,
&n->peer,
n->bw_out,
GNUNET_TIME_UNIT_FOREVER_REL,
NULL, NULL);
+ handle_peer_status_change (n);
}
schedule_quota_update (n);
}
*
* @param cls closure
* @param peer the peer that connected
- * @param latency current latency of the connection
- * @param distance in overlay hops, as given by transport plugin
+ * @param ats performance data
+ * @param ats_count number of entries in ats (excluding 0-termination)
*/
static void
handle_transport_notify_connect (void *cls,
const struct GNUNET_PeerIdentity *peer,
- struct GNUNET_TIME_Relative latency,
- unsigned int distance)
+ const struct GNUNET_TRANSPORT_ATS_Information *ats,
+ uint32_t ats_count)
{
struct Neighbour *n;
1,
GNUNET_NO);
n->is_connected = GNUNET_YES;
- n->last_latency = latency;
- n->last_distance = distance;
+ update_neighbour_performance (n, ats, ats_count);
GNUNET_BANDWIDTH_tracker_init (&n->available_send_window,
n->bw_out,
MAX_WINDOW_TIME_S);
{
struct DisconnectNotifyMessage cnm;
struct Neighbour *n;
+ struct ClientActiveRequest *car;
+ struct GNUNET_TIME_Relative left;
#if DEBUG_CORE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Peer `%4s' disconnected from us.\n", GNUNET_i2s (peer));
+ "Peer `%4s' disconnected from us; received notification from transport.\n",
+ GNUNET_i2s (peer));
#endif
n = find_neighbour (peer);
if (n == NULL)
GNUNET_break (0);
return;
}
- GNUNET_break (n->is_connected);
- cnm.header.size = htons (sizeof (struct DisconnectNotifyMessage));
- cnm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT);
- cnm.peer = *peer;
- send_to_all_clients (&cnm.header, GNUNET_YES, GNUNET_CORE_OPTION_SEND_DISCONNECT);
+ GNUNET_break (n->is_connected == GNUNET_YES);
+ if (n->status == PEER_STATE_KEY_CONFIRMED)
+ {
+ cnm.header.size = htons (sizeof (struct DisconnectNotifyMessage));
+ cnm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT);
+ cnm.reserved = htonl (0);
+ cnm.peer = *peer;
+ send_to_all_clients (&cnm.header, GNUNET_NO, GNUNET_CORE_OPTION_SEND_DISCONNECT);
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# established sessions"),
+ -1,
+ GNUNET_NO);
+ }
+
+ /* On transport disconnect transport doesn't cancel requests, so must do so here. */
+ if (n->th != NULL)
+ {
+ GNUNET_TRANSPORT_notify_transmit_ready_cancel (n->th);
+ n->th = NULL;
+ }
n->is_connected = GNUNET_NO;
+ n->status = PEER_STATE_DOWN;
+ while (NULL != (car = n->active_client_request_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (n->active_client_request_head,
+ n->active_client_request_tail,
+ car);
+ GNUNET_CONTAINER_multihashmap_remove (car->client->requests,
+ &n->peer.hashPubKey,
+ car);
+ GNUNET_free (car);
+ }
+
GNUNET_STATISTICS_update (stats,
gettext_noop ("# peers connected (transport)"),
-1,
GNUNET_NO);
+ if (n->dead_clean_task != GNUNET_SCHEDULER_NO_TASK)
+ GNUNET_SCHEDULER_cancel (n->dead_clean_task);
+ left = GNUNET_TIME_relative_subtract (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
+ GNUNET_CONSTANTS_DISCONNECT_SESSION_TIMEOUT);
+ n->last_activity = GNUNET_TIME_absolute_subtract (GNUNET_TIME_absolute_get (),
+ left);
+ n->dead_clean_task = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_DISCONNECT_SESSION_TIMEOUT,
+ &consider_free_task,
+ n);
+}
+
+
+/**
+ * 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;
}
static void
cleaning_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
{
- struct Neighbour *n;
struct Client *c;
#if DEBUG_CORE
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Core service shutting down.\n");
#endif
+ GNUNET_CONTAINER_multihashmap_iterate (neighbours,
+ &free_neighbour_helper,
+ NULL);
+ GNUNET_CONTAINER_multihashmap_destroy (neighbours);
+ neighbours = NULL;
+ GNUNET_STATISTICS_set (stats, gettext_noop ("# neighbour entries allocated"), 0, GNUNET_NO);
GNUNET_assert (transport != NULL);
GNUNET_TRANSPORT_disconnect (transport);
transport = NULL;
- while (NULL != (n = neighbours))
- {
- neighbours = n->next;
- GNUNET_assert (neighbour_count > 0);
- neighbour_count--;
- free_neighbour (n);
- }
- GNUNET_STATISTICS_set (stats, gettext_noop ("# neighbour entries allocated"), neighbour_count, GNUNET_NO);
GNUNET_SERVER_notification_context_destroy (notifier);
notifier = NULL;
while (NULL != (c = clients))
GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
if (peerinfo != NULL)
GNUNET_PEERINFO_disconnect (peerinfo);
+ if (mst != NULL)
+ GNUNET_SERVER_mst_destroy (mst);
}
* Initiate core service.
*
* @param cls closure
- * @param s scheduler to use
- * @param serv the initialized server
+ * @param server the initialized server
* @param c configuration to use
*/
static void
run (void *cls,
- struct GNUNET_SCHEDULER_Handle *s,
- struct GNUNET_SERVER_Handle *serv,
+ struct GNUNET_SERVER_Handle *server,
const struct GNUNET_CONFIGURATION_Handle *c)
{
+ static const struct GNUNET_SERVER_MessageHandler handlers[] = {
+ {&handle_client_init, NULL,
+ GNUNET_MESSAGE_TYPE_CORE_INIT, 0},
+ {&handle_client_iterate_peers, NULL,
+ GNUNET_MESSAGE_TYPE_CORE_ITERATE_PEERS,
+ sizeof (struct GNUNET_MessageHeader)},
+ {&handle_client_have_peer, NULL,
+ GNUNET_MESSAGE_TYPE_CORE_PEER_CONNECTED,
+ sizeof (struct GNUNET_MessageHeader) + sizeof(struct GNUNET_PeerIdentity)},
+ {&handle_client_request_info, NULL,
+ GNUNET_MESSAGE_TYPE_CORE_REQUEST_INFO,
+ sizeof (struct RequestInfoMessage)},
+ {&handle_client_send_request, NULL,
+ GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST,
+ sizeof (struct SendMessageRequest)},
+ {&handle_client_send, NULL,
+ GNUNET_MESSAGE_TYPE_CORE_SEND, 0},
+ {&handle_client_request_connect, NULL,
+ GNUNET_MESSAGE_TYPE_CORE_REQUEST_CONNECT,
+ sizeof (struct ConnectMessage)},
+ {NULL, NULL, 0, 0}
+ };
char *keyfile;
- sched = s;
- cfg = c;
+ cfg = c;
/* parse configuration */
if (
(GNUNET_OK !=
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_
("Core service is lacking key configuration settings. Exiting.\n"));
- GNUNET_SCHEDULER_shutdown (s);
+ GNUNET_SCHEDULER_shutdown ();
return;
}
- peerinfo = GNUNET_PEERINFO_connect (sched, cfg);
+ peerinfo = GNUNET_PEERINFO_connect (cfg);
if (NULL == peerinfo)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_("Could not access PEERINFO service. Exiting.\n"));
- GNUNET_SCHEDULER_shutdown (s);
+ GNUNET_SCHEDULER_shutdown ();
GNUNET_free (keyfile);
return;
}
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_("Core service could not access hostkey. Exiting.\n"));
GNUNET_PEERINFO_disconnect (peerinfo);
- GNUNET_SCHEDULER_shutdown (s);
+ GNUNET_SCHEDULER_shutdown ();
return;
}
+ neighbours = GNUNET_CONTAINER_multihashmap_create (128);
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);
+ self.public_key = &my_public_key;
+ self.peer = my_identity;
+ self.last_activity = GNUNET_TIME_UNIT_FOREVER_ABS;
+ self.status = PEER_STATE_KEY_CONFIRMED;
+ self.is_connected = GNUNET_YES;
/* setup notification */
- server = serv;
notifier = GNUNET_SERVER_notification_context_create (server,
MAX_NOTIFY_QUEUE);
GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL);
/* setup transport connection */
- transport = GNUNET_TRANSPORT_connect (sched,
- cfg,
+ transport = GNUNET_TRANSPORT_connect (cfg,
+ &my_identity,
NULL,
&handle_transport_receive,
&handle_transport_notify_connect,
&handle_transport_notify_disconnect);
GNUNET_assert (NULL != transport);
- stats = GNUNET_STATISTICS_create (sched, "core", cfg);
- GNUNET_SCHEDULER_add_delayed (sched,
- GNUNET_TIME_UNIT_FOREVER_REL,
+ stats = GNUNET_STATISTICS_create ("core", cfg);
+
+ GNUNET_STATISTICS_set (stats, gettext_noop ("# discarded CORE_SEND requests"), 0, GNUNET_NO);
+ GNUNET_STATISTICS_set (stats, gettext_noop ("# discarded lower priority CORE_SEND requests"), 0, GNUNET_NO);
+
+ mst = GNUNET_SERVER_mst_create (&deliver_message,
+ NULL);
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
&cleaning_task, NULL);
/* process client requests */
GNUNET_SERVER_add_handlers (server, handlers);