-fix time assertion introduce in last patch
[oweals/gnunet.git] / src / transport / gnunet-service-transport_neighbours.c
index d80149e47b62dff0824ebcbc3e22a1894e47164d..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
  */
 #define UTIL_TRANSMISSION_INTERVAL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
 
+/**
+ * State describing which kind a reply this neighbour should send
+ */
+enum GST_ACK_State
+{
+  /**
+   * We did not receive a CONNECT message for this neighbour
+   */
+  ACK_UNDEFINED = 0,
+
+  /* The neighbour received a CONNECT message and has to send a CONNECT_ACK
+   * as reply */
+  ACK_SEND_CONNECT_ACK = 1,
+
+  /* The neighbour sent a CONNECT_ACK message and has to send a SESSION_ACK
+   * as reply */
+  ACK_SEND_SESSION_ACK = 2
+};
+
+
 GNUNET_NETWORK_STRUCT_BEGIN
 
 /**
@@ -277,7 +297,6 @@ struct NeighbourAddress
   uint32_t keep_alive_nonce;
 };
 
-
 /**
  * Entry in neighbours.
  */
@@ -323,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?
    */
@@ -381,13 +405,19 @@ 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).  Set to 1 if we need to send a CONNECT_ACK.  Set to 2 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).
+   * blacklist).  Initially set to #ACK_UNDEFINED. Set to #ACK_SEND_CONNECT_ACK
+   * 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).
    */
-  int send_connect_ack;
+  enum GST_ACK_State ack_state;
 
   /**
    * Tracking utilization of outbound bandwidth
@@ -417,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
@@ -510,6 +540,22 @@ static GNUNET_SCHEDULER_TaskIdentifier util_transmission_tk;
 
 static struct GNUNET_CONTAINER_MultiPeerMap *registered_quota_notifications;
 
+static char *
+print_ack_state (enum GST_ACK_State s)
+{
+  switch (s) {
+    case ACK_UNDEFINED:
+      return "UNDEFINED";
+    case ACK_SEND_CONNECT_ACK:
+      return "SEND_CONNECT_ACK";
+    case ACK_SEND_SESSION_ACK:
+      return "SEND_SESSION_ACK";
+    default:
+      GNUNET_break (0);
+      return "N/A";
+  }
+}
+
 /**
  * Lookup a neighbour entry in the neighbours hash map.
  *
@@ -578,6 +624,8 @@ free_address (struct NeighbourAddress *na)
     GNUNET_ATS_address_in_use (GST_ats, na->address, na->session, GNUNET_NO);
   }
 
+  na->bandwidth_in = GNUNET_BANDWIDTH_value_init (0);
+  na->bandwidth_out = GNUNET_BANDWIDTH_value_init (0);
   na->ats_active = GNUNET_NO;
   na->keep_alive_nonce = 0;
   if (NULL != na->address)
@@ -649,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,
@@ -799,10 +847,8 @@ set_primary_address (struct NeighbourMapEntry *n,
       n->primary_address.bandwidth_out);
 }
 
-#if 0
-TODO: Implement this
 /**
- * Clear the primary address of a neighbour since this primary address is not
+ * Clear the primary address of a neighbour since this address is not
  * valid anymore and notify monitoring about it
  *
  * @param n the neighbour
@@ -810,11 +856,30 @@ TODO: Implement this
 static void
 unset_primary_address (struct NeighbourMapEntry *n)
 {
+  /* Unset primary address */
+  free_address (&n->primary_address);
 
+  /* Notify monitoring about it */
+  neighbour_change_cb (callback_cls,
+      &n->id,
+      NULL,
+      n->state, n->timeout,
+      n->primary_address.bandwidth_in,
+      n->primary_address.bandwidth_out);
 }
 
-#endif
-
+/**
+ * Clear the alternative address of a neighbour since this address is not
+ * valid anymore
+ *
+ * @param n the neighbour
+ */
+static void
+unset_alternative_address (struct NeighbourMapEntry *n)
+{
+  /* Unset primary address */
+  free_address (&n->alternative_address);
+}
 
 /**
  * Free a neighbour map entry.
@@ -854,6 +919,7 @@ free_neighbour (struct NeighbourMapEntry *n,
                           GNUNET_NO);
     disconnect_notify_cb (callback_cls, &n->id);
   }
+
   /* Mark peer as disconnected */
   set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED);
 
@@ -863,7 +929,7 @@ free_neighbour (struct NeighbourMapEntry *n,
     backup_primary = NULL;
 
   /* free addresses and mark as unused */
-  free_address (&n->primary_address);
+  unset_primary_address (n);
   free_address (&n->alternative_address);
 
   /* cut all transport-level connection for this peer */
@@ -885,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)
   {
@@ -993,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));
@@ -1014,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,
@@ -1043,7 +1115,6 @@ disconnect_neighbour (struct NeighbourMapEntry *n)
   {
   case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
   case GNUNET_TRANSPORT_PS_INIT_ATS:
-  case GNUNET_TRANSPORT_PS_INIT_BLACKLIST:
     /* other peer is completely unaware of us, no need to send DISCONNECT */
     free_neighbour (n, GNUNET_NO);
     return;
@@ -1051,11 +1122,8 @@ disconnect_neighbour (struct NeighbourMapEntry *n)
     send_disconnect (n);
     set_state (n, GNUNET_TRANSPORT_PS_DISCONNECT);
     break;
-  case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND:
   case GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS:
-  case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST:
     /* 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:
@@ -1063,11 +1131,9 @@ 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_BLACKLIST:
   case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
-  case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST:
-  case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
     /* we are currently connected, need to send disconnect and do
        internal notifications and update statistics */
     send_disconnect (n);
@@ -1079,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;
@@ -1168,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);
@@ -1234,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,
@@ -1257,7 +1325,6 @@ send_keepalive (struct NeighbourMapEntry *n)
   uint32_t nonce;
 
   GNUNET_assert ((GNUNET_TRANSPORT_PS_CONNECTED == n->state) ||
-                 (GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST == n->state) ||
                  (GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT));
   if (GNUNET_TIME_absolute_get_remaining (n->keep_alive_time).rel_value_us > 0)
     return; /* no keepalive needed at this time */
