REST/NAMESTORE: rework API
[oweals/gnunet.git] / src / transport / gnunet-service-transport.c
index 0f24892bdb6e557aa079cc72605689c19b611bfe..e1dfbae0f3c57a42337fde02c22cc1bef3b6a2e9 100644 (file)
@@ -1,26 +1,25 @@
 /*
  This file is part of GNUnet.
- (C) 2010,2011 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2010-2016 GNUnet e.V.
 
- GNUnet is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
- by the Free Software Foundation; either version 3, or (at your
- option) any later version.
+ GNUnet is free software: you can redistribute it and/or modify it
under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License,
+ or (at your option) any later version.
 
  GNUnet is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- General Public License for more details.
Affero General Public License for more details.
 
- You should have received a copy of the GNU General Public License
- along with GNUnet; see the file COPYING.  If not, write to the
- Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA.
- */
+ You should have received a copy of the GNU Affero General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
 /**
  * @file transport/gnunet-service-transport.c
- * @brief
+ * @brief main for gnunet-service-transport
  * @author Christian Grothoff
  */
 #include "platform.h"
@@ -31,8 +30,7 @@
 #include "gnunet_peerinfo_service.h"
 #include "gnunet_ats_service.h"
 #include "gnunet-service-transport.h"
-#include "gnunet-service-transport_blacklist.h"
-#include "gnunet-service-transport_clients.h"
+#include "gnunet-service-transport_ats.h"
 #include "gnunet-service-transport_hello.h"
 #include "gnunet-service-transport_neighbours.h"
 #include "gnunet-service-transport_plugins.h"
 #include "gnunet-service-transport_manipulation.h"
 #include "transport.h"
 
+/**
+ * Size of the blacklist hash map.
+ */
+#define TRANSPORT_BLACKLIST_HT_SIZE 64
+
+/**
+ * How many messages can we have pending for a given client process
+ * before we start to drop incoming messages?  We typically should
+ * have only one client and so this would be the primary buffer for
+  * messages, so the number should be chosen rather generously.
+ *
+ * The expectation here is that most of the time the queue is large
+ * enough so that a drop is virtually never required.  Note that
+ * this value must be about as large as 'TOTAL_MSGS' in the
+ * 'test_transport_api_reliability.c', otherwise that testcase may
+ * fail.
+ */
+#define MAX_PENDING (128 * 1024)
+
+
 /**
  * Information we need for an asynchronous session kill.
  */
