check
[oweals/gnunet.git] / src / core / gnunet-service-core.c
index 0770fb78a02e8fca13035c3669e172f298f71624..f54e4a7a0434537530cbec2904c460ccb6c52ec6 100644 (file)
  */
 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.
@@ -179,22 +188,21 @@ struct EncryptedMessage
   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 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;
 
@@ -213,6 +221,13 @@ struct EncryptedMessage
 };
 
 
+/**
+ * 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
@@ -224,17 +239,22 @@ struct PingMessage
    * 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;
 };
 
 
@@ -249,17 +269,17 @@ struct PongMessage
    * 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
@@ -387,12 +407,17 @@ struct MessageEntry
 };
 
 
+/**
+ * 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.
@@ -411,6 +436,18 @@ struct Neighbour
    */
   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.
@@ -448,6 +485,11 @@ struct Neighbour
    */
   struct SetKeyMessage *skm;
 
+  /**
+   * Performance data for the peer.
+   */ 
+  struct GNUNET_TRANSPORT_ATS_Information *ats;
+
   /**
    * Identity of the neighbour.
    */
@@ -512,11 +554,6 @@ struct 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?
    */
@@ -537,6 +574,11 @@ struct Neighbour
    */
   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
@@ -583,11 +625,6 @@ struct Neighbour
    */
   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?
    */
@@ -623,6 +660,12 @@ struct Client
    */
   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.
@@ -638,6 +681,59 @@ struct Client
 };
 
 
+/**
+ * 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.
  */
@@ -653,10 +749,6 @@ static struct GNUNET_PeerIdentity my_identity;
  */
 static struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key;
 
-/**
- * Our scheduler.
- */
-struct GNUNET_SCHEDULER_Handle *sched;
 
 /**
  * Handle to peerinfo service.
@@ -689,9 +781,14 @@ static struct Client *clients;
 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.
@@ -703,14 +800,8 @@ static struct GNUNET_STATISTICS_Handle *stats;
  */
 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;
 
@@ -719,6 +810,106 @@ 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;
+}    
 
 
 /**
@@ -730,7 +921,6 @@ static unsigned long long bandwidth_target_out_bps;
 static void
 update_preference_sum (unsigned long long inc)
 {
-  struct Neighbour *n;
   unsigned long long os;
 
   os = preference_sum;
@@ -739,13 +929,9 @@ update_preference_sum (unsigned long long inc)
     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);
 }
 
@@ -760,14 +946,7 @@ update_preference_sum (unsigned long long inc)
 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);
 }
 
 
@@ -833,30 +1012,51 @@ send_to_all_clients (const struct GNUNET_MessageHeader *msg,
  * Function called by transport telling us that a peer
  * changed status.
  *
- * @param peer the peer that changed status
+ * @param n the peer that changed status
  */
 static void
 handle_peer_status_change (struct Neighbour *n)
 {
-  struct PeerStatusNotifyMessage psnm;
+  struct PeerStatusNotifyMessage *psnm;
+  char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
+  struct GNUNET_TRANSPORT_ATS_Information *ats;
+  size_t size;
 
-  if (! n->is_connected)
+  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 (peer));
+             GNUNET_i2s (&n->peer));
 #endif
-  psnm.header.size = htons (sizeof (struct PeerStatusNotifyMessage));
-  psnm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_STATUS_CHANGE);
-  psnm.distance = htonl (n->last_distance);
-  psnm.latency = GNUNET_TIME_relative_hton (n->last_latency);
-  psnm.timeout = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (n->last_activity,
-                                                                     GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
-  psnm.bandwidth_in = n->bw_in;
-  psnm.bandwidth_out = n->bw_out;
-  psnm.peer = n->peer;
-  send_to_all_clients (&psnm.header, 
+  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, 
@@ -866,6 +1066,213 @@ handle_peer_status_change (struct Neighbour *n)
 }
 
 
+/**
+ * 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.
  */
@@ -880,8 +1287,6 @@ handle_client_init (void *cls,
   uint16_t msize;
   const uint16_t *types;
   uint16_t *wtypes;
-  struct Neighbour *n;
-  struct ConnectNotifyMessage cnm;
   unsigned int i;
 
 #if DEBUG_CORE_CLIENT
@@ -943,29 +1348,42 @@ handle_client_init (void *cls,
   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,
+                                            &notify_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.
  *
@@ -990,23 +1408,156 @@ handle_client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
   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,
@@ -1014,17 +1565,33 @@ 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));
@@ -1056,8 +1623,9 @@ handle_client_request_info (void *cls,
         }
       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 */
@@ -1082,22 +1650,20 @@ handle_client_request_info (void *cls,
                  (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);
 }
 
 
@@ -1111,6 +1677,7 @@ static void
 free_neighbour (struct Neighbour *n)
 {
   struct MessageEntry *m;
+  struct ClientActiveRequest *car;
 
 #if DEBUG_CORE
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -1139,23 +1706,37 @@ free_neighbour (struct Neighbour *n)
                                   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);
@@ -1186,7 +1767,7 @@ static void process_encrypted_neighbour_queue (struct Neighbour *n);
  */
 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)