@@ -1574,15 +1641,88 @@ 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);
 }
 
+static void
+send_session_connect_cont (void *cls,
+                      const struct GNUNET_PeerIdentity *target,
+                      int result,
+                      size_t size_payload,
+                      size_t size_on_wire)
+{
+  struct NeighbourMapEntry *n;
+
+  n = lookup_neighbour (target);
+  if (NULL == n)
+  {
+    /* CONNECT continuation was called after neighbor was freed,
+     * for example due to a time out for the state or the session
+     * used was already terminated: nothing to do here... */
+    return;
+  }
+
+  if ( (GNUNET_TRANSPORT_PS_CONNECT_SENT != n->state) &&
+       (GNUNET_TRANSPORT_PS_RECONNECT_SENT != n->state) &&
+       (GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT != n->state))
+  {
+    /* CONNECT continuation was called after neighbor changed state,
+     * for example due to a time out for the state or the session
+     * used was already terminated: nothing to do here... */
+    return;
+  }
+  if (GNUNET_OK == result)
+    return;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+            _("Failed to send CONNECT message to peer `%s' using address `%s' session %p\n"),
+            GNUNET_i2s (target),
+            GST_plugins_a2s (n->primary_address.address),
+            n->primary_address.session);
+
+  switch (n->state) {
+  case GNUNET_TRANSPORT_PS_CONNECT_SENT:
+    /* Remove address and request and additional one */
+    GNUNET_ATS_address_destroyed (GST_ats, n->primary_address.address,
+        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,
+        GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT));
+    break;
+  case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
+    /* Remove address and request and additional one */
+    GNUNET_ATS_address_destroyed (GST_ats, n->primary_address.address,
+        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,
+        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,
+        NULL );
+    unset_alternative_address (n);
+    set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECTED,
+        GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
+    break;
+  default:
+    disconnect_neighbour (n);
+    break;
+  }
+}
 
 /**
  * Send a SESSION_CONNECT message via the given address.
@@ -1596,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);
@@ -1625,8 +1766,8 @@ send_session_connect (struct NeighbourAddress *na)
                   na->session,
                   (const char *) &connect_msg, sizeof (struct SessionConnectMessage),
                   UINT_MAX,
-                  GNUNET_TIME_UNIT_FOREVER_REL,
-                  NULL, NULL))
+                  SETUP_CONNECTION_TIMEOUT,
+                  send_session_connect_cont, NULL))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                 _("Failed to transmit CONNECT message via plugin to %s\n"),
@@ -1638,43 +1779,114 @@ send_session_connect (struct NeighbourAddress *na)
       GNUNET_break (0);
       return;
     }
-    /* Hard failure to send the CONNECT message with this address:
-       Destroy address and session */
+
+    switch (n->state) {
+      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_INIT_ATS,
+          GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT));
+        /* Hard failure to send the CONNECT message with this address:
+           Destroy address and session */
+        break;
+      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_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:
+        disconnect_neighbour (n);
+        break;
+    }
     GNUNET_ATS_address_destroyed (GST_ats, na->address, na->session);
     GNUNET_ATS_address_destroyed (GST_ats, na->address, NULL);
-
-    /* Remove address and request and additional one */
-    /* TODO */
-
-    return;
   }
-
   GST_neighbours_notify_data_sent (&na->address->peer,
                                    na->address,
                                    na->session,
                                    sizeof (struct SessionConnectMessage));
+}
+
 