-struct SessionKiller
+struct GNUNET_ATS_SessionKiller
+{
+  /**
+   * Kept in a DLL.
+   */
+  struct GNUNET_ATS_SessionKiller *next;
+
+  /**
+   * Kept in a DLL.
+   */
+  struct GNUNET_ATS_SessionKiller *prev;
+
+  /**
+   * Session to kill.
+   */
+  struct GNUNET_ATS_Session *session;
+
+  /**
+   * Plugin for the session.
+   */
+  struct GNUNET_TRANSPORT_PluginFunctions *plugin;
+
+  /**
+   * The kill task.
+   */
+  struct GNUNET_SCHEDULER_Task *task;
+};
+
+
+/**
+ * What type of client is the `struct TransportClient` about?
+ */
+enum ClientType
+{
+  /**
+   * We do not know yet (client is fresh).
+   */
+  CT_NONE = 0,
+
+  /**
+   * Is the CORE service, we need to forward traffic to it.
+   */
+  CT_CORE = 1,
+
+  /**
+   * It is a monitor, forward monitor data.
+   */
+  CT_MONITOR = 2,
+
+  /**
+   * It is a blacklist, query about allowed connections.
+   */
+  CT_BLACKLIST = 3,
+
+  /**
+   * CORE client without any handlers.
+   */
+  CT_CORE_NO_HANDLERS = 4
+};
+
+
+/**
+ * Context we use when performing a blacklist check.
+ */
+struct GST_BlacklistCheck;
+
+/**
+ * Client connected to the transport service.
+ */
+struct TransportClient
+{
+
+  /**
+   * This is a doubly-linked list.
+   */
+  struct TransportClient *next;
+
+  /**
+   * This is a doubly-linked list.
+   */
+  struct TransportClient *prev;
+
+  /**
+   * Handle to the client.
+   */
+  struct GNUNET_SERVICE_Client *client;
+
+  /**
+   * Message queue to the client.
+   */
+  struct GNUNET_MQ_Handle *mq;
+
+  /**
+   * What type of client is this?
+   */
+  enum ClientType type;
+
+  union {
+
+    /**
+     * Peer identity to monitor the addresses of.
+     * Zero to monitor all neighbours.  Valid if
+     * @e type is CT_MONITOR.
+     */
+    struct GNUNET_PeerIdentity monitor_peer;
+
+    /**
+     * Additional details if @e type is CT_BLACKLIST.
+     */
+    struct {
+
+      /**
+       * Blacklist check that we're currently performing (or NULL
+       * if we're performing one that has been cancelled).
+       */
+      struct GST_BlacklistCheck *bc;
+
+      /**
+       * Set to #GNUNET_YES if we're currently waiting for a reply.
+       */
+      int waiting_for_reply;
+
+      /**
+       * #GNUNET_YES if we have to call receive_done for this client
+       */
+      int call_receive_done;
+
+    } blacklist;
+
+  } details;
+
+};
+
+
+
+/**
+ * Context we use when performing a blacklist check.
+ */
+struct GST_BlacklistCheck
+{
+
+  /**
+   * This is a linked list.
+   */
+  struct GST_BlacklistCheck *next;
+
+  /**
+   * This is a linked list.
+   */
+  struct GST_BlacklistCheck *prev;
+
+  /**
+   * Peer being checked.
+   */
+  struct GNUNET_PeerIdentity peer;
+
+  /**
+   * Continuation to call with the result.
+   */
+  GST_BlacklistTestContinuation cont;
+
+  /**
+   * Closure for @e cont.
+   */
+  void *cont_cls;
+
+  /**
+   * Address for #GST_blacklist_abort_matching(), can be NULL.
+   */
+  struct GNUNET_HELLO_Address *address;
+
+  /**
+   * Session for #GST_blacklist_abort_matching(), can be NULL.
+   */
+  struct GNUNET_ATS_Session *session;
+
+  /**
+   * Our current position in the blacklisters list.
+   */
+  struct TransportClient *bl_pos;
+
+  /**
+   * Current task performing the check.
+   */
+  struct GNUNET_SCHEDULER_Task *task;
+
+};
+
+
+/**
+ * Context for address to string operations
+ */
+struct AddressToStringContext
+{
+  /**
+   * This is a doubly-linked list.
+   */
+  struct AddressToStringContext *next;
+
+  /**
+   * This is a doubly-linked list.
+   */
+  struct AddressToStringContext *prev;
+
+  /**
+   * Client that made the request.
+   */
+  struct TransportClient* tc;
+};
+
+
+/**
+ * Closure for #handle_send_transmit_continuation()
+ */
+struct SendTransmitContinuationContext
+{
+
+  /**
+   * Client that made the request.
+   */
+  struct TransportClient *tc;
+
+  /**
+   * Peer that was the target.
+   */
+  struct GNUNET_PeerIdentity target;
+
+  /**
+   * At what time did we receive the message?
+   */
+  struct GNUNET_TIME_Absolute send_time;
+
+  /**
+   * Unique ID, for logging.
+   */
+  unsigned long long uuid;
+
+  /**
+   * Set to #GNUNET_YES if the connection for @e target goes
+   * down and we thus must no longer send the
+   * #GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_OK message.
+   */
+  int down;
+};
+
+
+/**
+ * Head of linked list of all clients to this service.
+ */
+static struct TransportClient *clients_head;
+
+/**
+ * Tail of linked list of all clients to this service.
+ */
+static struct TransportClient *clients_tail;
+
+/**
+ * Map of peer identities to active send transmit continuation
+ * contexts. Used to flag contexts as 'dead' when a connection goes
+ * down. Values are of type `struct SendTransmitContinuationContext
+ * *`.
+ */
+static struct GNUNET_CONTAINER_MultiPeerMap *active_stccs;
+
+/**
+ * Head of linked list of all pending address iterations
+ */
+static struct AddressToStringContext *a2s_head;
+
+/**
+ * Tail of linked list of all pending address iterations
+ */
+static struct AddressToStringContext *a2s_tail;
+
+/**
+ * Head of DLL of active blacklisting queries.
+ */
+static struct GST_BlacklistCheck *bc_head;
+
+/**
+ * Tail of DLL of active blacklisting queries.
+ */
+static struct GST_BlacklistCheck *bc_tail;
+
+/**
+ * Hashmap of blacklisted peers.  Values are of type 'char *' (transport names),
+ * can be NULL if we have no static blacklist.
+ */
+static struct GNUNET_CONTAINER_MultiPeerMap *blacklist;
+
+/**
+ * Notification context, to send updates on changes to active plugin
+ * connections.
+ */
+static struct GNUNET_NotificationContext *plugin_nc;
+
+/**
+ * Plugin monitoring client we are currently syncing, NULL if all
+ * monitoring clients are in sync.
+ */
+static struct TransportClient *sync_client;
+
+/**
+ * Peer identity that is all zeros, used as a way to indicate
+ * "all peers".  Used for comparissons.
+ */
+static struct GNUNET_PeerIdentity all_zeros;
+
+/**
+ * Statistics handle.
+ */
+struct GNUNET_STATISTICS_Handle *GST_stats;
+
+/**
+ * Configuration handle.
+ */
+const struct GNUNET_CONFIGURATION_Handle *GST_cfg;
+
+/**
+ * Configuration handle.
+ */
+struct GNUNET_PeerIdentity GST_my_identity;
+
+/**
+ * Handle to peerinfo service.
+ */
+struct GNUNET_PEERINFO_Handle *GST_peerinfo;
+
+/**
+ * Our private key.
+ */
+struct GNUNET_CRYPTO_EddsaPrivateKey *GST_my_private_key;
+
+/**
+ * ATS scheduling handle.
+ */
+struct GNUNET_ATS_SchedulingHandle *GST_ats;
+
+/**
+ * ATS connectivity handle.
+ */
+struct GNUNET_ATS_ConnectivityHandle *GST_ats_connect;
+
+/**
+ * Hello address expiration
+ */
+struct GNUNET_TIME_Relative hello_expiration;
+
+/**
+ * Head of DLL of asynchronous tasks to kill sessions.
+ */
+static struct GNUNET_ATS_SessionKiller *sk_head;
+
+/**
+ * Tail of DLL of asynchronous tasks to kill sessions.
+ */
+static struct GNUNET_ATS_SessionKiller *sk_tail;
+
+/**
+ * Interface scanner determines our LAN address range(s).
+ */
+struct GNUNET_NT_InterfaceScanner *GST_is;
+
+
+/**
+ * Queue the given message for transmission to the given client
+ *
+ * @param tc target of the message
+ * @param msg message to transmit
+ * @param may_drop #GNUNET_YES if the message can be dropped
+ */
+static void
+unicast (struct TransportClient *tc,
+         const struct GNUNET_MessageHeader *msg,
+         int may_drop)
+{
+  struct GNUNET_MQ_Envelope *env;
+
+  if ( (GNUNET_MQ_get_length (tc->mq) >= MAX_PENDING) &&
+       (GNUNET_YES == may_drop) )
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Dropping message of type %u and size %u, have %u/%u messages pending\n",
+                ntohs (msg->type),
+                ntohs (msg->size),
+                GNUNET_MQ_get_length (tc->mq),
+                MAX_PENDING);
+    GNUNET_STATISTICS_update (GST_stats,
+                              gettext_noop ("# messages dropped due to slow client"),
+                              1,
+                              GNUNET_NO);
+    return;
+  }
+  env = GNUNET_MQ_msg_copy (msg);
+  GNUNET_MQ_send (tc->mq,
+                 env);
+}
+
+
+/**
+ * Called whenever a client connects.  Allocates our
+ * data structures associated with that client.
+ *
+ * @param cls closure, NULL
+ * @param client identification of the client
+ * @param mq message queue for the client
+ * @return our `struct TransportClient`
+ */
+static void *
+client_connect_cb (void *cls,
+                  struct GNUNET_SERVICE_Client *client,
+                  struct GNUNET_MQ_Handle *mq)
+{
+  struct TransportClient *tc;
+
+  tc = GNUNET_new (struct TransportClient);
+  tc->client = client;
+  tc->mq = mq;
+  GNUNET_CONTAINER_DLL_insert (clients_head,
+                               clients_tail,
+                               tc);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Client %p connected\n",
+              tc);
+  return tc;
+}
+
+
+/**
+ * Perform next action in the blacklist check.
+ *
+ * @param cls the `struct BlacklistCheck*`
+ */
+static void
+do_blacklist_check (void *cls);
+
+
+/**
+ * Mark the peer as down so we don't call the continuation
+ * context in the future.
+ *
+ * @param cls a `struct TransportClient`
+ * @param peer a peer we are sending to
+ * @param value a `struct SendTransmitContinuationContext` to mark
+ * @return #GNUNET_OK (continue to iterate)
+ */
+static int
+mark_match_down (void *cls,
+                const struct GNUNET_PeerIdentity *peer,
+                void *value)
+{
+  struct TransportClient *tc = cls;
+  struct SendTransmitContinuationContext *stcc = value;
+
+  if (tc == stcc->tc)
+  {
+    stcc->down = GNUNET_YES;
+    stcc->tc = NULL;
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Called whenever a client is disconnected.  Frees our
+ * resources associated with that client.
+ *
+ * @param cls closure, NULL
+ * @param client identification of the client
+ * @param app_ctx our `struct TransportClient`
+ */
+static void
+client_disconnect_cb (void *cls,
+                     struct GNUNET_SERVICE_Client *client,
+                     void *app_ctx)
+{
+  struct TransportClient *tc = app_ctx;
+  struct GST_BlacklistCheck *bc;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Client %p disconnected, cleaning up.\n",
+              tc);
+  GNUNET_CONTAINER_multipeermap_iterate (active_stccs,
+                                        &mark_match_down,
+                                        tc);
+  for (struct AddressToStringContext *cur = a2s_head;
+       NULL != cur;
+       cur = cur->next)
+  {
+    if (cur->tc == tc)
+      cur->tc = NULL;
+  }
+  GNUNET_CONTAINER_DLL_remove (clients_head,
+                               clients_tail,
+                               tc);
+  switch (tc->type)
+  {
+  case CT_NONE:
+    break;
+  case CT_CORE:
+    break;
+  case CT_MONITOR:
+    break;
+  case CT_BLACKLIST:
+    for (bc = bc_head; NULL != bc; bc = bc->next)
+    {
+      if (bc->bl_pos != tc)
+        continue;
+      bc->bl_pos = tc->next;
+      if (NULL == bc->task)
+        bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
+                                            bc);
+    }
+    break;
+  case CT_CORE_NO_HANDLERS:
+    break;
+  }
+  GNUNET_free (tc);
+}
+
+
+/**
+ * Function called for each of our connected neighbours.  Notify the
+ * client about the existing neighbour.
+ *
+ * @param cls the `struct TransportClient *` to notify
+ * @param peer identity of the neighbour
+ * @param address the address
+ * @param state the current state of the peer
+ * @param state_timeout the time out for the state
+ * @param bandwidth_in inbound bandwidth in NBO
+ * @param bandwidth_out outbound bandwidth in NBO
+ */
+static void
+notify_client_about_neighbour (void *cls,
+                               const struct GNUNET_PeerIdentity *peer,
+                               const struct GNUNET_HELLO_Address *address,
+                               enum GNUNET_TRANSPORT_PeerState state,
+                               struct GNUNET_TIME_Absolute state_timeout,
+                               struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
+                               struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
+{
+  struct TransportClient *tc = cls;
+  struct ConnectInfoMessage cim;
+
+  if (GNUNET_NO == GST_neighbours_test_connected (peer))
+    return;
+  cim.header.size = htons (sizeof (struct ConnectInfoMessage));
+  cim.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT);
+  cim.id = *peer;
+  cim.quota_out = bandwidth_out;
+  unicast (tc,
+          &cim.header,
+          GNUNET_NO);
+}
+
+
+/**
+ * Initialize a normal client.  We got a start message from this
+ * client, add it to the list of clients for broadcasting of inbound
+ * messages.
+ *
+ * @param cls the client
+ * @param start the start message that was sent
+ */
+static void
+handle_client_start (void *cls,
+                    const struct StartMessage *start)
+{
+  struct TransportClient *tc = cls;
+  const struct GNUNET_MessageHeader *hello;
+  uint32_t options;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Client %p sent START\n",
+              tc);
+  options = ntohl (start->options);
+  if ((0 != (1 & options)) &&
+      (0 !=
+       memcmp (&start->self,
+               &GST_my_identity,
+               sizeof (struct GNUNET_PeerIdentity))))
+  {
+    /* client thinks this is a different peer, reject */
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (tc->client);
+    return;
+  }
+  if (CT_NONE != tc->type)
+  {
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (tc->client);
+    return;
+  }
+  if (0 != (2 & options))
+    tc->type = CT_CORE;
+  else
+    tc->type = CT_CORE_NO_HANDLERS;
+  hello = GST_hello_get ();
+  if (NULL != hello)
+    unicast (tc,
+             hello,
+             GNUNET_NO);
+  GST_neighbours_iterate (&notify_client_about_neighbour,
+                          tc);
+  GNUNET_SERVICE_client_continue (tc->client);
+}
+
+
+/**
+ * Client sent us a HELLO.  Check the request.
+ *
+ * @param cls the client
+ * @param message the HELLO message
+ */
+static int
+check_client_hello (void *cls,
+                   const struct GNUNET_MessageHeader *message)
+{
+  return GNUNET_OK; /* FIXME: check here? */
+}
+
+
+/**
+ * Client sent us a HELLO.  Process the request.
+ *
+ * @param cls the client
+ * @param message the HELLO message
+ */
+static void
+handle_client_hello (void *cls,
+                    const struct GNUNET_MessageHeader *message)
+{
+  struct TransportClient *tc = cls;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+             "Received HELLO message\n");
+  GST_validation_handle_hello (message);
+  GNUNET_SERVICE_client_continue (tc->client);
+}
+
+
+/**
+ * Function called after the transmission is done.  Notify the client that it is
+ * OK to send the next message.
+ *
+ * @param cls closure
+ * @param success #GNUNET_OK on success, #GNUNET_NO on failure, #GNUNET_SYSERR if we're not connected
+ * @param bytes_payload bytes payload sent
+ * @param bytes_on_wire bytes sent on wire
+ */
+static void
+handle_send_transmit_continuation (void *cls,
+                                   int success,
+                                   size_t bytes_payload,
+                                   size_t bytes_on_wire)
+{
+  struct SendTransmitContinuationContext *stcc = cls;
+  struct SendOkMessage send_ok_msg;
+  struct GNUNET_TIME_Relative delay;
+  const struct GNUNET_HELLO_Address *addr;
+
+  delay = GNUNET_TIME_absolute_get_duration (stcc->send_time);
+  addr = GST_neighbour_get_current_address (&stcc->target);
+  if (delay.rel_value_us > GNUNET_CONSTANTS_LATENCY_WARN.rel_value_us)
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "It took us %s to send %u/%u bytes to %s (%d, %s)\n",
+                GNUNET_STRINGS_relative_time_to_string (delay,
+                                                        GNUNET_YES),
+                (unsigned int) bytes_payload,
+                (unsigned int) bytes_on_wire,
+                GNUNET_i2s (&stcc->target),
+                success,
+                (NULL != addr) ? addr->transport_name : "%");
+  else
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "It took us %s to send %u/%u bytes to %s (%d, %s)\n",
+                GNUNET_STRINGS_relative_time_to_string (delay,
+                                                       GNUNET_YES),
+                (unsigned int) bytes_payload,
+                (unsigned int) bytes_on_wire,
+                GNUNET_i2s (&stcc->target),
+                success,
+                (NULL != addr) ? addr->transport_name : "%");
+
+  if (GNUNET_NO == stcc->down)
+  {
+    /* Only send confirmation if we are still connected */
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Sending SEND_OK for transmission request %llu\n",
+                stcc->uuid);
+    send_ok_msg.header.size = htons (sizeof (send_ok_msg));
+    send_ok_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_OK);
+    send_ok_msg.bytes_msg = htonl (bytes_payload);
+    send_ok_msg.bytes_physical = htonl (bytes_on_wire);
+    send_ok_msg.success = htonl (success);
+    send_ok_msg.peer = stcc->target;
+    unicast (stcc->tc,
+            &send_ok_msg.header,
+            GNUNET_NO);
+  }
+  GNUNET_assert (GNUNET_OK ==
+                 GNUNET_CONTAINER_multipeermap_remove (active_stccs,
+                                                       &stcc->target,
+                                                       stcc));
+  GNUNET_free (stcc);
+}
+
+
+/**
+ * Client asked for transmission to a peer.  Process the request.
+ *
+ * @param cls the client
+ * @param obm the send message that was sent
+ */
+static int
+check_client_send (void *cls,
+                  const struct OutboundMessage *obm)
+{
+  uint16_t size;
+  const struct GNUNET_MessageHeader *obmm;
+
+  size = ntohs (obm->header.size) - sizeof (struct OutboundMessage);
+  if (size < sizeof (struct GNUNET_MessageHeader))
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  obmm = (const struct GNUNET_MessageHeader *) &obm[1];
+  if (size != ntohs (obmm->size))
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Client asked for transmission to a peer.  Process the request.
+ *
+ * @param cls the client
+ * @param obm the send message that was sent
+ */
+static void
+handle_client_send (void *cls,
+                   const struct OutboundMessage *obm)
+{
+  static unsigned long long uuid_gen;
+  struct TransportClient *tc = cls;
+  const struct GNUNET_MessageHeader *obmm;
+  struct SendTransmitContinuationContext *stcc;
+
+  obmm = (const struct GNUNET_MessageHeader *) &obm[1];
+  if (GNUNET_NO == GST_neighbours_test_connected (&obm->peer))
+  {
+    /* not connected, not allowed to send; can happen due to asynchronous operations */
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Could not send message to peer `%s': not connected\n",
+                GNUNET_i2s (&obm->peer));
+    GNUNET_STATISTICS_update (GST_stats,
+                              gettext_noop
+                              ("# bytes payload dropped (other peer was not connected)"),
+                              ntohs (obmm->size),
+                             GNUNET_NO);
+    GNUNET_SERVICE_client_continue (tc->client);
+    return;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Received SEND request %llu for `%s' and first message of type %u and total size %u\n",
+              uuid_gen,
+              GNUNET_i2s (&obm->peer),
+              ntohs (obmm->type),
+              ntohs (obmm->size));
+  GNUNET_SERVICE_client_continue (tc->client);
+
+  stcc = GNUNET_new (struct SendTransmitContinuationContext);
+  stcc->target = obm->peer;
+  stcc->tc = tc;
+  stcc->send_time = GNUNET_TIME_absolute_get ();
+  stcc->uuid = uuid_gen++;
+  (void) GNUNET_CONTAINER_multipeermap_put (active_stccs,
+                                            &stcc->target,
+                                            stcc,
+                                            GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+  GST_manipulation_send (&obm->peer,
+                         obmm,
+                         ntohs (obmm->size),
+                         GNUNET_TIME_relative_ntoh (obm->timeout),
+                         &handle_send_transmit_continuation,
+                         stcc);
+}
+
+
+/**
+ * Take the given address and append it to the set of results sent back to
+ * the client.  This function may be called serveral times for a single
+ * conversion.   The last invocation will be with a @a address of
+ * NULL and a @a res of #GNUNET_OK.  Thus, to indicate conversion
+ * errors, the callback might be called first with @a address NULL and
+ * @a res being #GNUNET_SYSERR.  In that case, there will still be a
+ * subsequent call later with @a address NULL and @a res #GNUNET_OK.
+ *
+ * @param cls the `struct AddressToStringContext`
+ * @param buf text to transmit (contains the human-readable address, or NULL)
+ * @param res #GNUNET_OK if conversion was successful, #GNUNET_SYSERR on error,
+ *            never #GNUNET_NO
+ */
+static void
+transmit_address_to_client (void *cls,
+                            const char *buf,
+                            int res)
+{
+  struct AddressToStringContext *actx = cls;
+  struct GNUNET_MQ_Envelope *env;
+  struct AddressToStringResultMessage *atsm;
+  size_t slen;
+
+  GNUNET_assert ( (GNUNET_OK == res) ||
+                  (GNUNET_SYSERR == res) );
+  if (NULL == actx->tc)
+    return;
+  if (NULL == buf)
+  {
+    env = GNUNET_MQ_msg (atsm,
+                        GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_TO_STRING_REPLY);
+    if (GNUNET_OK == res)
+    {
+      /* this was the last call, transmit */
+      atsm->res = htonl (GNUNET_OK);
+      atsm->addr_len = htonl (0);
+      GNUNET_MQ_send (actx->tc->mq,
+                     env);
+      GNUNET_CONTAINER_DLL_remove (a2s_head,
+                                   a2s_tail,
+                                   actx);
+      GNUNET_free (actx);
+      return;
+    }
+    if (GNUNET_SYSERR == res)
+    {
+      /* address conversion failed, but there will be more callbacks */
+      atsm->res = htonl (GNUNET_SYSERR);
+      atsm->addr_len = htonl (0);
+      GNUNET_MQ_send (actx->tc->mq,
+                     env);
+      return;
+    }
+  }
+  GNUNET_assert (GNUNET_OK == res);
+  /* succesful conversion, append*/
+  slen = strlen (buf) + 1;
+  env = GNUNET_MQ_msg_extra (atsm,
+                            slen,
+                            GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_TO_STRING_REPLY);
+  atsm->res = htonl (GNUNET_YES);
+  atsm->addr_len = htonl (slen);
+  GNUNET_memcpy (&atsm[1],
+                buf,
+                slen);
+  GNUNET_MQ_send (actx->tc->mq,
+                 env);
+}
+
+
+/**
+ * Client asked to resolve an address.  Check the request.
+ *
+ * @param cls the client
+ * @param alum the resolution request
+ * @return #GNUNET_OK if @a alum is well-formed
+ */
+static int
+check_client_address_to_string (void *cls,
+                               const struct AddressLookupMessage *alum)
+{
+  const char *plugin_name;
+  const char *address;
+  uint32_t address_len;
+  uint16_t size;
+
+  size = ntohs (alum->header.size);
+  address_len = ntohs (alum->addrlen);
+  if (size <= sizeof (struct AddressLookupMessage) + address_len)
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  address = (const char *) &alum[1];
+  plugin_name = (const char *) &address[address_len];
+  if ('\0' != plugin_name[size - sizeof (struct AddressLookupMessage) - address_len - 1])
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Client asked to resolve an address.  Process the request.
+ *
+ * @param cls the client
+ * @param alum the resolution request
+ */
+static void
+handle_client_address_to_string (void *cls,
+                                const struct AddressLookupMessage *alum)
+{
+  struct TransportClient *tc = cls;
+  struct GNUNET_TRANSPORT_PluginFunctions *papi;
+  const char *plugin_name;
+  const char *address;
+  uint32_t address_len;
+  struct AddressToStringContext *actx;
+  struct GNUNET_MQ_Envelope *env;
+  struct AddressToStringResultMessage *atsm;
+  struct GNUNET_TIME_Relative rtimeout;
+  int32_t numeric;
+
+  address_len = ntohs (alum->addrlen);
+  address = (const char *) &alum[1];
+  plugin_name = (const char *) &address[address_len];
+  rtimeout = GNUNET_TIME_relative_ntoh (alum->timeout);
+  numeric = ntohs (alum->numeric_only);
+  papi = GST_plugins_printer_find (plugin_name);
+  if (NULL == papi)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Failed to find plugin `%s'\n",
+                plugin_name);
+    env = GNUNET_MQ_msg (atsm,
+                        GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_TO_STRING_REPLY);
+    atsm->res = htonl (GNUNET_SYSERR);
+    atsm->addr_len = htonl (0);
+    GNUNET_MQ_send (tc->mq,
+                   env);
+    env = GNUNET_MQ_msg (atsm,
+                        GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_TO_STRING_REPLY);
+    atsm->res = htonl (GNUNET_OK);
+    atsm->addr_len = htonl (0);
+    GNUNET_MQ_send (tc->mq,
+                   env);
+    return;
+  }
+  actx = GNUNET_new (struct AddressToStringContext);
+  actx->tc = tc;
+  GNUNET_CONTAINER_DLL_insert (a2s_head,
+                              a2s_tail,
+                              actx);
+  GNUNET_SERVICE_client_disable_continue_warning (tc->client);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Pretty-printing address of %u bytes using plugin `%s'\n",
+              address_len,
+              plugin_name);
+  papi->address_pretty_printer (papi->cls,
+                                plugin_name,
+                                address,
+                               address_len,
+                                numeric,
+                                rtimeout,
+                                &transmit_address_to_client,
+                                actx);
+}
+
+
+/**
+ * Compose #PeerIterateResponseMessage using the given peer and address.
+ *
+ * @param peer identity of the peer
+ * @param address the address, NULL on disconnect
+ * @return composed message
+ */
+static struct PeerIterateResponseMessage *
+compose_address_iterate_response_message (const struct GNUNET_PeerIdentity *peer,
+                                          const struct GNUNET_HELLO_Address *address)
 {
-  /**
-   * Kept in a DLL.
-   */
-  struct SessionKiller *next;
+  struct PeerIterateResponseMessage *msg;
+  size_t size;
+  size_t tlen;
+  size_t alen;
+  char *addr;
+
+  GNUNET_assert (NULL != peer);
+  if (NULL != address)
+  {
+    tlen = strlen (address->transport_name) + 1;
+    alen = address->address_length;
+  }
+  else
+  {
+    tlen = 0;
+    alen = 0;
+  }
+  size = (sizeof (struct PeerIterateResponseMessage) + alen + tlen);
+  msg = GNUNET_malloc (size);
+  msg->header.size = htons (size);
+  msg->header.type
+    = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_PEER_RESPONSE);
+  msg->reserved = htonl (0);
+  msg->peer = *peer;
+  msg->addrlen = htonl (alen);
+  msg->pluginlen = htonl (tlen);
+
+  if (NULL != address)
+  {
+    msg->local_address_info = htonl((uint32_t) address->local_info);
+    addr = (char *) &msg[1];
+    GNUNET_memcpy (addr,
+                  address->address,
+                  alen);
+    GNUNET_memcpy (&addr[alen],
+                  address->transport_name,
+                  tlen);
+  }
+  return msg;
+}
 
-  /**
-   * Kept in a DLL.
-   */
-  struct SessionKiller *prev;
 
+/**
+ * Context for #send_validation_information() and
+ * #send_peer_information().
+ */
+struct IterationContext
+{
   /**
-   * Session to kill.
+   * Context to use for the transmission.
    */
-  struct Session *session;
+  struct TransportClient *tc;
 
   /**
-   * Plugin for the session.
+   * Which peers do we care about?
    */
-  struct GNUNET_TRANSPORT_PluginFunctions *plugin;
+  struct GNUNET_PeerIdentity id;
 
   /**
-   * The kill task.
+   * #GNUNET_YES if @e id should be ignored because we want all peers.
    */
-  GNUNET_SCHEDULER_TaskIdentifier task;
+  int all;
 };
 
-/* globals */
 
 /**
- * Statistics handle.
+ * Output information of neighbours to the given client.
+ *
+ * @param cls the `struct PeerIterationContext *`
+ * @param peer identity of the neighbour
+ * @param address the address
+ * @param state current state this peer is in
+ * @param state_timeout timeout for the current state of the peer
+ * @param bandwidth_in inbound quota in NBO
+ * @param bandwidth_out outbound quota in NBO
  */
-struct GNUNET_STATISTICS_Handle *GST_stats;
+static void
+send_peer_information (void *cls,
+                       const struct GNUNET_PeerIdentity *peer,
+                       const struct GNUNET_HELLO_Address *address,
+                       enum GNUNET_TRANSPORT_PeerState state,
+                       struct GNUNET_TIME_Absolute state_timeout,
+                       struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
+                       struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
+{
+  struct IterationContext *pc = cls;
+  struct GNUNET_MQ_Envelope *env;
+  struct PeerIterateResponseMessage *msg;
+
+  if ( (GNUNET_YES != pc->all) &&
+       (0 != memcmp (peer,
+                    &pc->id,
+                    sizeof (pc->id))) )
+    return;
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Sending information about `%s' using address `%s' in state `%s'\n",
+              GNUNET_i2s(peer),
+              (NULL != address) ? GST_plugins_a2s (address) : "<none>",
+              GNUNET_TRANSPORT_ps2s (state));
+  msg = compose_address_iterate_response_message (peer,
+                                                 address);
+  msg->state = htonl (state);
+  msg->state_timeout = GNUNET_TIME_absolute_hton(state_timeout);
+  env = GNUNET_MQ_msg_copy (&msg->header);
+  GNUNET_free (msg);
+  GNUNET_MQ_send (pc->tc->mq,
+                 env);
+}
 
-/**
- * Configuration handle.
- */
-const struct GNUNET_CONFIGURATION_Handle *GST_cfg;
 
 /**
- * Configuration handle.
+ * Client asked to obtain information about a specific or all peers
+ * Process the request.
+ *
+ * @param cls the client
+ * @param msg the peer address information request
  */
-struct GNUNET_PeerIdentity GST_my_identity;
+static void
+handle_client_monitor_peers (void *cls,
+                            const struct PeerMonitorMessage *msg)
+{
+  struct TransportClient *tc = cls;
+  struct IterationContext pc;
 
-/**
- * Handle to peerinfo service.
- */
-struct GNUNET_PEERINFO_Handle *GST_peerinfo;
+  if (CT_NONE != tc->type)
+  {
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (tc->client);
+    return;
+  }
+  GNUNET_SERVICE_client_disable_continue_warning (tc->client);
+  GNUNET_SERVICE_client_mark_monitor (tc->client);
+
+  /* Send initial list */
+  pc.tc = tc;
+  if (0 == memcmp (&msg->peer,
+                   &all_zeros,
+                   sizeof (struct GNUNET_PeerIdentity)))
+  {
+    /* iterate over all neighbours */
+    pc.all = GNUNET_YES;
+    pc.id = msg->peer;
+  }
+  else
+  {
+    /* just return one neighbour */
+    pc.all = GNUNET_NO;
+    pc.id = msg->peer;
+  }
+  GST_neighbours_iterate (&send_peer_information,
+                          &pc);
+
+  if (GNUNET_YES != ntohl (msg->one_shot))
+  {
+    tc->details.monitor_peer = msg->peer;
+    tc->type = CT_MONITOR;
+    if (0 != memcmp (&msg->peer,
+                    &all_zeros,
+                    sizeof (struct GNUNET_PeerIdentity)))
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                 "Client %p started monitoring of the peer `%s'\n",
+                 tc,
+                 GNUNET_i2s (&msg->peer));
+    else
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                 "Client %p started monitoring all peers\n",
+                 tc);
+  }
+  else
+  {
+    struct GNUNET_MessageHeader *msg;
+    struct GNUNET_MQ_Envelope *env;
+
+    env = GNUNET_MQ_msg (msg,
+                        GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_PEER_RESPONSE_END);
+    GNUNET_MQ_send (tc->mq,
+                   env);
+  }
+}
 
