-fix time assertion introduce in last patch
[oweals/gnunet.git] / src / transport / gnunet-service-transport_neighbours.c
index 7378e72e11b5487198581acf960165644a5fe2ff..6c51d20d838ac530f1bd82bbeb877f73780610b5 100644 (file)
@@ -46,7 +46,7 @@
  * Time we give plugin to transmit DISCONNECT message before the
  * neighbour entry self-destructs.
  */
-#define DISCONNECT_SENT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 100)
+#define DISCONNECT_SENT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 500)
 
 /**
  * How often must a peer violate bandwidth quotas before we start
@@ -342,6 +342,11 @@ struct NeighbourMapEntry
    */
   GNUNET_SCHEDULER_TaskIdentifier task;
 
+  /**
+   * Task to disconnect neighbour after we received a DISCONNECT message
+   */
+  GNUNET_SCHEDULER_TaskIdentifier delayed_disconnect_task;
+
   /**
    * At what time should we sent the next keep-alive message?
    */
@@ -400,10 +405,15 @@ struct NeighbourMapEntry
   int expect_latency_response;
 
   /**
+   * When a peer wants to connect we have to reply to the 1st CONNECT message
+   * with a CONNECT_ACK message. But sometime we cannot send this message
+   * immediately since we do not have an address and then we have to remember
+   * to send this message as soon as we have an address.
+   *
    * Flag to set if we still need to send a CONNECT_ACK message to the other peer
    * (once we have an address to use and the peer has been allowed by our
    * blacklist).  Initially set to #ACK_UNDEFINED. Set to #ACK_SEND_CONNECT_ACK
-   * if we need to send a CONNECT_ACK.  Set to #ACK_SEND_CONNECT_ACK if we did
+   * if we need to send a CONNECT_ACK.  Set to #ACK_SEND_SESSION_ACK if we did
    * send a CONNECT_ACK and should go to 'S_CONNECTED' upon receiving a
    * 'SESSION_ACK' (regardless of what our own state machine might say).
    */
@@ -437,7 +447,7 @@ struct NeighbourMapEntry
 
 
 /**
- * Context for blacklist checks and the #handle_test_blacklist_cont()
+ * Context for blacklist checks and the #try_connect_bl_check_cont()
  * function.  Stores information about ongoing blacklist checks.
  */
 struct BlackListCheckContext
