trying to make KX logic slightly more readable
[oweals/gnunet.git] / src / cadet / gnunet-service-cadet-new_tunnels.c
index 111adffde486d78aba3963d5597990491afe0f71..020564d8ee06196706d57af364a9e28f8bcf44de 100644 (file)
  * @author Christian Grothoff
  *
  * FIXME:
- * - check KX estate machine -- make sure it is never stuck!
- * - clean up KX logic, including adding sender authentication
- * - implement connection management (evaluate, kill old ones,
- *   search for new ones)
- * - when managing connections, distinguish those that
- *   have (recently) had traffic from those that were
- *   never ready (or not recently)
+ * - KX:
+ *   + clean up KX logic, including adding sender authentication
+ *   + implement rekeying
+ *   + check KX estate machine -- make sure it is never stuck!
+ * - connection management
+ *   + properly (evaluate, kill old ones, search for new ones)
+ *   + when managing connections, distinguish those that
+ *     have (recently) had traffic from those that were
+ *     never ready (or not recently)
  */
 #include "platform.h"
 #include "gnunet_util_lib.h"
  */
 #define IDLE_DESTROY_DELAY GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 90)
 
-/**
- * Yuck, replace by 'offsetof' expression?
- * FIXME.
- */
-#define AX_HEADER_SIZE (sizeof (uint32_t) * 2\
-                        + sizeof (struct GNUNET_CRYPTO_EcdhePublicKey))
-
-
 /**
  * Maximum number of skipped keys we keep in memory per tunnel.
  */
@@ -308,6 +302,11 @@ struct CadetTunnel
    */
   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.
    */
@@ -354,17 +353,6 @@ struct CadetTunnel
    */
   struct CadetTunnelQueueEntry *tq_tail;
 
-
-  /**
-   * Ephemeral message in the queue (to avoid queueing more than one).
-   */
-  struct CadetConnectionQueue *ephm_hKILL;
-
-  /**
-   * Pong message in the queue.
-   */
-  struct CadetConnectionQueue *pong_hKILL;
-
   /**
    * How long do we wait until we retry the KX?
    */
@@ -406,12 +394,11 @@ 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->destination));
+                   "Tunnel %s",
+                   GNUNET_i2s (GCP_get_id (t->destination)));
   return buf;
 }
 
@@ -570,10 +557,10 @@ new_ephemeral (struct CadetTunnel *t)
  * at our message queue and if there is a message, picks a connection
  * to send it on.
  *
- * @param t tunnel to process messages on
+ * @param cls the `struct CadetTunnel` to process messages on
  */
 static void
-trigger_transmissions (struct CadetTunnel *t);
+trigger_transmissions (void *cls);
 
 
 /* ************************************** start core crypto ***************************** */
@@ -605,7 +592,8 @@ t_hmac (const void *plaintext,
                                  key, sizeof (*key),
                                  ctx, sizeof (ctx),
                                  NULL);
-  /* Two step: CADET_Hash is only 256 bits, HashCode is 512. */
+  /* Two step: GNUNET_ShortHash is only 256 bits,
+     GNUNET_HashCode is 512, so we truncate. */
   GNUNET_CRYPTO_hmac (&auth_key,
                       plaintext,
                       size,
@@ -819,12 +807,12 @@ t_h_encrypt (struct CadetTunnel *t,
                                      &ax->HKs,
                                      NULL, 0,
                                      NULL);
-  out_size = GNUNET_CRYPTO_symmetric_encrypt (&msg->Ns,
-                                              AX_HEADER_SIZE,
+  out_size = GNUNET_CRYPTO_symmetric_encrypt (&msg->ax_header.Ns,
+                                              sizeof (struct GNUNET_CADET_AxHeader),
                                               &ax->HKs,
                                               &iv,
-                                              &msg->Ns);
-  GNUNET_assert (AX_HEADER_SIZE == out_size);
+                                              &msg->ax_header.Ns);
+  GNUNET_assert (sizeof (struct GNUNET_CADET_AxHeader) == out_size);
 }
 
 
@@ -849,12 +837,12 @@ t_h_decrypt (struct CadetTunnel *t,
                                      &ax->HKr,
                                      NULL, 0,
                                      NULL);