-/**
- * Handle to our service's server.
- */
-static struct GNUNET_SERVER_Handle *GST_server;
 
 /**
- * Our private key.
+ * Function called by the plugin with information about the
+ * current sessions managed by the plugin (for monitoring).
+ *
+ * @param cls closure
+ * @param session session handle this information is about,
+ *        NULL to indicate that we are "in sync" (initial
+ *        iteration complete)
+ * @param info information about the state of the session,
+ *        NULL if @a session is also NULL and we are
+ *        merely signalling that the initial iteration is over
  */
-struct GNUNET_CRYPTO_EddsaPrivateKey *GST_my_private_key;
+static void
+plugin_session_info_cb (void *cls,
+                       struct GNUNET_ATS_Session *session,
+                       const struct GNUNET_TRANSPORT_SessionInfo *info)
+{
+  struct GNUNET_MQ_Envelope *env;
+  struct TransportPluginMonitorMessage *msg;
+  struct GNUNET_MessageHeader *sync;
+  size_t size;
+  size_t slen;
+  uint16_t alen;
+  char *name;
+  char *addr;
+
+  if (0 == GNUNET_notification_context_get_size (plugin_nc))
+  {
+    GST_plugins_monitor_subscribe (NULL,
+                                   NULL);
+    return;
+  }
+  if ( (NULL == info) &&
+       (NULL == session) )
+  {
+    /* end of initial iteration */
+    if (NULL != sync_client)
+    {
+      env = GNUNET_MQ_msg (sync,
+                          GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_PLUGIN_SYNC);
+      GNUNET_MQ_send (sync_client->mq,
+                     env);
+      sync_client = NULL;
+    }
+    return;
+  }
+  GNUNET_assert (NULL != info);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Plugin event for peer %s on transport %s\n",
+              GNUNET_i2s (&info->address->peer),
+              info->address->transport_name);
+  slen = strlen (info->address->transport_name) + 1;
+  alen = info->address->address_length;
+  size = sizeof (struct TransportPluginMonitorMessage) + slen + alen;
+  if (size > UINT16_MAX)
+  {
+    GNUNET_break (0);
+    return;
+  }
+  msg = GNUNET_malloc (size);
+  msg->header.size = htons (size);
+  msg->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_PLUGIN_EVENT);
+  msg->session_state = htons ((uint16_t) info->state);
+  msg->is_inbound = htons ((int16_t) info->is_inbound);
+  msg->msgs_pending = htonl (info->num_msg_pending);
+  msg->bytes_pending = htonl (info->num_bytes_pending);
+  msg->timeout = GNUNET_TIME_absolute_hton (info->session_timeout);
+  msg->delay = GNUNET_TIME_absolute_hton (info->receive_delay);
+  msg->peer = info->address->peer;
+  msg->session_id = (uint64_t) (intptr_t) session;
+  msg->plugin_name_len = htons (slen);
+  msg->plugin_address_len = htons (alen);
+  name = (char *) &msg[1];
+  GNUNET_memcpy (name,
+                info->address->transport_name,
+                slen);
+  addr = &name[slen];
+  GNUNET_memcpy (addr,
+          info->address->address,
+          alen);
+  if (NULL != sync_client)
+  {
+    struct GNUNET_MQ_Envelope *env;
+
+    env = GNUNET_MQ_msg_copy (&msg->header);
+    GNUNET_MQ_send (sync_client->mq,
+                   env);
+  }
+  else
+  {
+    GNUNET_notification_context_broadcast (plugin_nc,
+                                          &msg->header,
+                                          GNUNET_NO);
+  }
+  GNUNET_free (msg);
+}
+
 
 /**
- * ATS handle.
+ * Client asked to obtain information about all plugin connections.
+ *
+ * @param cls the client
+ * @param message the peer address information request
  */
-struct GNUNET_ATS_SchedulingHandle *GST_ats;
+static void
+handle_client_monitor_plugins (void *cls,
+                              const struct GNUNET_MessageHeader *message)
+{
+  struct TransportClient *tc = cls;
+
+  GNUNET_SERVICE_client_mark_monitor (tc->client);
+  GNUNET_SERVICE_client_disable_continue_warning (tc->client);
+  GNUNET_notification_context_add (plugin_nc,
+                                  tc->mq);
+  GNUNET_assert (NULL == sync_client);
+  sync_client = tc;
+  GST_plugins_monitor_subscribe (&plugin_session_info_cb,
+                                 NULL);
+}
+
 
 /**
- * Hello address expiration
+ * Broadcast the given message to all of our clients.
+ *
+ * @param msg message to broadcast
+ * @param may_drop #GNUNET_YES if the message can be dropped / is payload
  */
-struct GNUNET_TIME_Relative hello_expiration;
+void
+GST_clients_broadcast (const struct GNUNET_MessageHeader *msg,
+                       int may_drop)
+{
+  int done;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Asked to broadcast message of type %u with %u bytes\n",
+              (unsigned int) ntohs (msg->type),
+              (unsigned int) ntohs (msg->size));
+  done = GNUNET_NO;
+  for (struct TransportClient *tc = clients_head;
+       NULL != tc;
+       tc = tc->next)
+  {
+    if (CT_NONE == tc->type)
+      continue; /* client not yet ready */
+    if ( (GNUNET_YES == may_drop) &&
+         (CT_CORE != tc->type) )
+      continue; /* skip, this client does not care about payload */
+    unicast (tc,
+            msg,
+            may_drop);
+    done = GNUNET_YES;
+  }
+  if (GNUNET_NO == done)
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+               "Message of type %u not delivered, is CORE service up?\n",
+               ntohs (msg->type));
+}
+
 
 /**
- * DEBUGGING connection counter
+ * Broadcast the new active address to all clients monitoring the peer.
+ *
+ * @param peer peer this update is about (never NULL)
+ * @param address address, NULL on disconnect
+ * @param state the current state of the peer
+ * @param state_timeout the time out for the state
  */