@@ -1198,16 +1779,15 @@ do_encrypt (struct Neighbour *n,
                  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;
 }
@@ -1240,6 +1820,7 @@ send_keep_alive (void *cls,
   struct MessageEntry *me;
   struct PingMessage pp;
   struct PingMessage *pm;
+  struct GNUNET_CRYPTO_AesInitializationVector iv;
 
   n->keep_alive_task = GNUNET_SCHEDULER_NO_TASK;
   /* send PING */
@@ -1255,31 +1836,34 @@ send_keep_alive (void *cls,
   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);
 
@@ -1312,45 +1896,31 @@ consider_free_task (void *cls,
 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);
 }
@@ -1366,7 +1936,9 @@ consider_free_neighbour (struct 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;
@@ -1416,6 +1988,10 @@ notify_encrypted_transmit_ready (void *cls, size_t size, void *buf)
     }
   GNUNET_free (m);
   consider_free_neighbour (n);
+  GNUNET_STATISTICS_update (stats, 
+                           gettext_noop ("# encrypted bytes given to transport"), 
+                           ret, 
+                           GNUNET_NO);
   return ret;
 }
 
@@ -1456,8 +2032,7 @@ process_encrypted_neighbour_queue (struct Neighbour *n)
               "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,
@@ -1495,7 +2070,7 @@ process_encrypted_neighbour_queue (struct Neighbour *n)
  */
 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)
@@ -1513,20 +2088,23 @@ do_decrypt (struct Neighbour *n,
       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;
 }
