Merge branch 'master' of ssh://gnunet.org/gnunet
[oweals/gnunet.git] / src / cadet / gnunet-service-cadet-new_tunnels.c
index 4299d5bf749d873059e68e003bf131b1de4efe7f..d50860629312b54edd47d843109a8bee3a97927d 100644 (file)
@@ -1,4 +1,3 @@
-
 /*
      This file is part of GNUnet.
      Copyright (C) 2013, 2017 GNUnet e.V.
      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
      Boston, MA 02110-1301, USA.
 */
-
 /**
  * @file cadet/gnunet-service-cadet-new_tunnels.c
  * @brief Information we track per tunnel.
  * @author Bartlomiej Polot
  * @author Christian Grothoff
+ *
+ * FIXME:
+ * - proper connection evaluation during connection management:
+ *   + consider quality (or quality spread?) of current connection set
+ *     when deciding how often to do maintenance
+ *   + interact with PEER to drive DHT GET/PUT operations based
+ *     on how much we like our connections
  */
 #include "platform.h"
 #include "gnunet_util_lib.h"
+#include "gnunet_statistics_service.h"
 #include "gnunet_signatures.h"
-#include "cadet_protocol.h"
-#include "cadet_path.h"
 #include "gnunet-service-cadet-new.h"
+#include "cadet_protocol.h"
 #include "gnunet-service-cadet-new_channel.h"
 #include "gnunet-service-cadet-new_connection.h"
 #include "gnunet-service-cadet-new_tunnels.h"
 #include "gnunet-service-cadet-new_peer.h"
+#include "gnunet-service-cadet-new_paths.h"
+
 
+#define LOG(level, ...) GNUNET_log_from(level,"cadet-tun",__VA_ARGS__)
+
+/**
+ * How often do we try to decrypt payload with unverified key
+ * material?  Used to limit CPU increase upon receiving bogus
+ * KX.
+ */
+#define MAX_UNVERIFIED_ATTEMPTS 16
 
 /**
  * How long do we wait until tearing down an idle tunnel?
  */
 #define IDLE_DESTROY_DELAY GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 90)
 
+/**
+ * How long do we wait initially before retransmitting the KX?
+ * TODO: replace by 2 RTT if/once we have connection-level RTT data!
+ */
+#define INITIAL_KX_RETRY_DELAY GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 250)
+
+/**
+ * Maximum number of skipped keys we keep in memory per tunnel.
+ */
+#define MAX_SKIPPED_KEYS 64
+
+/**
+ * Maximum number of keys (and thus ratchet steps) we are willing to
+ * skip before we decide this is either a bogus packet or a DoS-attempt.
+ */
+#define MAX_KEY_GAP 256
+
 
 /**
  * Struct to old keys for skipped messages while advancing the Axolotl ratchet.
@@ -103,52 +135,61 @@ struct CadetTunnelAxolotl
   struct GNUNET_CRYPTO_SymmetricSessionKey RK;
 
   /**
-   * 32-byte header key (send).
+   * 32-byte header key (currently used for sending).
    */
   struct GNUNET_CRYPTO_SymmetricSessionKey HKs;
 
   /**
-   * 32-byte header key (recv)
+   * 32-byte header key (currently used for receiving)
    */
   struct GNUNET_CRYPTO_SymmetricSessionKey HKr;
 
   /**
-   * 32-byte next header key (send).
+   * 32-byte next header key (for sending), used once the
+   * ratchet advances.  We are sure that the sender has this
+   * key as well only after @e ratchet_allowed is #GNUNET_YES.
    */
   struct GNUNET_CRYPTO_SymmetricSessionKey NHKs;
 
   /**
-   * 32-byte next header key (recv).
+   * 32-byte next header key (for receiving).  To be tried
+   * when decrypting with @e HKr fails and thus the sender
+   * may have advanced the ratchet.
    */
   struct GNUNET_CRYPTO_SymmetricSessionKey NHKr;
 
   /**
-   * 32-byte chain keys (used for forward-secrecy updating, send).
+   * 32-byte chain keys (used for forward-secrecy) for
+   * sending messages. Updated for every message.
    */
   struct GNUNET_CRYPTO_SymmetricSessionKey CKs;
 
   /**
-   * 32-byte chain keys (used for forward-secrecy updating, recv).
+   * 32-byte chain keys (used for forward-secrecy) for
+   * receiving messages. Updated for every message. If
+   * messages are skipped, the respective derived MKs
+   * (and the current @HKr) are kept in the @e skipped_head DLL.
    */
   struct GNUNET_CRYPTO_SymmetricSessionKey CKr;
 
   /**
    * ECDH for key exchange (A0 / B0).
    */
-  struct GNUNET_CRYPTO_EcdhePrivateKey *kx_0;
+  struct GNUNET_CRYPTO_EcdhePrivateKey kx_0;
 
   /**
-   * ECDH Ratchet key (send).
+   * ECDH Ratchet key (our private key in the current DH).
    */
-  struct GNUNET_CRYPTO_EcdhePrivateKey *DHRs;
+  struct GNUNET_CRYPTO_EcdhePrivateKey DHRs;
 
   /**
-   * ECDH Ratchet key (recv).
+   * ECDH Ratchet key (other peer's public key in the current DH).
    */
   struct GNUNET_CRYPTO_EcdhePublicKey DHRr;
 
   /**
-   * When does this ratchet expire and a new one is triggered.
+   * Time when the current ratchet expires and a new one is triggered
+   * (if @e ratchet_allowed is #GNUNET_YES).
    */
   struct GNUNET_TIME_Absolute ratchet_expiration;
 
@@ -178,54 +219,34 @@ struct CadetTunnelAxolotl
   int ratchet_flag;
 
   /**
-   * Number of messages recieved since our last ratchet advance.
-   * - If this counter = 0, we cannot send a new ratchet key in next msg.
-   * - If this counter > 0, we can (but don't yet have to) send a new key.
+   * True (#GNUNET_YES) if we have received a message from the
+   * other peer that uses the keys from our last ratchet step.
+   * This implies that we are again allowed to advance the ratchet,
+   * otherwise we have to wait until the other peer sees our current
+   * ephemeral key and advances first.
+   *
+   * #GNUNET_NO if we have advanced the ratched but lack any evidence
+   * that the other peer has noticed this.
    */
-  unsigned int ratchet_allowed;
+  int ratchet_allowed;
 
   /**
    * Number of messages recieved since our last ratchet advance.
-   * - If this counter = 0, we cannot send a new ratchet key in next msg.
-   * - If this counter > 0, we can (but don't yet have to) send a new key.
+   *
+   * If this counter = 0, we cannot send a new ratchet key in the next
+   * message.
+   *
+   * If this counter > 0, we could (but don't have to) send a new key.
+   *
+   * Once the @e ratchet_counter is larger than
+   * #ratchet_messages (or @e ratchet_expiration time has past), and
+   * @e ratchet_allowed is #GNUNET_YES, we advance the ratchet.
    */
   unsigned int ratchet_counter;
 
 };
 
 
-/**
- * Entry in list of connections used by tunnel, with metadata.
- */
-struct CadetTConnection
-{
-  /**
-   * Next in DLL.
-   */
-  struct CadetTConnection *next;
-
-  /**
-   * Prev in DLL.
-   */
-  struct CadetTConnection *prev;
-
-  /**
-   * Connection handle.
-   */
-  struct CadetConnection *c;
-
-  /**
-   * Creation time, to keep oldest connection alive.
-   */
-  struct GNUNET_TIME_Absolute created;
-
-  /**
-   * Connection throughput, to keep fastest connection alive.
-   */
-  uint32_t throughput;
-};
-
-
 /**
  * Struct used to save messages in a non-ready tunnel to send once connected.
  */
@@ -249,7 +270,7 @@ struct CadetTunnelQueueEntry
   /**
    * Continuation to call once sent (on the channel layer).
    */
-  GNUNET_SCHEDULER_TaskCallback cont;
+  GCT_SendContinuation cont;
 
   /**
    * Closure for @c cont.
@@ -257,9 +278,15 @@ struct CadetTunnelQueueEntry
   void *cont_cls;
 
   /**
-   * (Encrypted) message to send follows.
+   * Envelope of message to send follows.
+   */
+  struct GNUNET_MQ_Envelope *env;
+
+  /**
+   * Where to put the connection identifier into the payload
+   * of the message in @e env once we have it?
    */
-  /* struct GNUNET_MessageHeader *msg; */
+  struct GNUNET_CADET_ConnectionTunnelIdentifier *cid;
 };
 
 
@@ -269,9 +296,9 @@ struct CadetTunnelQueueEntry
 struct CadetTunnel
 {
   /**
-   * Endpoint of the tunnel.
+   * Destination of the tunnel.
    */
-  struct CadetPeer *peer;
+  struct CadetPeer *destination;
 
   /**
    * Peer's ephemeral key, to recreate @c e_key and @c d_key when own
@@ -295,40 +322,74 @@ struct CadetTunnel
   struct CadetTunnelAxolotl ax;
 
   /**
-   * State of the tunnel connectivity.
+   * Unverified Axolotl info, used only if we got a fresh KX (not a
+   * KX_AUTH) while our end of the tunnel was still up.  In this case,
+   * we keep the fresh KX around but do not put it into action until
+   * we got encrypted payload that assures us of the authenticity of
+   * the KX.
    */
-  enum CadetTunnelCState cstate;
+  struct CadetTunnelAxolotl *unverified_ax;
 
   /**
-   * State of the tunnel encryption.
+   * Task scheduled if there are no more channels using the tunnel.
    */
-  enum CadetTunnelEState estate;
+  struct GNUNET_SCHEDULER_Task *destroy_task;
+
+  /**
+   * Task to trim connections if too many are present.
+   */
+  struct GNUNET_SCHEDULER_Task *maintain_connections_task;
+
+  /**
+   * Task to send messages from queue (if possible).
+   */
+  struct GNUNET_SCHEDULER_Task *send_task;
+
+  /**
+   * Task to trigger KX.
+   */
+  struct GNUNET_SCHEDULER_Task *kx_task;
+
+  /**
+   * Tokenizer for decrypted messages.
+   */
+  struct GNUNET_MessageStreamTokenizer *mst;
+
+  /**
+   * Dispatcher for decrypted messages only (do NOT use for sending!).
+   */
+  struct GNUNET_MQ_Handle *mq;
+
+  /**
+   * DLL of ready connections that are actively used to reach the destination peer.
+   */
+  struct CadetTConnection *connection_ready_head;
 
   /**
-   * Task to start the rekey process.
+   * DLL of ready connections that are actively used to reach the destination peer.
    */
-  struct GNUNET_SCHEDULER_Task *rekey_task;
+  struct CadetTConnection *connection_ready_tail;
 
   /**
-   * DLL of connections that are actively used to reach the destination peer.
+   * DLL of connections that we maintain that might be used to reach the destination peer.
    */
-  struct CadetTConnection *connection_head;
+  struct CadetTConnection *connection_busy_head;
 
   /**
-   * DLL of connections that are actively used to reach the destination peer.
+   * DLL of connections that we maintain that might be used to reach the destination peer.
    */
-  struct CadetTConnection *connection_tail;
+  struct CadetTConnection *connection_busy_tail;
 
   /**
    * Channels inside this tunnel. Maps
-   * `struct GCT_ChannelTunnelNumber` to a `struct CadetChannel`.
+   * `struct GNUNET_CADET_ChannelTunnelNumber` to a `struct CadetChannel`.
    */
   struct GNUNET_CONTAINER_MultiHashMap32 *channels;
 
   /**
    * Channel ID for the next created channel in this tunnel.
    */
-  struct GCT_ChannelTunnelNumber next_chid;
+  struct GNUNET_CADET_ChannelTunnelNumber next_ctn;
 
   /**
    * Queued messages, to transmit once tunnel gets connected.
@@ -341,37 +402,82 @@ struct CadetTunnel
   struct CadetTunnelQueueEntry *tq_tail;
 
   /**
-   * Task scheduled if there are no more channels using the tunnel.
+   * Identification of the connection from which we are currently processing
+   * a message. Only valid (non-NULL) during #handle_decrypted() and the
+   * handle-*()-functions called from our @e mq during that function.
    */
-  struct GNUNET_SCHEDULER_Task *destroy_task;
+  struct CadetTConnection *current_ct;
 
   /**
-   * Task to trim connections if too many are present.
+   * How long do we wait until we retry the KX?
    */
-  struct GNUNET_SCHEDULER_Task *trim_connections_task;
+  struct GNUNET_TIME_Relative kx_retry_delay;
 
   /**
-   * Ephemeral message in the queue (to avoid queueing more than one).
+   * When do we try the next KX?
    */
-  struct CadetConnectionQueue *ephm_hKILL;
+  struct GNUNET_TIME_Absolute next_kx_attempt;
 
   /**
-   * Pong message in the queue.
+   * Number of connections in the @e connection_ready_head DLL.
    */
-  struct CadetConnectionQueue *pong_hKILL;
+  unsigned int num_ready_connections;
 
   /**
-   * Number of connections in the @e connection_head DLL.
+   * Number of connections in the @e connection_busy_head DLL.
    */
-  unsigned int num_connections;
+  unsigned int num_busy_connections;
+
+  /**
+   * How often have we tried and failed to decrypt a message using
+   * the unverified KX material from @e unverified_ax?  Used to
+   * stop trying after #MAX_UNVERIFIED_ATTEMPTS.
+   */
+  unsigned int unverified_attempts;
 
   /**
    * Number of entries in the @e tq_head DLL.
    */
   unsigned int tq_len;
+
+  /**
+   * State of the tunnel encryption.
+   */
+  enum CadetTunnelEState estate;
+
+  /**
+   * Force triggering KX_AUTH independent of @e estate.
+   */
+  int kx_auth_requested;
+
 };
 
 