-static int connections;
+void
+GST_clients_broadcast_peer_notification (const struct GNUNET_PeerIdentity *peer,
+                                         const struct GNUNET_HELLO_Address *address,
+                                         enum GNUNET_TRANSPORT_PeerState state,
+                                         struct GNUNET_TIME_Absolute state_timeout)
+{
+  struct GNUNET_MQ_Envelope *env;
+  struct PeerIterateResponseMessage *msg;
+
+  msg = compose_address_iterate_response_message (peer,
+                                                 address);
+  msg->state = htonl (state);
+  msg->state_timeout = GNUNET_TIME_absolute_hton (state_timeout);
+  for (struct TransportClient *tc = clients_head;
+       NULL != tc;
+       tc = tc->next)
+  {
+    if (CT_MONITOR != tc->type)
+      continue;
+    if ((0 == memcmp (&tc->details.monitor_peer,
+                     &all_zeros,
+                      sizeof (struct GNUNET_PeerIdentity))) ||
+        (0 == memcmp (&tc->details.monitor_peer,
+                     peer,
+                      sizeof (struct GNUNET_PeerIdentity))))
+    {
+      env = GNUNET_MQ_msg_copy (&msg->header);
+      GNUNET_MQ_send (tc->mq,
+                     env);
+    }
+  }
+  GNUNET_free (msg);
+}
+
 
 /**
- * Head of DLL of asynchronous tasks to kill sessions.
+ * Mark the peer as down so we don't call the continuation
+ * context in the future.
+ *
+ * @param cls NULL
+ * @param peer peer that got disconnected
+ * @param value a `struct SendTransmitContinuationContext` to mark
+ * @return #GNUNET_OK (continue to iterate)
  */
-static struct SessionKiller *sk_head;
+static int
+mark_peer_down (void *cls,
+                const struct GNUNET_PeerIdentity *peer,
+                void *value)
+{
+  struct SendTransmitContinuationContext *stcc = value;
+
+  stcc->down = GNUNET_YES;
+  return GNUNET_OK;
+}
+
 
 /**
- * Tail of DLL of asynchronous tasks to kill sessions.
+ * Notify all clients about a disconnect, and cancel
+ * pending SEND_OK messages for this peer.
+ *
+ * @param peer peer that disconnected
  */
-static struct SessionKiller *sk_tail;
+void
+GST_clients_broadcast_disconnect (const struct GNUNET_PeerIdentity *peer)
+{
+  struct DisconnectInfoMessage disconnect_msg;
+
+  GNUNET_CONTAINER_multipeermap_get_multiple (active_stccs,
+                                              peer,
+                                              &mark_peer_down,
+                                              NULL);
+  disconnect_msg.header.size = htons (sizeof(struct DisconnectInfoMessage));
+  disconnect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_DISCONNECT);
+  disconnect_msg.reserved = htonl (0);
+  disconnect_msg.peer = *peer;
+  GST_clients_broadcast (&disconnect_msg.header,
+                         GNUNET_NO);
+
+}
+
 
 /**
  * Transmit our HELLO message to the given (connected) neighbour.
  *
  * @param cls the 'HELLO' message
- * @param target a connected neighbour
+ * @param peer identity of the peer
  * @param address the address
  * @param state current state this peer is in
  * @param state_timeout timeout for the current state of the peer
@@ -140,22 +1479,33 @@ static struct SessionKiller *sk_tail;
  * @param bandwidth_out outbound quota in NBO
  */
 static void
-transmit_our_hello (void *cls, const struct GNUNET_PeerIdentity *target,
-    const struct GNUNET_HELLO_Address *address,
-    enum GNUNET_TRANSPORT_PeerState state,
-    struct GNUNET_TIME_Absolute state_timeout,
-    struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
-    struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
+transmit_our_hello (void *cls,
+                    const struct GNUNET_PeerIdentity *peer,
+                   const struct GNUNET_HELLO_Address *address,
+                   enum GNUNET_TRANSPORT_PeerState state,
+                   struct GNUNET_TIME_Absolute state_timeout,
+                   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
+                   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
 {
   const struct GNUNET_MessageHeader *hello = cls;
 
-  if (GNUNET_NO == GST_neighbours_test_connected (target))
+  if (0 ==
+      memcmp (peer,
+              &GST_my_identity,
+              sizeof (struct GNUNET_PeerIdentity)))
+    return; /* not to ourselves */
+  if (GNUNET_NO == GST_neighbours_test_connected (peer))
     return;
 
-  GST_neighbours_send (target, hello, ntohs (hello->size), hello_expiration,
-      NULL, NULL );
+  GST_neighbours_send (peer,
+                      hello,
+                      ntohs (hello->size),
+                      hello_expiration,
+                       NULL,
+                      NULL);
 }
 
+
 /**
  * My HELLO has changed. Tell everyone who should know.
  *
@@ -163,25 +1513,33 @@ transmit_our_hello (void *cls, const struct GNUNET_PeerIdentity *target,
  * @param hello new HELLO
  */
 static void
-process_hello_update (void *cls, const struct GNUNET_MessageHeader *hello)
+process_hello_update (void *cls,
+                      const struct GNUNET_MessageHeader *hello)
 {
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Broadcasting HELLO to clients\n");
   GST_clients_broadcast (hello, GNUNET_NO);
-  GST_neighbours_iterate (&transmit_our_hello, (void *) hello);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Broadcasting HELLO to neighbours\n");
+  GST_neighbours_iterate (&transmit_our_hello,
+                          (void *) hello);
 }
 
+
 /**
  * We received some payload.  Prepare to pass it on to our clients.
  *
- * @param peer (claimed) identity of the other peer
- * @param address the address
- * @param session session used
+ * @param address address and (claimed) identity of the other peer
+ * @param session identifier used for this session (NULL for plugins
+ *                that do not offer bi-directional communication to the sender
+ *                using the same "connection")
  * @param message the message to process
  * @return how long the plugin should wait until receiving more data
  */
 static struct GNUNET_TIME_Relative
-process_payload (const struct GNUNET_PeerIdentity *peer,
-    const struct GNUNET_HELLO_Address *address, struct Session *session,
-    const struct GNUNET_MessageHeader *message)
+process_payload (const struct GNUNET_HELLO_Address *address,
+                 struct GNUNET_ATS_Session *session,
+                 const struct GNUNET_MessageHeader *message)
 {
   struct GNUNET_TIME_Relative ret;
   int do_forward;
@@ -191,48 +1549,58 @@ process_payload (const struct GNUNET_PeerIdentity *peer,
   char buf[size] GNUNET_ALIGN;
 
   do_forward = GNUNET_SYSERR;
-  ret = GST_neighbours_calculate_receive_delay (peer, msg_size, &do_forward);
-  if (!GST_neighbours_test_connected (peer))
+  ret = GST_neighbours_calculate_receive_delay (&address->peer,
+                                               msg_size,
+                                               &do_forward);
+  if (! GST_neighbours_test_connected (&address->peer))
   {
-    GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
-        "Discarded %u bytes type %u payload from peer `%s'\n", msg_size,
-        ntohs (message->type), GNUNET_i2s (peer));
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Discarded %u bytes type %u payload from peer `%s'\n",
+                (unsigned int) msg_size,
+                ntohs (message->type),
+                GNUNET_i2s (&address->peer));
     GNUNET_STATISTICS_update (GST_stats, gettext_noop
-    ("# bytes payload discarded due to not connected peer"), msg_size,
-        GNUNET_NO);
+                              ("# bytes payload discarded due to not connected peer"),
+                              msg_size,
+                              GNUNET_NO);
     return ret;
   }
 
-  GST_ats_add_address (address, session, NULL, 0);
-
   if (GNUNET_YES != do_forward)
     return ret;
   im = (struct InboundMessage *) buf;
   im->header.size = htons (size);
   im->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_RECV);
-  im->peer = *peer;
-  memcpy (&im[1], message, ntohs (message->size));
-  GST_clients_broadcast (&im->header, GNUNET_YES);
+  im->peer = address->peer;
+  GNUNET_memcpy (&im[1],
+                message,
+                ntohs (message->size));
+  GST_clients_broadcast (&im->header,
+                        GNUNET_YES);
   return ret;
 }
 
+
 /**
  * Task to asynchronously terminate a session.
  *
- * @param cls the `struct SessionKiller` with the information for the kill
- * @param tc scheduler context
+ * @param cls the `struct GNUNET_ATS_SessionKiller` with the information for the kill
  */
 static void
-kill_session_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+kill_session_task (void *cls)
 {
-  struct SessionKiller *sk = cls;
-
-  sk->task = GNUNET_SCHEDULER_NO_TASK;
-  GNUNET_CONTAINER_DLL_remove(sk_head, sk_tail, sk);
-  sk->plugin->disconnect_session (sk->plugin->cls, sk->session);
+  struct GNUNET_ATS_SessionKiller *sk = cls;
+
+  sk->task = NULL;
+  GNUNET_CONTAINER_DLL_remove (sk_head,
+                              sk_tail,
+                              sk);
+  sk->plugin->disconnect_session (sk->plugin->cls,
+                                 sk->session);
   GNUNET_free(sk);
 }
 
+
 /**
  * Force plugin to terminate session due to communication
  * issue.
@@ -241,10 +1609,11 @@ kill_session_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  * @param session session to termiante
  */
 static void
-kill_session (const char *plugin_name, struct Session *session)
+kill_session (const char *plugin_name,
+              struct GNUNET_ATS_Session *session)
 {
   struct GNUNET_TRANSPORT_PluginFunctions *plugin;
-  struct SessionKiller *sk;
+  struct GNUNET_ATS_SessionKiller *sk;
 
   for (sk = sk_head; NULL != sk; sk = sk->next)
     if (sk->session == session)
@@ -256,36 +1625,84 @@ kill_session (const char *plugin_name, struct Session *session)
     return;
   }
   /* need to issue disconnect asynchronously */
-  sk = GNUNET_new (struct SessionKiller);
+  sk = GNUNET_new (struct GNUNET_ATS_SessionKiller);
   sk->session = session;
   sk->plugin = plugin;
-  sk->task = GNUNET_SCHEDULER_add_now (&kill_session_task, sk);
-  GNUNET_CONTAINER_DLL_insert(sk_head, sk_tail, sk);
+  sk->task = GNUNET_SCHEDULER_add_now (&kill_session_task,
+                                      sk);
+  GNUNET_CONTAINER_DLL_insert (sk_head,
+                               sk_tail,
+                               sk);
+}
+
+
+/**
+ * Black list check result for try_connect call
+ * If connection to the peer is allowed request adddress and ???
+ *
+ * @param cls the message
+ * @param peer the peer
+ * @param address the address
+ * @param session the session
+ * @param result the result
+ */
+static void
+connect_bl_check_cont (void *cls,
+                       const struct GNUNET_PeerIdentity *peer,
+                      const struct GNUNET_HELLO_Address *address,
+                      struct GNUNET_ATS_Session *session,
+                       int result)
+{
+  struct GNUNET_MessageHeader *msg = cls;
+
+  if (GNUNET_OK == result)
+  {
+    /* Blacklist allows to speak to this peer, forward SYN to neighbours  */
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Received SYN message from peer `%s' at `%s'\n",
+                GNUNET_i2s (peer),
+                GST_plugins_a2s (address));
+    if (GNUNET_OK !=
+        GST_neighbours_handle_session_syn (msg,
+                                           peer))
+    {
+      GST_blacklist_abort_matching (address,
+                                   session);
+      kill_session (address->transport_name,
+                    session);
+    }
+    GNUNET_free (msg);
+    return;
+  }
+  GNUNET_free (msg);
+  if (GNUNET_SYSERR == result)
+    return; /* check was aborted, session destroyed */
+  /* Blacklist denies to speak to this peer */
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+             "Discarding SYN message from `%s' due to denied blacklist check\n",
+             GNUNET_i2s (peer));
+  kill_session (address->transport_name,
+               session);
 }
 