-  out_size = GNUNET_CRYPTO_symmetric_decrypt (&src->Ns,
-                                              AX_HEADER_SIZE,
+  out_size = GNUNET_CRYPTO_symmetric_decrypt (&src->ax_header.Ns,
+                                              sizeof (struct GNUNET_CADET_AxHeader),
                                               &ax->HKr,
                                               &iv,
-                                              &dst->Ns);
-  GNUNET_assert (AX_HEADER_SIZE == out_size);
+                                              &dst->ax_header.Ns);
+  GNUNET_assert (sizeof (struct GNUNET_CADET_AxHeader) == out_size);
 }
 
 
@@ -911,8 +899,8 @@ try_old_ax_keys (struct CadetTunnel *t,
   valid_HK = NULL;
   for (key = t->ax.skipped_head; NULL != key; key = key->next)
   {
-    t_hmac (&src->Ns,
-            AX_HEADER_SIZE + esize,
+    t_hmac (&src->ax_header,
+            sizeof (struct GNUNET_CADET_AxHeader) + esize,
             0,
             &key->HK,
             hmac);
@@ -937,15 +925,15 @@ try_old_ax_keys (struct CadetTunnel *t,
                                      &key->HK,
                                      NULL, 0,
                                      NULL);
-  res = GNUNET_CRYPTO_symmetric_decrypt (&src->Ns,
-                                         AX_HEADER_SIZE,
+  res = GNUNET_CRYPTO_symmetric_decrypt (&src->ax_header.Ns,
+                                         sizeof (struct GNUNET_CADET_AxHeader),
                                          &key->HK,
                                          &iv,
-                                         &plaintext_header.Ns);
-  GNUNET_assert (AX_HEADER_SIZE == res);
+                                         &plaintext_header.ax_header.Ns);
+  GNUNET_assert (sizeof (struct GNUNET_CADET_AxHeader) == res);
 
   /* Find the correct message key */
-  N = ntohl (plaintext_header.Ns);
+  N = ntohl (plaintext_header.ax_header.Ns);
   while ( (NULL != key) &&
           (N != key->Kn) )
     key = key->next;
@@ -1082,8 +1070,8 @@ t_ax_decrypt_and_validate (struct CadetTunnel *t,
   ax = &t->ax;
 
   /* Try current HK */
-  t_hmac (&src->Ns,
-          AX_HEADER_SIZE + esize,
+  t_hmac (&src->ax_header,
+          sizeof (struct GNUNET_CADET_AxHeader) + esize,
           0, &ax->HKr,
           &msg_hmac);
   if (0 != memcmp (&msg_hmac,
@@ -1097,8 +1085,8 @@ t_ax_decrypt_and_validate (struct CadetTunnel *t,
     struct GNUNET_CRYPTO_EcdhePublicKey *DHRp;
 
     /* Try Next HK */
-    t_hmac (&src->Ns,
-            AX_HEADER_SIZE + esize,
+    t_hmac (&src->ax_header,
+            sizeof (struct GNUNET_CADET_AxHeader) + esize,
             0,
             &ax->NHKr,
             &msg_hmac);
@@ -1117,9 +1105,9 @@ t_ax_decrypt_and_validate (struct CadetTunnel *t,
     t_h_decrypt (t,
                  src,
                  &plaintext_header);
-    Np = ntohl (plaintext_header.Ns);
-    PNp = ntohl (plaintext_header.PNs);
-    DHRp = &plaintext_header.DHRs;
+    Np = ntohl (plaintext_header.ax_header.Ns);
+    PNp = ntohl (plaintext_header.ax_header.PNs);
+    DHRp = &plaintext_header.ax_header.DHRs;
     store_ax_keys (t,
                    &HK,
                    PNp);
@@ -1149,8 +1137,8 @@ t_ax_decrypt_and_validate (struct CadetTunnel *t,
     t_h_decrypt (t,
                  src,
                  &plaintext_header);
-    Np = ntohl (plaintext_header.Ns);
-    PNp = ntohl (plaintext_header.PNs);
+    Np = ntohl (plaintext_header.ax_header.Ns);
+    PNp = ntohl (plaintext_header.ax_header.PNs);
   }
   if ( (Np != ax->Nr) &&
        (GNUNET_OK != store_ax_keys (t,
@@ -1173,6 +1161,27 @@ t_ax_decrypt_and_validate (struct CadetTunnel *t,
 }
 
 
+/**
+ * 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.
@@ -1201,6 +1210,14 @@ GCT_change_estate (struct CadetTunnel *t,
       GNUNET_SCHEDULER_cancel (t->kx_task);
       t->kx_task = NULL;
     }
+    if (CADET_TUNNEL_KEY_REKEY != old)
+    {
+      /* notify all channels that have been waiting */
+      GNUNET_CONTAINER_multihashmap32_iterate (t->channels,
+                                               &notify_tunnel_up_cb,
+                                               t);
+    }
+
     /* FIXME: schedule rekey task! */
   }
 }
@@ -1228,16 +1245,16 @@ send_kx (struct CadetTunnel *t,
   ct = get_ready_connection (t);
   if (NULL == ct)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Wanted to send KX on tunnel %s, but no connection is ready, deferring\n",
-                GCT_2s (t));
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Wanted to send KX on tunnel %s, but no connection is ready, deferring\n",
+         GCT_2s (t));
     return;
   }
   cc = ct->cc;
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Sending KX on tunnel %s using connection %s\n",
-              GCT_2s (t),
-              GCC_2s (ct->cc));
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Sending KX on tunnel %s using connection %s\n",
+       GCT_2s (t),
+       GCC_2s (ct->cc));
 
   // GNUNET_assert (GNUNET_NO == GCT_is_loopback (t));
   env = GNUNET_MQ_msg (msg,
@@ -1251,6 +1268,7 @@ send_kx (struct CadetTunnel *t,
                                       &msg->ephemeral_key);
   GNUNET_CRYPTO_ecdhe_key_get_public (ax->DHRs,
                                       &msg->ratchet_key);
+  ct->is_ready = GNUNET_NO;
   GCC_transmit (cc,
                 env);
   t->kx_retry_delay = GNUNET_TIME_STD_BACKOFF (t->kx_retry_delay);
@@ -1367,9 +1385,9 @@ GCT_handle_kx (struct CadetTConnection *ct,
          " known handshake key, exit\n");
     return;
   }
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Handling KX message for tunnel %s\n",
-              GCT_2s (t));
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Handling KX message for tunnel %s\n",
+       GCT_2s (t));
 
   ax->RK = keys[0];
   if (GNUNET_YES == am_I_alice)
@@ -1408,7 +1426,10 @@ GCT_handle_kx (struct CadetTConnection *ct,
        we can start transmitting! */
     GCT_change_estate (t,
                        CADET_TUNNEL_KEY_OK);
-    trigger_transmissions (t);
+    if (NULL != t->send_task)
+      GNUNET_SCHEDULER_cancel (t->send_task);
+    t->send_task = GNUNET_SCHEDULER_add_now (&trigger_transmissions,
+                                             t);
     break;
   case CADET_TUNNEL_KEY_PING:
     /* Got a key yet again; need encrypted payload to advance */
@@ -1435,26 +1456,37 @@ GCT_handle_kx (struct CadetTConnection *ct,
 static struct GNUNET_CADET_ChannelTunnelNumber
 get_next_free_ctn (struct CadetTunnel *t)
 {
+#define HIGH_BIT 0x8000000
   struct GNUNET_CADET_ChannelTunnelNumber ret;
   uint32_t ctn;
-
-  /* FIXME: this logic does NOT prevent both ends of the
-     channel from picking the same CTN!
-     Need to reserve one bit of the CTN for the
-     direction, i.e. which side established the connection! */
+  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))
-    ctn++;
-  t->next_ctn.cn = htonl (ctn + 1);
+  {
+    ctn = ((ctn + 1) & (~ HIGH_BIT)) | highbit;
+  }
+  t->next_ctn.cn = htonl (((ctn + 1) & (~ HIGH_BIT)) | highbit);
   ret.cn = ntohl (ctn);
   return ret;
 }
 
 
 /**
- * Add a channel to a tunnel.
+ * 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
@@ -1472,14 +1504,35 @@ GCT_add_channel (struct CadetTunnel *t,
                                                       ntohl (ctn.cn),
                                                       ch,
                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Adding channel %s to tunnel %s\n",
-              GCCH_2s (ch),
-              GCT_2s (t));
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Adding channel %s to tunnel %s\n",
+       GCCH_2s (ch),
+       GCT_2s (t));
+  if ( (CADET_TUNNEL_KEY_OK == t->estate) ||
+       (CADET_TUNNEL_KEY_REKEY == t->estate) )
+    GCCH_tunnel_up (ch);
   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;
+
+  GNUNET_CONTAINER_DLL_remove (t->connection_head,
+                               t->connection_tail,
+                               ct);
+  GNUNET_free (ct);
+}
+
+
 /**
  * This tunnel is no longer used, destroy it.
  *
@@ -1493,21 +1546,25 @@ destroy_tunnel (void *cls)
   struct CadetTunnelQueueEntry *tq;
 
   t->destroy_task = NULL;
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Destroying idle tunnel %s\n",
-              GCT_2s (t));
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Destroying idle tunnel %s\n",
+       GCT_2s (t));
   GNUNET_assert (0 == GNUNET_CONTAINER_multihashmap32_size (t->channels));
   while (NULL != (ct = t->connection_head))
   {
+    struct CadetConnection *cc;
+
     GNUNET_assert (ct->t == t);
-    GNUNET_CONTAINER_DLL_remove (t->connection_head,
-                                 t->connection_tail,
-                                 ct);
-    GCC_destroy (ct->cc);
-    GNUNET_free (ct);
+    cc = ct->cc;
+    GCT_connection_lost (ct);
+    GCC_destroy_without_tunnel (cc);
   }
   while (NULL != (tq = t->tq_head))
+  {
+    if (NULL != tq->cont)
+      tq->cont (tq->cont_cls);
     GCT_send_cancel (tq);
+  }
   GCP_drop_tunnel (t->destination,
                    t);
   GNUNET_CONTAINER_multihashmap32_destroy (t->channels);
@@ -1516,8 +1573,23 @@ destroy_tunnel (void *cls)
     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);
+  while (NULL != t->ax.skipped_head)
+    delete_skipped_key (t,
+                        t->ax.skipped_head);
+  GNUNET_assert (0 == t->ax.skipped);
+  GNUNET_free_non_null (t->ax.kx_0);
   GNUNET_free_non_null (t->ax.DHRs);
   GNUNET_free (t);
 }
@@ -1535,10 +1607,10 @@ GCT_remove_channel (struct CadetTunnel *t,
                     struct CadetChannel *ch,
                     struct GNUNET_CADET_ChannelTunnelNumber ctn)
 {
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Removing channel %s from tunnel %s\n",
-              GCCH_2s (ch),
-              GCT_2s (t));
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Removing channel %s from tunnel %s\n",
+       GCCH_2s (ch),
+       GCT_2s (t));
   GNUNET_assert (GNUNET_YES ==
                  GNUNET_CONTAINER_multihashmap32_remove (t->channels,
                                                          ntohl (ctn.cn),
@@ -1553,6 +1625,26 @@ GCT_remove_channel (struct CadetTunnel *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);
+  return GNUNET_OK;
+}
+
+
 /**
  * Destroys the tunnel @a t now, without delay. Used during shutdown.
  *
@@ -1561,9 +1653,17 @@ GCT_remove_channel (struct CadetTunnel *t,
 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 ==
                  GNUNET_CONTAINER_multihashmap32_size (t->channels));
-  GNUNET_SCHEDULER_cancel (t->destroy_task);
+  if (NULL != t->destroy_task)
+  {
+    GNUNET_SCHEDULER_cancel (t->destroy_task);
+    t->destroy_task = NULL;
+  }
   destroy_tunnel (t);
 }
 
@@ -1605,10 +1705,10 @@ try_send_normal_payload (struct CadetTunnel *t,
   if (NULL == tq)
   {
     /* no messages pending right now */
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Not sending payload of tunnel %s on ready connection %s (nothing pending)\n",
-                GCT_2s (t),
-                GCC_2s (ct->cc));
+    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' */
@@ -1619,10 +1719,10 @@ try_send_normal_payload (struct CadetTunnel *t,
   if (NULL != tq->cid)
     *tq->cid = *GCC_get_id (ct->cc);
   ct->is_ready = GNUNET_NO;
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Sending payload of tunnel %s on connection %s\n",
-              GCT_2s (t),
-              GCC_2s (ct->cc));
+  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)
@@ -1648,19 +1748,19 @@ connection_ready_cb (void *cls,
 
   if (GNUNET_NO == is_ready)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Connection %s no longer ready for tunnel %s\n",
-                GCC_2s (ct->cc),
-                GCT_2s (t));
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Connection %s no longer ready for tunnel %s\n",
+         GCC_2s (ct->cc),
+         GCT_2s (t));
     ct->is_ready = GNUNET_NO;
     return;
   }
   ct->is_ready = GNUNET_YES;
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Connection %s now ready for tunnel %s in state %s\n",
-              GCC_2s (ct->cc),
-              GCT_2s (t),
-              estate2s (t->estate));
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Connection %s now ready for tunnel %s in state %s\n",
+       GCC_2s (ct->cc),
+       GCT_2s (t),
+       estate2s (t->estate));
   switch (t->estate)
   {
   case CADET_TUNNEL_KEY_UNINITIALIZED:
@@ -1673,9 +1773,9 @@ connection_ready_cb (void *cls,
     if (NULL == t->kx_task)
     {
       t->kx_task
-        = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining (t->next_kx_attempt),
-                                        &retry_kx,
-                                        t);
+        = GNUNET_SCHEDULER_add_at (t->next_kx_attempt,
+                                   &retry_kx,
+                                   t);
     }
     break;
   case CADET_TUNNEL_KEY_OK:
@@ -1697,13 +1797,15 @@ connection_ready_cb (void *cls,
  * at our message queue and if there is a message, picks a connection
  * to send it on.
  *
- * @param t tunnel to process messages on
+ * @param cls the `struct CadetTunnel` to process messages on
  */
 static void
-trigger_transmissions (struct CadetTunnel *t)
+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);
@@ -1714,29 +1816,6 @@ trigger_transmissions (struct CadetTunnel *t)
 }
 
 