+/**
+ * Connection @a ct is now unready, clear it's ready flag
+ * and move it from the ready DLL to the busy DLL.
+ *
+ * @param ct connection to move to unready status
+ */
+static void
+mark_connection_unready (struct CadetTConnection *ct)
+{
+  struct CadetTunnel *t = ct->t;
+
+  GNUNET_assert (GNUNET_YES == ct->is_ready);
+  GNUNET_CONTAINER_DLL_remove (t->connection_ready_head,
+                               t->connection_ready_tail,
+                               ct);
+  GNUNET_assert (0 < t->num_ready_connections);
+  t->num_ready_connections--;
+  ct->is_ready = GNUNET_NO;
+  GNUNET_CONTAINER_DLL_insert (t->connection_busy_head,
+                               t->connection_busy_tail,
+                               ct);
+  t->num_busy_connections++;
+}
+
+
 /**
  * Get the static string for the peer this tunnel is directed.
  *
@@ -385,16 +491,51 @@ GCT_2s (const struct CadetTunnel *t)
   static char buf[64];
 
   if (NULL == t)
-    return "T(NULL)";
-
+    return "Tunnel(NULL)";
   GNUNET_snprintf (buf,
                    sizeof (buf),
-                   "T(%s)",
-                   GCP_2s (t->peer));
+                   "Tunnel %s",
+                   GNUNET_i2s (GCP_get_id (t->destination)));
   return buf;
 }
 
 
+/**
+ * Get string description for tunnel encryption state.
+ *
+ * @param es Tunnel state.
+ *
+ * @return String representation.
+ */
+static const char *
+estate2s (enum CadetTunnelEState es)
+{
+  static char buf[32];
+
+  switch (es)
+  {
+  case CADET_TUNNEL_KEY_UNINITIALIZED:
+    return "CADET_TUNNEL_KEY_UNINITIALIZED";
+  case CADET_TUNNEL_KEY_AX_RECV:
+    return "CADET_TUNNEL_KEY_AX_RECV";
+  case CADET_TUNNEL_KEY_AX_SENT:
+    return "CADET_TUNNEL_KEY_AX_SENT";
+  case CADET_TUNNEL_KEY_AX_SENT_AND_RECV:
+    return "CADET_TUNNEL_KEY_AX_SENT_AND_RECV";
+  case CADET_TUNNEL_KEY_AX_AUTH_SENT:
+    return "CADET_TUNNEL_KEY_AX_AUTH_SENT";
+  case CADET_TUNNEL_KEY_OK:
+    return "CADET_TUNNEL_KEY_OK";
+  default:
+    GNUNET_snprintf (buf,
+                     sizeof (buf),
+                     "%u (UNKNOWN STATE)",
+                     es);
+    return buf;
+  }
+}
+
+
 /**
  * Return the peer to which this tunnel goes.
  *
@@ -404,7 +545,7 @@ GCT_2s (const struct CadetTunnel *t)
 struct CadetPeer *
 GCT_get_destination (struct CadetTunnel *t)
 {
-  return t->peer;
+  return t->destination;
 }
 
 
@@ -422,6 +563,22 @@ GCT_count_channels (struct CadetTunnel *t)
 }
 
 
+/**
+ * Lookup a channel by its @a ctn.
+ *
+ * @param t tunnel to look in
+ * @param ctn number of channel to find
+ * @return NULL if channel does not exist
+ */
+struct CadetChannel *
+lookup_channel (struct CadetTunnel *t,
+                struct GNUNET_CADET_ChannelTunnelNumber ctn)
+{
+  return GNUNET_CONTAINER_multihashmap32_get (t->channels,
+                                              ntohl (ctn.cn));
+}
+
+
 /**
  * Count all created connections of a tunnel. Not necessarily ready connections!
  *
@@ -430,23 +587,27 @@ GCT_count_channels (struct CadetTunnel *t)
  * @return Number of connections created, either being established or ready.
  */
 unsigned int
-GCT_count_any_connections (struct CadetTunnel *t)
+GCT_count_any_connections (const struct CadetTunnel *t)
 {
-  return t->num_connections;
+  return t->num_ready_connections + t->num_busy_connections;
 }
 
 
 /**
- * Get the connectivity state of a tunnel.
- *
- * @param t Tunnel.
+ * Find first connection that is ready in the list of
+ * our connections.  Picks ready connections round-robin.
  *
- * @return Tunnel's connectivity state.
+ * @param t tunnel to search
+ * @return NULL if we have no connection that is ready
  */
-enum CadetTunnelCState
-GCT_get_cstate (struct CadetTunnel *t)
+static struct CadetTConnection *
+get_ready_connection (struct CadetTunnel *t)
 {
-  return t->cstate;
+  struct CadetTConnection *hd = t->connection_ready_head;
+
+  GNUNET_assert ( (NULL == hd) ||
+                  (GNUNET_YES == hd->is_ready) );
+  return hd;
 }
 
 
@@ -465,321 +626,2771 @@ GCT_get_estate (struct CadetTunnel *t)
 
 
 /**
- * Add a channel to a tunnel.
+ * Called when either we have a new connection, or a new message in the
+ * queue, or some existing connection has transmission capacity.  Looks
+ * at our message queue and if there is a message, picks a connection
+ * to send it on.
  *
- * @param t Tunnel.
- * @param ch Channel
- * @return unique number identifying @a ch within @a t
+ * @param cls the `struct CadetTunnel` to process messages on
  */
-struct GCT_ChannelTunnelNumber
-GCT_add_channel (struct CadetTunnel *t,
-                 struct CadetChannel *ch)
-{
-  struct GCT_ChannelTunnelNumber ret;
-  uint32_t chid;
+static void
+trigger_transmissions (void *cls);
 
-  chid = ntohl (t->next_chid.channel_in_tunnel);
-  while (NULL !=
-         GNUNET_CONTAINER_multihashmap32_get (t->channels,
-                                              chid))
-    chid++;
-  GNUNET_assert (GNUNET_YES ==
-                 GNUNET_CONTAINER_multihashmap32_put (t->channels,
-                                                      chid,
-                                                      ch,
-                                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
-  t->next_chid.channel_in_tunnel = htonl (chid + 1);
-  ret.channel_in_tunnel = htonl (chid);
-  return ret;
-}
+
+/* ************************************** start core crypto ***************************** */
 
 
 /**
- * This tunnel is no longer used, destroy it.
+ * Create a new Axolotl ephemeral (ratchet) key.
  *
- * @param cls the idle tunnel
+ * @param ax key material to update
  */
 static void
-destroy_tunnel (void *cls)
+new_ephemeral (struct CadetTunnelAxolotl *ax)
 {
-  struct CadetTunnel *t = cls;
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Creating new ephemeral ratchet key (DHRs)\n");
+  GNUNET_assert (GNUNET_OK ==
+                 GNUNET_CRYPTO_ecdhe_key_create2 (&ax->DHRs));
+}
 
-  t->destroy_task = NULL;
 
-  // FIXME: implement!
-  GCP_drop_tunnel (t->peer,
-                   t);
-  GNUNET_free (t);
+/**
+ * Calculate HMAC.
+ *
+ * @param plaintext Content to HMAC.
+ * @param size Size of @c plaintext.
+ * @param iv Initialization vector for the message.
+ * @param key Key to use.
+ * @param hmac[out] Destination to store the HMAC.
+ */
+static void
+t_hmac (const void *plaintext,
+        size_t size,
+        uint32_t iv,
+        const struct GNUNET_CRYPTO_SymmetricSessionKey *key,
+        struct GNUNET_ShortHashCode *hmac)
+{
+  static const char ctx[] = "cadet authentication key";
+  struct GNUNET_CRYPTO_AuthKey auth_key;
+  struct GNUNET_HashCode hash;
+
+  GNUNET_CRYPTO_hmac_derive_key (&auth_key,
+                                 key,
+                                 &iv, sizeof (iv),
+                                 key, sizeof (*key),
+                                 ctx, sizeof (ctx),
+                                 NULL);
+  /* Two step: GNUNET_ShortHash is only 256 bits,
+     GNUNET_HashCode is 512, so we truncate. */
+  GNUNET_CRYPTO_hmac (&auth_key,
+                      plaintext,
+                      size,
+                      &hash);
+  GNUNET_memcpy (hmac,
+                 &hash,
+                 sizeof (*hmac));
 }
 
 
 /**
- * Create a tunnel to @a destionation.  Must only be called
- * from within #GCP_get_tunnel().
+ * Perform a HMAC.
  *
- * @param destination where to create the tunnel to
- * @return new tunnel to @a destination
+ * @param key Key to use.
+ * @param[out] hash Resulting HMAC.
+ * @param source Source key material (data to HMAC).
+ * @param len Length of @a source.
  */
-struct CadetTunnel *
-GCT_create_tunnel (struct CadetPeer *destination)
+static void
+t_ax_hmac_hash (const struct GNUNET_CRYPTO_SymmetricSessionKey *key,
+                struct GNUNET_HashCode *hash,
+                const void *source,
+                unsigned int len)
 {
-  struct CadetTunnel *t;
+  static const char ctx[] = "axolotl HMAC-HASH";
+  struct GNUNET_CRYPTO_AuthKey auth_key;
+
+  GNUNET_CRYPTO_hmac_derive_key (&auth_key,
+                                 key,
+                                 ctx, sizeof (ctx),
+                                 NULL);
+  GNUNET_CRYPTO_hmac (&auth_key,
+                      source,
+                      len,
+                      hash);
+}
 
-  t = GNUNET_new (struct CadetTunnel);
-  t->peer = destination;
-  t->channels = GNUNET_CONTAINER_multihashmap32_create (8);
 
-  return t;
+/**
+ * Derive a symmetric encryption key from an HMAC-HASH.
+ *
+ * @param key Key to use for the HMAC.
+ * @param[out] out Key to generate.
+ * @param source Source key material (data to HMAC).
+ * @param len Length of @a source.
+ */
+static void
+t_hmac_derive_key (const struct GNUNET_CRYPTO_SymmetricSessionKey *key,
+                   struct GNUNET_CRYPTO_SymmetricSessionKey *out,
+                   const void *source,
+                   unsigned int len)
+{
+  static const char ctx[] = "axolotl derive key";
+  struct GNUNET_HashCode h;
+
+  t_ax_hmac_hash (key,
+                  &h,
+                  source,
+                  len);
+  GNUNET_CRYPTO_kdf (out, sizeof (*out),
+                     ctx, sizeof (ctx),
+                     &h, sizeof (h),
+                     NULL);
 }
 
 
 /**
- * Remove a channel from a tunnel.
+ * Encrypt data with the axolotl tunnel key.
  *
- * @param t Tunnel.
- * @param ch Channel
- * @param gid unique number identifying @a ch within @a t
+ * @param ax key material to use.
+ * @param dst Destination with @a size bytes for the encrypted data.
+ * @param src Source of the plaintext. Can overlap with @c dst, must contain @a size bytes
+ * @param size Size of the buffers at @a src and @a dst
  */
-void
-GCT_remove_channel (struct CadetTunnel *t,
-                    struct CadetChannel *ch,
-                    struct GCT_ChannelTunnelNumber gid)
+static void
+t_ax_encrypt (struct CadetTunnelAxolotl *ax,
+              void *dst,
+              const void *src,
+              size_t size)
 {
-  GNUNET_assert (GNUNET_YES ==
-                 GNUNET_CONTAINER_multihashmap32_remove (t->channels,
-                                                         ntohl (gid.channel_in_tunnel),
-                                                         ch));
-  if (0 ==
-      GNUNET_CONTAINER_multihashmap32_size (t->channels))
+  struct GNUNET_CRYPTO_SymmetricSessionKey MK;
+  struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
+  size_t out_size;
+
+  ax->ratchet_counter++;
+  if ( (GNUNET_YES == ax->ratchet_allowed) &&
+       ( (ratchet_messages <= ax->ratchet_counter) ||
+         (0 == GNUNET_TIME_absolute_get_remaining (ax->ratchet_expiration).rel_value_us)) )
+  {
+    ax->ratchet_flag = GNUNET_YES;
+  }
+  if (GNUNET_YES == ax->ratchet_flag)
   {
-    t->destroy_task = GNUNET_SCHEDULER_add_delayed (IDLE_DESTROY_DELAY,
-                                                    &destroy_tunnel,
-                                                    t);
+    /* Advance ratchet */
+    struct GNUNET_CRYPTO_SymmetricSessionKey keys[3];
+    struct GNUNET_HashCode dh;
+    struct GNUNET_HashCode hmac;
+    static const char ctx[] = "axolotl ratchet";
+
+    new_ephemeral (ax);
+    ax->HKs = ax->NHKs;
+
+    /* RK, NHKs, CKs = KDF( HMAC-HASH(RK, DH(DHRs, DHRr)) ) */
+    GNUNET_CRYPTO_ecc_ecdh (&ax->DHRs,
+                            &ax->DHRr,
+                            &dh);
+    t_ax_hmac_hash (&ax->RK,
+                    &hmac,
+                    &dh,
+                    sizeof (dh));
+    GNUNET_CRYPTO_kdf (keys, sizeof (keys),
+                       ctx, sizeof (ctx),
+                       &hmac, sizeof (hmac),
+                       NULL);
+    ax->RK = keys[0];
+    ax->NHKs = keys[1];
+    ax->CKs = keys[2];
+
+    ax->PNs = ax->Ns;
+    ax->Ns = 0;
+    ax->ratchet_flag = GNUNET_NO;
+    ax->ratchet_allowed = GNUNET_NO;
+    ax->ratchet_counter = 0;
+    ax->ratchet_expiration
+      = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get(),
+                                  ratchet_time);
   }
+
+  t_hmac_derive_key (&ax->CKs,
+                     &MK,
+                     "0",
+                     1);
+  GNUNET_CRYPTO_symmetric_derive_iv (&iv,
+                                     &MK,
+                                     NULL, 0,
+                                     NULL);
+
+  out_size = GNUNET_CRYPTO_symmetric_encrypt (src,
+                                              size,
+                                              &MK,
+                                              &iv,
+                                              dst);
+  GNUNET_assert (size == out_size);
+  t_hmac_derive_key (&ax->CKs,
+                     &ax->CKs,
+                     "1",
+                     1);
 }
 
 
 /**
- * Sends an already built message on a tunnel, encrypting it and
- * choosing the best connection if not provided.
+ * Decrypt data with the axolotl tunnel key.
  *
- * @param message Message to send. Function modifies it.
- * @param t Tunnel on which this message is transmitted.
- * @param cont Continuation to call once message is really sent.
- * @param cont_cls Closure for @c cont.
- * @return Handle to cancel message. NULL if @c cont is NULL.
+ * @param ax key material to use.
+ * @param dst Destination for the decrypted data, must contain @a size bytes.
+ * @param src Source of the ciphertext. Can overlap with @c dst, must contain @a size bytes.
+ * @param size Size of the @a src and @a dst buffers
  */
-struct CadetTunnelQueueEntry *
-GCT_send (struct CadetTunnel *t,
-          const struct GNUNET_MessageHeader *message,
-          GNUNET_SCHEDULER_TaskCallback cont,
-          void *cont_cls)
+static void
+t_ax_decrypt (struct CadetTunnelAxolotl *ax,
+              void *dst,
+              const void *src,
+              size_t size)
 {
-  struct CadetTunnelQueueEntry *q;
-  uint16_t payload_size;
+  struct GNUNET_CRYPTO_SymmetricSessionKey MK;
+  struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
+  size_t out_size;
+
+  t_hmac_derive_key (&ax->CKr,
+                     &MK,
+                     "0",
+                     1);
+  GNUNET_CRYPTO_symmetric_derive_iv (&iv,
+                                     &MK,
+                                     NULL, 0,
+                                     NULL);
+  GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader));
+  out_size = GNUNET_CRYPTO_symmetric_decrypt (src,
+                                              size,
+                                              &MK,
+                                              &iv,
+                                              dst);
+  GNUNET_assert (out_size == size);
+  t_hmac_derive_key (&ax->CKr,
+                     &ax->CKr,
+                     "1",
+                     1);
+}
 
-  payload_size = ntohs (message->size);
 