+
 /**
  * Function called by the transport for each received message.
  *
  * @param cls closure, const char* with the name of the plugin we received the message from
- * @param peer (claimed) identity of the other peer
+ * @param address address and (claimed) identity of the other peer
  * @param message the message, NULL if we only care about
  *                learning about the delay until we should receive again
  * @param session identifier used for this session (NULL for plugins
  *                that do not offer bi-directional communication to the sender
  *                using the same "connection")
- * @param sender_address binary address of the sender (if we established the
- *                connection or are otherwise sure of it; should be NULL
- *                for inbound TCP/UDP connections since it it not clear
- *                that we could establish ourselves a connection to that
- *                IP address and get the same system)
- * @param sender_address_len number of bytes in @a sender_address
  * @return how long the plugin should wait until receiving more data
  *         (plugins that do not support this, can ignore the return value)
  */
 struct GNUNET_TIME_Relative
 GST_receive_callback (void *cls,
                       const struct GNUNET_HELLO_Address *address,
-                      struct Session *session,
+                      struct GNUNET_ATS_Session *session,
                       const struct GNUNET_MessageHeader *message)
 {
   const char *plugin_name = cls;
@@ -296,14 +1713,18 @@ GST_receive_callback (void *cls,
   if (NULL == message)
     goto end;
   type = ntohs (message->type);
-  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
-      "Received Message with type %u from peer `%s'\n", type,
-      GNUNET_i2s (&address->peer));
-
-  GNUNET_STATISTICS_update (GST_stats, gettext_noop
-  ("# bytes total received"), ntohs (message->size), GNUNET_NO);
-  GST_neighbours_notify_data_recv (&address->peer, address, session, message);
-
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Received message with type %u from peer `%s' at %s\n",
+              type,
+              GNUNET_i2s (&address->peer),
+              GST_plugins_a2s (address));
+
+  GNUNET_STATISTICS_update (GST_stats,
+                            gettext_noop ("# bytes total received"),
+                            ntohs (message->size),
+                            GNUNET_NO);
+  GST_neighbours_notify_data_recv (address,
+                                   message);
   switch (type)
   {
   case GNUNET_MESSAGE_TYPE_HELLO_LEGACY:
@@ -312,74 +1733,108 @@ GST_receive_callback (void *cls,
   case GNUNET_MESSAGE_TYPE_HELLO:
     if (GNUNET_OK != GST_validation_handle_hello (message))
     {
-      GNUNET_break_op(0);
-      kill_session (plugin_name, session);
+      GNUNET_break_op (0);
+      GST_blacklist_abort_matching (address,
+                                   session);
     }
     return ret;
   case GNUNET_MESSAGE_TYPE_TRANSPORT_PING:
-    GNUNET_log(GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
-        "Processing `%s' from `%s'\n", "PING", GST_plugins_a2s (address));
-    if (GNUNET_OK
-        != GST_validation_handle_ping (&address->peer, message, address, session))
-      kill_session (plugin_name, session);
-    break;
-  case GNUNET_MESSAGE_TYPE_TRANSPORT_PONG:
-    GNUNET_log(GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
-        "Processing `%s' from `%s'\n", "PONG",
-        GST_plugins_a2s (address));
-    if (GNUNET_OK != GST_validation_handle_pong (&address->peer, message))
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Processing PING from `%s'\n",
+                GST_plugins_a2s (address));
+    if (GNUNET_OK !=
+        GST_validation_handle_ping (&address->peer,
+                                    message,
+                                    address,
+                                    session))
     {
-      GNUNET_break_op(0);
-      kill_session (plugin_name, session);
+      GST_blacklist_abort_matching (address,
+                                   session);
+      kill_session (plugin_name,
+                    session);
     }
     break;
-  case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT:
-    if (GNUNET_OK
-        != GST_neighbours_handle_connect (message, &address->peer, address, session))
+  case GNUNET_MESSAGE_TYPE_TRANSPORT_PONG:
+    GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+               "Processing PONG from `%s'\n",
+               GST_plugins_a2s (address));
+    if (GNUNET_OK !=
+       GST_validation_handle_pong (&address->peer,
+                                   message))
     {
-      GNUNET_break_op(0);
+      GNUNET_break_op (0);
+      GST_blacklist_abort_matching (address,
+                                   session);
       kill_session (plugin_name, session);
     }
     break;
-  case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT_ACK:
-    if (GNUNET_OK
-        != GST_neighbours_handle_connect_ack (message, &address->peer, address, session))
+  case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN:
+    /* Do blacklist check if communication with this peer is allowed */
+    (void) GST_blacklist_test_allowed (&address->peer,
+                                      NULL,
+                                      &connect_bl_check_cont,
+                                      GNUNET_copy_message (message),
+                                      address,
+                                      session);
+    break;
+  case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN_ACK:
+    if (GNUNET_OK !=
+        GST_neighbours_handle_session_syn_ack (message,
+                                               address,
+                                               session))
     {
+      GST_blacklist_abort_matching (address, session);
       kill_session (plugin_name, session);
     }
     break;
   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK:
-    if (GNUNET_OK
-        != GST_neighbours_handle_session_ack (message, &address->peer, address, session))
+    if (GNUNET_OK !=
+        GST_neighbours_handle_session_ack (message,
+                                           address,
+                                           session))
     {
       GNUNET_break_op(0);
+      GST_blacklist_abort_matching (address, session);
       kill_session (plugin_name, session);
     }
     break;
   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT:
-    GST_neighbours_handle_disconnect_message (&address->peer, message);
+    GST_neighbours_handle_disconnect_message (&address->peer,
+                                              message);
+    break;
+  case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_QUOTA:
+    GST_neighbours_handle_quota_message (&address->peer,
+                                         message);
     break;
   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE:
-    GST_neighbours_keepalive (&address->peer, message);
+    GST_neighbours_keepalive (&address->peer,
+                              message);
     break;
   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE:
-    GST_neighbours_keepalive_response (&address->peer, message);
+    GST_neighbours_keepalive_response (&address->peer,
+                                       message);
     break;
   default:
     /* should be payload */
-    GNUNET_STATISTICS_update (GST_stats, gettext_noop
-    ("# bytes payload received"), ntohs (message->size), GNUNET_NO);
-    GST_neighbours_notify_payload_recv (&address->peer, address, session, message);
-    ret = process_payload (&address->peer, address, session, message);
+    GNUNET_STATISTICS_update (GST_stats,
+                              gettext_noop ("# bytes payload received"),
+                              ntohs (message->size),
+                              GNUNET_NO);
+    ret = process_payload (address,
+                           session,
+                           message);
     break;
   }
-  end:
-  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
-      "Allowing receive from peer %s to continue in %s\n", GNUNET_i2s (&address->peer),
-      GNUNET_STRINGS_relative_time_to_string (ret, GNUNET_YES));
+ end:
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Allowing receive from peer %s to continue in %s\n",
+              GNUNET_i2s (&address->peer),
+              GNUNET_STRINGS_relative_time_to_string (ret,
+                                                      GNUNET_YES));
   return ret;
 }
 
+
 /**
  * Function that will be called for each address the transport
  * is aware that it might be reachable under.  Update our HELLO.
@@ -390,12 +1845,43 @@ GST_receive_callback (void *cls,
  * @param address the address to add or remove
  */
 static void
-plugin_env_address_change_notification (void *cls, int add_remove,
-    const struct GNUNET_HELLO_Address *address)
+plugin_env_address_change_notification (void *cls,
+                                        int add_remove,
+                                        const struct GNUNET_HELLO_Address *address)
 {
-  GST_hello_modify_addresses (add_remove, address);
+  static int addresses = 0;
+
+  if (GNUNET_YES == add_remove)
+  {
+    addresses ++;
+    GNUNET_STATISTICS_update (GST_stats,
+                              "# transport addresses",
+                              1,
+                              GNUNET_NO);
+  }
+  else if (GNUNET_NO == add_remove)
+  {
+    if (0 == addresses)
+    {
+      GNUNET_break (0);
+    }
+    else
+    {
+      addresses --;
+      GNUNET_STATISTICS_update (GST_stats,
+                                "# transport addresses",
+                                -1,
+                                GNUNET_NO);
+    }
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Transport now has %u addresses to communicate\n",
+              addresses);
+  GST_hello_modify_addresses (add_remove,
+                              address);
 }
 
+
 /**
  * Function that will be called whenever the plugin internally
  * cleans up a session pointer and hence the service needs to
@@ -405,223 +1891,109 @@ plugin_env_address_change_notification (void *cls, int add_remove,
  * should be called BEFORE a potential "TransmitContinuation"
  * from the "TransmitFunction".
  *
- * @param cls closure
- * @param peer which peer was the session for
- * @param session which session is being destoyed
- */
-static void
-plugin_env_session_end (void *cls, const struct GNUNET_PeerIdentity *peer,
-    struct Session *session)
-{
-  const char *transport_name = cls;
-  struct GNUNET_HELLO_Address address;
-  struct SessionKiller *sk;
-
-  GNUNET_assert(strlen (transport_name) > 0);
-  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Session %p to peer `%s' ended \n",
-      session, GNUNET_i2s (peer));
-  if (NULL != session)
-    GNUNET_log_from(GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
-        "transport-ats", "Telling ATS to destroy session %p from peer %s\n",
-        session, GNUNET_i2s (peer));
-
-  memset (&address, '\0', sizeof (address));
-  address.peer = *peer;
-  address.address = NULL;
-  address.address_length = 0;
-  address.transport_name = transport_name;
-  GST_neighbours_session_terminated (peer, session);
-
-  /* Tell ATS that session has ended */
-  GNUNET_ATS_address_destroyed (GST_ats, &address, session);
-  for (sk = sk_head; NULL != sk; sk = sk->next)
-  {
-    if (sk->session == session)
-    {
-      GNUNET_CONTAINER_DLL_remove(sk_head, sk_tail, sk);
-      GNUNET_SCHEDULER_cancel (sk->task);
-      GNUNET_free(sk);
-      break;
-    }
-  }
-}
-
-/**
- * Function that will be called to figure if an address is an loopback,
- * LAN, WAN etc. address
- *
- * @param cls closure
- * @param addr binary address
- * @param addrlen length of the address
- * @return ATS Information containing the network type
- */
-static struct GNUNET_ATS_Information
-plugin_env_address_to_type (void *cls, const struct sockaddr *addr,
-    size_t addrlen)
-{
-  struct GNUNET_ATS_Information ats;
-
-  ats.type = htonl (GNUNET_ATS_NETWORK_TYPE);
-  ats.value = htonl (GNUNET_ATS_NET_UNSPECIFIED);
-  if (NULL == GST_ats)
-  {
-    GNUNET_break(0);
-    return ats;
-  }
-  if (((addr->sa_family != AF_INET) && (addrlen != sizeof(struct sockaddr_in)))
-      && ((addr->sa_family != AF_INET6)
-          && (addrlen != sizeof(struct sockaddr_in6)))
-      && (addr->sa_family != AF_UNIX))
-  {
-    GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
-        "Malformed address with length %u `%s'\n", addrlen,
-        GNUNET_a2s (addr, addrlen));
-    GNUNET_break(0);
-    return ats;
-  }
-  return GNUNET_ATS_address_get_type (GST_ats, addr, addrlen);
-}
-
-/**
- * Notify ATS about the new address including the network this address is
- * located in.
- *
- * @param address the address
- * @param session the session
- * @param ats ats information
- * @param ats_count number of @a ats information
- */
-void
-GST_ats_add_address (const struct GNUNET_HELLO_Address *address,
-    struct Session *session, const struct GNUNET_ATS_Information *ats,
-    uint32_t ats_count)
-{
-  struct GNUNET_TRANSPORT_PluginFunctions *papi;
-  struct GNUNET_ATS_Information ats2[ats_count + 1];
-  uint32_t net;
-
-  /* valid new address, let ATS know! */
-  if (NULL == address->transport_name)
-  {
-    GNUNET_break(0);
-    return;
-  }
-  if (NULL == (papi = GST_plugins_find (address->transport_name)))
-  {
-    /* we don't have the plugin for this address */
-    GNUNET_break(0);
-    return;
-  }
-
-  if (GNUNET_YES == GNUNET_ATS_session_known (GST_ats, address, session))
-  {
-    GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
-        "ATS already knows the address, not passing it on again\n");
-    return;
-  }
-
-  net = papi->get_network (NULL, session);
-  if (GNUNET_ATS_NET_UNSPECIFIED == net)
-  {
-    GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
-        _("Could not obtain a valid network for `%s' %s (%s)\n"),
-        GNUNET_i2s (&address->peer), GST_plugins_a2s (address),
-        address->transport_name);
-    GNUNET_break(0);
-  }
-  ats2[0].type = htonl (GNUNET_ATS_NETWORK_TYPE);
-  ats2[0].value = htonl (net);
-  memcpy (&ats2[1], ats, sizeof(struct GNUNET_ATS_Information) * ats_count);
-  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
-      "Notifying ATS about peer `%s''s new address `%s' session %p in network %s\n",
-      GNUNET_i2s (&address->peer),
-      (0 == address->address_length) ? "<inbound>" : GST_plugins_a2s (address),
-      session, GNUNET_ATS_print_network_type (net));
-  GNUNET_ATS_address_add (GST_ats, address, session, ats2, ats_count + 1);
-}
-
-/**
- * Notify ATS about property changes to an address
- *
- * @param peer the peer
- * @param address the address
- * @param session the session
- * @param ats performance information
- * @param ats_count number of elements in @a ats
+ * @param cls closure
+ * @param address which address was the session for
+ * @param session which session is being destoyed
  */
-void
-GST_ats_update_metrics (const struct GNUNET_PeerIdentity *peer,
-    const struct GNUNET_HELLO_Address *address, struct Session *session,
-    const struct GNUNET_ATS_Information *ats, uint32_t ats_count)
+static void
+plugin_env_session_end (void *cls,
+                        const struct GNUNET_HELLO_Address *address,
+                        struct GNUNET_ATS_Session *session)
 {
-  struct GNUNET_ATS_Information *ats_new;
+  struct GNUNET_ATS_SessionKiller *sk;
 
-  if (GNUNET_NO == GNUNET_ATS_session_known (GST_ats, address, session))
+  if (NULL == address)
+  {
+    GNUNET_break (0);
     return;
-
-  /* Call to manipulation to manipulate ATS information */
-  ats_new = GST_manipulation_manipulate_metrics (peer, address, session, ats,
-      ats_count);
-  if (NULL == ats_new)
+  }
+  if (NULL == session)
   {
-    GNUNET_break(0);
+    GNUNET_break (0);
     return;
   }
-  if (GNUNET_NO == GNUNET_ATS_address_update (GST_ats, address, session,
-        ats_new, ats_count))
+  GNUNET_assert (strlen (address->transport_name) > 0);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Notification from plugin about terminated session %p from peer `%s' address `%s'\n",
+              session,
+              GNUNET_i2s (&address->peer),
+              GST_plugins_a2s (address));
+
+  GST_neighbours_session_terminated (&address->peer,
+                                    session);
+  GST_ats_del_session (address,
+                       session);
+  GST_blacklist_abort_matching (address,
+                               session);
+
+  for (sk = sk_head; NULL != sk; sk = sk->next)
   {
-    GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
-        _("Address or session unknown: failed to update properties for peer `%s' plugin `%s' address `%s' session %p\n"),
-        GNUNET_i2s (peer), address->transport_name, GST_plugins_a2s (address),
-        session);
+    if (sk->session == session)
+    {
+      GNUNET_CONTAINER_DLL_remove (sk_head,
+                                  sk_tail,
+                                  sk);
+      GNUNET_SCHEDULER_cancel (sk->task);
+      GNUNET_free(sk);
+      break;
+    }
   }
