i am a dumb dummy
[oweals/gnunet.git] / src / core / gnunet-service-core.c
index 0b04e485e5a2529e6c31b17aee27750eb2b4e3f7..76e26cc8f962ef02022ae18c26cb473c865e7a37 100644 (file)
@@ -44,7 +44,7 @@
 
 #define DEBUG_HANDSHAKE GNUNET_NO
 
-#define DEBUG_CORE_QUOTA GNUNET_YES
+#define DEBUG_CORE_QUOTA GNUNET_NO
 
 /**
  * Receive and send buffer windows grow over time.  For
@@ -473,6 +473,11 @@ struct Neighbour
    */
   struct SetKeyMessage *skm;
 
+  /**
+   * Performance data for the peer.
+   */ 
+  struct GNUNET_TRANSPORT_ATS_Information *ats;
+
   /**
    * Identity of the neighbour.
    */
@@ -557,6 +562,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
@@ -859,6 +869,21 @@ derive_pong_iv (struct GNUNET_CRYPTO_AesInitializationVector *iv,
 }
 
 
+/**
+ * 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);
+}
+
+
 /**
  * A preference value for a neighbour was update.  Update
  * the preference sum accordingly.
@@ -976,7 +1001,10 @@ send_to_all_clients (const struct GNUNET_MessageHeader *msg,
 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) ||
        (n->status != PEER_STATE_KEY_CONFIRMED) )
@@ -986,17 +1014,33 @@ handle_peer_status_change (struct Neighbour *n)
               "Peer `%4s' changed status\n",
              GNUNET_i2s (&n->peer));
 #endif
-  psnm.header.size = htons (sizeof (struct PeerStatusNotifyMessage));
-  psnm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_STATUS_CHANGE);
-  psnm.ats_count = htonl (0);
-  psnm.ats.type = htonl (0);
-  psnm.ats.value = htonl (0);
-  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, 
@@ -1032,7 +1076,13 @@ schedule_peer_messages (struct Neighbour *n)
       mqe = mqe->next;
     }
   if (queue_size >= MAX_PEER_QUEUE_SIZE)
-    return; /* queue still full */
+    {
+#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;
@@ -1045,6 +1095,11 @@ schedule_peer_messages (struct Neighbour *n)
     }
   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,
@@ -1078,11 +1133,16 @@ handle_client_send_request (void *cls,
   req = (const struct SendMessageRequest*) message;
   n = find_neighbour (&req->peer);
   if ( (n == NULL) ||
-       (GNUNET_YES != n->is_connected) )
+       (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, 
@@ -1103,6 +1163,10 @@ handle_client_send_request (void *cls,
     }
   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)
@@ -1144,7 +1208,10 @@ handle_client_init (void *cls,
   const uint16_t *types;
   uint16_t *wtypes;
   struct Neighbour *n;
-  struct ConnectNotifyMessage cnm;
+  struct ConnectNotifyMessage *cnm;
+  char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
+  struct GNUNET_TRANSPORT_ATS_Information *ats;
+  size_t size;
   unsigned int i;
 
 #if DEBUG_CORE_CLIENT
@@ -1206,22 +1273,39 @@ 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);
-      cnm.ats_count = htonl (0);
-      cnm.ats.type = htonl (0);
-      cnm.ats.value = htonl (0);
       n = neighbours;
       while (n != NULL)
        {
+         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);
+             cnm->peer = n->peer;
+             send_to_client (c, &cnm->header, GNUNET_NO);
            }
          n = n->next;
        }
@@ -1243,7 +1327,7 @@ destroy_active_client_request (void *cls,
                               const GNUNET_HashCode *key,
                               void *value)
 {
-  struct ClientActiveRequest *car = cls;
+  struct ClientActiveRequest *car = value;
   struct Neighbour *n;
   struct GNUNET_PeerIdentity peer;
 
@@ -1305,6 +1389,76 @@ handle_client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
 }
 
 
+/**
+ * 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 Neighbour *n;
+  struct ConnectNotifyMessage *cnm;
+  struct GNUNET_MessageHeader done_msg;
+  struct GNUNET_SERVER_TransmitContext *tc;
+  char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
+  struct GNUNET_TRANSPORT_ATS_Information *ats;
+  size_t size;
+
+  /* notify new client about existing neighbours */
+  tc = GNUNET_SERVER_transmit_context_create (client);
+  cnm = (struct ConnectNotifyMessage*) buf;
+  n = neighbours;
+  while (n != NULL)
+    {
+      if (n->status == PEER_STATE_KEY_CONFIRMED)
+        {
+         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);
+        }
+      n = n->next;
+    }
+  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.
  */
@@ -1314,17 +1468,31 @@ 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;
 
 #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));
@@ -1389,15 +1557,12 @@ handle_client_request_info (void *cls,
   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);
 }
 
 
@@ -1467,6 +1632,7 @@ free_neighbour (struct Neighbour *n)
       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_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);