+static void
+send_session_connect_ack_cont (void *cls,
+                      const struct GNUNET_PeerIdentity *target,
+                      int result,
+                      size_t size_payload,
+                      size_t size_on_wire)
+{
+  struct NeighbourMapEntry *n;
+
+  n = lookup_neighbour (target);
+  if (NULL == n)
+  {
+    /* CONNECT_ACK continuation was called after neighbor was freed,
+     * for example due to a time out for the state or the session
+     * used was already terminated: nothing to do here... */
+    return;
+  }
+
+  if (GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK != n->state)
+  {
+    /* CONNECT_ACK continuation was called after neighbor changed state,
+     * for example due to a time out for the state or the session
+     * used was already terminated: nothing to do here... */
+    return;
+  }
+  if (GNUNET_OK == result)
+    return;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+            _("Failed to send CONNECT_ACK message to peer `%s' using address `%s' session %p\n"),
+            GNUNET_i2s (target),
+            GST_plugins_a2s (n->primary_address.address),
+            n->primary_address.session);
+
+  /* Failed to send CONNECT_ACK message with this address */
+  GNUNET_ATS_address_destroyed (GST_ats, n->primary_address.address,
+      n->primary_address.session);
+  GNUNET_ATS_address_destroyed (GST_ats, n->primary_address.address,
+      NULL);
+
+  /* Remove address and request and additional one */
+  unset_primary_address (n);
+  n->ack_state = ACK_SEND_CONNECT_ACK;
+  set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS,
+      GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
+  return;
 }
 
 
 /**
- * Send a SESSION_CONNECT_ACK message via the given address.
+ * Send a CONNECT_ACK message via the given address.
  *
  * @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_session_connect_ack_message (const struct GNUNET_HELLO_Address *address,
+send_connect_ack_message (const struct GNUNET_HELLO_Address *address,
                                  struct Session *session,
                                  struct GNUNET_TIME_Absolute timestamp)
 {
   struct GNUNET_TRANSPORT_PluginFunctions *papi;
   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);
@@ -1695,12 +1907,42 @@ send_session_connect_ack_message (const struct GNUNET_HELLO_Address *address,
   connect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT_ACK);
   connect_msg.reserved = htonl (0);
   connect_msg.timestamp = GNUNET_TIME_absolute_hton (timestamp);
-  (void) papi->send (papi->cls,
+
+  if (GNUNET_SYSERR == papi->send (papi->cls,
                     session,
                     (const char *) &connect_msg, sizeof (struct SessionConnectMessage),
                     UINT_MAX,
                     GNUNET_TIME_UNIT_FOREVER_REL,
-                    NULL, NULL);
+                    send_session_connect_ack_cont, NULL))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                _("Failed to transmit CONNECT_ACK message via plugin to %s\n"),
+                GST_plugins_a2s (address));
+
+    n = lookup_neighbour (&address->peer);
+    if (NULL == n)
+    {
+      GNUNET_break (0);
+      return;
+    }
+    /* Hard failure to send the CONNECT_ACK message with this address:
+       Destroy session (and address)  */
+    if (GNUNET_YES == GNUNET_HELLO_address_check_option(address,
+        GNUNET_HELLO_ADDRESS_INFO_INBOUND))
+    {
+      GNUNET_ATS_address_destroyed (GST_ats, address, session);
+      GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
+    }
+    else
+      GNUNET_ATS_address_destroyed (GST_ats, address, session);
+
+    /* Remove address and request and additional one */
+    unset_primary_address (n);
+    n->ack_state = ACK_SEND_CONNECT_ACK;
+    set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS,
+        GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
+    return;
+  }
 
 }
 
@@ -1836,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);
 
@@ -1879,6 +2120,7 @@ setup_neighbour (const struct GNUNET_PeerIdentity *peer)
              GNUNET_i2s (peer));
   n = GNUNET_new (struct NeighbourMapEntry);
   n->id = *peer;
+  n->ack_state = ACK_UNDEFINED;
   n->latency = GNUNET_TIME_UNIT_FOREVER_REL;
   n->last_util_transmission = GNUNET_TIME_absolute_get();
   n->util_payload_bytes_recv = 0;
@@ -1897,33 +2139,6 @@ setup_neighbour (const struct GNUNET_PeerIdentity *peer)
   return n;
 }
 
-
-/**
- * Check if the two given addresses are the same.
- * Actually only checks if the sessions are non-NULL
- * (which they should be) and then if they are identical;
- * the actual addresses don't matter if the session
- * pointers match anyway, and we must have session pointers
- * at this time.
- *
- * @param a1 first address to compare
- * @param a2 other address to compare
- * @return #GNUNET_NO if the addresses do not match, #GNUNET_YES if they do match
- */
-static int
-address_matches (const struct NeighbourAddress *a1,
-                const struct NeighbourAddress *a2)
-{
-  if ( (NULL == a1->session) ||
-       (NULL == a2->session) )
-  {
-    GNUNET_break (0);
-    return 0;
-  }
-  return (a1->session == a2->session) ? GNUNET_YES : GNUNET_NO;
-}
-
-
 /* We received a address suggestion after requesting an address in
  * try_connect or after receiving a connect, switch to address
  */
@@ -2030,11 +2245,8 @@ GST_neighbours_try_connect (const struct GNUNET_PeerIdentity *target)
       free_neighbour (n, GNUNET_NO);
       break;
     case GNUNET_TRANSPORT_PS_INIT_ATS:
-    case GNUNET_TRANSPORT_PS_INIT_BLACKLIST:
     case GNUNET_TRANSPORT_PS_CONNECT_SENT:
-    case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND:
     case GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS:
-    case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST:
     case GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK:
       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                   "Ignoring request to try to connect to `%s', already trying!\n",
@@ -2042,9 +2254,7 @@ GST_neighbours_try_connect (const struct GNUNET_PeerIdentity *target)
       return; /* already trying */
     case GNUNET_TRANSPORT_PS_CONNECTED:
     case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
-    case GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST:
     case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
-    case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST:
     case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                   "Ignoring request to try to connect, already connected to `%s'!\n",
@@ -2057,6 +2267,7 @@ GST_neighbours_try_connect (const struct GNUNET_PeerIdentity *target)
     case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
       /* should not be possible */
       GNUNET_assert (0);