-  GNUNET_free(ats_new);
 }
 
+
 /**
- * Function that will be called to figure if an address is an loopback,
- * LAN, WAN etc. address
+ * Black list check result from blacklist check triggered when a
+ * plugin gave us a new session in #plugin_env_session_start().  If
+ * connection to the peer is disallowed, kill the session.
  *
- * @param cls closure
+ * @param cls NULL
  * @param peer the peer
- * @param address binary address
- * @param address_len length of the @a address
- * @param session the session
- * @param ats the ats information to update
- * @param ats_count the number of @a ats elements
+ * @param address address associated with the request
+ * @param session session associated with the request
+ * @param result the result
  */
 static void
-plugin_env_update_metrics (void *cls,
-    const struct GNUNET_HELLO_Address *address,
-    struct Session *session,
-    const struct GNUNET_ATS_Information *ats,
-    uint32_t ats_count)
+plugin_env_session_start_bl_check_cont (void *cls,
+                                        const struct GNUNET_PeerIdentity *peer,
+                                       const struct GNUNET_HELLO_Address *address,
+                                       struct GNUNET_ATS_Session *session,
+                                        int result)
 {
-  if ((NULL == ats) || (0 == ats_count))
+  if (GNUNET_OK != result)
+  {
+    kill_session (address->transport_name,
+                  session);
     return;
-  GNUNET_assert(NULL != GST_ats);
-
-  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
-      "Updating metrics for peer `%s' address %s session %p\n",
-      GNUNET_i2s (&address->peer), GST_plugins_a2s (address), session);
-  GST_ats_update_metrics (&address->peer, address, session, ats, ats_count);
+  }
+  if (GNUNET_YES !=
+      GNUNET_HELLO_address_check_option (address,
+                                        GNUNET_HELLO_ADDRESS_INFO_INBOUND))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Informing verifier about inbound session's address `%s'\n",
+                GST_plugins_a2s (address));
+    GST_validation_handle_address (address);
+  }
 }
 
+
 /**
  * Plugin tells transport service about a new inbound session
  *
  * @param cls unused
  * @param address the address
  * @param session the new session
- * @param ats ats information
- * @param ats_count number of @a ats information
+ * @param scope network scope information
  */
 static void
-plugin_env_session_start (void *cls, struct GNUNET_HELLO_Address *address,
-    struct Session *session, const struct GNUNET_ATS_Information *ats,
-    uint32_t ats_count)
+plugin_env_session_start (void *cls,
+                          const struct GNUNET_HELLO_Address *address,
+                          struct GNUNET_ATS_Session *session,
+                          enum GNUNET_NetworkType scope)
 {
-  int inbound;
+  struct GNUNET_ATS_Properties prop;
+
   if (NULL == address)
   {
     GNUNET_break(0);
@@ -632,20 +2004,38 @@ plugin_env_session_start (void *cls, struct GNUNET_HELLO_Address *address,
     GNUNET_break(0);
     return;
   }
-
-  if (GNUNET_HELLO_ADDRESS_INFO_INBOUND
-      == (address->local_info & GNUNET_HELLO_ADDRESS_INFO_INBOUND))
-    inbound = GNUNET_YES;
-  else
-    inbound = GNUNET_NO;
-
-  GNUNET_log(GNUNET_ERROR_TYPE_INFO,
-      "Notification from plugin `%s' about new %ssession %p from peer `%s' address `%s'\n",
-      address->transport_name, (GNUNET_YES == inbound) ? "inbound " : "",
-      session, GNUNET_i2s (&address->peer), GST_plugins_a2s (address));
-  GST_ats_add_address (address, session, ats, ats_count);
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Notification from plugin `%s' about new session from peer `%s' address `%s'\n",
+              address->transport_name,
+              GNUNET_i2s (&address->peer),
+              GST_plugins_a2s (address));
+  if (GNUNET_YES ==
+      GNUNET_HELLO_address_check_option (address,
+                                         GNUNET_HELLO_ADDRESS_INFO_INBOUND))
+  {
+    /* inbound is always new, but outbound MAY already be known, but
+       for example for UNIX, we have symmetric connections and thus we
+       may not know the address yet; add if necessary! */
+    /* FIXME: maybe change API here so we just pass scope? */
+    memset (&prop,
+           0,
+           sizeof (prop));
+    GNUNET_break (GNUNET_NT_UNSPECIFIED != scope);
+    prop.scope = scope;
+    GST_ats_add_inbound_address (address,
+                                 session,
+                                 &prop);
+  }
+  /* Do blacklist check if communication with this peer is allowed */
+  (void) GST_blacklist_test_allowed (&address->peer,
+                                    address->transport_name,
+                                    &plugin_env_session_start_bl_check_cont,
+                                    NULL,
+                                    address,
+                                    session);
 }
 
+
 /**
  * Function called by ATS to notify the callee that the
  * assigned bandwidth or address for a given peer was changed.  If the
@@ -654,6 +2044,7 @@ plugin_env_session_start (void *cls, struct GNUNET_HELLO_Address *address,
  * actually happened.
  *
  * @param cls closure
+ * @param peer the peer this address is intended for
  * @param address address to use (for peer given in address)
  * @param session session to use (if available)
  * @param bandwidth_out assigned outbound bandwidth for the connection in NBO,
@@ -664,135 +2055,251 @@ plugin_env_session_start (void *cls, struct GNUNET_HELLO_Address *address,
  * @param ats_count number of @a ats elements
  */
 static void
-ats_request_address_change (void *cls, const struct GNUNET_PeerIdentity *peer,
-    const struct GNUNET_HELLO_Address *address, struct Session *session,
-    struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
-    struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
-    const struct GNUNET_ATS_Information *ats, uint32_t ats_count)
+ats_request_address_change (void *cls,
+                            const struct GNUNET_PeerIdentity *peer,
+                            const struct GNUNET_HELLO_Address *address,
+                            struct GNUNET_ATS_Session *session,
+                            struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
+                            struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
 {
   uint32_t bw_in = ntohl (bandwidth_in.value__);
   uint32_t bw_out = ntohl (bandwidth_out.value__);
 
+  if (NULL == peer)
+  {
+    /* ATS service died, all suggestions become invalid!
+       (but we'll keep using the allocations for a little
+       while, to keep going while ATS restarts) */
+    /* FIXME: We should drop all
+       connections now, as ATS won't explicitly tell
+       us and be unaware of ongoing resource allocations! */
+    return;
+  }
   /* ATS tells me to disconnect from peer */
   if ((0 == bw_in) && (0 == bw_out))
   {
-    GNUNET_log(GNUNET_ERROR_TYPE_INFO,
-        "ATS tells me to disconnect from peer `%s'\n",
-        GNUNET_i2s (&address->peer));
-    GST_neighbours_force_disconnect (&address->peer);
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "ATS tells me to disconnect from peer `%s'\n",
+                GNUNET_i2s (peer));
+    GST_neighbours_force_disconnect (peer);
     return;
   }
-
-  GST_neighbours_switch_to_address (&address->peer, address, session, ats,
-      ats_count, bandwidth_in, bandwidth_out);
+  GNUNET_assert (NULL != address);
+  GNUNET_STATISTICS_update (GST_stats,
+                            "# ATS suggestions received",
+                            1,
+                            GNUNET_NO);
+  GST_neighbours_switch_to_address (address,
+                                    session,
+                                    bandwidth_in,
+                                    bandwidth_out);
 }
 
+
+/**
+ * Closure for #test_connection_ok().
+ */
+struct TestConnectionContext
+{
+  /**
+   * Is this the first neighbour we're checking?
+   */
+  int first;
+
+  /**
+   * Handle to the blacklisting client we need to ask.
+   */
+  struct TransportClient *tc;
+};
+
+
 /**
- * Function called to notify transport users that another
- * peer connected to us.
+ * Got the result about an existing connection from a new blacklister.
+ * Shutdown the neighbour if necessary.
  *
- * @param cls closure
- * @param peer the peer that connected
- * @param bandwidth_in inbound bandwidth in NBO
- * @param bandwidth_out outbound bandwidth in NBO
+ * @param cls unused
+ * @param peer the neighbour that was investigated
+ * @param address address associated with the request
+ * @param session session associated with the request
+ * @param allowed #GNUNET_OK if we can keep it,
+ *                #GNUNET_NO if we must shutdown the connection
  */
 static void
-neighbours_connect_notification (void *cls,
-    const struct GNUNET_PeerIdentity *peer,
-    struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
-    struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
+confirm_or_drop_neighbour (void *cls,
+                           const struct GNUNET_PeerIdentity *peer,
+                          const struct GNUNET_HELLO_Address *address,
+                          struct GNUNET_ATS_Session *session,
+                           int allowed)
 {
-  size_t len = sizeof(struct ConnectInfoMessage);
-  char buf[len] GNUNET_ALIGN;
-  struct ConnectInfoMessage *connect_msg = (struct ConnectInfoMessage *) buf;
+  if (GNUNET_OK == allowed)
+    return;                     /* we're done */
+  GNUNET_STATISTICS_update (GST_stats,
+                            gettext_noop ("# disconnects due to blacklist"),
+                           1,
+                            GNUNET_NO);
+  GST_neighbours_force_disconnect (peer);
+}
 
-  connections++;
-  GNUNET_log(GNUNET_ERROR_TYPE_INFO,
-      "We are now connected to peer `%s' and %u peers in total\n",
-      GNUNET_i2s (peer), connections);
-  connect_msg->header.size = htons (sizeof(buf));
-  connect_msg->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT);
-  connect_msg->id = *peer;
-  connect_msg->quota_in = bandwidth_in;
-  connect_msg->quota_out = bandwidth_out;
-  GST_clients_broadcast (&connect_msg->header, GNUNET_NO);
+
+/**
+ * Test if an existing connection is still acceptable given a new
+ * blacklisting client.
+ *
+ * @param cls the `struct TestConnectionContext *`
+ * @param peer identity of the peer
+ * @param address the address
+ * @param state current state this peer is in
+ * @param state_timeout timeout for the current state of the peer
+ * @param bandwidth_in bandwidth assigned inbound
+ * @param bandwidth_out bandwidth assigned outbound
+ */
+static void
+test_connection_ok (void *cls,
+                    const struct GNUNET_PeerIdentity *peer,
+                   const struct GNUNET_HELLO_Address *address,
+                   enum GNUNET_TRANSPORT_PeerState state,
+                   struct GNUNET_TIME_Absolute state_timeout,
+                   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
+                   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
+{
+  struct TestConnectionContext *tcc = cls;
+  struct GST_BlacklistCheck *bc;
+
+  bc = GNUNET_new (struct GST_BlacklistCheck);
+  GNUNET_CONTAINER_DLL_insert (bc_head,
+                              bc_tail,
+                              bc);
+  bc->peer = *peer;
+  bc->address = GNUNET_HELLO_address_copy (address);
+  bc->cont = &confirm_or_drop_neighbour;
+  bc->cont_cls = NULL;
+  bc->bl_pos = tcc->tc;
+  if (GNUNET_YES == tcc->first)
+  {
+    /* all would wait for the same client, no need to
+     * create more than just the first task right now */
+    bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
+                                        bc);
+    tcc->first = GNUNET_NO;
+  }
 }
 
+
 /**
- * Function called to notify transport users that another
- * peer disconnected from us.
+ * Initialize a blacklisting client.  We got a blacklist-init
+ * message from this client, add it to the list of clients
+ * to query for blacklisting.
  *
- * @param cls closure
- * @param peer the peer that disconnected
+ * @param cls the client
+ * @param message the blacklist-init message that was sent
  */
 static void
-neighbours_disconnect_notification (void *cls,
-    const struct GNUNET_PeerIdentity *peer)
+handle_client_blacklist_init (void *cls,
+                             const struct GNUNET_MessageHeader *message)
 {
-  struct DisconnectInfoMessage disconnect_msg;
+  struct TransportClient *tc = cls;
+  struct TestConnectionContext tcc;
 
-  connections--;
-  GNUNET_log(GNUNET_ERROR_TYPE_INFO,
-      "Peer `%s' disconnected and we are connected to %u peers\n",
-      GNUNET_i2s (peer), connections);
+  if (CT_NONE != tc->type)
+  {
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (tc->client);
+    return;
+  }
+  GNUNET_SERVICE_client_mark_monitor (tc->client);
+  tc->type = CT_BLACKLIST;
+  tc->details.blacklist.call_receive_done = GNUNET_YES;
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "New blacklist client %p\n",
+              tc);
+  /* confirm that all existing connections are OK! */
+  tcc.tc = tc;
+  tcc.first = GNUNET_YES;
+  GST_neighbours_iterate (&test_connection_ok,
+                         &tcc);
+}
 