-  q = GNUNET_malloc (sizeof (*q) +
-                     payload_size);
-  /* FIXME: encrypt 'message' to end of 'q' */
-  q->t = t;
-  q->cont = cont;
-  q->cont_cls = cont_cls;
-  GNUNET_CONTAINER_DLL_insert_tail (t->tq_head,
-                                    t->tq_tail,
-                                    q);
-  /* FIXME: initiate transmission process! */
-  return q;
+/**
+ * Encrypt header with the axolotl header key.
+ *
+ * @param ax key material to use.
+ * @param[in|out] msg Message whose header to encrypt.
+ */
+static void
+t_h_encrypt (struct CadetTunnelAxolotl *ax,
+             struct GNUNET_CADET_TunnelEncryptedMessage *msg)
+{
+  struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
+  size_t out_size;
+
+  GNUNET_CRYPTO_symmetric_derive_iv (&iv,
+                                     &ax->HKs,
+                                     NULL, 0,
+                                     NULL);
+  out_size = GNUNET_CRYPTO_symmetric_encrypt (&msg->ax_header,
+                                              sizeof (struct GNUNET_CADET_AxHeader),
+                                              &ax->HKs,
+                                              &iv,
+                                              &msg->ax_header);
+  GNUNET_assert (sizeof (struct GNUNET_CADET_AxHeader) == out_size);
 }
 
 
 /**
- * Cancel a previously sent message while it's in the queue.
- *
- * ONLY can be called before the continuation given to the send
- * function is called. Once the continuation is called, the message is
- * no longer in the queue!
+ * Decrypt header with the current axolotl header key.
  *
- * @param q Handle to the queue entry to cancel.
+ * @param ax key material to use.
+ * @param src Message whose header to decrypt.
+ * @param dst Where to decrypt header to.
  */
-void
-GCT_send_cancel (struct CadetTunnelQueueEntry *q)
+static void
+t_h_decrypt (struct CadetTunnelAxolotl *ax,
+             const struct GNUNET_CADET_TunnelEncryptedMessage *src,
+             struct GNUNET_CADET_TunnelEncryptedMessage *dst)
 {
-  struct CadetTunnel *t = q->t;
-
-  GNUNET_CONTAINER_DLL_remove (t->tq_head,
-                               t->tq_tail,
-                               q);
-  GNUNET_free (q);
+  struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
+  size_t out_size;
+
+  GNUNET_CRYPTO_symmetric_derive_iv (&iv,
+                                     &ax->HKr,
+                                     NULL, 0,
+                                     NULL);
+  out_size = GNUNET_CRYPTO_symmetric_decrypt (&src->ax_header.Ns,
+                                              sizeof (struct GNUNET_CADET_AxHeader),
+                                              &ax->HKr,
+                                              &iv,
+                                              &dst->ax_header.Ns);
+  GNUNET_assert (sizeof (struct GNUNET_CADET_AxHeader) == out_size);
 }
 
 
 /**
- * Iterate over all connections of a tunnel.
+ * Delete a key from the list of skipped keys.
  *
- * @param t Tunnel whose connections to iterate.
- * @param iter Iterator.
- * @param iter_cls Closure for @c iter.
+ * @param ax key material to delete @a key from.
+ * @param key Key to delete.
  */
-void
-GCT_iterate_connections (struct CadetTunnel *t,
-                         GCT_ConnectionIterator iter,
-                         void *iter_cls)
+static void
+delete_skipped_key (struct CadetTunnelAxolotl *ax,
+                    struct CadetTunnelSkippedKey *key)
 {
-  for (struct CadetTConnection *ct = t->connection_head;
-       NULL != ct;
-       ct = ct->next)
-    iter (iter_cls,
-          ct->c);
+  GNUNET_CONTAINER_DLL_remove (ax->skipped_head,
+                               ax->skipped_tail,
+                               key);
+  GNUNET_free (key);
+  ax->skipped--;
 }
 
 
 /**
- * Closure for #iterate_channels_cb.
+ * Decrypt and verify data with the appropriate tunnel key and verify that the
+ * data has not been altered since it was sent by the remote peer.
+ *
+ * @param ax key material to use.
+ * @param dst Destination for the plaintext.
+ * @param src Source of the message. Can overlap with @c dst.
+ * @param size Size of the message.
+ * @return Size of the decrypted data, -1 if an error was encountered.
  */
-struct ChanIterCls
+static ssize_t
+try_old_ax_keys (struct CadetTunnelAxolotl *ax,
+                 void *dst,
+                 const struct GNUNET_CADET_TunnelEncryptedMessage *src,
+                 size_t size)
 {
-  /**
-   * Function to call.
-   */
-  GCT_ChannelIterator iter;
-
-  /**
-   * Closure for @e iter.
-   */
-  void *iter_cls;
-};
+  struct CadetTunnelSkippedKey *key;
+  struct GNUNET_ShortHashCode *hmac;
+  struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
+  struct GNUNET_CADET_TunnelEncryptedMessage plaintext_header;
+  struct GNUNET_CRYPTO_SymmetricSessionKey *valid_HK;
+  size_t esize;
+  size_t res;
+  size_t len;
+  unsigned int N;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Trying skipped keys\n");
+  hmac = &plaintext_header.hmac;
+  esize = size - sizeof (struct GNUNET_CADET_TunnelEncryptedMessage);
+
+  /* Find a correct Header Key */
+  valid_HK = NULL;
+  for (key = ax->skipped_head; NULL != key; key = key->next)
+  {
+    t_hmac (&src->ax_header,
+            sizeof (struct GNUNET_CADET_AxHeader) + esize,
+            0,
+            &key->HK,
+            hmac);
+    if (0 == memcmp (hmac,
+                     &src->hmac,
+                     sizeof (*hmac)))
+    {
+      valid_HK = &key->HK;
+      break;
+    }
+  }
+  if (NULL == key)
+    return -1;
+
+  /* Should've been checked in -cadet_connection.c handle_cadet_encrypted. */
+  GNUNET_assert (size > sizeof (struct GNUNET_CADET_TunnelEncryptedMessage));
+  len = size - sizeof (struct GNUNET_CADET_TunnelEncryptedMessage);
+  GNUNET_assert (len >= sizeof (struct GNUNET_MessageHeader));
+
+  /* Decrypt header */
+  GNUNET_CRYPTO_symmetric_derive_iv (&iv,
+                                     &key->HK,
+                                     NULL, 0,
+                                     NULL);
+  res = GNUNET_CRYPTO_symmetric_decrypt (&src->ax_header.Ns,
+                                         sizeof (struct GNUNET_CADET_AxHeader),
+                                         &key->HK,
+                                         &iv,
+                                         &plaintext_header.ax_header.Ns);
+  GNUNET_assert (sizeof (struct GNUNET_CADET_AxHeader) == res);
+
+  /* Find the correct message key */
+  N = ntohl (plaintext_header.ax_header.Ns);
+  while ( (NULL != key) &&
+          (N != key->Kn) )
+    key = key->next;
+  if ( (NULL == key) ||
+       (0 != memcmp (&key->HK,
+                     valid_HK,
+                     sizeof (*valid_HK))) )
+    return -1;
+
+  /* Decrypt payload */
+  GNUNET_CRYPTO_symmetric_derive_iv (&iv,
+                                     &key->MK,
+                                     NULL,
+                                     0,
+                                     NULL);
+  res = GNUNET_CRYPTO_symmetric_decrypt (&src[1],
+                                         len,
+                                         &key->MK,
+                                         &iv,
+                                         dst);
+  delete_skipped_key (ax,
+                      key);
+  return res;
+}
 
 
 /**
- * Helper function for #GCT_iterate_channels.
+ * Delete a key from the list of skipped keys.
  *
- * @param cls the `struct ChanIterCls`
- * @param key unused
- * @param value a `struct CadetChannel`
- * @return #GNUNET_OK
+ * @param ax key material to delete from.
+ * @param HKr Header Key to use.
  */