+      return;
     default:
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                   "Unhandled state `%s'\n",
@@ -2079,322 +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_test_blacklist_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 (GNUNET_OK == result)
-  {
-    GST_ats_add_address (bcc->na.address, bcc->na.session, NULL, 0);
-  }
-  else
-  {
-    /* Blacklist disagreed on connecting to a peer with this address
-     * Destroy address because we are not allowed to use it
-     */
-    if (NULL != bcc->na.session)
-      GNUNET_ATS_address_destroyed (GST_ats, bcc->na.address, bcc->na.session);
-    GNUNET_ATS_address_destroyed (GST_ats, bcc->na.address, NULL);
-  }
-  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_DEBUG,
-              "Received blacklist result for peer `%s' in state %s/%d\n",
-              GNUNET_i2s (peer),
-              GNUNET_TRANSPORT_ps2s (n->state),
-              n->send_connect_ack);
-  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; still, pass address to ATS as a
-       possibility */
-    break;
-  case GNUNET_TRANSPORT_PS_INIT_BLACKLIST:
-    /* check if the address the blacklist was fine with matches
-       ATS suggestion, if so, we can move on! */
-    if ( (GNUNET_OK == result) &&
-        (1 == n->send_connect_ack) )
-    {
-      n->send_connect_ack = 2;
-      send_session_connect_ack_message (bcc->na.address,
-                                       bcc->na.session,
-                                       n->connect_ack_timestamp);
-    }
-    if (GNUNET_YES != address_matches (&bcc->na, &n->primary_address))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Blacklist result for peer %s is for non-primary address, ignored\n",
-                  GNUNET_i2s (peer));
-      break; /* result for an address we currently don't care about */
-    }
-    if (GNUNET_OK == result)
-    {
-      set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECT_SENT,
-          GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT));
-      send_session_connect (&n->primary_address);
-    }
-    else
-    {
-      free_address (&n->primary_address);
-      GNUNET_break (0);
-      set_state_and_timeout (n, GNUNET_TRANSPORT_PS_INIT_ATS,
-          GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
-    }
-    break;
-  case GNUNET_TRANSPORT_PS_CONNECT_SENT:
-    /* waiting on CONNECT_ACK, send ACK if one is pending */
-    if ( (GNUNET_OK == result) &&
-        (1 == n->send_connect_ack) )
-    {
-      n->send_connect_ack = 2;
-      send_session_connect_ack_message (n->primary_address.address,
-                                       n->primary_address.session,
-                                       n->connect_ack_timestamp);
-    }
-    break;
-  case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND:
-    set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS,
-        GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
-    GNUNET_ATS_reset_backoff (GST_ats, peer);
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Suggesting address for peer %s to ATS\n",
-                GNUNET_i2s (peer));
-    n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, peer,
-        &address_suggest_cont, n);
-    break;
-  case GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS:
-    /* waiting on ATS suggestion, don't care about blacklist */
-    break;
-  case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST:
-    if (GNUNET_YES != address_matches (&bcc->na, &n->primary_address))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Blacklist result ignored, as it is not for our primary address\n");
-      break; /* result for an address we currently don't care about */
-    }
-    if (GNUNET_OK == result)
-    {
-      set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK,
-          GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT));
-      send_session_connect_ack_message (bcc->na.address,
-                                       bcc->na.session,
-                                       n->connect_ack_timestamp);
-      if (1 == n->send_connect_ack)
-       n->send_connect_ack = 2;
-    }
-    else
-    {
-      struct GNUNET_TRANSPORT_PluginFunctions *plugin;
-
-      plugin = GST_plugins_find (bcc->na.address->transport_name);
-      if ( (NULL != plugin) &&
-           (NULL != bcc->na.session) )
-      {
-        plugin->disconnect_session (plugin->cls,
-                                    bcc->na.session);
-        break;
-      }
-      GNUNET_break (NULL != plugin);
-      free_address (&n->primary_address);
-      set_state_and_timeout (n, GNUNET_TRANSPORT_PS_INIT_ATS,
-          GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
-      GNUNET_ATS_reset_backoff (GST_ats, peer);
-    }
-    break;
-  case GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK:
-    /* waiting on SESSION_ACK, send ACK if one is pending */
-    if ( (GNUNET_OK == result) &&
-        (1 == n->send_connect_ack) )
-    {
-      n->send_connect_ack = 2;
-      send_session_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_BLACKLIST:
-    if ( (GNUNET_OK == result) &&
-        (1 == n->send_connect_ack) )
-    {
-      n->send_connect_ack = 2;
-      send_session_connect_ack_message (bcc->na.address,
-                                       bcc->na.session,
-                                       n->connect_ack_timestamp);
-    }
-    if (GNUNET_YES != address_matches (&bcc->na, &n->primary_address))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Blacklist result ignored, as it is not for our primary address\n");
-      break; /* result for an address we currently don't care about */
-    }
-    if (GNUNET_OK == result)
-    {
-      set_state_and_timeout (n, GNUNET_TRANSPORT_PS_RECONNECT_SENT, GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT));
-      send_session_connect (&n->primary_address);
-    }
-    else
-    {
-      set_state_and_timeout (n, GNUNET_TRANSPORT_PS_RECONNECT_ATS, GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
-    }
-    break;
-  case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
-    /* waiting on CONNECT_ACK, don't care about blacklist */
-    if ( (GNUNET_OK == result) &&
-        (1 == n->send_connect_ack) )
-    {
-      n->send_connect_ack = 2;
-      send_session_connect_ack_message (n->primary_address.address,
-                                       n->primary_address.session,
-                                       n->connect_ack_timestamp);
-    }
-    break;
-  case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST:
-    if (GNUNET_YES != address_matches (&bcc->na, &n->alternative_address))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Blacklist result ignored, as it is not for our primary address\n");
-      break; /* result for an address we currently don't care about */
-    }
-    if (GNUNET_OK == result)
-    {
-      send_session_connect (&n->alternative_address);
-      set_state (n, GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT);
-    }
-    else
-    {
-      set_state(n, GNUNET_TRANSPORT_PS_CONNECTED);
-      free_address (&n->alternative_address);
-    }
-    break;
-  case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
-    /* waiting on CONNECT_ACK, don't care about blacklist */
-    if ( (GNUNET_OK == result) &&
-        (1 == n->send_connect_ack) )
-    {
-      n->send_connect_ack = 2;
-      send_session_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 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
-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,
-                                               address->transport_name,
-                                               &handle_test_blacklist_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_DEBUG,
-              "Received CONNECT message from peer `%s'\n",
-             GNUNET_i2s (peer));
   if (ntohs (message->size) != sizeof (struct SessionConnectMessage))
   {
     GNUNET_break_op (0);
@@ -2416,72 +2327,85 @@ GST_neighbours_handle_connect (const struct GNUNET_MessageHeader *message,
   ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
   n = lookup_neighbour (peer);
   if (NULL == n)
+  {
+    /* This is a new neighbour and set to not connected */
     n = setup_neighbour (peer);
-  n->send_connect_ack = 1;
+  }
+
+  /* Remember this CONNECT message in neighbour */
+  n->ack_state = ACK_SEND_CONNECT_ACK;
   n->connect_ack_timestamp = ts;
 
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Received SESSION_CONNECT for peer `%s' in state %s/%d\n",
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Received CONNECT for peer `%s' in state %s/%s\n",
               GNUNET_i2s (peer),
               GNUNET_TRANSPORT_ps2s (n->state),
-              n->send_connect_ack);
+              print_ack_state (n->ack_state));
+
   switch (n->state)
   {
   case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
-    /* Do a blacklist check for the new address */
-    set_state (n, GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND);
-    check_blacklist (peer, ts, address, session);
+    /* Request an address from ATS to send CONNECT_ACK to this peer */
+    set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS,
+        GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
+    if (NULL == n->suggest_handle)
+      GNUNET_ATS_suggest_address (GST_ats, peer, address_suggest_cont, n);
     break;
   case GNUNET_TRANSPORT_PS_INIT_ATS:
-    /* CONNECT message takes priority over us asking ATS for address */
-    set_state (n, GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND);
-    /* fallthrough */
-  case GNUNET_TRANSPORT_PS_INIT_BLACKLIST:
-  case GNUNET_TRANSPORT_PS_CONNECT_SENT:
-  case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND:
+    /* CONNECT message takes priority over us asking ATS for address:
+     * Wait for ATS to suggest an address and send CONNECT_ACK */
+    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_RECV_ATS:
-  case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST:
+    /* 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 */
-    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;
-       still, it can never hurt to have an alternative address, so also
-       tell ATS  about it */
+    /* 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->send_connect_ack = 0;
-    send_session_connect_ack_message (n->primary_address.address,
-                                     n->primary_address.session, ts);
-    check_blacklist (peer, ts, address, session);
+    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_RECONNECT_ATS:
-  case GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST:
+    /* 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 */
-    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_BLACKLIST:
   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->send_connect_ack = 0;
-    send_session_connect_ack_message (n->primary_address.address,
-                                     n->primary_address.session, ts);
-    check_blacklist (peer, ts, address, session);
+    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_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);
-    set_state (n, GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS);
+    /* 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 */
+    GNUNET_ATS_suggest_address (GST_ats, peer, address_suggest_cont, n);
     GNUNET_ATS_reset_backoff (GST_ats, peer);