-  GST_manipulation_peer_disconnect (peer);
-  disconnect_msg.header.size = htons (sizeof(struct DisconnectInfoMessage));
-  disconnect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_DISCONNECT);
-  disconnect_msg.reserved = htonl (0);
-  disconnect_msg.peer = *peer;
-  GST_clients_broadcast (&disconnect_msg.header, GNUNET_NO);
+
+/**
+ * Free the given entry in the blacklist.
+ *
+ * @param cls unused
+ * @param key host identity (unused)
+ * @param value the blacklist entry
+ * @return #GNUNET_OK (continue to iterate)
+ */
+static int
+free_blacklist_entry (void *cls,
+                     const struct GNUNET_PeerIdentity *key,
+                     void *value)
+{
+  char *be = value;
+
+  GNUNET_free_non_null (be);
+  return GNUNET_OK;
 }
 
+
 /**
- * Function called to notify transport users that a neighbour peer changed its
- * active address.
+ * Set traffic metric to manipulate
  *
  * @param cls closure
- * @param peer peer this update is about (never NULL)
- * @param address address, NULL on disconnect
- * @param state current state this peer is in
- * @param state_timeout timeout for the current state of the peer
- * @param bandwidth_in bandwidth assigned inbound
- * @param bandwidth_out bandwidth assigned outbound
+ * @param message containing information
  */
 static void
-neighbours_changed_notification (void *cls,
-    const struct GNUNET_PeerIdentity *peer,
-    const struct GNUNET_HELLO_Address *address,
-    enum GNUNET_TRANSPORT_PeerState state,
-    struct GNUNET_TIME_Absolute state_timeout,
-    struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
-    struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
+handle_client_set_metric (void *cls,
+                         const struct TrafficMetricMessage *tm)
 {
-  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
-      "Notifying about change for peer `%s' with address `%s' in state `%s' timing out at %s\n",
-      GNUNET_i2s (peer),
-      (NULL != address) ? GST_plugins_a2s (address) : "<none>",
-      GNUNET_TRANSPORT_p2s (state),
-      GNUNET_STRINGS_absolute_time_to_string (state_timeout));
+  struct TransportClient *tc = cls;
 
-  GST_clients_broadcast_peer_notification (peer, address, state, state_timeout);
+  GST_manipulation_set_metric (tm);
+  GNUNET_SERVICE_client_continue (tc->client);
 }
 
+
 /**
  * Function called when the service shuts down.  Unloads our plugins
  * and cancels pending validations.
  *
  * @param cls closure, unused
- * @param tc task context (unused)
  */
 static void
-shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+shutdown_task (void *cls)
 {
+  struct AddressToStringContext *cur;
+
   GST_neighbours_stop ();
-  GST_validation_stop ();
   GST_plugins_unload ();
-
+  GST_validation_stop ();
+  GST_ats_done ();
   GNUNET_ATS_scheduling_done (GST_ats);
   GST_ats = NULL;
-  GST_clients_stop ();
-  GST_blacklist_stop ();
+  GNUNET_ATS_connectivity_done (GST_ats_connect);
+  GST_ats_connect = NULL;
+  GNUNET_NT_scanner_done (GST_is);
+  GST_is = NULL;
+  while (NULL != (cur = a2s_head))
+  {
+    GNUNET_CONTAINER_DLL_remove (a2s_head,
+                                a2s_tail,
+                                cur);
+    GNUNET_free (cur);
+  }
+  if (NULL != plugin_nc)
+  {
+    GNUNET_notification_context_destroy (plugin_nc);
+    plugin_nc = NULL;
+  }
+  GNUNET_CONTAINER_multipeermap_destroy (active_stccs);
+  active_stccs = NULL;
+  if (NULL != blacklist)
+  {
+    GNUNET_CONTAINER_multipeermap_iterate (blacklist,
+                                          &free_blacklist_entry,
+                                          NULL);
+    GNUNET_CONTAINER_multipeermap_destroy (blacklist);
+    blacklist = NULL;
+  }
   GST_hello_stop ();
   GST_manipulation_stop ();
 
@@ -808,22 +2315,466 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   }
   if (NULL != GST_my_private_key)
   {
-    GNUNET_free(GST_my_private_key);
+    GNUNET_free (GST_my_private_key);
     GST_my_private_key = NULL;
   }
-  GST_server = NULL;
 }
 
+
+/**
+ * Perform next action in the blacklist check.
+ *
+ * @param cls the `struct GST_BlacklistCheck *`
+ */
+static void
+do_blacklist_check (void *cls)
+{
+  struct GST_BlacklistCheck *bc = cls;
+  struct TransportClient *tc;
+  struct GNUNET_MQ_Envelope *env;
+  struct BlacklistMessage *bm;
+
+  bc->task = NULL;
+  while (NULL != (tc = bc->bl_pos))
+  {
+    if (CT_BLACKLIST == tc->type)
+      break;
+    bc->bl_pos = tc->next;
+  }
+  if (NULL == tc)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "No other blacklist clients active, will allow neighbour `%s'\n",
+                GNUNET_i2s (&bc->peer));
+
+    bc->cont (bc->cont_cls,
+             &bc->peer,
+             bc->address,
+             bc->session,
+             GNUNET_OK);
+    GST_blacklist_test_cancel (bc);
+    return;
+  }
+  if ( (NULL != tc->details.blacklist.bc) ||
+       (GNUNET_NO != tc->details.blacklist.waiting_for_reply) )
+    return;                     /* someone else busy with this client */
+  tc->details.blacklist.bc = bc;
+  env = GNUNET_MQ_msg (bm,
+                      GNUNET_MESSAGE_TYPE_TRANSPORT_BLACKLIST_QUERY);
+  bm->is_allowed = htonl (0);
+  bm->peer = bc->peer;
+  GNUNET_MQ_send (tc->mq,
+                 env);
+  if (GNUNET_YES == tc->details.blacklist.call_receive_done)
+  {
+    tc->details.blacklist.call_receive_done = GNUNET_NO;
+    GNUNET_SERVICE_client_continue (tc->client);
+  }
+  tc->details.blacklist.waiting_for_reply = GNUNET_YES;
+}
+
+
+/**
+ * A blacklisting client has sent us reply. Process it.
+ *
+ * @param cls the client
+ * @param msg the blacklist-reply message that was sent
+ */
+static void
+handle_client_blacklist_reply (void *cls,
+                              const struct BlacklistMessage *msg)
+{
+  struct TransportClient *tc = cls;
+  struct GST_BlacklistCheck *bc;
+
+  if (CT_BLACKLIST != tc->type)
+  {
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (tc->client);
+    return;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Blacklist client %p sent reply for `%s'\n",
+              tc,
+              GNUNET_i2s (&msg->peer));
+  bc = tc->details.blacklist.bc;
+  tc->details.blacklist.bc = NULL;
+  tc->details.blacklist.waiting_for_reply = GNUNET_NO;
+  tc->details.blacklist.call_receive_done = GNUNET_YES;
+  if (NULL != bc)
+  {
+    /* only run this if the blacklist check has not been
+     * cancelled in the meantime... */
+    GNUNET_assert (bc->bl_pos == tc);
+    if (ntohl (msg->is_allowed) == GNUNET_SYSERR)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "Blacklist check failed, peer not allowed\n");
+      /* For the duration of the continuation, make the ongoing
+        check invisible (to avoid double-cancellation); then
+        add it back again so we can re-use GST_blacklist_test_cancel() */
+      GNUNET_CONTAINER_DLL_remove (bc_head,
+                                  bc_tail,
+                                  bc);
+      bc->cont (bc->cont_cls,
+               &bc->peer,
+               bc->address,
+               bc->session,
+               GNUNET_NO);
+      GNUNET_CONTAINER_DLL_insert (bc_head,
+                                  bc_tail,
+                                  bc);
+      GST_blacklist_test_cancel (bc);
+      tc->details.blacklist.call_receive_done = GNUNET_NO;
+      GNUNET_SERVICE_client_continue (tc->client);
+      return;
+    }
+    else
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "Blacklist check succeeded, continuing with checks\n");
+      tc->details.blacklist.call_receive_done = GNUNET_NO;
+      GNUNET_SERVICE_client_continue (tc->client);
+      bc->bl_pos = tc->next;
+      bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
+                                          bc);
+    }
+  }
+  /* check if any other blacklist checks are waiting for this blacklister */
+  for (bc = bc_head; bc != NULL; bc = bc->next)
+    if ( (bc->bl_pos == tc) &&
+        (NULL == bc->task) )
+    {
+      bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
+                                          bc);
+      break;
+    }
+}
+
+
+/**
+ * Add the given peer to the blacklist (for the given transport).
+ *
+ * @param peer peer to blacklist
+ * @param transport_name transport to blacklist for this peer, NULL for all
+ */
+void
+GST_blacklist_add_peer (const struct GNUNET_PeerIdentity *peer,
+                        const char *transport_name)
+{
+  char *transport = NULL;
+
+  if (NULL != transport_name)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+               "Adding peer `%s' with plugin `%s' to blacklist\n",
+               GNUNET_i2s (peer),
+               transport_name);
+    transport = GNUNET_strdup (transport_name);
+  }
+  else
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+               "Adding peer `%s' with all plugins to blacklist\n",
+               GNUNET_i2s (peer));
+  if (NULL == blacklist)
+    blacklist =
+      GNUNET_CONTAINER_multipeermap_create (TRANSPORT_BLACKLIST_HT_SIZE,
+                                           GNUNET_NO);
+
+  GNUNET_CONTAINER_multipeermap_put (blacklist,
+                                    peer,
+                                     transport,
+                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+}
+
+
+/**
+ * Abort blacklist if @a address and @a session match.
+ *
+ * @param address address used to abort matching checks
+ * @param session session used to abort matching checks
+ */
+void
+GST_blacklist_abort_matching (const struct GNUNET_HELLO_Address *address,
+                             struct GNUNET_ATS_Session *session)
+{
+  struct GST_BlacklistCheck *bc;
+  struct GST_BlacklistCheck *n;
+
+  n = bc_head;
+  while (NULL != (bc = n))
+  {
+    n = bc->next;
+    if ( (bc->session == session) &&
+        (0 == GNUNET_HELLO_address_cmp (bc->address,
+                                        address)) )
+    {
+      bc->cont (bc->cont_cls,
+               &bc->peer,
+               bc->address,
+               bc->session,
+               GNUNET_SYSERR);
+      GST_blacklist_test_cancel (bc);
+    }
+  }
+}
+
+
+/**
+ * Test if the given blacklist entry matches.  If so,
+ * abort the iteration.
+ *
+ * @param cls the transport name to match (const char*)
+ * @param key the key (unused)
+ * @param value the 'char *' (name of a blacklisted transport)
+ * @return #GNUNET_OK if the entry does not match, #GNUNET_NO if it matches
+ */
+static int
+test_blacklisted (void *cls,
+                 const struct GNUNET_PeerIdentity *key,
+                 void *value)
+{
+  const char *transport_name = cls;
+  char *be = value;
+
+  /* Blacklist entry be:
+   *  (NULL == be): peer is blacklisted with all plugins
+   *  (NULL != be): peer is blacklisted for a specific plugin
+   *
+   * If (NULL != transport_name) we look for a transport specific entry:
+   *  if (transport_name == be) forbidden
+   *
+   */
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Comparing BL request for peer `%4s':`%s' with BL entry: `%s'\n",
+             GNUNET_i2s (key),
+             (NULL == transport_name) ? "unspecified" : transport_name,
+             (NULL == be) ? "all plugins" : be);
+  /* all plugins for this peer were blacklisted: disallow */
+  if (NULL == value)
+    return GNUNET_NO;
+
+  /* blacklist check for specific transport */
+  if ( (NULL != transport_name) &&
+       (NULL != value) )
+  {
+    if (0 == strcmp (transport_name,
+                    be))
+      return GNUNET_NO;           /* plugin is blacklisted! */
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Test if a peer/transport combination is blacklisted.
+ *
+ * @param peer the identity of the peer to test
+ * @param transport_name name of the transport to test, never NULL
+ * @param cont function to call with result
+ * @param cont_cls closure for @a cont
+ * @param address address to pass back to @a cont, can be NULL
+ * @param session session to pass back to @a cont, can be NULL
+ * @return handle to the blacklist check, NULL if the decision
+ *        was made instantly and @a cont was already called
+ */
+struct GST_BlacklistCheck *
+GST_blacklist_test_allowed (const struct GNUNET_PeerIdentity *peer,
+                            const char *transport_name,
+                            GST_BlacklistTestContinuation cont,
+                            void *cont_cls,
+                           const struct GNUNET_HELLO_Address *address,
+                           struct GNUNET_ATS_Session *session)
+{
+  struct GST_BlacklistCheck *bc;
+  struct TransportClient *tc;
+
+  GNUNET_assert (NULL != peer);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Blacklist check for peer `%s':%s\n",
+              GNUNET_i2s (peer),
+              (NULL != transport_name) ? transport_name : "unspecified");
+
+  /* Check local blacklist by iterating over hashmap
+   * If iteration is aborted, we found a matching blacklist entry */
+  if ((NULL != blacklist) &&
+      (GNUNET_SYSERR ==
+       GNUNET_CONTAINER_multipeermap_get_multiple (blacklist, peer,
+                                                   &test_blacklisted,
+                                                   (void *) transport_name)))
+  {
+    /* Disallowed by config, disapprove instantly */
+    GNUNET_STATISTICS_update (GST_stats,
+                              gettext_noop ("# disconnects due to blacklist"),
+                              1,
+                             GNUNET_NO);
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                _("Disallowing connection to peer `%s' on transport %s\n"),
+               GNUNET_i2s (peer),
+                (NULL != transport_name) ? transport_name : "unspecified");
+    if (NULL != cont)
+      cont (cont_cls,
+           peer,
+           address,
+           session,
+           GNUNET_NO);
+    return NULL;
+  }
+
+  for (tc = clients_head; NULL != tc; tc = tc->next)
+    if (CT_BLACKLIST == tc->type)
+      break;
+  if (NULL == tc)
+  {
+    /* no blacklist clients, approve instantly */
+    if (NULL != cont)
+      cont (cont_cls,
+           peer,
+           address,
+           session,
+           GNUNET_OK);
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Allowing connection to peer `%s' %s\n",
+               GNUNET_i2s (peer),
+                (NULL != transport_name) ? transport_name : "");
+    return NULL;
+  }
+
+  /* need to query blacklist clients */
+  bc = GNUNET_new (struct GST_BlacklistCheck);
+  GNUNET_CONTAINER_DLL_insert (bc_head,
+                              bc_tail,
+                              bc);
+  bc->peer = *peer;
+  bc->address = GNUNET_HELLO_address_copy (address);
+  bc->session = session;
+  bc->cont = cont;
+  bc->cont_cls = cont_cls;
+  bc->bl_pos = tc;
+  bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
+                                      bc);
+  return bc;
+}
+
+
+/**
+ * Cancel a blacklist check.
+ *
+ * @param bc check to cancel
+ */
+void
+GST_blacklist_test_cancel (struct GST_BlacklistCheck *bc)
+{
+  GNUNET_CONTAINER_DLL_remove (bc_head,
+                               bc_tail,
+                               bc);
+  if (NULL != bc->bl_pos)
+  {
+    if ( (CT_BLACKLIST == bc->bl_pos->type) &&
+        (bc->bl_pos->details.blacklist.bc == bc) )
+    {
+      /* we're at the head of the queue, remove us! */
+      bc->bl_pos->details.blacklist.bc = NULL;
+    }
+  }
+  if (NULL != bc->task)
+  {
+    GNUNET_SCHEDULER_cancel (bc->task);
+    bc->task = NULL;
+  }
+  GNUNET_free_non_null (bc->address);
+  GNUNET_free (bc);
+}
+
+
+/**
+ * Function to iterate over options in the blacklisting section for a peer.
+ *
+ * @param cls closure
+ * @param section name of the section
+ * @param option name of the option
+ * @param value value of the option
+ */
+static void
+blacklist_cfg_iter (void *cls,
+                    const char *section,
+                   const char *option,
+                   const char *value)
+{
+  unsigned int *res = cls;
+  struct GNUNET_PeerIdentity peer;
+  char *plugs;
+  char *pos;
+
+  if (GNUNET_OK !=
+      GNUNET_CRYPTO_eddsa_public_key_from_string (option,
+                                                  strlen (option),
+                                                  &peer.public_key))
+    return;
+
+  if ((NULL == value) || (0 == strcmp(value, "")))
+  {
+    /* Blacklist whole peer */
+    GST_blacklist_add_peer (&peer, NULL);
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+               _("Adding blacklisting entry for peer `%s'\n"),
+                GNUNET_i2s (&peer));
+  }
+  else
+  {
+    plugs = GNUNET_strdup (value);
+    for (pos = strtok (plugs, " "); pos != NULL; pos = strtok (NULL, " "))
+      {
+       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                   _("Adding blacklisting entry for peer `%s':`%s'\n"),
+                   GNUNET_i2s (&peer), pos);
+       GST_blacklist_add_peer (&peer, pos);
+      }
+    GNUNET_free (plugs);
+  }
+  (*res)++;
+}
+
+
+/**
+ * Read blacklist configuration
+ *
+ * @param cfg the configuration handle
+ * @param my_id my peer identity
+ */
+static void
+read_blacklist_configuration (const struct GNUNET_CONFIGURATION_Handle *cfg,
+                             const struct GNUNET_PeerIdentity *my_id)
+{
+  char cfg_sect[512];
+  unsigned int res = 0;
+
+  GNUNET_snprintf (cfg_sect,
+                  sizeof (cfg_sect),
+                  "transport-blacklist-%s",
+                  GNUNET_i2s_full (my_id));
+  GNUNET_CONFIGURATION_iterate_section_values (cfg,
+                                               cfg_sect,
+                                               &blacklist_cfg_iter,
+                                               &res);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Loaded %u blacklisting entries from configuration\n",
+              res);
+}
+
+
 /**
  * Initiate transport service.
  *
  * @param cls closure
- * @param server the initialized server
  * @param c configuration to use
+ * @param service the initialized service
  */
 static void