-static int
-iterate_channels_cb (void *cls,
-                     uint32_t key,
-                     void *value)
+static void
+store_skipped_key (struct CadetTunnelAxolotl *ax,
+                   const struct GNUNET_CRYPTO_SymmetricSessionKey *HKr)
 {
-  struct ChanIterCls *ctx = cls;
-  struct CadetChannel *ch = value;
-
-  ctx->iter (ctx->iter_cls,
-             ch);
-  return GNUNET_OK;
+  struct CadetTunnelSkippedKey *key;
+
+  key = GNUNET_new (struct CadetTunnelSkippedKey);
+  key->timestamp = GNUNET_TIME_absolute_get ();
+  key->Kn = ax->Nr;
+  key->HK = ax->HKr;
+  t_hmac_derive_key (&ax->CKr,
+                     &key->MK,
+                     "0",
+                     1);
+  t_hmac_derive_key (&ax->CKr,
+                     &ax->CKr,
+                     "1",
+                     1);
+  GNUNET_CONTAINER_DLL_insert (ax->skipped_head,
+                               ax->skipped_tail,
+                               key);
+  ax->skipped++;
+  ax->Nr++;
 }
 
 
 /**
- * Iterate over all channels of a tunnel.
+ * Stage skipped AX keys and calculate the message key.
+ * Stores each HK and MK for skipped messages.
  *
- * @param t Tunnel whose channels to iterate.
- * @param iter Iterator.
- * @param iter_cls Closure for @c iter.
+ * @param ax key material to use
+ * @param HKr Header key.
+ * @param Np Received meesage number.
+ * @return #GNUNET_OK if keys were stored.
+ *         #GNUNET_SYSERR if an error ocurred (@a Np not expected).
+ */
+static int
+store_ax_keys (struct CadetTunnelAxolotl *ax,
+               const struct GNUNET_CRYPTO_SymmetricSessionKey *HKr,
+               uint32_t Np)
+{
+  int gap;
+
+  gap = Np - ax->Nr;
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Storing skipped keys [%u, %u)\n",
+       ax->Nr,
+       Np);
+  if (MAX_KEY_GAP < gap)
+  {
+    /* Avoid DoS (forcing peer to do more than #MAX_KEY_GAP HMAC operations) */
+    /* TODO: start new key exchange on return */
+    GNUNET_break_op (0);
+    LOG (GNUNET_ERROR_TYPE_WARNING,
+         "Got message %u, expected %u+\n",
+         Np,
+         ax->Nr);
+    return GNUNET_SYSERR;
+  }
+  if (0 > gap)
+  {
+    /* Delayed message: don't store keys, flag to try old keys. */
+    return GNUNET_SYSERR;
+  }
+
+  while (ax->Nr < Np)
+    store_skipped_key (ax,
+                       HKr);
+
+  while (ax->skipped > MAX_SKIPPED_KEYS)
+    delete_skipped_key (ax,
+                        ax->skipped_tail);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Decrypt and verify data with the appropriate tunnel key and verify that the
+ * data has not been altered since it was sent by the remote peer.
+ *
+ * @param ax key material to use
+ * @param dst Destination for the plaintext.
+ * @param src Source of the message. Can overlap with @c dst.
+ * @param size Size of the message.
+ * @return Size of the decrypted data, -1 if an error was encountered.
+ */
+static ssize_t
+t_ax_decrypt_and_validate (struct CadetTunnelAxolotl *ax,
+                           void *dst,
+                           const struct GNUNET_CADET_TunnelEncryptedMessage *src,
+                           size_t size)
+{
+  struct GNUNET_ShortHashCode msg_hmac;
+  struct GNUNET_HashCode hmac;
+  struct GNUNET_CADET_TunnelEncryptedMessage plaintext_header;
+  uint32_t Np;
+  uint32_t PNp;
+  size_t esize; /* Size of encryped payload */
+
+  esize = size - sizeof (struct GNUNET_CADET_TunnelEncryptedMessage);
+
+  /* Try current HK */
+  t_hmac (&src->ax_header,
+          sizeof (struct GNUNET_CADET_AxHeader) + esize,
+          0, &ax->HKr,
+          &msg_hmac);
+  if (0 != memcmp (&msg_hmac,
+                   &src->hmac,
+                   sizeof (msg_hmac)))
+  {
+    static const char ctx[] = "axolotl ratchet";
+    struct GNUNET_CRYPTO_SymmetricSessionKey keys[3]; /* RKp, NHKp, CKp */
+    struct GNUNET_CRYPTO_SymmetricSessionKey HK;
+    struct GNUNET_HashCode dh;
+    struct GNUNET_CRYPTO_EcdhePublicKey *DHRp;
+
+    /* Try Next HK */
+    t_hmac (&src->ax_header,
+            sizeof (struct GNUNET_CADET_AxHeader) + esize,
+            0,
+            &ax->NHKr,
+            &msg_hmac);
+    if (0 != memcmp (&msg_hmac,
+                     &src->hmac,
+                     sizeof (msg_hmac)))
+    {
+      /* Try the skipped keys, if that fails, we're out of luck. */
+      return try_old_ax_keys (ax,
+                              dst,
+                              src,
+                              size);
+    }
+    HK = ax->HKr;
+    ax->HKr = ax->NHKr;
+    t_h_decrypt (ax,
+                 src,
+                 &plaintext_header);
+    Np = ntohl (plaintext_header.ax_header.Ns);
+    PNp = ntohl (plaintext_header.ax_header.PNs);
+    DHRp = &plaintext_header.ax_header.DHRs;
+    store_ax_keys (ax,
+                   &HK,
+                   PNp);
+
+    /* RKp, NHKp, CKp = KDF (HMAC-HASH (RK, DH (DHRp, DHRs))) */
+    GNUNET_CRYPTO_ecc_ecdh (&ax->DHRs,
+                            DHRp,
+                            &dh);
+    t_ax_hmac_hash (&ax->RK,
+                    &hmac,
+                    &dh, sizeof (dh));
+    GNUNET_CRYPTO_kdf (keys, sizeof (keys),
+                       ctx, sizeof (ctx),
+                       &hmac, sizeof (hmac),
+                       NULL);
+
+    /* Commit "purported" keys */
+    ax->RK = keys[0];
+    ax->NHKr = keys[1];
+    ax->CKr = keys[2];
+    ax->DHRr = *DHRp;
+    ax->Nr = 0;
+    ax->ratchet_allowed = GNUNET_YES;
+  }
+  else
+  {
+    t_h_decrypt (ax,
+                 src,
+                 &plaintext_header);
+    Np = ntohl (plaintext_header.ax_header.Ns);
+    PNp = ntohl (plaintext_header.ax_header.PNs);
+  }
+  if ( (Np != ax->Nr) &&
+       (GNUNET_OK != store_ax_keys (ax,
+                                    &ax->HKr,
+                                    Np)) )
+  {
+    /* Try the skipped keys, if that fails, we're out of luck. */
+    return try_old_ax_keys (ax,
+                            dst,
+                            src,
+                            size);
+  }
+
+  t_ax_decrypt (ax,
+                dst,
+                &src[1],
+                esize);
+  ax->Nr = Np + 1;
+  return esize;
+}
+
+
+/**
+ * Our tunnel became ready for the first time, notify channels
+ * that have been waiting.
+ *
+ * @param cls our tunnel, not used
+ * @param key unique ID of the channel, not used
+ * @param value the `struct CadetChannel` to notify
+ * @return #GNUNET_OK (continue to iterate)
+ */
+static int
+notify_tunnel_up_cb (void *cls,
+                     uint32_t key,
+                     void *value)
+{
+  struct CadetChannel *ch = value;
+
+  GCCH_tunnel_up (ch);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Change the tunnel encryption state.
+ * If the encryption state changes to OK, stop the rekey task.
+ *
+ * @param t Tunnel whose encryption state to change, or NULL.
+ * @param state New encryption state.
+ */
+void
+GCT_change_estate (struct CadetTunnel *t,
+                   enum CadetTunnelEState state)
+{
+  enum CadetTunnelEState old = t->estate;
+
+  t->estate = state;
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "%s estate changed from %s to %s\n",
+       GCT_2s (t),
+       estate2s (old),
+       estate2s (state));
+
+  if ( (CADET_TUNNEL_KEY_OK != old) &&
+       (CADET_TUNNEL_KEY_OK == t->estate) )
+  {
+    if (NULL != t->kx_task)
+    {
+      GNUNET_SCHEDULER_cancel (t->kx_task);
+      t->kx_task = NULL;
+    }
+    /* notify all channels that have been waiting */
+    GNUNET_CONTAINER_multihashmap32_iterate (t->channels,
+                                             &notify_tunnel_up_cb,
+                                             t);
+    if (NULL != t->send_task)
+      GNUNET_SCHEDULER_cancel (t->send_task);
+    t->send_task = GNUNET_SCHEDULER_add_now (&trigger_transmissions,
+                                             t);
+  }
+}
+
+
+/**
+ * Send a KX message.
+ *
+ * @param t tunnel on which to send the KX_AUTH
+ * @param ct Tunnel and connection on which to send the KX_AUTH, NULL if
+ *           we are to find one that is ready.
+ * @param ax axolotl key context to use
+ */
+static void
+send_kx (struct CadetTunnel *t,
+         struct CadetTConnection *ct,
+         struct CadetTunnelAxolotl *ax)
+{
+  struct CadetConnection *cc;
+  struct GNUNET_MQ_Envelope *env;
+  struct GNUNET_CADET_TunnelKeyExchangeMessage *msg;
+  enum GNUNET_CADET_KX_Flags flags;
+
+  if ( (NULL == ct) ||
+       (GNUNET_NO == ct->is_ready) )
+    ct = get_ready_connection (t);
+  if (NULL == ct)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Wanted to send %s in state %s, but no connection is ready, deferring\n",
+         GCT_2s (t),
+         estate2s (t->estate));
+    t->next_kx_attempt = GNUNET_TIME_absolute_get ();
+    return;
+  }
+  cc = ct->cc;
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Sending KX on %s via %s in state %s\n",
+       GCT_2s (t),
+       GCC_2s (cc),
+       estate2s (t->estate));
+  env = GNUNET_MQ_msg (msg,
+                       GNUNET_MESSAGE_TYPE_CADET_TUNNEL_KX);
+  flags = GNUNET_CADET_KX_FLAG_FORCE_REPLY; /* always for KX */
+  msg->flags = htonl (flags);
+  msg->cid = *GCC_get_id (cc);
+  GNUNET_CRYPTO_ecdhe_key_get_public (&ax->kx_0,
+                                      &msg->ephemeral_key);
+  GNUNET_CRYPTO_ecdhe_key_get_public (&ax->DHRs,
+                                      &msg->ratchet_key);
+  mark_connection_unready (ct);
+  t->kx_retry_delay = GNUNET_TIME_STD_BACKOFF (t->kx_retry_delay);
+  t->next_kx_attempt = GNUNET_TIME_relative_to_absolute (t->kx_retry_delay);
+  if (CADET_TUNNEL_KEY_UNINITIALIZED == t->estate)
+    GCT_change_estate (t,
+                       CADET_TUNNEL_KEY_AX_SENT);
+  else if (CADET_TUNNEL_KEY_AX_RECV == t->estate)
+    GCT_change_estate (t,
+                       CADET_TUNNEL_KEY_AX_SENT_AND_RECV);
+  GCC_transmit (cc,
+                env);
+}
+
+
+/**
+ * Send a KX_AUTH message.
+ *
+ * @param t tunnel on which to send the KX_AUTH
+ * @param ct Tunnel and connection on which to send the KX_AUTH, NULL if
+ *           we are to find one that is ready.
+ * @param ax axolotl key context to use
+ * @param force_reply Force the other peer to reply with a KX_AUTH message
+ *         (set if we would like to transmit right now, but cannot)
+ */
+static void
+send_kx_auth (struct CadetTunnel *t,
+              struct CadetTConnection *ct,
+              struct CadetTunnelAxolotl *ax,
+              int force_reply)
+{
+  struct CadetConnection *cc;
+  struct GNUNET_MQ_Envelope *env;
+  struct GNUNET_CADET_TunnelKeyExchangeAuthMessage *msg;
+  enum GNUNET_CADET_KX_Flags flags;
+
+  if ( (NULL == ct) ||
+       (GNUNET_NO == ct->is_ready) )
+    ct = get_ready_connection (t);
+  if (NULL == ct)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Wanted to send KX_AUTH on %s, but no connection is ready, deferring\n",
+         GCT_2s (t));
+    t->next_kx_attempt = GNUNET_TIME_absolute_get ();
+    t->kx_auth_requested = GNUNET_YES; /* queue KX_AUTH independent of estate */
+    return;
+  }
+  t->kx_auth_requested = GNUNET_NO; /* clear flag */
+  cc = ct->cc;
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Sending KX_AUTH on %s using %s\n",
+       GCT_2s (t),
+       GCC_2s (ct->cc));
+
+  env = GNUNET_MQ_msg (msg,
+                       GNUNET_MESSAGE_TYPE_CADET_TUNNEL_KX_AUTH);
+  flags = GNUNET_CADET_KX_FLAG_NONE;
+  if (GNUNET_YES == force_reply)
+    flags |= GNUNET_CADET_KX_FLAG_FORCE_REPLY;
+  msg->kx.flags = htonl (flags);
+  msg->kx.cid = *GCC_get_id (cc);
+  GNUNET_CRYPTO_ecdhe_key_get_public (&ax->kx_0,
+                                      &msg->kx.ephemeral_key);
+  GNUNET_CRYPTO_ecdhe_key_get_public (&ax->DHRs,
+                                      &msg->kx.ratchet_key);
+  /* Compute authenticator (this is the main difference to #send_kx()) */
+  GNUNET_CRYPTO_hash (&ax->RK,
+                      sizeof (ax->RK),
+                      &msg->auth);
+
+  /* Compute when to be triggered again; actual job will
+     be scheduled via #connection_ready_cb() */
+  t->kx_retry_delay
+    = GNUNET_TIME_STD_BACKOFF (t->kx_retry_delay);
+  t->next_kx_attempt
+    = GNUNET_TIME_relative_to_absolute (t->kx_retry_delay);
+
+  /* Send via cc, mark it as unready */
+  mark_connection_unready (ct);
+
+  /* Update state machine, unless we are already OK */
+  if (CADET_TUNNEL_KEY_OK != t->estate)
+    GCT_change_estate (t,
+                       CADET_TUNNEL_KEY_AX_AUTH_SENT);
+
+  GCC_transmit (cc,
+                env);
+}
+
+
+/**
+ * Cleanup state used by @a ax.
+ *
+ * @param ax state to free, but not memory of @a ax itself
+ */
+static void
+cleanup_ax (struct CadetTunnelAxolotl *ax)
+{
+  while (NULL != ax->skipped_head)
+    delete_skipped_key (ax,
+                        ax->skipped_head);
+  GNUNET_assert (0 == ax->skipped);
+  GNUNET_CRYPTO_ecdhe_key_clear (&ax->kx_0);
+  GNUNET_CRYPTO_ecdhe_key_clear (&ax->DHRs);
+}
+
+
+/**
+ * Update our Axolotl key state based on the KX data we received.
+ * Computes the new chain keys, and root keys, etc, and also checks
+ * wether this is a replay of the current chain.
+ *
+ * @param[in|out] axolotl chain key state to recompute
+ * @param pid peer identity of the other peer
+ * @param ephemeral_key ephemeral public key of the other peer
+ * @param ratchet_key senders next ephemeral public key
+ * @return #GNUNET_OK on success, #GNUNET_NO if the resulting
+ *       root key is already in @a ax and thus the KX is useless;
+ *       #GNUNET_SYSERR on hard errors (i.e. @a pid is #my_full_id)
+ */
+static int
+update_ax_by_kx (struct CadetTunnelAxolotl *ax,
+                 const struct GNUNET_PeerIdentity *pid,
+                 const struct GNUNET_CRYPTO_EcdhePublicKey *ephemeral_key,
+                 const struct GNUNET_CRYPTO_EcdhePublicKey *ratchet_key)
+{
+  struct GNUNET_HashCode key_material[3];
+  struct GNUNET_CRYPTO_SymmetricSessionKey keys[5];
+  const char salt[] = "CADET Axolotl salt";
+  int am_I_alice;
+
+  if (0 > GNUNET_CRYPTO_cmp_peer_identity (&my_full_id,
+                                           pid))
+    am_I_alice = GNUNET_YES;
+  else if (0 < GNUNET_CRYPTO_cmp_peer_identity (&my_full_id,
+                                                pid))
+    am_I_alice = GNUNET_NO;
+  else
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+
+  if (0 == memcmp (&ax->DHRr,
+                   ratchet_key,
+                   sizeof (*ratchet_key)))
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Ratchet key already known. Ignoring KX.\n");
+    return GNUNET_NO;
+  }
+
+  ax->DHRr = *ratchet_key;
+
+  /* ECDH A B0 */
+  if (GNUNET_YES == am_I_alice)
+  {
+    GNUNET_CRYPTO_eddsa_ecdh (my_private_key,      /* A */
+                              ephemeral_key, /* B0 */
+                              &key_material[0]);
+  }
+  else
+  {
+    GNUNET_CRYPTO_ecdh_eddsa (&ax->kx_0,            /* B0 */
+                              &pid->public_key,    /* A */
+                              &key_material[0]);
+  }
+
+  /* ECDH A0 B */
+  if (GNUNET_YES == am_I_alice)
+  {
+    GNUNET_CRYPTO_ecdh_eddsa (&ax->kx_0,            /* A0 */
+                              &pid->public_key,    /* B */
+                              &key_material[1]);
+  }
+  else
+  {
+    GNUNET_CRYPTO_eddsa_ecdh (my_private_key,      /* A */
+                              ephemeral_key, /* B0 */
+                              &key_material[1]);
+
+
+  }
+
+  /* ECDH A0 B0 */
+  /* (This is the triple-DH, we could probably safely skip this,
+     as A0/B0 are already in the key material.) */
+  GNUNET_CRYPTO_ecc_ecdh (&ax->kx_0,             /* A0 or B0 */
+                          ephemeral_key,  /* B0 or A0 */
+                          &key_material[2]);
+
+  /* KDF */
+  GNUNET_CRYPTO_kdf (keys, sizeof (keys),
+                     salt, sizeof (salt),
+                     &key_material, sizeof (key_material),
+                     NULL);
+
+  if (0 == memcmp (&ax->RK,
+                   &keys[0],
+                   sizeof (ax->RK)))
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Root key of handshake already known. Ignoring KX.\n");
+    return GNUNET_NO;
+  }
+
+  ax->RK = keys[0];
+  if (GNUNET_YES == am_I_alice)
+  {
+    ax->HKr = keys[1];
+    ax->NHKs = keys[2];
+    ax->NHKr = keys[3];
+    ax->CKr = keys[4];
+    ax->ratchet_flag = GNUNET_YES;
+  }
+  else
+  {
+    ax->HKs = keys[1];
+    ax->NHKr = keys[2];
+    ax->NHKs = keys[3];
+    ax->CKs = keys[4];
+    ax->ratchet_flag = GNUNET_NO;
+    ax->ratchet_expiration
+      = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get(),
+                                  ratchet_time);
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Try to redo the KX or KX_AUTH handshake, if we can.
+ *
+ * @param cls the `struct CadetTunnel` to do KX for.
+ */
+static void
+retry_kx (void *cls)
+{
+  struct CadetTunnel *t = cls;
+  struct CadetTunnelAxolotl *ax;
+
+  t->kx_task = NULL;
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Trying to make KX progress on %s in state %s\n",
+       GCT_2s (t),
+       estate2s (t->estate));
+  switch (t->estate)
+  {
+  case CADET_TUNNEL_KEY_UNINITIALIZED: /* first attempt */
+  case CADET_TUNNEL_KEY_AX_SENT:       /* trying again */
+    send_kx (t,
+             NULL,
+             &t->ax);
+    break;
+  case CADET_TUNNEL_KEY_AX_RECV:
+  case CADET_TUNNEL_KEY_AX_SENT_AND_RECV:
+    /* We are responding, so only require reply
+       if WE have a channel waiting. */
+    if (NULL != t->unverified_ax)
+    {
+      /* Send AX_AUTH so we might get this one verified */
+      ax = t->unverified_ax;
+    }
+    else
+    {
+      /* How can this be? */
+      GNUNET_break (0);
+      ax = &t->ax;
+    }
+    send_kx_auth (t,
+                  NULL,
+                  ax,
+                  (0 == GCT_count_channels (t))
+                  ? GNUNET_NO
+                  : GNUNET_YES);
+    break;
+  case CADET_TUNNEL_KEY_AX_AUTH_SENT:
+    /* We are responding, so only require reply
+       if WE have a channel waiting. */
+    if (NULL != t->unverified_ax)
+    {
+      /* Send AX_AUTH so we might get this one verified */
+      ax = t->unverified_ax;
+    }
+    else
+    {
+      /* How can this be? */
+      GNUNET_break (0);
+      ax = &t->ax;
+    }
+    send_kx_auth (t,
+                  NULL,
+                  ax,
+                  (0 == GCT_count_channels (t))
+                  ? GNUNET_NO
+                  : GNUNET_YES);
+    break;
+  case CADET_TUNNEL_KEY_OK:
+    /* Must have been the *other* peer asking us to
+       respond with a KX_AUTH. */
+    if (NULL != t->unverified_ax)
+    {
+      /* Sending AX_AUTH in response to AX so we might get this one verified */
+      ax = t->unverified_ax;
+    }
+    else
+    {
+      /* Sending AX_AUTH in response to AX_AUTH */
+      ax = &t->ax;
+    }
+    send_kx_auth (t,
+                  NULL,
+                  ax,
+                  GNUNET_NO);
+    break;
+  }
+}
+
+
+/**
+ * Handle KX message that lacks authentication (and which will thus
+ * only be considered authenticated after we respond with our own
+ * KX_AUTH and finally successfully decrypt payload).
+ *
+ * @param ct connection/tunnel combo that received encrypted message
+ * @param msg the key exchange message
+ */
+void
+GCT_handle_kx (struct CadetTConnection *ct,
+               const struct GNUNET_CADET_TunnelKeyExchangeMessage *msg)
+{
+  struct CadetTunnel *t = ct->t;
+  struct CadetTunnelAxolotl *ax;
+  int ret;
+
+  if (0 ==
+      memcmp (&t->ax.DHRr,
+              &msg->ratchet_key,
+              sizeof (msg->ratchet_key)))
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Got duplicate KX. Firing back KX_AUTH.\n");
+    send_kx_auth (t,
+                  ct,
+                  &t->ax,
+                  GNUNET_NO);
+    return;
+  }
+
+  /* We only keep ONE unverified KX around, so if there is an existing one,
+     clean it up. */
+  if (NULL != t->unverified_ax)
+  {
+    if (0 ==
+        memcmp (&t->unverified_ax->DHRr,
+                &msg->ratchet_key,
+                sizeof (msg->ratchet_key)))
+    {
+      LOG (GNUNET_ERROR_TYPE_DEBUG,
+           "Got duplicate unverified KX on %s. Fire back KX_AUTH again.\n",
+           GCT_2s (t));
+      send_kx_auth (t,
+                    ct,
+                    t->unverified_ax,
+                    GNUNET_NO);
+      return;
+    }
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Dropping old unverified KX state. Got a fresh KX for %s.\n",
+         GCT_2s (t));
+    memset (t->unverified_ax,
+            0,
+            sizeof (struct CadetTunnelAxolotl));
+    t->unverified_ax->DHRs = t->ax.DHRs;
+    t->unverified_ax->kx_0 = t->ax.kx_0;
+  }
+  else
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Creating fresh unverified KX for %s.\n",
+         GCT_2s (t));
+    t->unverified_ax = GNUNET_new (struct CadetTunnelAxolotl);
+    t->unverified_ax->DHRs = t->ax.DHRs;
+    t->unverified_ax->kx_0 = t->ax.kx_0;
+  }
+  /* Set as the 'current' RK/DHRr the one we are currently using,
+     so that the duplicate-detection logic of
+     #update_ax_by_kx can work. */
+  t->unverified_ax->RK = t->ax.RK;
+  t->unverified_ax->DHRr = t->ax.DHRr;
+  t->unverified_attempts = 0;
+  ax = t->unverified_ax;
+
+  /* Update 'ax' by the new key material */
+  ret = update_ax_by_kx (ax,
+                         GCP_get_id (t->destination),
+                         &msg->ephemeral_key,
+                         &msg->ratchet_key);
+  GNUNET_break (GNUNET_SYSERR != ret);
+  if (GNUNET_OK != ret)
+    return; /* duplicate KX, nothing to do */
+
+  /* move ahead in our state machine */
+  if (CADET_TUNNEL_KEY_UNINITIALIZED == t->estate)
+    GCT_change_estate (t,
+                       CADET_TUNNEL_KEY_AX_RECV);
+  else if (CADET_TUNNEL_KEY_AX_SENT == t->estate)
+    GCT_change_estate (t,
+                       CADET_TUNNEL_KEY_AX_SENT_AND_RECV);
+
+  /* KX is still not done, try again our end. */
+  if (CADET_TUNNEL_KEY_OK != t->estate)
+  {
+    if (NULL != t->kx_task)
+      GNUNET_SCHEDULER_cancel (t->kx_task);
+    t->kx_task
+      = GNUNET_SCHEDULER_add_now (&retry_kx,
+                                  t);
+  }
+}
+
+
+/**
+ * Handle KX_AUTH message.
+ *
+ * @param ct connection/tunnel combo that received encrypted message
+ * @param msg the key exchange message
+ */
+void
+GCT_handle_kx_auth (struct CadetTConnection *ct,
+                    const struct GNUNET_CADET_TunnelKeyExchangeAuthMessage *msg)
+{
+  struct CadetTunnel *t = ct->t;
+  struct CadetTunnelAxolotl ax_tmp;
+  struct GNUNET_HashCode kx_auth;
+  int ret;
+
+  if ( (CADET_TUNNEL_KEY_UNINITIALIZED == t->estate) ||
+       (CADET_TUNNEL_KEY_AX_RECV == t->estate) )
+  {
+    /* Confusing, we got a KX_AUTH before we even send our own
+       KX. This should not happen. We'll send our own KX ASAP anyway,
+       so let's ignore this here. */
+    GNUNET_break_op (0);
+    return;
+  }
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Handling KX_AUTH message for %s\n",
+       GCT_2s (t));
+
+  /* We do everything in ax_tmp until we've checked the authentication
+     so we don't clobber anything we care about by accident. */
+  ax_tmp = t->ax;
+
+  /* Update 'ax' by the new key material */
+  ret = update_ax_by_kx (&ax_tmp,
+                         GCP_get_id (t->destination),
+                         &msg->kx.ephemeral_key,
+                         &msg->kx.ratchet_key);
+  if (GNUNET_OK != ret)
+  {
+    if (GNUNET_NO == ret)
+      GNUNET_STATISTICS_update (stats,
+                                "# redundant KX_AUTH received",
+                                1,
+                                GNUNET_NO);
+    else
+      GNUNET_break (0); /* connect to self!? */
+    return;
+  }
+  GNUNET_CRYPTO_hash (&ax_tmp.RK,
+                      sizeof (ax_tmp.RK),
+                      &kx_auth);
+  if (0 != memcmp (&kx_auth,
+                   &msg->auth,
+                   sizeof (kx_auth)))
+  {
+    /* This KX_AUTH is not using the latest KX/KX_AUTH data
+       we transmitted to the sender, refuse it, try KX again. */
+    GNUNET_STATISTICS_update (stats,
+                              "# KX_AUTH not using our last KX received (auth failure)",
+                              1,
+                              GNUNET_NO);
+    send_kx (t,
+             ct,
+             &t->ax);
+    return;
+  }
+  /* Yep, we're good. */
+  t->ax = ax_tmp;
+  if (NULL != t->unverified_ax)
+  {
+    /* We got some "stale" KX before, drop that. */
+    cleanup_ax (t->unverified_ax);
+    GNUNET_free (t->unverified_ax);
+    t->unverified_ax = NULL;
+  }
+
+  /* move ahead in our state machine */
+  switch (t->estate)
+  {
+  case CADET_TUNNEL_KEY_UNINITIALIZED:
+  case CADET_TUNNEL_KEY_AX_RECV:
+    /* Checked above, this is impossible. */
+    GNUNET_assert (0);
+    break;
+  case CADET_TUNNEL_KEY_AX_SENT:      /* This is the normal case */
+  case CADET_TUNNEL_KEY_AX_SENT_AND_RECV: /* both peers started KX */
+  case CADET_TUNNEL_KEY_AX_AUTH_SENT: /* both peers now did KX_AUTH */
+    GCT_change_estate (t,
+                       CADET_TUNNEL_KEY_OK);
+    break;
+  case CADET_TUNNEL_KEY_OK:
+    /* Did not expect another KX_AUTH, but so what, still acceptable.
+       Nothing to do here. */
+    break;
+  }
+}
+
+
+
+/* ************************************** end core crypto ***************************** */
+
+
+/**
+ * Compute the next free channel tunnel number for this tunnel.
+ *
+ * @param t the tunnel
+ * @return unused number that can uniquely identify a channel in the tunnel
+ */
+static struct GNUNET_CADET_ChannelTunnelNumber
+get_next_free_ctn (struct CadetTunnel *t)
+{
+#define HIGH_BIT 0x8000000
+  struct GNUNET_CADET_ChannelTunnelNumber ret;
+  uint32_t ctn;
+  int cmp;
+  uint32_t highbit;
+
+  cmp = GNUNET_CRYPTO_cmp_peer_identity (&my_full_id,
+                                         GCP_get_id (GCT_get_destination (t)));
+  if (0 < cmp)
+    highbit = HIGH_BIT;
+  else if (0 > cmp)
+    highbit = 0;
+  else
+    GNUNET_assert (0); // loopback must never go here!
+  ctn = ntohl (t->next_ctn.cn);
+  while (NULL !=
+         GNUNET_CONTAINER_multihashmap32_get (t->channels,
+                                              ctn | highbit))
+  {
+    ctn = ((ctn + 1) & (~ HIGH_BIT));
+  }
+  t->next_ctn.cn = htonl ((ctn + 1) & (~ HIGH_BIT));
+  ret.cn = htonl (ctn | highbit);
+  return ret;
+}
+
+
+/**
+ * Add a channel to a tunnel, and notify channel that we are ready
+ * for transmission if we are already up.  Otherwise that notification
+ * will be done later in #notify_tunnel_up_cb().
+ *
+ * @param t Tunnel.
+ * @param ch Channel
+ * @return unique number identifying @a ch within @a t
+ */
+struct GNUNET_CADET_ChannelTunnelNumber
+GCT_add_channel (struct CadetTunnel *t,
+                 struct CadetChannel *ch)
+{
+  struct GNUNET_CADET_ChannelTunnelNumber ctn;
+
+  ctn = get_next_free_ctn (t);
+  if (NULL != t->destroy_task)
+  {
+    GNUNET_SCHEDULER_cancel (t->destroy_task);
+    t->destroy_task = NULL;
+  }
+  GNUNET_assert (GNUNET_YES ==
+                 GNUNET_CONTAINER_multihashmap32_put (t->channels,
+                                                      ntohl (ctn.cn),
+                                                      ch,
+                                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Adding %s to %s\n",
+       GCCH_2s (ch),
+       GCT_2s (t));
+  switch (t->estate)
+  {
+  case CADET_TUNNEL_KEY_UNINITIALIZED:
+    /* waiting for connection to start KX */
+    break;
+  case CADET_TUNNEL_KEY_AX_RECV:
+  case CADET_TUNNEL_KEY_AX_SENT:
+  case CADET_TUNNEL_KEY_AX_SENT_AND_RECV:
+    /* we're currently waiting for KX to complete */
+    break;
+  case CADET_TUNNEL_KEY_AX_AUTH_SENT:
+    /* waiting for OTHER peer to send us data,
+       we might need to prompt more aggressively! */
+    if (NULL == t->kx_task)
+      t->kx_task
+        = GNUNET_SCHEDULER_add_at (t->next_kx_attempt,
+                                   &retry_kx,
+                                   t);
+    break;
+  case CADET_TUNNEL_KEY_OK:
+    /* We are ready. Tell the new channel that we are up. */
+    GCCH_tunnel_up (ch);
+    break;
+  }
+  return ctn;
+}
+
+
+/**
+ * We lost a connection, remove it from our list and clean up
+ * the connection object itself.
+ *
+ * @param ct binding of connection to tunnel of the connection that was lost.
+ */
+void
+GCT_connection_lost (struct CadetTConnection *ct)
+{
+  struct CadetTunnel *t = ct->t;
+
+  if (GNUNET_YES == ct->is_ready)
+  {
+    GNUNET_CONTAINER_DLL_remove (t->connection_ready_head,
+                                 t->connection_ready_tail,
+                                 ct);
+    t->num_ready_connections--;
+  }
+  else
+  {
+    GNUNET_CONTAINER_DLL_remove (t->connection_busy_head,
+                                 t->connection_busy_tail,
+                                 ct);
+    t->num_busy_connections--;
+  }
+  GNUNET_free (ct);
+}
+
+
+/**
+ * Clean up connection @a ct of a tunnel.
+ *
+ * @param cls the `struct CadetTunnel`
+ * @param ct connection to clean up
+ */
+static void
+destroy_t_connection (void *cls,
+                      struct CadetTConnection *ct)
+{
+  struct CadetTunnel *t = cls;
+  struct CadetConnection *cc = ct->cc;
+
+  GNUNET_assert (ct->t == t);
+  GCT_connection_lost (ct);
+  GCC_destroy_without_tunnel (cc);
+}
+
+
+/**
+ * This tunnel is no longer used, destroy it.
+ *
+ * @param cls the idle tunnel
+ */
+static void
+destroy_tunnel (void *cls)
+{
+  struct CadetTunnel *t = cls;
+  struct CadetTunnelQueueEntry *tq;
+
+  t->destroy_task = NULL;
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Destroying idle %s\n",
+       GCT_2s (t));
+  GNUNET_assert (0 == GCT_count_channels (t));
+  GCT_iterate_connections (t,
+                           &destroy_t_connection,
+                           t);
+  GNUNET_assert (NULL == t->connection_ready_head);
+  GNUNET_assert (NULL == t->connection_busy_head);
+  while (NULL != (tq = t->tq_head))
+  {
+    if (NULL != tq->cont)
+      tq->cont (tq->cont_cls,
+                NULL);
+    GCT_send_cancel (tq);
+  }
+  GCP_drop_tunnel (t->destination,
+                   t);
+  GNUNET_CONTAINER_multihashmap32_destroy (t->channels);
+  if (NULL != t->maintain_connections_task)
+  {
+    GNUNET_SCHEDULER_cancel (t->maintain_connections_task);
+    t->maintain_connections_task = NULL;
+  }
+  if (NULL != t->send_task)
+  {
+    GNUNET_SCHEDULER_cancel (t->send_task);
+    t->send_task = NULL;
+  }
+  if (NULL != t->kx_task)
+  {
+    GNUNET_SCHEDULER_cancel (t->kx_task);
+    t->kx_task = NULL;
+  }
+  GNUNET_MST_destroy (t->mst);
+  GNUNET_MQ_destroy (t->mq);
+  if (NULL != t->unverified_ax)
+  {
+    cleanup_ax (t->unverified_ax);
+    GNUNET_free (t->unverified_ax);
+  }
+  cleanup_ax (&t->ax);
+  GNUNET_assert (NULL == t->destroy_task);
+  GNUNET_free (t);
+}
+
+
+/**
+ * Remove a channel from a tunnel.
+ *
+ * @param t Tunnel.
+ * @param ch Channel
+ * @param ctn unique number identifying @a ch within @a t
+ */
+void
+GCT_remove_channel (struct CadetTunnel *t,
+                    struct CadetChannel *ch,
+                    struct GNUNET_CADET_ChannelTunnelNumber ctn)
+{
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Removing %s from %s\n",
+       GCCH_2s (ch),
+       GCT_2s (t));
+  GNUNET_assert (GNUNET_YES ==
+                 GNUNET_CONTAINER_multihashmap32_remove (t->channels,
+                                                         ntohl (ctn.cn),
+                                                         ch));
+  if ( (0 ==
+        GCT_count_channels (t)) &&
+       (NULL == t->destroy_task) )
+  {
+    t->destroy_task
+      = GNUNET_SCHEDULER_add_delayed (IDLE_DESTROY_DELAY,
+                                      &destroy_tunnel,
+                                      t);
+  }
+}
+
+
+/**
+ * Destroy remaining channels during shutdown.
+ *
+ * @param cls the `struct CadetTunnel` of the channel
+ * @param key key of the channel
+ * @param value the `struct CadetChannel`
+ * @return #GNUNET_OK (continue to iterate)
+ */
+static int
+destroy_remaining_channels (void *cls,
+                            uint32_t key,
+                            void *value)
+{
+  struct CadetChannel *ch = value;
+
+  GCCH_handle_remote_destroy (ch,
+                              NULL);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Destroys the tunnel @a t now, without delay. Used during shutdown.
+ *
+ * @param t tunnel to destroy
+ */
+void
+GCT_destroy_tunnel_now (struct CadetTunnel *t)
+{
+  GNUNET_assert (GNUNET_YES == shutting_down);
+  GNUNET_CONTAINER_multihashmap32_iterate (t->channels,
+                                           &destroy_remaining_channels,
+                                           t);
+  GNUNET_assert (0 ==
+                 GCT_count_channels (t));
+  if (NULL != t->destroy_task)
+  {
+    GNUNET_SCHEDULER_cancel (t->destroy_task);
+    t->destroy_task = NULL;
+  }
+  destroy_tunnel (t);
+}
+
+
+/**
+ * Send normal payload from queue in @a t via connection @a ct.
+ * Does nothing if our payload queue is empty.
+ *
+ * @param t tunnel to send data from
+ * @param ct connection to use for transmission (is ready)
+ */
+static void
+try_send_normal_payload (struct CadetTunnel *t,
+                         struct CadetTConnection *ct)
+{
+  struct CadetTunnelQueueEntry *tq;
+
+  GNUNET_assert (GNUNET_YES == ct->is_ready);
+  tq = t->tq_head;
+  if (NULL == tq)
+  {
+    /* no messages pending right now */
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Not sending payload of %s on ready %s (nothing pending)\n",
+         GCT_2s (t),
+         GCC_2s (ct->cc));
+    return;
+  }
+  /* ready to send message 'tq' on tunnel 'ct' */
+  GNUNET_assert (t == tq->t);
+  GNUNET_CONTAINER_DLL_remove (t->tq_head,
+                               t->tq_tail,
+                               tq);
+  if (NULL != tq->cid)
+    *tq->cid = *GCC_get_id (ct->cc);
+  mark_connection_unready (ct);
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Sending payload of %s on %s\n",
+       GCT_2s (t),
+       GCC_2s (ct->cc));
+  GCC_transmit (ct->cc,
+                tq->env);
+  if (NULL != tq->cont)
+    tq->cont (tq->cont_cls,
+              GCC_get_id (ct->cc));
+  GNUNET_free (tq);
+}
+
+
+/**
+ * A connection is @a is_ready for transmission.  Looks at our message
+ * queue and if there is a message, sends it out via the connection.
+ *
+ * @param cls the `struct CadetTConnection` that is @a is_ready
+ * @param is_ready #GNUNET_YES if connection are now ready,
+ *                 #GNUNET_NO if connection are no longer ready
+ */
+static void
+connection_ready_cb (void *cls,
+                     int is_ready)
+{
+  struct CadetTConnection *ct = cls;
+  struct CadetTunnel *t = ct->t;
+
+  if (GNUNET_NO == is_ready)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "%s no longer ready for %s\n",
+         GCC_2s (ct->cc),
+         GCT_2s (t));
+    mark_connection_unready (ct);
+    return;
+  }
+  GNUNET_assert (GNUNET_NO == ct->is_ready);
+  GNUNET_CONTAINER_DLL_remove (t->connection_busy_head,
+                               t->connection_busy_tail,
+                               ct);
+  GNUNET_assert (0 < t->num_busy_connections);
+  t->num_busy_connections--;
+  ct->is_ready = GNUNET_YES;
+  GNUNET_CONTAINER_DLL_insert_tail (t->connection_ready_head,
+                                    t->connection_ready_tail,
+                                    ct);
+  t->num_ready_connections++;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "%s now ready for %s in state %s\n",
+       GCC_2s (ct->cc),
+       GCT_2s (t),
+       estate2s (t->estate));
+  switch (t->estate)
+  {
+  case CADET_TUNNEL_KEY_UNINITIALIZED:
+    /* Do not begin KX if WE have no channels waiting! */
+    if (0 == GCT_count_channels (t))
+      return;
+    /* We are uninitialized, just transmit immediately,
+       without undue delay. */
+    if (NULL != t->kx_task)
+    {
+      GNUNET_SCHEDULER_cancel (t->kx_task);
+      t->kx_task = NULL;
+    }
+    send_kx (t,
+             ct,
+             &t->ax);
+    break;
+  case CADET_TUNNEL_KEY_AX_RECV:
+  case CADET_TUNNEL_KEY_AX_SENT:
+  case CADET_TUNNEL_KEY_AX_SENT_AND_RECV:
+  case CADET_TUNNEL_KEY_AX_AUTH_SENT:
+    /* we're currently waiting for KX to complete, schedule job */
+    if (NULL == t->kx_task)
+      t->kx_task
+        = GNUNET_SCHEDULER_add_at (t->next_kx_attempt,
+                                   &retry_kx,
+                                   t);
+    break;
+  case CADET_TUNNEL_KEY_OK:
+    if (GNUNET_YES == t->kx_auth_requested)
+    {
+      if (NULL != t->kx_task)
+      {
+        GNUNET_SCHEDULER_cancel (t->kx_task);
+        t->kx_task = NULL;
+      }
+      send_kx_auth (t,
+                    ct,
+                    &t->ax,
+                    GNUNET_NO);
+      return;
+    }
+    try_send_normal_payload (t,
+                             ct);
+    break;
+  }
+}
+
+
+/**
+ * Called when either we have a new connection, or a new message in the
+ * queue, or some existing connection has transmission capacity.  Looks
+ * at our message queue and if there is a message, picks a connection
+ * to send it on.
+ *
+ * @param cls the `struct CadetTunnel` to process messages on
+ */
+static void
+trigger_transmissions (void *cls)
+{
+  struct CadetTunnel *t = cls;
+  struct CadetTConnection *ct;
+
+  t->send_task = NULL;
+  if (NULL == t->tq_head)
+    return; /* no messages pending right now */
+  ct = get_ready_connection (t);
+  if (NULL == ct)
+    return; /* no connections ready */
+  try_send_normal_payload (t,
+                           ct);
+}
+
+
+/**
+ * Closure for #evaluate_connection. Used to assemble summary information
+ * about the existing connections so we can evaluate a new path.
+ */
+struct EvaluationSummary
+{
+
+  /**
+   * Minimum length of any of our connections, `UINT_MAX` if we have none.
+   */
+  unsigned int min_length;
+
+  /**
+   * Maximum length of any of our connections, 0 if we have none.
+   */
+  unsigned int max_length;
+
+  /**
+   * Minimum desirability of any of our connections, UINT64_MAX if we have none.
+   */
+  GNUNET_CONTAINER_HeapCostType min_desire;
+
+  /**
+   * Maximum desirability of any of our connections, 0 if we have none.
+   */
+  GNUNET_CONTAINER_HeapCostType max_desire;
+
+  /**
+   * Path we are comparing against for #evaluate_connection, can be NULL.
+   */
+  struct CadetPeerPath *path;
+
+  /**
+   * Connection deemed the "worst" so far encountered by #evaluate_connection,
+   * NULL if we did not yet encounter any connections.
+   */
+  struct CadetTConnection *worst;
+
+  /**
+   * Numeric score of @e worst, only set if @e worst is non-NULL.
+   */
+  double worst_score;
+
+  /**
+   * Set to #GNUNET_YES if we have a connection over @e path already.
+   */
+  int duplicate;
+
+};
+
+
+/**
+ * Evaluate a connection, updating our summary information in @a cls about
+ * what kinds of connections we have.
+ *
+ * @param cls the `struct EvaluationSummary *` to update
+ * @param ct a connection to include in the summary
+ */
+static void
+evaluate_connection (void *cls,
+                     struct CadetTConnection *ct)
+{
+  struct EvaluationSummary *es = cls;
+  struct CadetConnection *cc = ct->cc;
+  struct CadetPeerPath *ps = GCC_get_path (cc);
+  const struct CadetConnectionMetrics *metrics;
+  GNUNET_CONTAINER_HeapCostType ct_desirability;
+  struct GNUNET_TIME_Relative uptime;
+  struct GNUNET_TIME_Relative last_use;
+  uint32_t ct_length;
+  double score;
+  double success_rate;
+
+  if (ps == es->path)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Ignoring duplicate path %s.\n",
+         GCPP_2s (es->path));
+    es->duplicate = GNUNET_YES;
+    return;
+  }
+  ct_desirability = GCPP_get_desirability (ps);
+  ct_length = GCPP_get_length (ps);
+  metrics = GCC_get_metrics (cc);
+  uptime = GNUNET_TIME_absolute_get_duration (metrics->age);
+  last_use = GNUNET_TIME_absolute_get_duration (metrics->last_use);
+  /* We add 1.0 here to avoid division by zero. */
+  success_rate = (metrics->num_acked_transmissions + 1.0) / (metrics->num_successes + 1.0);
+  score
+    = ct_desirability
+    + 100.0 / (1.0 + ct_length) /* longer paths = better */
+    + sqrt (uptime.rel_value_us / 60000000LL) /* larger uptime = better */
+    - last_use.rel_value_us / 1000L;          /* longer idle = worse */
+  score *= success_rate;        /* weigh overall by success rate */
+
+  if ( (NULL == es->worst) ||
+       (score < es->worst_score) )
+  {
+    es->worst = ct;
+    es->worst_score = score;
+  }
+  es->min_length = GNUNET_MIN (es->min_length,
+                               ct_length);
+  es->max_length = GNUNET_MAX (es->max_length,
+                               ct_length);
+  es->min_desire = GNUNET_MIN (es->min_desire,
+                               ct_desirability);
+  es->max_desire = GNUNET_MAX (es->max_desire,
+                               ct_desirability);
+}
+
+
+/**
+ * Consider using the path @a p for the tunnel @a t.
+ * The tunnel destination is at offset @a off in path @a p.
+ *
+ * @param cls our tunnel
+ * @param path a path to our destination
+ * @param off offset of the destination on path @a path
+ * @return #GNUNET_YES (should keep iterating)
+ */
+static int
+consider_path_cb (void *cls,
+                  struct CadetPeerPath *path,
+                  unsigned int off)
+{
+  struct CadetTunnel *t = cls;
+  struct EvaluationSummary es;
+  struct CadetTConnection *ct;
+
+  GNUNET_assert (off < GCPP_get_length (path));
+  es.min_length = UINT_MAX;
+  es.max_length = 0;
+  es.max_desire = 0;
+  es.min_desire = UINT64_MAX;
+  es.path = path;
+  es.duplicate = GNUNET_NO;
+  es.worst = NULL;
+
+  /* Compute evaluation summary over existing connections. */
+  GCT_iterate_connections (t,
+                           &evaluate_connection,
+                           &es);
+  if (GNUNET_YES == es.duplicate)
+    return GNUNET_YES;
+
+  /* FIXME: not sure we should really just count
+     'num_connections' here, as they may all have
+     consistently failed to connect. */
+
+  /* We iterate by increasing path length; if we have enough paths and
+     this one is more than twice as long than what we are currently
+     using, then ignore all of these super-long ones! */
+  if ( (GCT_count_any_connections (t) > DESIRED_CONNECTIONS_PER_TUNNEL) &&
+       (es.min_length * 2 < off) &&
+       (es.max_length < off) )
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Ignoring paths of length %u, they are way too long.\n",
+         es.min_length * 2);
+    return GNUNET_NO;
+  }
+  /* If we have enough paths and this one looks no better, ignore it. */
+  if ( (GCT_count_any_connections (t) >= DESIRED_CONNECTIONS_PER_TUNNEL) &&
+       (es.min_length < GCPP_get_length (path)) &&
+       (es.min_desire > GCPP_get_desirability (path)) &&
+       (es.max_length < off) )
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Ignoring path (%u/%llu) to %s, got something better already.\n",
+         GCPP_get_length (path),
+         (unsigned long long) GCPP_get_desirability (path),
+         GCP_2s (t->destination));
+    return GNUNET_YES;
+  }
+
+  /* Path is interesting (better by some metric, or we don't have
+     enough paths yet). */
+  ct = GNUNET_new (struct CadetTConnection);
+  ct->created = GNUNET_TIME_absolute_get ();
+  ct->t = t;
+  ct->cc = GCC_create (t->destination,
+                       path,
+                       off,
+                       GNUNET_CADET_OPTION_DEFAULT, /* FIXME: set based on what channels want/need! */
+                       ct,
+                       &connection_ready_cb,
+                       ct);
+
+  /* FIXME: schedule job to kill connection (and path?)  if it takes
+     too long to get ready! (And track performance data on how long
+     other connections took with the tunnel!)
+     => Note: to be done within 'connection'-logic! */
+  GNUNET_CONTAINER_DLL_insert (t->connection_busy_head,
+                               t->connection_busy_tail,
+                               ct);
+  t->num_busy_connections++;
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Found interesting path %s for %s, created %s\n",
+       GCPP_2s (path),
+       GCT_2s (t),
+       GCC_2s (ct->cc));
+  return GNUNET_YES;
+}
+
+
+/**
+ * Function called to maintain the connections underlying our tunnel.
+ * Tries to maintain (incl. tear down) connections for the tunnel, and
+ * if there is a significant change, may trigger transmissions.
+ *
+ * Basically, needs to check if there are connections that perform
+ * badly, and if so eventually kill them and trigger a replacement.
+ * The strategy is to open one more connection than
+ * #DESIRED_CONNECTIONS_PER_TUNNEL, and then periodically kick out the
+ * least-performing one, and then inquire for new ones.
+ *
+ * @param cls the `struct CadetTunnel`
+ */
+static void
+maintain_connections_cb (void *cls)
+{
+  struct CadetTunnel *t = cls;
+  struct GNUNET_TIME_Relative delay;
+  struct EvaluationSummary es;
+
+  t->maintain_connections_task = NULL;
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Performing connection maintenance for %s.\n",
+       GCT_2s (t));
+
+  es.min_length = UINT_MAX;
+  es.max_length = 0;
+  es.max_desire = 0;
+  es.min_desire = UINT64_MAX;
+  es.path = NULL;
+  es.worst = NULL;
+  es.duplicate = GNUNET_NO;
+  GCT_iterate_connections (t,
+                           &evaluate_connection,
+                           &es);
+  if ( (NULL != es.worst) &&
+       (GCT_count_any_connections (t) > DESIRED_CONNECTIONS_PER_TUNNEL) )
+  {
+    /* Clear out worst-performing connection 'es.worst'. */
+    destroy_t_connection (t,
+                          es.worst);
+  }
+
+  /* Consider additional paths */
+  (void) GCP_iterate_paths (t->destination,
+                            &consider_path_cb,
+                            t);
+
+  /* FIXME: calculate when to try again based on how well we are doing;
+     in particular, if we have to few connections, we might be able
+     to do without this (as PATHS should tell us whenever a new path
+     is available instantly; however, need to make sure this job is
+     restarted after that happens).
+     Furthermore, if the paths we do know are in a reasonably narrow
+     quality band and are plentyful, we might also consider us stabilized
+     and then reduce the frequency accordingly.  */
+  delay = GNUNET_TIME_UNIT_MINUTES;
+  t->maintain_connections_task
+    = GNUNET_SCHEDULER_add_delayed (delay,
+                                    &maintain_connections_cb,
+                                    t);
+}
+
+
+/**
+ * Consider using the path @a p for the tunnel @a t.
+ * The tunnel destination is at offset @a off in path @a p.
+ *
+ * @param cls our tunnel
+ * @param path a path to our destination
+ * @param off offset of the destination on path @a path
+ */
+void
+GCT_consider_path (struct CadetTunnel *t,
+                   struct CadetPeerPath *p,
+                   unsigned int off)
+{
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Considering %s for %s\n",
+       GCPP_2s (p),
+       GCT_2s (t));
+  (void) consider_path_cb (t,
+                           p,
+                           off);
+}
+
+
+/**
+ * We got a keepalive. Track in statistics.
+ *
+ * @param cls the `struct CadetTunnel` for which we decrypted the message
+ * @param msg  the message we received on the tunnel
+ */
+static void
+handle_plaintext_keepalive (void *cls,
+                            const struct GNUNET_MessageHeader *msg)
+{
+  struct CadetTunnel *t = cls;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Received KEEPALIVE on %s\n",
+       GCT_2s (t));
+  GNUNET_STATISTICS_update (stats,
+                            "# keepalives received",
+                            1,
+                            GNUNET_NO);
+}
+
+
+/**
+ * Check that @a msg is well-formed.
+ *
+ * @param cls the `struct CadetTunnel` for which we decrypted the message
+ * @param msg  the message we received on the tunnel
+ * @return #GNUNET_OK (any variable-size payload goes)
+ */
+static int
+check_plaintext_data (void *cls,
+                      const struct GNUNET_CADET_ChannelAppDataMessage *msg)
+{
+  return GNUNET_OK;
+}
+
+
+/**
+ * We received payload data for a channel.  Locate the channel
+ * and process the data, or return an error if the channel is unknown.
+ *
+ * @param cls the `struct CadetTunnel` for which we decrypted the message
+ * @param msg the message we received on the tunnel
+ */
+static void
+handle_plaintext_data (void *cls,
+                       const struct GNUNET_CADET_ChannelAppDataMessage *msg)
+{
+  struct CadetTunnel *t = cls;
+  struct CadetChannel *ch;
+
+  ch = lookup_channel (t,
+                       msg->ctn);
+  if (NULL == ch)
+  {
+    /* We don't know about such a channel, might have been destroyed on our
+       end in the meantime, or never existed. Send back a DESTROY. */
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Received %u bytes of application data for unknown channel %u, sending DESTROY\n",
+         (unsigned int) (ntohs (msg->header.size) - sizeof (*msg)),
+         ntohl (msg->ctn.cn));
+    GCT_send_channel_destroy (t,
+                              msg->ctn);
+    return;
+  }
+  GCCH_handle_channel_plaintext_data (ch,
+                                      GCC_get_id (t->current_ct->cc),
+                                      msg);
+}
+
+
+/**
+ * We received an acknowledgement for data we sent on a channel.
+ * Locate the channel and process it, or return an error if the
+ * channel is unknown.
+ *
+ * @param cls the `struct CadetTunnel` for which we decrypted the message
+ * @param ack the message we received on the tunnel
+ */
+static void
+handle_plaintext_data_ack (void *cls,
+                           const struct GNUNET_CADET_ChannelDataAckMessage *ack)
+{
+  struct CadetTunnel *t = cls;
+  struct CadetChannel *ch;
+
+  ch = lookup_channel (t,
+                       ack->ctn);
+  if (NULL == ch)
+  {
+    /* We don't know about such a channel, might have been destroyed on our
+       end in the meantime, or never existed. Send back a DESTROY. */
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Received DATA_ACK for unknown channel %u, sending DESTROY\n",
+         ntohl (ack->ctn.cn));
+    GCT_send_channel_destroy (t,
+                              ack->ctn);
+    return;
+  }
+  GCCH_handle_channel_plaintext_data_ack (ch,
+                                          GCC_get_id (t->current_ct->cc),
+                                          ack);
+}
+
+
+/**
+ * We have received a request to open a channel to a port from
+ * another peer.  Creates the incoming channel.
+ *
+ * @param cls the `struct CadetTunnel` for which we decrypted the message
+ * @param copen the message we received on the tunnel
+ */
+static void
+handle_plaintext_channel_open (void *cls,
+                               const struct GNUNET_CADET_ChannelOpenMessage *copen)
+{
+  struct CadetTunnel *t = cls;
+  struct CadetChannel *ch;
+
+  ch = GNUNET_CONTAINER_multihashmap32_get (t->channels,
+                                            ntohl (copen->ctn.cn));
+  if (NULL != ch)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Received duplicate channel CHANNEL_OPEN on port %s from %s (%s), resending ACK\n",
+         GNUNET_h2s (&copen->port),
+         GCT_2s (t),
+         GCCH_2s (ch));
+    GCCH_handle_duplicate_open (ch,
+                                GCC_get_id (t->current_ct->cc));
+    return;
+  }
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Received CHANNEL_OPEN on port %s from %s\n",
+       GNUNET_h2s (&copen->port),
+       GCT_2s (t));
+  ch = GCCH_channel_incoming_new (t,
+                                  copen->ctn,
+                                  &copen->port,
+                                  ntohl (copen->opt));
+  if (NULL != t->destroy_task)
+  {
+    GNUNET_SCHEDULER_cancel (t->destroy_task);
+    t->destroy_task = NULL;
+  }
+  GNUNET_assert (GNUNET_OK ==
+                 GNUNET_CONTAINER_multihashmap32_put (t->channels,
+                                                      ntohl (copen->ctn.cn),
+                                                      ch,
+                                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+}
+
+
+/**
+ * Send a DESTROY message via the tunnel.
+ *
+ * @param t the tunnel to transmit over
+ * @param ctn ID of the channel to destroy
  */
 void
-GCT_iterate_channels (struct CadetTunnel *t,
-                      GCT_ChannelIterator iter,
-                      void *iter_cls)
+GCT_send_channel_destroy (struct CadetTunnel *t,
+                          struct GNUNET_CADET_ChannelTunnelNumber ctn)
 {
-  struct ChanIterCls ctx;
+  struct GNUNET_CADET_ChannelManageMessage msg;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Sending DESTORY message for channel ID %u\n",
+       ntohl (ctn.cn));
+  msg.header.size = htons (sizeof (msg));
+  msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_DESTROY);
+  msg.reserved = htonl (0);
+  msg.ctn = ctn;
+  GCT_send (t,
+            &msg.header,
+            NULL,
+            NULL);
+}
 