-/**
- * 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;
-
-  t->maintain_connections_task = NULL;
-  GNUNET_break (0); // FIXME: implement!
-}
-
-
 /**
  * Consider using the path @a p for the tunnel @a t.
  * The tunnel destination is at offset @a off in path @a p.
@@ -1765,7 +1844,13 @@ consider_path_cb (void *cls,
 
     ps = GCC_get_path (ct->cc);
     if (ps == path)
+    {
+      LOG (GNUNET_ERROR_TYPE_DEBUG,
+           "Ignoring duplicate path %s for tunnel %s.\n",
+           GCPP_2s (path),
+           GCT_2s (t));
       return GNUNET_YES; /* duplicate */
+    }
     min_length = GNUNET_MIN (min_length,
                              GCPP_get_length (ps));
     max_desire = GNUNET_MAX (max_desire,
@@ -1782,9 +1867,9 @@ consider_path_cb (void *cls,
   if ( (t->num_connections > DESIRED_CONNECTIONS_PER_TUNNEL) &&
        (min_length * 2 < off) )
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Ignoring paths of length %u, they are way too long.\n",
-                min_length * 2);
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Ignoring paths of length %u, they are way too long.\n",
+         min_length * 2);
     return GNUNET_NO;
   }
   /* If we have enough paths and this one looks no better, ignore it. */