@@ -687,7 +697,7 @@ set_timeout (struct NeighbourMapEntry *n,
     struct GNUNET_TIME_Absolute timeout)
 {
   n->timeout = timeout;
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Neighbour `%s' changed timeout %s\n",
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Neighbour `%s' changed timeout %s\n",
       GNUNET_i2s (&n->id),
       GNUNET_STRINGS_absolute_time_to_string (timeout));
   neighbour_change_cb (callback_cls,
@@ -941,6 +951,13 @@ free_neighbour (struct NeighbourMapEntry *n,
     n->suggest_handle = NULL;
   }
 
+  /* Cancel the disconnect task */
+  if (GNUNET_SCHEDULER_NO_TASK != n->delayed_disconnect_task)
+  {
+    GNUNET_SCHEDULER_cancel (n->delayed_disconnect_task);
+    n->delayed_disconnect_task = GNUNET_SCHEDULER_NO_TASK;
+  }
+
   /* Cancel the master task */
   if (GNUNET_SCHEDULER_NO_TASK != n->task)
   {
@@ -1049,7 +1066,7 @@ send_disconnect (struct NeighbourMapEntry *n)
 {
   struct SessionDisconnectMessage disconnect_msg;
 
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Sending DISCONNECT message to peer `%4s'\n",
               GNUNET_i2s (&n->id));
   disconnect_msg.header.size = htons (sizeof (struct SessionDisconnectMessage));
@@ -1070,10 +1087,9 @@ send_disconnect (struct NeighbourMapEntry *n)
                                          &disconnect_msg.purpose,
                                          &disconnect_msg.signature));
 
-  (void) send_with_session (n,
-                           (const char *) &disconnect_msg, sizeof (disconnect_msg),
-                           UINT32_MAX, GNUNET_TIME_UNIT_FOREVER_REL,
-                           GNUNET_NO, &send_disconnect_cont, NULL);
+  (void) send_with_session (n, (const char *) &disconnect_msg,
+      sizeof (disconnect_msg), UINT32_MAX, GNUNET_TIME_UNIT_FOREVER_REL,
+      GNUNET_NO, &send_disconnect_cont, NULL );
   GNUNET_STATISTICS_update (GST_stats,
                             gettext_noop
                             ("# DISCONNECT messages sent"), 1,
@@ -1108,7 +1124,6 @@ disconnect_neighbour (struct NeighbourMapEntry *n)
     break;
   case GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS:
     /* we never ACK'ed the other peer's request, no need to send DISCONNECT */
-    set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED);
     free_neighbour (n, GNUNET_NO);
     return;
   case GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK:
@@ -1116,6 +1131,7 @@ disconnect_neighbour (struct NeighbourMapEntry *n)
     send_disconnect (n);
     set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT);
     break;
+  case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
   case GNUNET_TRANSPORT_PS_CONNECTED:
   case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
     /* we are currently connected, need to send disconnect and do
@@ -1129,14 +1145,10 @@ disconnect_neighbour (struct NeighbourMapEntry *n)
     set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT);
     break;
   case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
-    /* ATS address request timeout, disconnect without sending disconnect message */
-    GNUNET_STATISTICS_set (GST_stats,
-                           gettext_noop ("# peers connected"),
-                           --neighbours_connected,
-                           GNUNET_NO);
-    disconnect_notify_cb (callback_cls, &n->id);
-    set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT);
-    break;
+    /* Disconnecting while waiting for an ATS address to reconnect,
+     * cannot send DISCONNECT */
+    free_neighbour (n, GNUNET_NO);
+    return;
   case GNUNET_TRANSPORT_PS_DISCONNECT:
     /* already disconnected, ignore */
     break;
@@ -1218,9 +1230,10 @@ transmit_send_continuation (void *cls,
                              ("# transmission failures for messages to other peers"),
                              1, GNUNET_NO);
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Sending message to `%s' of type %u was a %s\n",
+             "Sending message to `%s' of type %u with %u bytes was a %s\n",
              GNUNET_i2s (receiver),
               ntohs (((struct GNUNET_MessageHeader *) mq->message_buf)->type),
+              mq->message_buf_size,
               (success == GNUNET_OK) ? "success" : "FAILURE");
   if (NULL != mq->cont)
     mq->cont (mq->cont_cls, success, size_payload, physical);
@@ -1284,6 +1297,11 @@ try_transmission_to_peer (struct NeighbourMapEntry *n)
     return;                     /* no more messages */
   GNUNET_CONTAINER_DLL_remove (n->messages_head, n->messages_tail, mq);
   n->is_active = mq;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+      "Giving message with %u bytes to plugin session %p\n",
+      mq->message_buf_size, n->primary_address.session);
+
   (void) send_with_session (n,
                            mq->message_buf, mq->message_buf_size,
                            0 /* priority */, timeout, GNUNET_NO,
@@ -1623,10 +1641,11 @@ GST_neighbours_send (const struct GNUNET_PeerIdentity *target, const void *msg,
   mq->message_buf = (const char *) &mq[1];
   mq->message_buf_size = msg_size;
   mq->timeout = GNUNET_TIME_relative_to_absolute (timeout);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Enqueueing %u bytes to send to peer %s\n",
+      msg_size, GNUNET_i2s (target));
+
   GNUNET_CONTAINER_DLL_insert_tail (n->messages_head, n->messages_tail, mq);
-  if ( (NULL != n->is_active) ||
-       ( (NULL == n->primary_address.session) && (NULL == n->primary_address.address)) )
-    return;
   if (GNUNET_SCHEDULER_NO_TASK != n->task)
     GNUNET_SCHEDULER_cancel (n->task);
   n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
@@ -1675,7 +1694,7 @@ send_session_connect_cont (void *cls,
         n->primary_address.session);
     GNUNET_ATS_address_destroyed (GST_ats, n->primary_address.address, NULL );
     unset_primary_address (n);
-    set_state_and_timeout (n, GNUNET_TRANSPORT_PS_RECONNECT_ATS,
+    set_state_and_timeout (n, GNUNET_TRANSPORT_PS_INIT_ATS,
         GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT));
     break;
   case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
@@ -1684,11 +1703,13 @@ send_session_connect_cont (void *cls,
         n->primary_address.session);
     GNUNET_ATS_address_destroyed (GST_ats, n->primary_address.address, NULL );
     unset_primary_address (n);
-    set_state_and_timeout (n, GNUNET_TRANSPORT_PS_INIT_ATS,
+    set_state_and_timeout (n, GNUNET_TRANSPORT_PS_RECONNECT_ATS,
         GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
     break;
   case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
     /* Remove address and request and go back to primary address */
+    GNUNET_STATISTICS_update (GST_stats, gettext_noop
+        ("# Failed attempts to switch addresses (failed to send CONNECT CONT)"), 1, GNUNET_NO);
     GNUNET_ATS_address_destroyed (GST_ats, n->alternative_address.address,
         n->alternative_address.session);
     GNUNET_ATS_address_destroyed (GST_ats, n->alternative_address.address,
@@ -1698,7 +1719,6 @@ send_session_connect_cont (void *cls,
         GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
     break;
   default:
-    GNUNET_break(0);
     disconnect_neighbour (n);
     break;
   }
@@ -1716,9 +1736,10 @@ send_session_connect (struct NeighbourAddress *na)
   struct SessionConnectMessage connect_msg;
   struct NeighbourMapEntry *n;
 
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Sending SESSION_CONNECT message to peer %s\n",
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Sending SESSION_CONNECT message to peer `%s'\n",
               GNUNET_i2s (&na->address->peer));
+
   if (NULL == (papi = GST_plugins_find (na->address->transport_name)))
   {
     GNUNET_break (0);
@@ -1763,7 +1784,7 @@ send_session_connect (struct NeighbourAddress *na)
       case GNUNET_TRANSPORT_PS_CONNECT_SENT:
         /* Remove address and request and additional one */
         unset_primary_address (n);
-        set_state_and_timeout (n, GNUNET_TRANSPORT_PS_RECONNECT_ATS,
+        set_state_and_timeout (n, GNUNET_TRANSPORT_PS_INIT_ATS,
           GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT));
         /* Hard failure to send the CONNECT message with this address:
            Destroy address and session */
@@ -1771,17 +1792,18 @@ send_session_connect (struct NeighbourAddress *na)
       case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
         /* Remove address and request and additional one */
         unset_primary_address (n);
-        set_state_and_timeout (n, GNUNET_TRANSPORT_PS_INIT_ATS,
+        set_state_and_timeout (n, GNUNET_TRANSPORT_PS_RECONNECT_ATS,
           GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
         break;
       case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
+        GNUNET_STATISTICS_update (GST_stats, gettext_noop
+            ("# Failed attempts to switch addresses (failed to send CONNECT)"), 1, GNUNET_NO);
         /* Remove address and request and additional one */
         unset_alternative_address (n);
         set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECTED,
           GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
         break;
       default:
-        GNUNET_break (0);
         disconnect_neighbour (n);
         break;
     }
@@ -1850,6 +1872,7 @@ send_session_connect_ack_cont (void *cls,
  * @param address address to use
  * @param session session to use
  * @param timestamp timestamp to use for the ACK message
+ * @return GNUNET_SYSERR if sending immediately failed, GNUNET_OK otherwise
  */
 static void
 send_connect_ack_message (const struct GNUNET_HELLO_Address *address,
@@ -1860,9 +1883,10 @@ send_connect_ack_message (const struct GNUNET_HELLO_Address *address,
   struct SessionConnectMessage connect_msg;
   struct NeighbourMapEntry *n;
 
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Sending CONNECT_ACK to peer `%s'\n",
               GNUNET_i2s (&address->peer));
+
   if (NULL == (papi = GST_plugins_find (address->transport_name)))
   {
     GNUNET_break (0);
@@ -2054,16 +2078,15 @@ notification_cb(void *cls, const struct GNUNET_PeerIdentity *key, void *value)
   return GNUNET_OK;
 }
 
-static
-int
+static int
 free_notification_cb(void *cls, const struct GNUNET_PeerIdentity *key,
     void *value)
 {
   /* struct NeighbourMapEntry *n = cls; */
   struct QuotaNotificationRequest *qnr = value;
 
-  GNUNET_CONTAINER_multipeermap_remove (registered_quota_notifications, key,
-      qnr);
+  GNUNET_break (GNUNET_OK == GNUNET_CONTAINER_multipeermap_remove (registered_quota_notifications, key,
+      qnr));
   GNUNET_free(qnr->plugin);
   GNUNET_free(qnr);
 
@@ -2267,198 +2290,22 @@ GST_neighbours_try_connect (const struct GNUNET_PeerIdentity *target)
 }
 
 
-/**
- * Function called with the result of a blacklist check.
- *
- * @param cls closure with the `struct BlackListCheckContext`
- * @param peer peer this check affects
- * @param result #GNUNET_OK if the address is allowed
- */
-static void
-handle_connect_blacklist_check_cont (void *cls,
-                           const struct GNUNET_PeerIdentity *peer,
-                           int result)
-{
-  struct BlackListCheckContext *bcc = cls;
-  struct NeighbourMapEntry *n;
-
-  bcc->bc = NULL;
-  GNUNET_CONTAINER_DLL_remove (bc_head,
-                              bc_tail,
-                              bcc);
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Connection to new address of peer `%s' based on blacklist is `%s'\n",
-              GNUNET_i2s (peer),
-              (GNUNET_OK == result) ? "allowed" : "FORBIDDEN");
-
-  if (NULL == (n = lookup_neighbour (peer)))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "No neighbor entry for peer `%s', ignoring blacklist result\n",
-                GNUNET_i2s (peer));
-    goto cleanup; /* nobody left to care about new address */
-  }
-
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Blacklist check after CONNECT for peer `%s' in state %s/%s: %s\n",
-              GNUNET_i2s (peer),
-              GNUNET_TRANSPORT_ps2s (n->state),
-              print_ack_state (n->ack_state),
-              (GNUNET_OK == result) ? "OK" : "FAIL");
-
-  switch (n->state)
-  {
-  case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
-    /* This should not be possible */
-    GNUNET_break (0);
-    free_neighbour (n, GNUNET_NO);
-    break;
-  case GNUNET_TRANSPORT_PS_INIT_ATS:
-    /* Waiting on ATS suggestion */
-    break;
-  case GNUNET_TRANSPORT_PS_CONNECT_SENT:
-#if 0
-    /* TODO Why should I send an connect ACK message */
-    /* waiting on CONNECT_ACK, send ACK if one is pending */
-
-    if ( (GNUNET_OK == result) &&
-        (ACK_SEND_CONNECT_ACK == n->ack_state) )
-    {
-      n->ack_state = ACK_SEND_SESSION_ACK;
-      send_connect_ack_message (n->primary_address.address,
-                                       n->primary_address.session,
-                                       n->connect_ack_timestamp);
-    }
-#endif
-    break;
-  case GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS:
-    /* waiting on ATS suggestion, don't care about blacklist */
-    break;
-  case GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK:
-    /* waiting on SESSION_ACK, send ACK if one is pending */
-    if ( (GNUNET_OK == result) &&
-        (ACK_SEND_CONNECT_ACK == n->ack_state) )
-    {
-      /* TODO: Why should this happen? */
-      /* *Debug message: */ GNUNET_break (0);
-
-      n->ack_state = ACK_SEND_SESSION_ACK;
-      send_connect_ack_message (n->primary_address.address,
-                                       n->primary_address.session,
-                                       n->connect_ack_timestamp);
-    }
-    break;
-  case GNUNET_TRANSPORT_PS_CONNECTED:
-    /* already connected, don't care about blacklist */
-    break;
-  case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
-    /* still waiting on ATS suggestion, don't care about blacklist */
-    break;
-  case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
-    /* waiting on CONNECT_ACK, don't care about blacklist */
-    if ( (GNUNET_OK == result) &&
-        (ACK_SEND_CONNECT_ACK == n->ack_state) )
-    {
-      n->ack_state = ACK_SEND_SESSION_ACK;
-      send_connect_ack_message (n->primary_address.address,
-                                       n->primary_address.session,
-                                       n->connect_ack_timestamp);
-    }
-    break;
-  case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
-    /* waiting on CONNECT_ACK, don't care about blacklist */
-    if ( (GNUNET_OK == result) &&
-        (ACK_SEND_CONNECT_ACK == n->ack_state) )
-    {
-      n->ack_state = ACK_SEND_SESSION_ACK;
-      send_connect_ack_message (n->primary_address.address,
-                                       n->primary_address.session,
-                                       n->connect_ack_timestamp);
-    }
-    break;
-  case GNUNET_TRANSPORT_PS_DISCONNECT:
-    /* Nothing to do here, ATS will already do what can be done */
-    break;
-  case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
-    /* should not be possible */
-    GNUNET_assert (0);
-    break;
-  default:
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unhandled state `%s'\n",
-                GNUNET_TRANSPORT_ps2s (n->state));
-    GNUNET_break (0);
-    free_neighbour (n, GNUNET_NO);
-    break;
-  }
- cleanup:
-  GNUNET_HELLO_address_free (bcc->na.address);
-  GNUNET_free (bcc);
-}
-
-
-/**
- * We received a CONNECT message and want to know if connecting to a particular
- * peer via a particular address is allowed.  Check it!
- *
- * @param peer identity of the peer to switch the address for
- * @param ts time at which the check was initiated
- * @param address address of the other peer, NULL if other peer
- *                       connected to us
- * @param session session to use (or NULL)
- */
-static void
-connect_check_blacklist (const struct GNUNET_PeerIdentity *peer,
-                struct GNUNET_TIME_Absolute ts,
-                const struct GNUNET_HELLO_Address *address,
-                struct Session *session)
-{
-  struct BlackListCheckContext *bcc;
-  struct GST_BlacklistCheck *bc;
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Checking peer `%s' against blacklist\n",
-              GNUNET_i2s (peer));
-  bcc = GNUNET_new (struct BlackListCheckContext);
-  bcc->na.address = GNUNET_HELLO_address_copy (address);
-  bcc->na.session = session;
-  bcc->na.connect_timestamp = ts;
-  GNUNET_CONTAINER_DLL_insert (bc_head,
-                              bc_tail,
-                              bcc);
-  if (NULL != (bc = GST_blacklist_test_allowed (peer,
-        (NULL != address) ? address->transport_name : NULL,
-        &handle_connect_blacklist_check_cont, bcc)))
-    bcc->bc = bc;
-  /* if NULL == bc, 'cont' was already called and 'bcc' already free'd, so
-     we must only store 'bc' if 'bc' is non-NULL... */
-}
-
-
 /**
  * We received a 'SESSION_CONNECT' message from the other peer.
  * Consider switching to it.
  *
  * @param message possibly a 'struct SessionConnectMessage' (check format)
  * @param peer identity of the peer to switch the address for
- * @param address address of the other peer, NULL if other peer
- *                       connected to us
- * @param session session to use (or NULL)
  * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
  */
 int
 GST_neighbours_handle_connect (const struct GNUNET_MessageHeader *message,
-                               const struct GNUNET_PeerIdentity *peer,
-                               const struct GNUNET_HELLO_Address *address,
-                               struct Session *session)
+                               const struct GNUNET_PeerIdentity *peer)
 {
   const struct SessionConnectMessage *scm;
   struct NeighbourMapEntry *n;
   struct GNUNET_TIME_Absolute ts;
 
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Received CONNECT message from peer `%s' with `%s' %p\n",
-             GNUNET_i2s (peer), GST_plugins_a2s (address), session);
   if (ntohs (message->size) != sizeof (struct SessionConnectMessage))
   {
     GNUNET_break_op (0);
@@ -2489,7 +2336,7 @@ GST_neighbours_handle_connect (const struct GNUNET_MessageHeader *message,
   n->ack_state = ACK_SEND_CONNECT_ACK;
   n->connect_ack_timestamp = ts;
 
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Received CONNECT for peer `%s' in state %s/%s\n",
               GNUNET_i2s (peer),
               GNUNET_TRANSPORT_ps2s (n->state),
@@ -2510,44 +2357,49 @@ GST_neighbours_handle_connect (const struct GNUNET_MessageHeader *message,
     set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS,
         GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
     break;
-  case GNUNET_TRANSPORT_PS_CONNECT_SENT:
   case GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS:
+    /* We already wait for an address to send an CONNECT_ACK */
+    break;
+  case GNUNET_TRANSPORT_PS_CONNECT_SENT:
   case GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK:
-    /* It can never hurt to have an alternative address in the above cases,
-       see if it is allowed */
-    connect_check_blacklist (peer, ts, address, session);
+    /* Send ACK immediately */
+    n->ack_state = ACK_SEND_SESSION_ACK;
+    send_connect_ack_message (n->primary_address.address,
+                              n->primary_address.session, ts);
     break;
   case GNUNET_TRANSPORT_PS_CONNECTED:
     /* we are already connected and can thus send the ACK immediately */
     GNUNET_assert (NULL != n->primary_address.address);
     GNUNET_assert (NULL != n->primary_address.session);
-    n->ack_state = ACK_UNDEFINED;
+    n->ack_state = ACK_SEND_SESSION_ACK;
     send_connect_ack_message (n->primary_address.address,
                               n->primary_address.session, ts);
-    connect_check_blacklist (peer, ts, address, session);
     break;
   case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
+    /* We wait for ATS address suggestion */
+    break;
   case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
-    /* It can never hurt to have an alternative address in the above cases,
-       see if it is allowed */
-    connect_check_blacklist (peer, ts, address, session);
+    /* We received a CONNECT message while waiting for a CONNECT_ACK in fast
+     * reconnect. Send CONNECT_ACK immediately */
+    n->ack_state = ACK_SEND_SESSION_ACK;
+    send_connect_ack_message (n->primary_address.address,
+        n->primary_address.session, n->connect_ack_timestamp);
     break;
   case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
-    /* we are already connected and can thus send the ACK immediately;
+    /* We are already connected and can thus send the ACK immediately;
        still, it can never hurt to have an alternative address, so also
        tell ATS  about it */
     GNUNET_assert (NULL != n->primary_address.address);
     GNUNET_assert (NULL != n->primary_address.session);
-    n->ack_state = ACK_UNDEFINED;
+    n->ack_state = ACK_SEND_SESSION_ACK;
     send_connect_ack_message (n->primary_address.address,
-                                     n->primary_address.session, ts);
-    connect_check_blacklist (peer, ts, address, session);
+        n->primary_address.session, ts);
     break;
   case GNUNET_TRANSPORT_PS_DISCONNECT:
-    /* get rid of remains without terminating sessions, ready to re-try */
+    /* Get rid of remains without terminating sessions, ready to re-try */
     free_neighbour (n, GNUNET_YES);
     n = setup_neighbour (peer);
-    /* Remember the CONNECT timestamp for ACK message */
+    /* Remember the CONNECT time stamp for ACK message */
     n->ack_state = ACK_SEND_CONNECT_ACK;
     n->connect_ack_timestamp = ts;
     /* Request an address for the peer */
@@ -2577,8 +2429,10 @@ switch_address_bl_check_cont (void *cls,
   struct GNUNET_TRANSPORT_PluginFunctions *papi;
   struct NeighbourMapEntry *n;
 
+  papi = GST_plugins_find (blc_ctx->address->transport_name);
+
   if ( (NULL == (n = lookup_neighbour (peer))) || (result == GNUNET_NO) ||
-       (NULL == (papi = GST_plugins_find (blc_ctx->address->transport_name))) )
+       (NULL == (papi)) )
   {
     if (NULL == n)
     {
@@ -2594,7 +2448,7 @@ switch_address_bl_check_cont (void *cls,
           blc_ctx->session,
           GNUNET_i2s (&blc_ctx->address->peer));
     }
-    if (NULL == (papi = GST_plugins_find (blc_ctx->address->transport_name)))
+    if (NULL == papi)
     {
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
           "Plugin `%s' for suggested address `%s' session %p for peer `%s' is not available\n",
@@ -2623,7 +2477,7 @@ switch_address_bl_check_cont (void *cls,
   }
 
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-      "Blacklist accepted to switch to suggested address `%s' session %p for peer `%s'\n",
+      "Blacklist accepted address `%s' session %p for peer `%s'\n",
       GST_plugins_a2s (blc_ctx->address),
       blc_ctx->session,
       GNUNET_i2s (&blc_ctx->address->peer));
@@ -2663,21 +2517,30 @@ switch_address_bl_check_cont (void *cls,
      * set primary address and send CONNECT message*/
     set_primary_address (n, blc_ctx->address, blc_ctx->session,
         blc_ctx->bandwidth_in, blc_ctx->bandwidth_out, GNUNET_NO);
+    if ( (ACK_SEND_CONNECT_ACK == n->ack_state) )
+    {
+      /* Send pending CONNECT_ACK message */
+      n->ack_state = ACK_SEND_SESSION_ACK;
+      send_connect_ack_message (n->primary_address.address,
+          n->primary_address.session, n->connect_ack_timestamp);
+    }
     set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECT_SENT,
         GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT));
     send_session_connect (&n->primary_address);
     break;
   case GNUNET_TRANSPORT_PS_CONNECT_SENT:
-    /* waiting on CONNECT_ACK, send ACK if one is pending */
+    /* ATS suggested a new address while waiting for an CONNECT_ACK:
+     * Switch and send new CONNECT */
+    /* ATS suggests a different address, switch again */
+    set_primary_address (n, blc_ctx->address, blc_ctx->session,
+        blc_ctx->bandwidth_in, blc_ctx->bandwidth_out, GNUNET_NO);
     if (ACK_SEND_CONNECT_ACK == n->ack_state)
     {
+      /* Send pending CONNECT_ACK message */
       n->ack_state = ACK_SEND_SESSION_ACK;
       send_connect_ack_message (n->primary_address.address,
           n->primary_address.session, n->connect_ack_timestamp);
     }
-    /* ATS suggests a different address, switch again */
-    set_primary_address (n, blc_ctx->address, blc_ctx->session,
-        blc_ctx->bandwidth_in, blc_ctx->bandwidth_out, GNUNET_NO);
     set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECT_SENT,
         GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT));
     send_session_connect (&n->primary_address);
@@ -2693,22 +2556,23 @@ switch_address_bl_check_cont (void *cls,
     send_connect_ack_message (n->primary_address.address,
                               n->primary_address.session,
                               n->connect_ack_timestamp);
-    if (ACK_SEND_CONNECT_ACK == n->ack_state)
+    if ( (ACK_SEND_CONNECT_ACK == n->ack_state) ||
+         (ACK_UNDEFINED == n->ack_state) )
       n->ack_state = ACK_SEND_SESSION_ACK;
     break;
   case GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK:
     /* ATS asks us to switch while we were trying to connect; switch to new
        address and check blacklist again */
-    set_primary_address (n, blc_ctx->address, blc_ctx->session,
-        blc_ctx->bandwidth_in, blc_ctx->bandwidth_out, GNUNET_NO);
-    set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK,
-        GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT));
     if ( (ACK_SEND_CONNECT_ACK == n->ack_state) )
     {
       n->ack_state = ACK_SEND_SESSION_ACK;
       send_connect_ack_message (n->primary_address.address,
           n->primary_address.session, n->connect_ack_timestamp);
     }