+    set_state (n, GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS);
     break;
   case GNUNET_TRANSPORT_PS_DISCONNECT_FINISHED:
     /* should not be possible */
@@ -2505,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)
     {
@@ -2522,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",
@@ -2551,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));
@@ -2591,50 +2517,62 @@ 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_INIT_BLACKLIST:
-    /* 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_timeout (n, GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT));
-    check_blacklist (&n->id, n->connect_ack_timestamp,
-                     blc_ctx->address, blc_ctx->session);
-    break;
   case GNUNET_TRANSPORT_PS_CONNECT_SENT:
+    /* 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);
-    set_state_and_timeout (n, GNUNET_TRANSPORT_PS_INIT_BLACKLIST,
-        GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT));
-    check_blacklist (&n->id, n->connect_ack_timestamp,
-                     blc_ctx->address, blc_ctx->session);
+    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_RECV_ATS:
+    /* We requested an address and ATS suggests one:
+     * set primary address and send CONNECT_ACK message*/
     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_BLACKLIST,
-        GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT));
-    check_blacklist (&n->id, n->connect_ack_timestamp,
-        blc_ctx->address, blc_ctx->session);
-    break;
-  case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND:
-    set_timeout (n, GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT));
-    check_blacklist (&n->id, n->connect_ack_timestamp,
-        blc_ctx->address, blc_ctx->session);
+    /* Send an ACK message as a response to the CONNECT msg */
+    set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK,
+        GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT));
+    send_connect_ack_message (n->primary_address.address,
+                              n->primary_address.session,
+                              n->connect_ack_timestamp);
+    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_BLACKLIST:
   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 */