@@ -1792,11 +1877,11 @@ consider_path_cb (void *cls,
        (min_length < GCPP_get_length (path)) &&
        (max_desire > GCPP_get_desirability (path)) )
   {
-    GNUNET_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));
+    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;
   }
 
@@ -1818,15 +1903,46 @@ consider_path_cb (void *cls,
                                t->connection_tail,
                                ct);
   t->num_connections++;
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Found interesting path %s for tunnel %s, created connection %s\n",
-              GCPP_2s (path),
-              GCT_2s (t),
-              GCC_2s (ct->cc));
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Found interesting path %s for tunnel %s, created connection %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;
+
+  t->maintain_connections_task = NULL;
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Performing connection maintenance for tunnel %s.\n",
+       GCT_2s (t));
+
+  (void) GCP_iterate_paths (t->destination,
+                            &consider_path_cb,
+                            t);
+
+  GNUNET_break (0); // FIXME: implement!
+}
+
+
 /**
  * Consider using the path @a p for the tunnel @a t.
  * The tunnel destination is at offset @a off in path @a p.
@@ -1847,7 +1963,7 @@ GCT_consider_path (struct CadetTunnel *t,
 
 
 /**
- * NOT IMPLEMENTED.
+ * 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
@@ -1858,7 +1974,13 @@ handle_plaintext_keepalive (void *cls,
 {
   struct CadetTunnel *t = cls;
 
-  GNUNET_break (0); // FIXME
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Received KEEPALIVE on tunnel %s\n",
+       GCT_2s (t));
+  GNUNET_STATISTICS_update (stats,
+                            "# keepalives received",
+                            1,
+                            GNUNET_NO);
 }
 
 
@@ -1948,28 +2070,38 @@ handle_plaintext_data_ack (void *cls,
  * another peer.  Creates the incoming channel.
  *
  * @param cls the `struct CadetTunnel` for which we decrypted the message
- * @param cc the message we received on the tunnel
+ * @param copen the message we received on the tunnel
  */
 static void
 handle_plaintext_channel_open (void *cls,
-                               const struct GNUNET_CADET_ChannelOpenMessage *cc)
+                               const struct GNUNET_CADET_ChannelOpenMessage *copen)
 {
   struct CadetTunnel *t = cls;
   struct CadetChannel *ch;
-  struct GNUNET_CADET_ChannelTunnelNumber ctn;
 
+  ch = GNUNET_CONTAINER_multihashmap32_get (t->channels,
+                                            ntohl (copen->ctn.cn));
+  if (NULL != ch)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Receicved duplicate 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);
+    return;
+  }
   LOG (GNUNET_ERROR_TYPE_DEBUG,
-       "Receicved channel OPEN on port %s from peer %s\n",
-       GNUNET_h2s (&cc->port),
-       GCP_2s (GCT_get_destination (t)));
-  ctn = get_next_free_ctn (t);
+       "Receicved channel OPEN on port %s from %s\n",
+       GNUNET_h2s (&copen->port),
+       GCT_2s (t));
   ch = GCCH_channel_incoming_new (t,
-                                  ctn,
-                                  &cc->port,
-                                  ntohl (cc->opt));
+                                  copen->ctn,
+                                  &copen->port,
+                                  ntohl (copen->opt));
   GNUNET_assert (GNUNET_OK ==
                  GNUNET_CONTAINER_multihashmap32_put (t->channels,
-                                                      ntohl (ctn.cn),
+                                                      ntohl (copen->ctn.cn),
                                                       ch,
                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
 }
@@ -2023,12 +2155,16 @@ handle_plaintext_channel_open_ack (void *cls,
     /* 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, sending DESTROY\n",
-         GCCH_2s (ch));
+         "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);
 }
 
@@ -2054,10 +2190,14 @@ handle_plaintext_channel_destroy (void *cls,
     /* 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. Ignoring.\n",
-         GCCH_2s (ch));
+         "Received channel DESTORY for unknown channel %u. Ignoring.\n",
+         ntohl (cm->ctn.cn));
     return;
   }
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Receicved channel DESTROY on %s from %s\n",
+       GCCH_2s (ch),
+       GCT_2s (t));
   GCCH_handle_remote_destroy (ch);
 }
 
@@ -2107,42 +2247,39 @@ decrypted_error_cb (void *cls,
 struct CadetTunnel *
 GCT_create_tunnel (struct CadetPeer *destination)
 {
+  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,
-                             NULL),
+                             t),
     GNUNET_MQ_hd_var_size (plaintext_data,
                            GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA,
                            struct GNUNET_CADET_ChannelAppDataMessage,
-                           NULL),
+                           t),
     GNUNET_MQ_hd_fixed_size (plaintext_data_ack,
                              GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA_ACK,
                              struct GNUNET_CADET_ChannelDataAckMessage,
-                             NULL),
+                             t),
     GNUNET_MQ_hd_fixed_size (plaintext_channel_open,
                              GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN,
                              struct GNUNET_CADET_ChannelOpenMessage,
-                             NULL),
+                             t),
     GNUNET_MQ_hd_fixed_size (plaintext_channel_open_ack,
                              GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK,
                              struct GNUNET_CADET_ChannelManageMessage,
-                             NULL),
+                             t),
     GNUNET_MQ_hd_fixed_size (plaintext_channel_destroy,
                              GNUNET_MESSAGE_TYPE_CADET_CHANNEL_DESTROY,
                              struct GNUNET_CADET_ChannelManageMessage,
-                             NULL),
+                             t),
     GNUNET_MQ_handler_end ()
   };
-  struct CadetTunnel *t;
 
-  t = GNUNET_new (struct CadetTunnel);
   new_ephemeral (t);
+  t->ax.kx_0 = GNUNET_CRYPTO_ecdhe_key_create ();
   t->destination = destination;
   t->channels = GNUNET_CONTAINER_multihashmap32_create (8);
-  (void) GCP_iterate_paths (destination,
-                            &consider_path_cb,
-                            t);
   t->maintain_connections_task
     = GNUNET_SCHEDULER_add_now (&maintain_connections_cb,
                                 t);
@@ -2165,8 +2302,10 @@ GCT_create_tunnel (struct CadetPeer *destination)
  * @param t a tunnel
  * @param cid connection identifer to use for the connection
  * @param path path to use for the connection
+ * @return #GNUNET_OK on success,
+ *         #GNUNET_SYSERR on failure (duplicate connection)
  */
-void
+int
 GCT_add_inbound_connection (struct CadetTunnel *t,
                             const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid,
                             struct CadetPeerPath *path)
@@ -2181,7 +2320,16 @@ GCT_add_inbound_connection (struct CadetTunnel *t,
                                ct,
                                cid,
                                &connection_ready_cb,
-                               t);
+                               ct);
+  if (NULL == ct->cc)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Tunnel %s refused inbound connection %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!)
@@ -2194,6 +2342,7 @@ GCT_add_inbound_connection (struct CadetTunnel *t,
        "Tunnel %s has new connection %s\n",
        GCT_2s (t),
        GCC_2s (ct->cc));