-  ctx.iter = iter;
-  ctx.iter_cls = iter_cls;
-  GNUNET_CONTAINER_multihashmap32_iterate (t->channels,
-                                           &iterate_channels_cb,
-                                           &ctx);
 
+/**
+ * We have received confirmation from the target peer that the
+ * given channel could be established (the port is open).
+ * Tell the client.
+ *
+ * @param cls the `struct CadetTunnel` for which we decrypted the message
+ * @param cm the message we received on the tunnel
+ */
+static void
+handle_plaintext_channel_open_ack (void *cls,
+                                   const struct GNUNET_CADET_ChannelManageMessage *cm)
+{
+  struct CadetTunnel *t = cls;
+  struct CadetChannel *ch;
+
+  ch = lookup_channel (t,
+                       cm->ctn);
+  if (NULL == ch)
+  {
+    /* We don't know about such a channel, might have been destroyed on our
+       end in the meantime, or never existed. Send back a DESTROY. */
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Received channel OPEN_ACK for unknown channel %u, sending DESTROY\n",
+         ntohl (cm->ctn.cn));
+    GCT_send_channel_destroy (t,
+                              cm->ctn);
+    return;
+  }
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Received channel OPEN_ACK on channel %s from %s\n",
+       GCCH_2s (ch),
+       GCT_2s (t));
+  GCCH_handle_channel_open_ack (ch,
+                                GCC_get_id (t->current_ct->cc));
 }
 
 
 /**
- * Call #GCCH_debug() on a channel.
+ * We received a message saying that a channel should be destroyed.
+ * Pass it on to the correct channel.
  *
- * @param cls points to the log level to use
- * @param key unused
- * @param value the `struct CadetChannel` to dump
- * @return #GNUNET_OK (continue iteration)
+ * @param cls the `struct CadetTunnel` for which we decrypted the message
+ * @param cm the message we received on the tunnel
+ */
+static void
+handle_plaintext_channel_destroy (void *cls,
+                                  const struct GNUNET_CADET_ChannelManageMessage *cm)
+{
+  struct CadetTunnel *t = cls;
+  struct CadetChannel *ch;
+
+  ch = lookup_channel (t,
+                       cm->ctn);
+  if (NULL == ch)
+  {
+    /* We don't know about such a channel, might have been destroyed on our
+       end in the meantime, or never existed. */
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Received channel DESTORY for unknown channel %u. Ignoring.\n",
+         ntohl (cm->ctn.cn));
+    return;
+  }
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Received channel DESTROY on %s from %s\n",
+       GCCH_2s (ch),
+       GCT_2s (t));
+  GCCH_handle_remote_destroy (ch,
+                              GCC_get_id (t->current_ct->cc));
+}
+
+
+/**
+ * Handles a message we decrypted, by injecting it into
+ * our message queue (which will do the dispatching).
+ *
+ * @param cls the `struct CadetTunnel` that got the message
+ * @param msg the message
+ * @return #GNUNET_OK (continue to process)
  */
 static int