+    set_primary_address (n, blc_ctx->address, blc_ctx->session,
+        blc_ctx->bandwidth_in, blc_ctx->bandwidth_out, GNUNET_NO);
+    set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK,
+        GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT));
     break;
   case GNUNET_TRANSPORT_PS_CONNECTED:
     GNUNET_assert (NULL != n->primary_address.address);
@@ -2726,11 +2590,20 @@ switch_address_bl_check_cont (void *cls,
         blc_ctx->bandwidth_in, blc_ctx->bandwidth_out);
     set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT,
         GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT));
+    GNUNET_STATISTICS_update (GST_stats, gettext_noop
+        ("# Attempts to switch addresses"), 1, GNUNET_NO);
     send_session_connect (&n->alternative_address);
     break;
   case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
     set_primary_address (n, blc_ctx->address, blc_ctx->session,
         blc_ctx->bandwidth_in, blc_ctx->bandwidth_out, GNUNET_NO);
+    if ( (ACK_SEND_CONNECT_ACK == n->ack_state) )
+    {
+      /* Send pending CONNECT_ACK message */
+      n->ack_state = ACK_SEND_SESSION_ACK;
+      send_connect_ack_message (n->primary_address.address,
+          n->primary_address.session, n->connect_ack_timestamp);
+    }
     set_state_and_timeout (n, GNUNET_TRANSPORT_PS_RECONNECT_SENT,
         GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT));
     send_session_connect (&n->primary_address);