+  return GNUNET_OK;
 }
 
 
@@ -2269,7 +2418,10 @@ GCT_handle_encrypted (struct CadetTConnection *ct,
   {
     GCT_change_estate (t,
                        CADET_TUNNEL_KEY_OK);
-    trigger_transmissions (t);
+    if (NULL != t->send_task)
+      GNUNET_SCHEDULER_cancel (t->send_task);
+    t->send_task = GNUNET_SCHEDULER_add_now (&trigger_transmissions,
+                                             t);
   }
   /* The MST will ultimately call #handle_decrypted() on each message. */
   GNUNET_break_op (GNUNET_OK ==
@@ -2304,7 +2456,7 @@ GCT_send (struct CadetTunnel *t,
 
   payload_size = ntohs (message->size);
   LOG (GNUNET_ERROR_TYPE_DEBUG,
-       "Encrypting %u bytes of for tunnel %s\n",
+       "Encrypting %u bytes for tunnel %s\n",
        (unsigned int) payload_size,
        GCT_2s (t));
   env = GNUNET_MQ_msg_extra (ax_msg,
@@ -2314,14 +2466,14 @@ GCT_send (struct CadetTunnel *t,
                 &ax_msg[1],
                 message,
                 payload_size);
-  ax_msg->Ns = htonl (t->ax.Ns++);
-  ax_msg->PNs = htonl (t->ax.PNs);
+  ax_msg->ax_header.Ns = htonl (t->ax.Ns++);
+  ax_msg->ax_header.PNs = htonl (t->ax.PNs);
   GNUNET_CRYPTO_ecdhe_key_get_public (t->ax.DHRs,
-                                      &ax_msg->DHRs);
+                                      &ax_msg->ax_header.DHRs);
   t_h_encrypt (t,
                ax_msg);
-  t_hmac (&ax_msg->Ns,
-          AX_HEADER_SIZE + payload_size,
+  t_hmac (&ax_msg->ax_header,
+          sizeof (struct GNUNET_CADET_AxHeader) + payload_size,
           0,
           &t->ax.HKs,
           &ax_msg->hmac);
@@ -2335,7 +2487,11 @@ GCT_send (struct CadetTunnel *t,
   GNUNET_CONTAINER_DLL_insert_tail (t->tq_head,
                                     t->tq_tail,
                                     tq);
-  trigger_transmissions (t);
+  if (NULL != t->send_task)
+    GNUNET_SCHEDULER_cancel (t->send_task);
+  t->send_task
+    = GNUNET_SCHEDULER_add_now (&trigger_transmissions,
+                                t);
   return tq;
 }