@@ -1613,7 +2191,7 @@ select_messages (struct Neighbour *n,
           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;
@@ -1634,7 +2212,7 @@ select_messages (struct Neighbour *n,
                   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;
@@ -1676,7 +2254,7 @@ select_messages (struct Neighbour *n,
     }
   /* 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) )
     {
@@ -1691,10 +2269,13 @@ select_messages (struct Neighbour *n,
           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
@@ -1775,13 +2356,14 @@ batch_message (struct Neighbour *n,
 #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;
@@ -1833,9 +2415,9 @@ batch_message (struct Neighbour *n,
          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;
@@ -1851,7 +2433,7 @@ batch_message (struct Neighbour *n,
 #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;
 }
@@ -1871,7 +2453,9 @@ discard_expired_messages (struct Neighbour *n)
   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;
@@ -1879,23 +2463,30 @@ discard_expired_messages (struct Neighbour *n)
     {
       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);
 }
 
 
@@ -1956,18 +2547,18 @@ process_plaintext_neighbour_queue (struct Neighbour *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)
@@ -1983,8 +2574,7 @@ process_plaintext_neighbour_queue (struct Neighbour *n)
     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,
@@ -1995,8 +2585,7 @@ process_plaintext_neighbour_queue (struct Neighbour *n)
     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,
@@ -2045,8 +2634,7 @@ process_plaintext_neighbour_queue (struct Neighbour *n)
 #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;
     }
@@ -2056,7 +2644,7 @@ process_plaintext_neighbour_queue (struct Neighbour *n)
              (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 ());
@@ -2070,38 +2658,43 @@ process_plaintext_neighbour_queue (struct Neighbour *n)
   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_hmac (&n->encrypt_key,
-                     &ph->sequence_number,
-                     esize - sizeof (GNUNET_HashCode), 
-                     &ph->hmac);
-  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->hmac),
-             (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->hmac,
-                             &em->hmac, 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);
 }
 
 
@@ -2128,8 +2721,7 @@ schedule_quota_update (struct Neighbour *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);
 }
@@ -2153,10 +2745,6 @@ create_neighbour (const struct GNUNET_PeerIdentity *pid)
              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 ();
@@ -2167,8 +2755,15 @@ create_neighbour (const struct GNUNET_PeerIdentity *pid)
   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;
@@ -2202,6 +2797,7 @@ handle_client_send (void *cls,
   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);
@@ -2211,10 +2807,15 @@ handle_client_send (void *cls,
   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);
@@ -2227,8 +2828,15 @@ handle_client_send (void *cls,
               (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;
@@ -2237,7 +2845,7 @@ handle_client_send (void *cls,
   pos = n->messages;
   while (pos != NULL) 
     {
-      if (pos->priority < min_prio)
+      if (pos->priority <= min_prio)
        {
          min_prio_entry = pos;
          min_prio_prev = prev;
@@ -2252,7 +2860,8 @@ handle_client_send (void *cls,
       /* 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",
@@ -2261,15 +2870,21 @@ handle_client_send (void *cls,
                      (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
@@ -2283,16 +2898,19 @@ handle_client_send (void *cls,
              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;
@@ -2320,13 +2938,33 @@ handle_client_send (void *cls,
  * @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));
@@ -2336,10 +2974,8 @@ notify_transport_connect_done (void *cls, size_t size, void *buf)
              _("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;
 }
@@ -2361,27 +2997,57 @@ handle_client_request_connect (void *cls,
   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,
+                                                          &notify_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,
@@ -2401,14 +3067,23 @@ handle_client_request_connect (void *cls,
  * @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 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)
+                              const struct GNUNET_HELLO_Message *hello,
+                              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
@@ -2422,7 +3097,7 @@ process_hello_retry_send_key (void *cls,
        {
          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,
@@ -2444,8 +3119,7 @@ process_hello_retry_send_key (void *cls,
                                    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;
@@ -2502,10 +3176,11 @@ send_key (struct Neighbour *n)
   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)
@@ -2647,22 +3322,26 @@ send_key (struct Neighbour *n)
   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, 
@@ -2670,7 +3349,7 @@ send_key (struct Neighbour *n)
 #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:
@@ -2679,8 +3358,7 @@ send_key (struct Neighbour *n)
   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);    
 }
 
@@ -2691,10 +3369,15 @@ send_key (struct Neighbour *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);
+
 
 
 /**
@@ -2705,15 +3388,24 @@ handle_set_key (struct Neighbour *n,
  * @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 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)
+                                    const struct GNUNET_HELLO_Message *hello,
+                                    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;
@@ -2727,11 +3419,11 @@ process_hello_retry_handle_set_key (void *cls,
                      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));
@@ -2752,41 +3444,88 @@ process_hello_retry_handle_set_key (void *cls,
 }
 
 
+/**
+ * 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"), 
@@ -2798,6 +3537,7 @@ handle_ping (struct Neighbour *n, const struct PingMessage *m)
       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,
@@ -2807,29 +3547,32 @@ handle_ping (struct Neighbour *n, const struct PingMessage *m)
   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);
@@ -2841,13 +3584,21 @@ handle_ping (struct Neighbour *n, const struct PingMessage *m)
  *
  * @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,
@@ -2856,13 +3607,15 @@ handle_pong (struct Neighbour *n,
 #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;
@@ -2871,23 +3624,20 @@ handle_pong (struct Neighbour *n,
                            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
@@ -2899,9 +3649,9 @@ handle_pong (struct Neighbour *n,
       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)
@@ -2939,24 +3689,44 @@ handle_pong (struct Neighbour *n,
 #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);
@@ -2974,9 +3744,14 @@ handle_pong (struct Neighbour *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)
+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;
@@ -3024,7 +3799,7 @@ handle_set_key (struct Neighbour *n, const struct SetKeyMessage *m)
                   &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));
@@ -3046,7 +3821,7 @@ handle_set_key (struct Neighbour *n, const struct SetKeyMessage *m)
   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
@@ -3074,13 +3849,14 @@ handle_set_key (struct Neighbour *n, const struct SetKeyMessage *m)
                            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)
     {
@@ -3128,14 +3904,14 @@ handle_set_key (struct Neighbour *n, const struct SetKeyMessage *m)
     {
       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);
     }
 }
@@ -3154,9 +3930,22 @@ send_p2p_message_to_client (struct Neighbour *sender,
                             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",
@@ -3164,12 +3953,19 @@ send_p2p_message_to_client (struct Neighbour *sender,
               (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);
 }
 
@@ -3258,10 +4054,17 @@ deliver_message (void *cls,
 /**
  * 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];
@@ -3269,42 +4072,49 @@ handle_encrypted_message (struct Neighbour *n,
   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->hmac,
-                  &buf[ENCRYPTED_HEADER_SIZE], 
-                 size - ENCRYPTED_HEADER_SIZE))
-    return;
-  pt = (struct EncryptedMessage *) buf;
   /* validate hash */
-  GNUNET_CRYPTO_hmac (&n->decrypt_key,
-                     &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->hmac, 
+
+  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);
@@ -3350,18 +4160,22 @@ handle_encrypted_message (struct Neighbour *n,
     }
   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,
@@ -3392,10 +4206,9 @@ handle_encrypted_message (struct Neighbour *n,
     }
   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,
@@ -3403,6 +4216,7 @@ handle_encrypted_message (struct Neighbour *n,
                         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)], 
@@ -3418,16 +4232,15 @@ handle_encrypted_message (struct Neighbour *n,
  * @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;
@@ -3450,9 +4263,7 @@ handle_transport_receive (void *cls,
   n = find_neighbour (peer);
   if (n == NULL)
     n = create_neighbour (peer);
-  changed = (latency.value != n->last_latency.value) || (distance != n->last_distance);
-  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);
@@ -3464,8 +4275,13 @@ handle_transport_receive (void *cls,
           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) +
@@ -3477,10 +4293,16 @@ handle_transport_receive (void *cls,
       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))
@@ -3502,7 +4324,8 @@ handle_transport_receive (void *cls,
           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))