@@ -1589,8 +1755,7 @@ send_keep_alive (void *cls,
               ((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 
@@ -1636,8 +1801,7 @@ consider_free_neighbour (struct Neighbour *n)
        (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));
+  left = GNUNET_TIME_absolute_get_remaining (get_neighbour_timeout (n));
   if (left.rel_value > 0)
     {
       if (n->dead_clean_task != GNUNET_SCHEDULER_NO_TASK)
@@ -2665,17 +2829,16 @@ 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 */
-      n->th = NULL; /* If this happens because of a timeout, reset n-th so another message may be sent for this peer! */
 #if DEBUG_CORE
-      GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Timeout on notify connect!\n");
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Timeout on notify connect!\n");
 #endif
       return 0;
     }
-  n->th = NULL;
   if (buf == NULL)
     {
       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@@ -3049,10 +3212,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);
+
 
 
 /**
@@ -3085,7 +3253,7 @@ 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
        {
@@ -3110,15 +3278,58 @@ 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;
@@ -3160,6 +3371,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,
@@ -3206,14 +3418,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,
@@ -3266,7 +3485,7 @@ handle_pong (struct Neighbour *n,
                   "PONG", GNUNET_i2s (&t.target), 
                  (unsigned int) t.challenge);
 #endif
-      GNUNET_break_op (0);
+      GNUNET_break_op (n->ping_challenge != t.challenge);
       return;
     }
   switch (n->status)
@@ -3307,10 +3526,33 @@ handle_pong (struct Neighbour *n,
           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.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:
@@ -3336,9 +3578,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;
@@ -3443,6 +3690,7 @@ handle_set_key (struct Neighbour *n, const struct SetKeyMessage *m)
       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)
     {
@@ -3490,14 +3738,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);
     }
 }
@@ -3516,9 +3764,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",
@@ -3526,13 +3787,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->ats_count = htonl (0);
-  ntm->ats.type = htonl (0);
-  ntm->ats.value = htonl (0);
+  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);
 }
 
@@ -3621,10 +3888,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];
@@ -3776,6 +4050,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)], 
@@ -3791,16 +4066,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;
@@ -3836,7 +4110,9 @@ handle_transport_receive (void *cls,
           return;
         }
       GNUNET_STATISTICS_update (stats, gettext_noop ("# session keys received"), 1, GNUNET_NO);
-      handle_set_key (n, (const struct SetKeyMessage *) message);
+      handle_set_key (n,
+                     (const struct SetKeyMessage *) message,
+                     ats, ats_count);
       break;
     case GNUNET_MESSAGE_TYPE_CORE_ENCRYPTED_MESSAGE:
       if (size < sizeof (struct EncryptedMessage) +
@@ -3851,7 +4127,9 @@ handle_transport_receive (void *cls,
           GNUNET_break_op (0);
           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))
@@ -3873,7 +4151,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))
@@ -3895,7 +4174,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,
@@ -3938,6 +4218,8 @@ 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;
@@ -3965,16 +4247,33 @@ 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).rel_value > GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_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
@@ -3993,10 +4292,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,
@@ -4016,14 +4318,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;
 
@@ -4051,6 +4353,7 @@ handle_transport_notify_connect (void *cls,
                            1, 
                            GNUNET_NO);
   n->is_connected = GNUNET_YES;      
+  update_neighbour_performance (n, ats, ats_count);
   GNUNET_BANDWIDTH_tracker_init (&n->available_send_window,
                                 n->bw_out,
                                 MAX_WINDOW_TIME_S);
@@ -4105,9 +4408,18 @@ handle_transport_notify_disconnect (void *cls,
       cnm.header.size = htons (sizeof (struct DisconnectNotifyMessage));
       cnm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT);
       cnm.peer = *peer;
-      send_to_all_clients (&cnm.header, GNUNET_YES, GNUNET_CORE_OPTION_SEND_DISCONNECT);
+      send_to_all_clients (&cnm.header, GNUNET_NO, GNUNET_CORE_OPTION_SEND_DISCONNECT);
+    }
+
+  /* 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,
@@ -4149,9 +4461,6 @@ cleaning_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Core service shutting down.\n");
 #endif
-  GNUNET_assert (transport != NULL);
-  GNUNET_TRANSPORT_disconnect (transport);
-  transport = NULL;
   while (NULL != (n = neighbours))
     {
       neighbours = n->next;
@@ -4159,6 +4468,9 @@ cleaning_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
       neighbour_count--;
       free_neighbour (n);
     }
+  GNUNET_assert (transport != NULL);
+  GNUNET_TRANSPORT_disconnect (transport);
+  transport = NULL;
   GNUNET_STATISTICS_set (stats, gettext_noop ("# neighbour entries allocated"), neighbour_count, GNUNET_NO);
   GNUNET_SERVER_notification_context_destroy (notifier);
   notifier = NULL;
@@ -4190,6 +4502,9 @@ run (void *cls,
   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_request_info, NULL,
      GNUNET_MESSAGE_TYPE_CORE_REQUEST_INFO,
      sizeof (struct RequestInfoMessage)},