@@ -2745,7 +2618,8 @@ switch_address_bl_check_cont (void *cls,
     send_session_connect (&n->primary_address);
     break;
   case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
-    if (n->primary_address.session == blc_ctx->session)
+    if ( (0 == GNUNET_HELLO_address_cmp(n->primary_address.address,
+        blc_ctx->address) && n->primary_address.session == blc_ctx->session) )
     {
       /* ATS switches back to still-active session */
       free_address (&n->alternative_address);
@@ -2808,7 +2682,6 @@ GST_neighbours_switch_to_address (const struct GNUNET_PeerIdentity *peer,
 {
   struct NeighbourMapEntry *n;
   struct GST_BlacklistCheck *blc;
-  struct GNUNET_TRANSPORT_PluginFunctions *papi;
   struct BlacklistCheckSwitchContext *blc_ctx;
   int c;
 
@@ -2825,7 +2698,7 @@ GST_neighbours_switch_to_address (const struct GNUNET_PeerIdentity *peer,
   }
 
   /* Check if plugin is available */
-  if (NULL == (papi = GST_plugins_find (address->transport_name)))
+  if (NULL == (GST_plugins_find (address->transport_name)))
   {
     /* we don't have the plugin for this address */
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -2847,7 +2720,7 @@ GST_neighbours_switch_to_address (const struct GNUNET_PeerIdentity *peer,
   }
 
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-    "ATS tells us to switch to %s address '%s' session %p for "
+    "ATS suggests %s address '%s' session %p for "
     "peer `%s' in state %s/%s (quota in/out %u %u )\n",
     GNUNET_HELLO_address_check_option (address,
         GNUNET_HELLO_ADDRESS_INFO_INBOUND) ? "inbound" : "outbound",
@@ -3039,7 +2912,7 @@ master_task (void *cls,
 
   n->task = GNUNET_SCHEDULER_NO_TASK;
   delay = GNUNET_TIME_absolute_get_remaining (n->timeout);
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Master task runs for neighbour `%s' in state %s with timeout in %s\n",
              GNUNET_i2s (&n->id),
              GNUNET_TRANSPORT_ps2s(n->state),
@@ -3139,6 +3012,8 @@ master_task (void *cls,
       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                  "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs (after trying to CONNECT on alternative address)\n",
                  GNUNET_i2s (&n->id));
+      GNUNET_STATISTICS_update (GST_stats, gettext_noop
+          ("# Failed attempts to switch addresses (no response)"), 1, GNUNET_NO);
       disconnect_neighbour (n);
       return;
     }
@@ -3189,6 +3064,9 @@ send_session_ack_message (struct NeighbourMapEntry *n)
 {
   struct GNUNET_MessageHeader msg;
 
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Sending SESSION_ACK message to peer `%s'\n",
+              GNUNET_i2s (&n->id));
+
   msg.size = htons (sizeof (struct GNUNET_MessageHeader));
   msg.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK);
   (void) send_with_session(n,
@@ -3291,7 +3169,7 @@ GST_neighbours_handle_connect_ack (const struct GNUNET_MessageHeader *message,
                               1, GNUNET_NO);
     break;
   case GNUNET_TRANSPORT_PS_CONNECTED:
-    /* duplicate CONNECT_ACK, let's answer by duplciate SESSION_ACK just in case */
+    /* duplicate CONNECT_ACK, let's answer by duplicate SESSION_ACK just in case */
     send_session_ack_message (n);
     break;
   case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
@@ -3303,7 +3181,7 @@ GST_neighbours_handle_connect_ack (const struct GNUNET_MessageHeader *message,
                               1, GNUNET_NO);
     break;
   case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
-    /* new address worked; go back to connected! */
+    /* Reconnecting with new address address worked; go back to connected! */
     set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECTED,
         GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
     send_session_ack_message (n);
@@ -3314,12 +3192,15 @@ GST_neighbours_handle_connect_ack (const struct GNUNET_MessageHeader *message,
         GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
     GNUNET_break (GNUNET_NO == n->alternative_address.ats_active);
 
+    /* Notify about session... perhaps we obtained it */
     GST_ats_add_address (n->alternative_address.address,
-                         n->alternative_address.session,
-                         NULL, 0);
+        n->alternative_address.session, NULL, 0);
+    /* Set primary addresses */
     set_primary_address (n, n->alternative_address.address,
         n->alternative_address.session, n->alternative_address.bandwidth_in,
         n->alternative_address.bandwidth_out, GNUNET_YES);
+    GNUNET_STATISTICS_update (GST_stats, gettext_noop
+        ("# Successful attempts to switch addresses"), 1, GNUNET_NO);
 
     free_address (&n->alternative_address);
     send_session_ack_message (n);
@@ -3386,8 +3267,7 @@ GST_neighbours_session_terminated (const struct GNUNET_PeerIdentity *peer,
       if ( (GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT == n->state) )
         set_state (n, GNUNET_TRANSPORT_PS_CONNECTED);
       else
-       GNUNET_break (0);
-      free_address (&n->alternative_address);
+        free_address (&n->alternative_address);
     }
     return GNUNET_NO; /* doesn't affect us further */
   }
@@ -3408,9 +3288,13 @@ GST_neighbours_session_terminated (const struct GNUNET_PeerIdentity *peer,
     /* The session used to send the CONNECT terminated:
      * this implies a connect error*/
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Could not send CONNECT message with address `%s' session %p: session terminated, requesting new address\n",
-                GST_plugins_a2s (n->primary_address.address), n->primary_address.session,
+                "Failed to send CONNECT in %s with `%s' %p: session terminated\n",
+                "CONNECT_SENT",
+                GST_plugins_a2s (n->primary_address.address),
+                n->primary_address.session,
                 GNUNET_i2s (peer));
+
+    /* Destroy the address since it cannot be used */
     GNUNET_ATS_address_destroyed (GST_ats, n->primary_address.address, NULL);
     unset_primary_address (n);
     set_state_and_timeout (n, GNUNET_TRANSPORT_PS_INIT_ATS,
@@ -3423,6 +3307,7 @@ GST_neighbours_session_terminated (const struct GNUNET_PeerIdentity *peer,
     free_neighbour (n, GNUNET_NO);
     return GNUNET_YES;
   case GNUNET_TRANSPORT_PS_CONNECTED:
+    /* Our primary connection died, try a fast reconnect */
     unset_primary_address (n);
     set_state_and_timeout (n, GNUNET_TRANSPORT_PS_RECONNECT_ATS,
         GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
@@ -3432,16 +3317,33 @@ GST_neighbours_session_terminated (const struct GNUNET_PeerIdentity *peer,
     GNUNET_break (0);
     break;
   case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
-    set_state_and_timeout (n, GNUNET_TRANSPORT_PS_RECONNECT_ATS, GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Failed to send CONNECT in %s with `%s' %p: session terminated\n",
+                "RECONNECT_SENT",
+                GST_plugins_a2s (n->primary_address.address),
+                n->primary_address.session,
+                GNUNET_i2s (peer));
+
+    /* Destroy the address since it cannot be used */
+    GNUNET_ATS_address_destroyed (GST_ats, n->primary_address.address, NULL);
+    unset_primary_address (n);
+    set_state_and_timeout (n, GNUNET_TRANSPORT_PS_RECONNECT_ATS,
+        GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
     break;
   case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
     /* primary went down while we were waiting for CONNECT_ACK on secondary;
        secondary as primary */
-    GNUNET_ATS_address_destroyed (GST_ats, n->primary_address.address, NULL);
+
+    /* Destroy the inbound address since it cannot be used */
+    if (GNUNET_YES
+        == GNUNET_HELLO_address_check_option (n->primary_address.address,
+            GNUNET_HELLO_ADDRESS_INFO_INBOUND))
+      GNUNET_ATS_address_destroyed (GST_ats, n->primary_address.address, NULL);
     free_address (&n->primary_address);
     n->primary_address = n->alternative_address;
     memset (&n->alternative_address, 0, sizeof (struct NeighbourAddress));
-    set_state_and_timeout (n, GNUNET_TRANSPORT_PS_RECONNECT_ATS, GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT));
+    set_state_and_timeout (n, GNUNET_TRANSPORT_PS_RECONNECT_ATS,
+        GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT));
     break;
   case GNUNET_TRANSPORT_PS_DISCONNECT:
     free_address (&n->primary_address);
@@ -3484,7 +3386,7 @@ GST_neighbours_handle_session_ack (const struct GNUNET_MessageHeader *message,
 {
   struct NeighbourMapEntry *n;
 
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Received SESSION_ACK message from peer `%s'\n",
               GNUNET_i2s (peer));
   if (ntohs (message->size) != sizeof (struct GNUNET_MessageHeader))
@@ -3501,39 +3403,64 @@ GST_neighbours_handle_session_ack (const struct GNUNET_MessageHeader *message,
     GNUNET_break_op (0);
     return GNUNET_SYSERR;
   }
-  /* check if we are in a plausible state for having sent
-     a CONNECT_ACK.  If not, return, otherwise break */
 
-  /* TODO I have no idea we we should state GNUNET_TRANSPORT_PS_CONNECT_SENT
-   * Perhaps SWITCHING? Have to check */
-  if ( /*(*/ (GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK != n->state) /* &&
-        (GNUNET_TRANSPORT_PS_CONNECT_SENT != n->state) ) ||
-       (ACK_SEND_SESSION_ACK != n->ack_state) */)
-  {
-    if (GNUNET_TRANSPORT_PS_CONNECT_SENT != n->state)
-      GNUNET_break (0); /* TESTING */
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Received %s for peer `%s' in state %s/%s\n",
+              "SESSION_ACK",
+              GNUNET_i2s (peer),
+              GNUNET_TRANSPORT_ps2s (n->state),
+              print_ack_state (n->ack_state));
 
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Received SESSION_ACK message from peer `%s' in state %s/%s\n",
+  /* Check if we are in a plausible state for having sent
+     a CONNECT_ACK.  If not, return, otherwise break.
+
+     The remote peers sends a SESSION_ACK as a response for a CONNECT_ACK
+     message.
+
+     We expect a SESSION_ACK:
+     - If a remote peer has sent a CONNECT, we responded with a CONNECT_ACK and
+     now wait for the ACK to finally be connected
+     - If we sent a CONNECT_ACK to this peer before */
+
+  if (   (GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK != n->state) &&
+         (ACK_SEND_SESSION_ACK != n->ack_state))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Received unexpected SESSION_ACK message from peer `%s' in state %s/%s\n",
                 GNUNET_i2s (peer),
                 GNUNET_TRANSPORT_ps2s (n->state),
                 print_ack_state (n->ack_state));
+
     GNUNET_STATISTICS_update (GST_stats,
                               gettext_noop ("# unexpected SESSION_ACK messages"), 1,
                               GNUNET_NO);
     return GNUNET_OK;
   }
-  /* We are connected */
-  set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECTED, GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
-  GNUNET_STATISTICS_set (GST_stats,
-                        gettext_noop ("# peers connected"),
-                        ++neighbours_connected,
-                        GNUNET_NO);
 
-  /* Notify about connection */
-  connect_notify_cb (callback_cls, &n->id,
+  /* We are connected */
+  if (GNUNET_NO == GST_neighbours_test_connected(&n->id))
+  {
+    /* Notify about connection */
+    connect_notify_cb (callback_cls, &n->id,
                      n->primary_address.bandwidth_in,
-                     n->primary_address.bandwidth_out);
+                     n->primary_address.bandwidth_out);\
+
+     GNUNET_STATISTICS_set (GST_stats,
+                            gettext_noop ("# peers connected"),
+                            ++neighbours_connected,
+                            GNUNET_NO);
+  }
+
+  if (GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT == n->state)
+  {
+    /* We tried to switch addresses while being connect. We explicitly wait
+     * for a CONNECT_ACK before going to GNUNET_TRANSPORT_PS_CONNECTED,
+     * so we do not want to set the address as in use! */
+    return GNUNET_OK;
+  }
+
+  set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECTED,
+    GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
 
   /* Add session to ATS since no session was given (NULL) and we may have
    * obtained a new session */
@@ -3599,6 +3526,18 @@ GST_neighbours_set_incoming_quota (const struct GNUNET_PeerIdentity *neighbour,
   disconnect_neighbour (n);
 }
 
+void delayed_disconnect (void *cls,
+    const struct GNUNET_SCHEDULER_TaskContext* tc)
+{
+  struct NeighbourMapEntry *n = cls;
+
+  n->delayed_disconnect_task = GNUNET_SCHEDULER_NO_TASK;
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Disconnecting by request from peer %s\n",
+              GNUNET_i2s (&n->id));
+  free_neighbour (n, GNUNET_NO);
+}
+
 
 /**
  * We received a disconnect message from the given peer,
@@ -3619,10 +3558,10 @@ GST_neighbours_handle_disconnect_message (const struct GNUNET_PeerIdentity *peer
               GNUNET_i2s (peer));
   if (ntohs (msg->size) != sizeof (struct SessionDisconnectMessage))
   {
-    // GNUNET_break_op (0);
+    GNUNET_break_op (0);
     GNUNET_STATISTICS_update (GST_stats,
                               gettext_noop
-                              ("# disconnect messages ignored (old format)"), 1,
+                              ("# disconnect messages ignored (malformed)"), 1,
                               GNUNET_NO);
     return;
   }
@@ -3670,15 +3609,7 @@ GST_neighbours_handle_disconnect_message (const struct GNUNET_PeerIdentity *peer
     GNUNET_break_op (0);
     return;
   }
-  if (GNUNET_YES == test_connected (n))
-    GNUNET_STATISTICS_update (GST_stats,
-                             gettext_noop
-                             ("# other peer asked to disconnect from us"), 1,
-                             GNUNET_NO);
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Disconnecting by request from peer %s\n",
-              GNUNET_i2s (peer));
-  disconnect_neighbour (n);
+  n->delayed_disconnect_task = GNUNET_SCHEDULER_add_now (&delayed_disconnect, n);
 }
 
 
@@ -3908,9 +3839,8 @@ GST_neighbours_stop ()
     util_transmission_tk = GNUNET_SCHEDULER_NO_TASK;
   }
 
-  GNUNET_CONTAINER_multipeermap_iterate (neighbours,
-                                        &disconnect_all_neighbours,
-                                         NULL);
+  GNUNET_CONTAINER_multipeermap_iterate (neighbours, &disconnect_all_neighbours,
+      NULL );
   GNUNET_CONTAINER_multipeermap_destroy (neighbours);
 
   next = pending_bc_head;