-debug_channel (void *cls,
-               uint32_t key,
-               void *value)
+handle_decrypted (void *cls,
+                  const struct GNUNET_MessageHeader *msg)
 {
-  const enum GNUNET_ErrorType *level = cls;
-  struct CadetChannel *ch = value;
+  struct CadetTunnel *t = cls;
 
-  GCCH_debug (ch, *level);
+  GNUNET_assert (NULL != t->current_ct);
+  GNUNET_MQ_inject_message (t->mq,
+                            msg);
   return GNUNET_OK;
 }
 
 
 /**
- * Get string description for tunnel connectivity state.
+ * Function called if we had an error processing
+ * an incoming decrypted message.
  *
- * @param cs Tunnel state.
+ * @param cls the `struct CadetTunnel`
+ * @param error error code
+ */
+static void
+decrypted_error_cb (void *cls,
+                    enum GNUNET_MQ_Error error)
+{
+  GNUNET_break_op (0);
+}
+
+
+/**
+ * Create a tunnel to @a destionation.  Must only be called
+ * from within #GCP_get_tunnel().
  *
- * @return String representation.
+ * @param destination where to create the tunnel to
+ * @return new tunnel to @a destination
  */
-static const char *
-cstate2s (enum CadetTunnelCState cs)
+struct CadetTunnel *
+GCT_create_tunnel (struct CadetPeer *destination)
 {
-  static char buf[32];
+  struct CadetTunnel *t = GNUNET_new (struct CadetTunnel);
+  struct GNUNET_MQ_MessageHandler handlers[] = {
+    GNUNET_MQ_hd_fixed_size (plaintext_keepalive,
+                             GNUNET_MESSAGE_TYPE_CADET_CHANNEL_KEEPALIVE,
+                             struct GNUNET_MessageHeader,
+                             t),
+    GNUNET_MQ_hd_var_size (plaintext_data,
+                           GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA,
+                           struct GNUNET_CADET_ChannelAppDataMessage,
+                           t),
+    GNUNET_MQ_hd_fixed_size (plaintext_data_ack,
+                             GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA_ACK,
+                             struct GNUNET_CADET_ChannelDataAckMessage,
+                             t),
+    GNUNET_MQ_hd_fixed_size (plaintext_channel_open,
+                             GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN,
+                             struct GNUNET_CADET_ChannelOpenMessage,
+                             t),
+    GNUNET_MQ_hd_fixed_size (plaintext_channel_open_ack,
+                             GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK,
+                             struct GNUNET_CADET_ChannelManageMessage,
+                             t),
+    GNUNET_MQ_hd_fixed_size (plaintext_channel_destroy,
+                             GNUNET_MESSAGE_TYPE_CADET_CHANNEL_DESTROY,
+                             struct GNUNET_CADET_ChannelManageMessage,
+                             t),
+    GNUNET_MQ_handler_end ()
+  };
+
+  t->kx_retry_delay = INITIAL_KX_RETRY_DELAY;
+  new_ephemeral (&t->ax);
+  GNUNET_assert (GNUNET_OK ==
+                 GNUNET_CRYPTO_ecdhe_key_create2 (&t->ax.kx_0));
+  t->destination = destination;
+  t->channels = GNUNET_CONTAINER_multihashmap32_create (8);
+  t->maintain_connections_task
+    = GNUNET_SCHEDULER_add_now (&maintain_connections_cb,
+                                t);
+  t->mq = GNUNET_MQ_queue_for_callbacks (NULL,
+                                         NULL,
+                                         NULL,
+                                         NULL,
+                                         handlers,
+                                         &decrypted_error_cb,
+                                         t);
+  t->mst = GNUNET_MST_create (&handle_decrypted,
+                              t);
+  return t;
+}
 