+    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_BLACKLIST,
-        GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT));
-    check_blacklist (&n->id, n->connect_ack_timestamp,
-        blc_ctx->address, blc_ctx->session);
+    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);
@@ -2648,67 +2586,52 @@ switch_address_bl_check_cont (void *cls,
     }
     /* ATS asks us to switch a life connection; see if we can get
        a CONNECT_ACK on it before we actually do this! */
-    set_state (n, GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST);
     set_alternative_address (n, blc_ctx->address, blc_ctx->session,
         blc_ctx->bandwidth_in, blc_ctx->bandwidth_out);
-    check_blacklist (&n->id, GNUNET_TIME_absolute_get (),
-        blc_ctx->address, blc_ctx->session);
+    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);
-    set_state_and_timeout (n, GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST,
-        GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT));
-    check_blacklist (&n->id, n->connect_ack_timestamp,
-        blc_ctx->address, blc_ctx->session);
-    break;
-  case GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST:
-    /* ATS asks us to switch while we were trying to reconnect; 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_timeout (n, GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT));
-    check_blacklist (&n->id, n->connect_ack_timestamp,
-        blc_ctx->address, blc_ctx->session);
+    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);
     break;
   case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
     /* ATS asks us to switch while we were trying to reconnect; switch to new
-       address and check blacklist again */
+       address and send CONNECT 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_RECONNECT_BLACKLIST,
-        GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT));
-    check_blacklist (&n->id, n->connect_ack_timestamp,
-        blc_ctx->address, blc_ctx->session);
-    break;
-  case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST:
-    if (n->primary_address.session == blc_ctx->session)
-    {
-      /* ATS switches back to still-active session */
-      set_state(n, GNUNET_TRANSPORT_PS_CONNECTED);
-      free_address (&n->alternative_address);
-      break;
-    }
-    /* ATS asks us to switch a life connection, update blacklist check */
-    set_primary_address (n, blc_ctx->address, blc_ctx->session,
-        blc_ctx->bandwidth_in, blc_ctx->bandwidth_out, GNUNET_NO);
-    check_blacklist (&n->id, GNUNET_TIME_absolute_get (),
-        blc_ctx->address, blc_ctx->session);
+    set_state_and_timeout (n, GNUNET_TRANSPORT_PS_RECONNECT_SENT,
+        GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT));
+    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);
       set_state (n, GNUNET_TRANSPORT_PS_CONNECTED);
       break;
     }
-    /* ATS asks us to switch a life connection, update blacklist check */
-    set_state (n, GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST);
+    /* ATS asks us to switch a life connection, send */
     set_alternative_address (n, blc_ctx->address, blc_ctx->session,
         blc_ctx->bandwidth_in, blc_ctx->bandwidth_out);
-    check_blacklist (&n->id, GNUNET_TIME_absolute_get (),
-        blc_ctx->address, blc_ctx->session);
+    set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT,
+        GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT));
+    send_session_connect (&n->alternative_address);
     break;
   case GNUNET_TRANSPORT_PS_DISCONNECT:
     /* not going to switch addresses while disconnecting */
@@ -2759,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;
 
@@ -2776,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,
@@ -2798,12 +2720,12 @@ 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 "
-    "peer `%s' in state %s/%d (quota in/out %u %u )\n",
+    "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" : "",
+        GNUNET_HELLO_ADDRESS_INFO_INBOUND) ? "inbound" : "outbound",
     GST_plugins_a2s (address), session, GNUNET_i2s (peer),
-    GNUNET_TRANSPORT_ps2s (n->state), n->send_connect_ack,
+    GNUNET_TRANSPORT_ps2s (n->state), print_ack_state (n->ack_state),
     ntohl (bandwidth_in.value__), ntohl (bandwidth_out.value__));
 
   /* Perform blacklist check */
@@ -3013,16 +2935,6 @@ master_task (void *cls,
       return;
     }
     break;
-  case GNUNET_TRANSPORT_PS_INIT_BLACKLIST:
-    if (0 == delay.rel_value_us)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                 "Connection to `%s' timed out waiting for BLACKLIST to approve address\n",
-                 GNUNET_i2s (&n->id));
-      free_neighbour (n, GNUNET_NO);
-      return;
-    }
-    break;
   case GNUNET_TRANSPORT_PS_CONNECT_SENT:
     if (0 == delay.rel_value_us)
     {
@@ -3031,48 +2943,31 @@ master_task (void *cls,
                  GNUNET_i2s (&n->id));
       /* We could not send to this address, delete address and session */
       if (NULL != n->primary_address.session)
-        GNUNET_ATS_address_destroyed (GST_ats,
-            n->primary_address.address, n->primary_address.session);
-      GNUNET_ATS_address_destroyed (GST_ats,
-          n->primary_address.address, NULL);
-      disconnect_neighbour (n);
-      return;
-    }
-    break;
-  case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND:
-    if (0 == delay.rel_value_us)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Connection to `%s' timed out waiting BLACKLIST to approve address to use for received CONNECT\n",
-                  GNUNET_i2s (&n->id));
-      free_neighbour (n, GNUNET_NO);
+        GNUNET_ATS_address_destroyed (GST_ats, n->primary_address.address,
+            n->primary_address.session);
+      GNUNET_ATS_address_destroyed (GST_ats, n->primary_address.address, NULL);
+
+      /* Remove address and request and additional one */
+      unset_primary_address (n);
+      set_state_and_timeout (n, GNUNET_TRANSPORT_PS_INIT_ATS,
+          GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
       return;
     }
     break;
   case GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS:
     if (0 == delay.rel_value_us)
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                  "Connection to `%s' timed out waiting ATS to provide address to use for CONNECT_ACK\n",
                  GNUNET_i2s (&n->id));
       free_neighbour (n, GNUNET_NO);
       return;
     }
     break;