-run (void *cls, struct GNUNET_SERVER_Handle *server,
-    const struct GNUNET_CONFIGURATION_Handle *c)
+run (void *cls,
+     const struct GNUNET_CONFIGURATION_Handle *c,
+     struct GNUNET_SERVICE_Handle *service)
 {
   char *keyfile;
   struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
@@ -834,62 +2785,73 @@ run (void *cls, struct GNUNET_SERVER_Handle *server,
 
   /* setup globals */
   GST_cfg = c;
-  if (GNUNET_OK
-      != GNUNET_CONFIGURATION_get_value_filename (c, "PEER", "PRIVATE_KEY",
-          &keyfile))
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_filename (c,
+                                               "PEER",
+                                               "PRIVATE_KEY",
+                                               &keyfile))
   {
     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
         _("Transport service is lacking key configuration settings. Exiting.\n"));
     GNUNET_SCHEDULER_shutdown ();
     return;
   }
-  if (GNUNET_OK
-      != GNUNET_CONFIGURATION_get_value_time (c, "transport",
-          "HELLO_EXPIRATION", &hello_expiration))
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_time (c,
+                                           "transport",
+                                           "HELLO_EXPIRATION",
+                                           &hello_expiration))
   {
     hello_expiration = GNUNET_CONSTANTS_HELLO_ADDRESS_EXPIRATION;
   }
-  GST_server = server;
   pk = GNUNET_CRYPTO_eddsa_key_create_from_file (keyfile);
-  GNUNET_free(keyfile);
-  GNUNET_assert(NULL != pk);
+  GNUNET_free (keyfile);
+  GNUNET_assert (NULL != pk);
   GST_my_private_key = pk;
 
   GST_stats = GNUNET_STATISTICS_create ("transport", GST_cfg);
   GST_peerinfo = GNUNET_PEERINFO_connect (GST_cfg);
   GNUNET_CRYPTO_eddsa_key_get_public (GST_my_private_key,
-      &GST_my_identity.public_key);
-  GNUNET_assert(NULL != GST_my_private_key);
+                                      &GST_my_identity.public_key);
+  GNUNET_assert (NULL != GST_my_private_key);
 
-  GNUNET_log(GNUNET_ERROR_TYPE_INFO, "My identity is `%4s'\n",
-      GNUNET_i2s (&GST_my_identity));
+  GNUNET_log(GNUNET_ERROR_TYPE_INFO,
+             "My identity is `%s'\n",
+             GNUNET_i2s_full (&GST_my_identity));
 
-  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
-      NULL );
+  GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
+                                NULL);
   if (NULL == GST_peerinfo)
   {
-    GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
-        _("Could not access PEERINFO service.  Exiting.\n"));
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+               _("Could not access PEERINFO service.  Exiting.\n"));
     GNUNET_SCHEDULER_shutdown ();
     return;
   }
 
   max_fd_rlimit = 0;
-  max_fd_cfg = 0;
 #if HAVE_GETRLIMIT
-  struct rlimit r_file;
-  if (0 == getrlimit (RLIMIT_NOFILE, &r_file))
   {
-    max_fd_rlimit = r_file.rlim_cur;
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-        "Maximum number of open files was: %u/%u\n",
-        r_file.rlim_cur,
-        r_file.rlim_max);
+    struct rlimit r_file;
+
+    if (0 == getrlimit (RLIMIT_NOFILE,
+                       &r_file))
+    {
+      max_fd_rlimit = r_file.rlim_cur;
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                 "Maximum number of open files was: %u/%u\n",
+                 (unsigned int) r_file.rlim_cur,
+                 (unsigned int) r_file.rlim_max);
+    }
+    max_fd_rlimit = (9 * max_fd_rlimit) / 10; /* Keep 10% for rest of transport */
   }
-  max_fd_rlimit = (9 * max_fd_rlimit) / 10; /* Keep 10% for rest of transport */
 #endif
-  GNUNET_CONFIGURATION_get_value_number (GST_cfg, "transport", "MAX_FD",
-      &max_fd_cfg);
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_number (GST_cfg,
+                                             "transport",
+                                             "MAX_FD",
+                                             &max_fd_cfg))
+    max_fd_cfg = max_fd_rlimit;
 
   if (max_fd_cfg > max_fd_rlimit)
     max_fd = max_fd_cfg;
@@ -898,48 +2860,89 @@ run (void *cls, struct GNUNET_SERVER_Handle *server,
   if (max_fd < DEFAULT_MAX_FDS)
     max_fd = DEFAULT_MAX_FDS;
 
-  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
-      "Limiting number of sockets to %u: validation %u, neighbors: %u\n",
-      max_fd, (max_fd / 3), (max_fd / 3) * 2);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Limiting number of sockets to %u: validation %u, neighbors: %u\n",
+              max_fd,
+              (max_fd / 3),
+              (max_fd / 3) * 2);
 
-  friend_only = GNUNET_CONFIGURATION_get_value_yesno (GST_cfg, "topology",
-      "FRIENDS-ONLY");
+  friend_only = GNUNET_CONFIGURATION_get_value_yesno (GST_cfg,
+                                                      "topology",
+                                                      "FRIENDS-ONLY");
   if (GNUNET_SYSERR == friend_only)
     friend_only = GNUNET_NO; /* According to topology defaults */
   /* start subsystems */
-  GST_hello_start (friend_only, &process_hello_update, NULL );
-  GNUNET_assert(NULL != GST_hello_get());
-  GST_blacklist_start (GST_server, GST_cfg, &GST_my_identity);
-  GST_ats = GNUNET_ATS_scheduling_init (GST_cfg, &ats_request_address_change,
-      NULL );
-  GST_manipulation_init (GST_cfg);
+  read_blacklist_configuration (GST_cfg,
+                               &GST_my_identity);
+  GST_is = GNUNET_NT_scanner_init ();
+  GST_ats_connect = GNUNET_ATS_connectivity_init (GST_cfg);
+  GST_ats = GNUNET_ATS_scheduling_init (GST_cfg,
+                                        &ats_request_address_change,
+                                        NULL);
+  GST_ats_init ();
+  GST_manipulation_init ();
   GST_plugins_load (&GST_manipulation_recv,
-      &plugin_env_address_change_notification,
-      &plugin_env_session_start,
-      &plugin_env_session_end,
-      &plugin_env_address_to_type,
-      &plugin_env_update_metrics);
-  GST_neighbours_start (NULL, &neighbours_connect_notification,
-      &neighbours_disconnect_notification, &neighbours_changed_notification,
-      (max_fd / 3) * 2);
-  GST_clients_start (GST_server);
+                    &plugin_env_address_change_notification,
+                    &plugin_env_session_start,
+                    &plugin_env_session_end);
+  GST_hello_start (friend_only,
+                   &process_hello_update,
+                   NULL);
+  GST_neighbours_start ((max_fd / 3) * 2);
+  active_stccs = GNUNET_CONTAINER_multipeermap_create (128,
+                                                      GNUNET_YES);
+  plugin_nc = GNUNET_notification_context_create (0);
   GST_validation_start ((max_fd / 3));
 }
 
+
 /**
- * The main function for the transport service.
- *
- * @param argc number of arguments from the command line
- * @param argv command line arguments
- * @return 0 ok, 1 on error
+ * Define "main" method using service macro.
  */
-int
-main (int argc, char * const *argv)
-{
-  return
-      (GNUNET_OK
-          == GNUNET_SERVICE_run (argc, argv, "transport",
-              GNUNET_SERVICE_OPTION_NONE, &run, NULL )) ? 0 : 1;
-}
+GNUNET_SERVICE_MAIN
+("transport",
+ GNUNET_SERVICE_OPTION_NONE,
+ &run,
+ &client_connect_cb,
+ &client_disconnect_cb,
+ NULL,
+ GNUNET_MQ_hd_fixed_size (client_start,
+                         GNUNET_MESSAGE_TYPE_TRANSPORT_START,
+                         struct StartMessage,
+                         NULL),
+ GNUNET_MQ_hd_var_size (client_hello,
+                       GNUNET_MESSAGE_TYPE_HELLO,
+                       struct GNUNET_MessageHeader,
+                       NULL),
+ GNUNET_MQ_hd_var_size (client_send,
+                       GNUNET_MESSAGE_TYPE_TRANSPORT_SEND,
+                       struct OutboundMessage,
+                       NULL),
+ GNUNET_MQ_hd_var_size (client_address_to_string,
+                       GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_TO_STRING,
+                       struct AddressLookupMessage,
+                       NULL),
+ GNUNET_MQ_hd_fixed_size (client_monitor_peers,
+                         GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_PEER_REQUEST,
+                         struct PeerMonitorMessage,
+                         NULL),
+ GNUNET_MQ_hd_fixed_size (client_blacklist_init,
+                         GNUNET_MESSAGE_TYPE_TRANSPORT_BLACKLIST_INIT,
+                         struct GNUNET_MessageHeader,
+                         NULL),
+ GNUNET_MQ_hd_fixed_size (client_blacklist_reply,
+                         GNUNET_MESSAGE_TYPE_TRANSPORT_BLACKLIST_REPLY,
+                         struct BlacklistMessage,
+                         NULL),
+ GNUNET_MQ_hd_fixed_size (client_set_metric,
+                         GNUNET_MESSAGE_TYPE_TRANSPORT_TRAFFIC_METRIC,
+                         struct TrafficMetricMessage,
+                         NULL),
+ GNUNET_MQ_hd_fixed_size (client_monitor_plugins,
+                         GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_PLUGIN_START,
+                         struct GNUNET_MessageHeader,
+                         NULL),
+ GNUNET_MQ_handler_end ());
+
 
 /* end of file gnunet-service-transport.c */