@@ -3524,7 +4347,8 @@ handle_transport_receive (void *cls,
           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,
@@ -3539,14 +4363,16 @@ handle_transport_receive (void *cls,
       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);
     }
@@ -3568,11 +4394,14 @@ neighbour_quota_update (void *cls,
 {
   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,
@@ -3584,6 +4413,9 @@ neighbour_quota_update (void *cls,
      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;
@@ -3595,17 +4427,34 @@ neighbour_quota_update (void *cls,
   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,
@@ -3623,10 +4472,13 @@ neighbour_quota_update (void *cls,
              (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,
@@ -3646,14 +4498,14 @@ neighbour_quota_update (void *cls,
  *
  * @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;
 
@@ -3681,8 +4533,7 @@ handle_transport_notify_connect (void *cls,
                            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);
@@ -3717,11 +4568,13 @@ handle_transport_notify_disconnect (void *cls,
 {
   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)
@@ -3729,33 +4582,70 @@ handle_transport_notify_disconnect (void *cls,
       GNUNET_break (0);
       return;
     }
-  GNUNET_break (n->is_connected);
+  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_YES, GNUNET_CORE_OPTION_SEND_DISCONNECT);
+      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 (sched,
-                            n->dead_clean_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 (sched,
-                                                    GNUNET_CONSTANTS_DISCONNECT_SESSION_TIMEOUT,
+  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;
+}
+
+
 /**
  * Last task run during shutdown.  Disconnects us from
  * the transport.
@@ -3763,24 +4653,21 @@ handle_transport_notify_disconnect (void *cls,
 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))
@@ -3800,22 +4687,29 @@ cleaning_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  * Initiate core service.
  *
  * @param cls closure
- * @param s scheduler to use
  * @param server the initialized server
  * @param c configuration to use
  */
 static void
 run (void *cls,
-     struct GNUNET_SCHEDULER_Handle *s,
      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,
@@ -3825,8 +4719,7 @@ run (void *cls,
   };
   char *keyfile;
 
-  sched = s;
-  cfg = c;  
+  cfg = c;    
   /* parse configuration */
   if (
        (GNUNET_OK !=
@@ -3847,15 +4740,15 @@ run (void *cls,
       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;
     }
@@ -3866,29 +4759,38 @@ run (void *cls,
       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 */
   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);
+  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 (sched,
-                                GNUNET_TIME_UNIT_FOREVER_REL,
+  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
                                 &cleaning_task, NULL);
   /* process client requests */
   GNUNET_SERVER_add_handlers (server, handlers);