-  case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST:
-    if (0 == delay.rel_value_us)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                 "Connection to `%s' timed out waiting BLACKLIST to approve address to use for CONNECT_ACK\n",
-                 GNUNET_i2s (&n->id));
-      free_neighbour (n, GNUNET_NO);
-      return;
-    }
-    break;
   case GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK:
     if (0 == delay.rel_value_us)
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                  "Connection to `%s' timed out waiting for other peer to send SESSION_ACK\n",
                  GNUNET_i2s (&n->id));
       disconnect_neighbour (n);
@@ -3082,7 +2977,7 @@ master_task (void *cls,
   case GNUNET_TRANSPORT_PS_CONNECTED:
     if (0 == delay.rel_value_us)
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                  "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs\n",
                  GNUNET_i2s (&n->id));
       disconnect_neighbour (n);
@@ -3094,51 +2989,31 @@ master_task (void *cls,
   case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
     if (0 == delay.rel_value_us)
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                  "Connection to `%s' timed out, waiting for ATS replacement address\n",
                  GNUNET_i2s (&n->id));
       disconnect_neighbour (n);
       return;
     }
     break;
-  case GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST:
-    if (0 == delay.rel_value_us)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                 "Connection to `%s' timed out, waiting for BLACKLIST to approve replacement address\n",
-                 GNUNET_i2s (&n->id));
-      disconnect_neighbour (n);
-      return;
-    }
-    break;
   case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
     if (0 == delay.rel_value_us)
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                  "Connection to `%s' timed out, waiting for other peer to CONNECT_ACK replacement address\n",
                  GNUNET_i2s (&n->id));
       disconnect_neighbour (n);
       return;
     }
     break;
-  case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST:
-    if (0 == delay.rel_value_us)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                 "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs\n",
-                 GNUNET_i2s (&n->id));
-      disconnect_neighbour (n);
-      return;
-    }
-    try_transmission_to_peer (n);
-    send_keepalive (n);
-    break;
   case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
     if (0 == delay.rel_value_us)
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+      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;
     }
@@ -3146,7 +3021,7 @@ master_task (void *cls,
     send_keepalive (n);
     break;
   case GNUNET_TRANSPORT_PS_DISCONNECT:
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                "Cleaning up connection to `%s' after sending DISCONNECT\n",
                GNUNET_i2s (&n->id));
     free_neighbour (n, GNUNET_NO);
@@ -3163,10 +3038,9 @@ master_task (void *cls,
     break;
   }
   if ( (GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT == n->state) ||
-       (GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST == n->state) ||
        (GNUNET_TRANSPORT_PS_CONNECTED == n->state) )
   {
-    /* if we are *now* in one of these three states, we're sending
+    /* if we are *now* in one of the two states, we're sending
        keep alive messages, so we need to consider the keepalive
        delay, not just the connection timeout */
     delay = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (n->keep_alive_time),
@@ -3190,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,
@@ -3220,7 +3097,7 @@ GST_neighbours_handle_connect_ack (const struct GNUNET_MessageHeader *message,
   struct GNUNET_TIME_Absolute ts;
   struct NeighbourMapEntry *n;
 
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Received CONNECT_ACK message from peer `%s'\n",
               GNUNET_i2s (peer));
 
@@ -3251,7 +3128,6 @@ GST_neighbours_handle_connect_ack (const struct GNUNET_MessageHeader *message,
     free_neighbour (n, GNUNET_NO);
     return GNUNET_SYSERR;
   case GNUNET_TRANSPORT_PS_INIT_ATS:
-  case GNUNET_TRANSPORT_PS_INIT_BLACKLIST:
     GNUNET_STATISTICS_update (GST_stats,
                               gettext_noop
                               ("# unexpected CONNECT_ACK messages (not ready)"),
@@ -3264,7 +3140,8 @@ GST_neighbours_handle_connect_ack (const struct GNUNET_MessageHeader *message,
                   "CONNECT_ACK ignored as the timestamp does not match our CONNECT request\n");
       return GNUNET_OK;
     }
-    set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECTED, GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
+    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,
@@ -3284,9 +3161,7 @@ GST_neighbours_handle_connect_ack (const struct GNUNET_MessageHeader *message,
                 GNUNET_YES);
     send_session_ack_message (n);
     break;
-  case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND:
   case GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS:
-  case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST:
   case GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK:
     GNUNET_STATISTICS_update (GST_stats,
                               gettext_noop
@@ -3294,11 +3169,10 @@ 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:
-  case GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST:
     /* we didn't expect any CONNECT_ACK, as we are waiting for ATS
        to give us a new address... */
     GNUNET_STATISTICS_update (GST_stats,
@@ -3307,25 +3181,26 @@ 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! */
-    set_state (n, GNUNET_TRANSPORT_PS_CONNECTED);
-    send_session_ack_message (n);
-    break;
-  case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST:
-    /* duplicate CONNECT_ACK, let's answer by duplciate SESSION_ACK just in case */
+    /* 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);
     break;
   case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
     /* new address worked; adopt it and go back to connected! */
-    set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECTED, GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
+    set_state_and_timeout (n, GNUNET_TRANSPORT_PS_CONNECTED,
+        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);
@@ -3389,12 +3264,10 @@ GST_neighbours_session_terminated (const struct GNUNET_PeerIdentity *peer,
   {
     if (session == n->alternative_address.session)
     {
-      if ( (GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST == n->state) ||
-          (GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT == n->state) )
+      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 */
   }
@@ -3411,55 +3284,66 @@ GST_neighbours_session_terminated (const struct GNUNET_PeerIdentity *peer,
     GNUNET_break (0);
     free_neighbour (n, GNUNET_NO);
     return GNUNET_YES;
-  case GNUNET_TRANSPORT_PS_INIT_BLACKLIST:
   case GNUNET_TRANSPORT_PS_CONNECT_SENT:
     /* 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);
-    free_address (&n->primary_address);
+    unset_primary_address (n);
     set_state_and_timeout (n, GNUNET_TRANSPORT_PS_INIT_ATS,
         GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT));
     break;
-  case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND:
   case GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS:
-  case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST:
   case GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK:
     /* error on inbound session; free neighbour entirely */
     free_address (&n->primary_address);
     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));
-    free_address (&n->primary_address);
     break;
   case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
     /* we don't have an address, how can it go down? */
     GNUNET_break (0);
     break;
-  case GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST:
   case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
-    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_BLACKLIST:
-    /* primary went down while we were checking secondary against
-       blacklist, adopt secondary as primary */
-    free_address (&n->primary_address);
-    set_state_and_timeout (n, GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST, GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT));
-    n->primary_address = n->alternative_address;
-    memset (&n->alternative_address, 0, sizeof (struct NeighbourAddress));
+    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 */
+
+    /* 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_SENT, 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);
@@ -3519,34 +3403,71 @@ 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 */
-  if ( ( (GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK != n->state) &&
-        (GNUNET_TRANSPORT_PS_CONNECT_SENT != n->state) ) ||
-       (2 != n->send_connect_ack) )
+
+  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));
+
+  /* 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_DEBUG,
-                "Received SESSION_ACK message from peer `%s' in state %s/%d\n",
+    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),
-                n->send_connect_ack);
+                print_ack_state (n->ack_state));
+
     GNUNET_STATISTICS_update (GST_stats,
                               gettext_noop ("# unexpected SESSION_ACK messages"), 1,
                               GNUNET_NO);
     return GNUNET_OK;
   }