-  switch (cs)
+
+/**
+ * Add a @a connection to the @a tunnel.
+ *
+ * @param t a tunnel
+ * @param cid connection identifer to use for the connection
+ * @param options options for the connection
+ * @param path path to use for the connection
+ * @return #GNUNET_OK on success,
+ *         #GNUNET_SYSERR on failure (duplicate connection)
+ */
+int
+GCT_add_inbound_connection (struct CadetTunnel *t,
+                            const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid,
+                            enum GNUNET_CADET_ChannelOption options,
+                            struct CadetPeerPath *path)
+{
+  struct CadetTConnection *ct;
+
+  ct = GNUNET_new (struct CadetTConnection);
+  ct->created = GNUNET_TIME_absolute_get ();
+  ct->t = t;
+  ct->cc = GCC_create_inbound (t->destination,
+                               path,
+                               options,
+                               ct,
+                               cid,
+                               &connection_ready_cb,
+                               ct);
+  if (NULL == ct->cc)
   {
-    case CADET_TUNNEL_NEW:
-      return "CADET_TUNNEL_NEW";
-    case CADET_TUNNEL_SEARCHING:
-      return "CADET_TUNNEL_SEARCHING";
-    case CADET_TUNNEL_WAITING:
-      return "CADET_TUNNEL_WAITING";
-    case CADET_TUNNEL_READY:
-      return "CADET_TUNNEL_READY";
-    case CADET_TUNNEL_SHUTDOWN:
-      return "CADET_TUNNEL_SHUTDOWN";
-    default:
-      SPRINTF (buf, "%u (UNKNOWN STATE)", cs);
-      return buf;
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "%s refused inbound %s (duplicate)\n",
+         GCT_2s (t),
+         GCC_2s (ct->cc));
+    GNUNET_free (ct);
+    return GNUNET_SYSERR;
   }
