i am a dumb dummy
[oweals/gnunet.git] / src / core / gnunet-service-core.c
index 1a43b46c351c809cfbf39c0db7f3825d053f922c..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, 
@@ -1164,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
@@ -1226,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;
        }
@@ -1327,41 +1391,68 @@ 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 GNUNET_SERVER_Client *client,
+                            const struct GNUNET_MessageHeader *message)
 
 {
   struct Neighbour *n;
-  struct ConnectNotifyMessage cnm;
+  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 */
-  cnm.header.size = htons (sizeof (struct ConnectNotifyMessage));
-  cnm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT);
-  done_msg.size = htons (sizeof (struct GNUNET_MessageHeader));
-  done_msg.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT);
   tc = GNUNET_SERVER_transmit_context_create (client);
+  cnm = (struct ConnectNotifyMessage*) buf;
   n = neighbours;
-  cnm.ats_count = htonl (0);
-  cnm.ats.type = htonl (0);
-  cnm.ats.value = htonl (0);
   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");
+                      "Sending `%s' message to client.\n",
+                     "NOTIFY_CONNECT");
 #endif
-          cnm.peer = n->peer;
-          GNUNET_SERVER_transmit_context_append_message (tc, &cnm.header);
+          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);
@@ -1541,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);
@@ -1663,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 
@@ -1710,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)
@@ -2745,7 +2835,7 @@ notify_transport_connect_done (void *cls, size_t size, void *buf)
       /* 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");
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Timeout on notify connect!\n");
 #endif
       return 0;
     }
@@ -3122,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);
+
 
 
 /**
@@ -3158,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
        {
@@ -3183,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;
@@ -3233,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,
@@ -3279,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,
@@ -3380,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_NO, 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:
@@ -3409,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;
@@ -3516,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)
     {
@@ -3563,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);
     }
 }
@@ -3589,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",
@@ -3599,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);
 }
 
@@ -3694,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];
@@ -3849,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)], 
@@ -3864,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;
@@ -3909,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) +
@@ -3924,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))
@@ -3946,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))
@@ -3968,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,
@@ -4011,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;
@@ -4038,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
@@ -4066,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,
@@ -4089,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;
 
@@ -4124,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);
@@ -4180,6 +4410,14 @@ handle_transport_notify_disconnect (void *cls,
       cnm.peer = *peer;
       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))