-  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);
-  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);\
 
-  GST_ats_add_address (n->primary_address.address,
-                       n->primary_address.session,
-                       NULL, 0);
+     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 */
+  GST_ats_add_address (n->primary_address.address, n->primary_address.session,
+      NULL, 0);
+
+  /* Set primary address to used */
   set_primary_address (n,
               n->primary_address.address,
               n->primary_address.session,
@@ -3595,7 +3516,7 @@ GST_neighbours_set_incoming_quota (const struct GNUNET_PeerIdentity *neighbour,
   GNUNET_BANDWIDTH_tracker_update_quota (&n->in_tracker, quota);
   if (0 != ntohl (quota.value__))
     return;
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Disconnecting peer `%4s' due to SET_QUOTA\n",
               GNUNET_i2s (&n->id));
   if (GNUNET_YES == test_connected (n))
@@ -3605,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,
@@ -3620,15 +3553,15 @@ GST_neighbours_handle_disconnect_message (const struct GNUNET_PeerIdentity *peer
   struct NeighbourMapEntry *n;
   const struct SessionDisconnectMessage *sdm;
 
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Received DISCONNECT message from peer `%s'\n",
               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;
   }
@@ -3676,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);
 }
 
 
@@ -3780,7 +3705,7 @@ GST_neighbours_force_disconnect (const struct GNUNET_PeerIdentity *target)
                              gettext_noop
                              ("# disconnected from peer upon explicit request"), 1,
                              GNUNET_NO);
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Forced disconnect from peer %s\n",
               GNUNET_i2s (target));
   disconnect_neighbour (n);
@@ -3806,17 +3731,12 @@ GST_neighbour_get_latency (const struct GNUNET_PeerIdentity *peer)
   {
   case GNUNET_TRANSPORT_PS_CONNECTED:
   case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_CONNECT_SENT:
-  case GNUNET_TRANSPORT_PS_CONNECTED_SWITCHING_BLACKLIST:
   case GNUNET_TRANSPORT_PS_RECONNECT_SENT:
   case GNUNET_TRANSPORT_PS_RECONNECT_ATS:
-  case GNUNET_TRANSPORT_PS_RECONNECT_BLACKLIST:
     return n->latency;
   case GNUNET_TRANSPORT_PS_NOT_CONNECTED:
-  case GNUNET_TRANSPORT_PS_INIT_BLACKLIST:
   case GNUNET_TRANSPORT_PS_INIT_ATS:
-  case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST_INBOUND:
   case GNUNET_TRANSPORT_PS_CONNECT_RECV_ATS:
-  case GNUNET_TRANSPORT_PS_CONNECT_RECV_BLACKLIST:
   case GNUNET_TRANSPORT_PS_CONNECT_RECV_ACK:
   case GNUNET_TRANSPORT_PS_CONNECT_SENT:
   case GNUNET_TRANSPORT_PS_DISCONNECT:
@@ -3919,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;