+  /* FIXME: schedule job to kill connection (and path?)  if it takes
+     too long to get ready! (And track performance data on how long
+     other connections took with the tunnel!)
+     => Note: to be done within 'connection'-logic! */
+  GNUNET_CONTAINER_DLL_insert (t->connection_busy_head,
+                               t->connection_busy_tail,
+                               ct);
+  t->num_busy_connections++;
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "%s has new %s\n",
+       GCT_2s (t),
+       GCC_2s (ct->cc));
+  return GNUNET_OK;
 }
 
 
 /**
- * Get string description for tunnel encryption state.
+ * Handle encrypted message.
  *
- * @param es Tunnel state.
+ * @param ct connection/tunnel combo that received encrypted message
+ * @param msg the encrypted message to decrypt
+ */
+void
+GCT_handle_encrypted (struct CadetTConnection *ct,
+                      const struct GNUNET_CADET_TunnelEncryptedMessage *msg)
+{
+  struct CadetTunnel *t = ct->t;
+  uint16_t size = ntohs (msg->header.size);
+  char cbuf [size] GNUNET_ALIGN;
+  ssize_t decrypted_size;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "%s received %u bytes of encrypted data in state %d\n",
+       GCT_2s (t),
+       (unsigned int) size,
+       t->estate);
+
+  switch (t->estate)
+  {
+  case CADET_TUNNEL_KEY_UNINITIALIZED:
+  case CADET_TUNNEL_KEY_AX_RECV:
+    /* We did not even SEND our KX, how can the other peer
+       send us encrypted data? Must have been that we went
+       down and the other peer still things we are up.
+       Let's send it KX back. */
+    GNUNET_STATISTICS_update (stats,
+                              "# received encrypted without any KX",
+                              1,
+                              GNUNET_NO);
+    if (NULL != t->kx_task)
+    {
+      GNUNET_SCHEDULER_cancel (t->kx_task);
+      t->kx_task = NULL;
+    }
+    send_kx (t,
+             ct,
+             &t->ax);
+    return;
+  case CADET_TUNNEL_KEY_AX_SENT_AND_RECV:
+    /* We send KX, and other peer send KX to us at the same time.
+       Neither KX is AUTH'ed, so let's try KX_AUTH this time. */
+    GNUNET_STATISTICS_update (stats,
+                              "# received encrypted without KX_AUTH",
+                              1,
+                              GNUNET_NO);
+    if (NULL != t->kx_task)
+    {
+      GNUNET_SCHEDULER_cancel (t->kx_task);
+      t->kx_task = NULL;
+    }
+    send_kx_auth (t,
+                  ct,
+                  &t->ax,
+                  GNUNET_YES);
+    return;
+  case CADET_TUNNEL_KEY_AX_SENT:
+    /* We did not get the KX of the other peer, but that
+       might have been lost.  Send our KX again immediately. */
+    GNUNET_STATISTICS_update (stats,
+                              "# received encrypted without KX",
+                              1,
+                              GNUNET_NO);
+    if (NULL != t->kx_task)
+    {
+      GNUNET_SCHEDULER_cancel (t->kx_task);
+      t->kx_task = NULL;
+    }
+    send_kx (t,
+             ct,
+             &t->ax);
+    return;
+  case CADET_TUNNEL_KEY_AX_AUTH_SENT:
+    /* Great, first payload, we might graduate to OK! */
+  case CADET_TUNNEL_KEY_OK:
+    /* We are up and running, all good. */
+    break;
+  }
+
+  decrypted_size = -1;
+  if (CADET_TUNNEL_KEY_OK == t->estate)
+  {
+    /* We have well-established key material available,
+       try that. (This is the common case.) */
+    decrypted_size = t_ax_decrypt_and_validate (&t->ax,
+                                                cbuf,
+                                                msg,
+                                                size);
+  }
+
+  if ( (-1 == decrypted_size) &&
+       (NULL != t->unverified_ax) )
+  {
+    /* We have un-authenticated KX material available. We should try
+       this as a back-up option, in case the sender crashed and
+       switched keys. */
+    decrypted_size = t_ax_decrypt_and_validate (t->unverified_ax,
+                                                cbuf,
+                                                msg,
+                                                size);
+    if (-1 != decrypted_size)
+    {
+      /* It worked! Treat this as authentication of the AX data! */
+      cleanup_ax (&t->ax);
+      t->ax = *t->unverified_ax;
+      GNUNET_free (t->unverified_ax);
+      t->unverified_ax = NULL;
+    }
+    if (CADET_TUNNEL_KEY_AX_AUTH_SENT == t->estate)
+    {
+      /* First time it worked, move tunnel into production! */
+      GCT_change_estate (t,
+                         CADET_TUNNEL_KEY_OK);
+      if (NULL != t->send_task)
+        GNUNET_SCHEDULER_cancel (t->send_task);
+      t->send_task = GNUNET_SCHEDULER_add_now (&trigger_transmissions,
+                                               t);
+    }
+  }
+  if (NULL != t->unverified_ax)
+  {
+    /* We had unverified KX material that was useless; so increment
+       counter and eventually move to ignore it.  Note that we even do
+       this increment if we successfully decrypted with the old KX
+       material and thus didn't even both with the new one.  This is
+       the ideal case, as a malicious injection of bogus KX data
+       basically only causes us to increment a counter a few times. */
+    t->unverified_attempts++;
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Failed to decrypt message with unverified KX data %u times\n",
+         t->unverified_attempts);
+    if (t->unverified_attempts > MAX_UNVERIFIED_ATTEMPTS)
+    {
+      cleanup_ax (t->unverified_ax);
+      GNUNET_free (t->unverified_ax);
+      t->unverified_ax = NULL;
+    }
+  }
+
+  if (-1 == decrypted_size)
+  {
+    /* Decryption failed for good, complain. */
+    LOG (GNUNET_ERROR_TYPE_WARNING,
+         "%s failed to decrypt and validate encrypted data, retrying KX\n",
+         GCT_2s (t));
+    GNUNET_STATISTICS_update (stats,
+                              "# unable to decrypt",
+                              1,
+                              GNUNET_NO);
+    if (NULL != t->kx_task)
+    {
+      GNUNET_SCHEDULER_cancel (t->kx_task);
+      t->kx_task = NULL;
+    }
+    send_kx (t,
+             ct,
+             &t->ax);
+    return;
+  }
+  GNUNET_STATISTICS_update (stats,
+                            "# decrypted bytes",
+                            decrypted_size,
+                            GNUNET_NO);
+
+  /* The MST will ultimately call #handle_decrypted() on each message. */
+  t->current_ct = ct;
+  GNUNET_break_op (GNUNET_OK ==
+                   GNUNET_MST_from_buffer (t->mst,
+                                           cbuf,
+                                           decrypted_size,
+                                           GNUNET_YES,
+                                           GNUNET_NO));
+  t->current_ct = NULL;
+}
+
+
+/**
+ * Sends an already built message on a tunnel, encrypting it and
+ * choosing the best connection if not provided.
  *
- * @return String representation.
+ * @param message Message to send. Function modifies it.
+ * @param t Tunnel on which this message is transmitted.
+ * @param cont Continuation to call once message is really sent.
+ * @param cont_cls Closure for @c cont.
+ * @return Handle to cancel message
  */
-static const char *
-estate2s (enum CadetTunnelEState es)
+struct CadetTunnelQueueEntry *
+GCT_send (struct CadetTunnel *t,
+          const struct GNUNET_MessageHeader *message,
+          GCT_SendContinuation cont,
+          void *cont_cls)
 {
-  static char buf[32];
+  struct CadetTunnelQueueEntry *tq;
+  uint16_t payload_size;
+  struct GNUNET_MQ_Envelope *env;
+  struct GNUNET_CADET_TunnelEncryptedMessage *ax_msg;
 
-  switch (es)
+  if (CADET_TUNNEL_KEY_OK != t->estate)
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+  payload_size = ntohs (message->size);
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Encrypting %u bytes for %s\n",
+       (unsigned int) payload_size,
+       GCT_2s (t));
+  env = GNUNET_MQ_msg_extra (ax_msg,
+                             payload_size,
+                             GNUNET_MESSAGE_TYPE_CADET_TUNNEL_ENCRYPTED);
+  t_ax_encrypt (&t->ax,
+                &ax_msg[1],
+                message,
+                payload_size);
+  GNUNET_STATISTICS_update (stats,
+                            "# encrypted bytes",
+                            payload_size,
+                            GNUNET_NO);
+  ax_msg->ax_header.Ns = htonl (t->ax.Ns++);
+  ax_msg->ax_header.PNs = htonl (t->ax.PNs);
+  /* FIXME: we should do this once, not once per message;
+     this is a point multiplication, and DHRs does not
+     change all the time. */
+  GNUNET_CRYPTO_ecdhe_key_get_public (&t->ax.DHRs,
+                                      &ax_msg->ax_header.DHRs);
+  t_h_encrypt (&t->ax,
+               ax_msg);
+  t_hmac (&ax_msg->ax_header,
+          sizeof (struct GNUNET_CADET_AxHeader) + payload_size,
+          0,
+          &t->ax.HKs,
+          &ax_msg->hmac);
+
+  tq = GNUNET_malloc (sizeof (*tq));
+  tq->t = t;
+  tq->env = env;
+  tq->cid = &ax_msg->cid; /* will initialize 'ax_msg->cid' once we know the connection */
+  tq->cont = cont;
+  tq->cont_cls = cont_cls;
+  GNUNET_CONTAINER_DLL_insert_tail (t->tq_head,
+                                    t->tq_tail,
+                                    tq);
+  if (NULL != t->send_task)
+    GNUNET_SCHEDULER_cancel (t->send_task);
+  t->send_task
+    = GNUNET_SCHEDULER_add_now (&trigger_transmissions,
+                                t);
+  return tq;
+}
+
+
+/**
+ * Cancel a previously sent message while it's in the queue.
+ *
+ * ONLY can be called before the continuation given to the send
+ * function is called. Once the continuation is called, the message is
+ * no longer in the queue!
+ *
+ * @param tq Handle to the queue entry to cancel.
+ */
+void
+GCT_send_cancel (struct CadetTunnelQueueEntry *tq)
+{
+  struct CadetTunnel *t = tq->t;
+
+  GNUNET_CONTAINER_DLL_remove (t->tq_head,
+                               t->tq_tail,
+                               tq);
+  GNUNET_MQ_discard (tq->env);
+  GNUNET_free (tq);
+}
+
+
+/**
+ * Iterate over all connections of a tunnel.
+ *
+ * @param t Tunnel whose connections to iterate.
+ * @param iter Iterator.
+ * @param iter_cls Closure for @c iter.
+ */
+void
+GCT_iterate_connections (struct CadetTunnel *t,
+                         GCT_ConnectionIterator iter,
+                         void *iter_cls)
+{
+  struct CadetTConnection *n;
+  for (struct CadetTConnection *ct = t->connection_ready_head;
+       NULL != ct;
+       ct = n)
   {
-    case CADET_TUNNEL_KEY_UNINITIALIZED:
-      return "CADET_TUNNEL_KEY_UNINITIALIZED";
-    case CADET_TUNNEL_KEY_SENT:
-      return "CADET_TUNNEL_KEY_SENT";
-    case CADET_TUNNEL_KEY_PING:
-      return "CADET_TUNNEL_KEY_PING";
-    case CADET_TUNNEL_KEY_OK:
-      return "CADET_TUNNEL_KEY_OK";
-    case CADET_TUNNEL_KEY_REKEY:
-      return "CADET_TUNNEL_KEY_REKEY";
-    default:
-      SPRINTF (buf, "%u (UNKNOWN STATE)", es);
-      return buf;
+    n = ct->next;
+    iter (iter_cls,
+          ct);
+  }
+  for (struct CadetTConnection *ct = t->connection_busy_head;
+       NULL != ct;
+       ct = n)
+  {
+    n = ct->next;
+    iter (iter_cls,
+          ct);
   }
 }
 
 
+/**
+ * Closure for #iterate_channels_cb.
+ */
+struct ChanIterCls
+{
+  /**
+   * Function to call.
+   */
+  GCT_ChannelIterator iter;
+
+  /**
+   * Closure for @e iter.
+   */
+  void *iter_cls;
+};
+
+
+/**
+ * Helper function for #GCT_iterate_channels.
+ *
+ * @param cls the `struct ChanIterCls`
+ * @param key unused
+ * @param value a `struct CadetChannel`
+ * @return #GNUNET_OK
+ */
+static int
+iterate_channels_cb (void *cls,
+                     uint32_t key,
+                     void *value)
+{
+  struct ChanIterCls *ctx = cls;
+  struct CadetChannel *ch = value;
+
+  ctx->iter (ctx->iter_cls,
+             ch);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Iterate over all channels of a tunnel.
+ *
+ * @param t Tunnel whose channels to iterate.
+ * @param iter Iterator.
+ * @param iter_cls Closure for @c iter.
+ */
+void
+GCT_iterate_channels (struct CadetTunnel *t,
+                      GCT_ChannelIterator iter,
+                      void *iter_cls)
+{
+  struct ChanIterCls ctx;
+
+  ctx.iter = iter;
+  ctx.iter_cls = iter_cls;
+  GNUNET_CONTAINER_multihashmap32_iterate (t->channels,
+                                           &iterate_channels_cb,
+                                           &ctx);
+
+}
+
+
+/**
+ * Call #GCCH_debug() on a channel.
+ *
+ * @param cls points to the log level to use
+ * @param key unused
+ * @param value the `struct CadetChannel` to dump
+ * @return #GNUNET_OK (continue iteration)
+ */
+static int
+debug_channel (void *cls,
+               uint32_t key,
+               void *value)
+{
+  const enum GNUNET_ErrorType *level = cls;
+  struct CadetChannel *ch = value;
+
+  GCCH_debug (ch, *level);
+  return GNUNET_OK;
+}
+
+
 #define LOG2(level, ...) GNUNET_log_from_nocheck(level,"cadet-tun",__VA_ARGS__)
 
 
@@ -803,15 +3414,11 @@ GCT_debug (const struct CadetTunnel *t,
     return;
 
   LOG2 (level,
-        "TTT TUNNEL TOWARDS %s in cstate %s, estate %s tq_len: %u #cons: %u\n",
+        "TTT TUNNEL TOWARDS %s in estate %s tq_len: %u #cons: %u\n",
         GCT_2s (t),
-        cstate2s (t->cstate),
         estate2s (t->estate),
         t->tq_len,
-        t->num_connections);
-#if DUMP_KEYS_TO_STDERR
-  ax_debug (t->ax, level);
-#endif
+        GCT_count_any_connections (t));
   LOG2 (level,
         "TTT channels:\n");
   GNUNET_CONTAINER_multihashmap32_iterate (t->channels,
@@ -819,8 +3426,12 @@ GCT_debug (const struct CadetTunnel *t,
                                            &level);
   LOG2 (level,
         "TTT connections:\n");
-  for (iter_c = t->connection_head; NULL != iter_c; iter_c = iter_c->next)
-    GCC_debug (iter_c->c, level);
+  for (iter_c = t->connection_ready_head; NULL != iter_c; iter_c = iter_c->next)
+    GCC_debug (iter_c->cc,
+               level);
+  for (iter_c = t->connection_busy_head; NULL != iter_c; iter_c = iter_c->next)
+    GCC_debug (iter_c->cc,
+               level);
 
   LOG2 (level,
         "TTT TUNNEL END\n");