changing time measurement from milliseconds to microseconds
[oweals/gnunet.git] / src / mesh / gnunet-service-mesh-enc.c
index 223861ea6a0ccf969ebfd57a7a1d9e426f98aa02..23d8512877c84424cbc194d392c0ec6b894a4e6f 100644 (file)
@@ -191,6 +191,11 @@ struct MeshPeerQueue
      */
   struct MeshConnection *c;
 
+    /**
+     * Is FWD in c?
+     */
+  int fwd;
+
     /**
      * Channel this message belongs to, if known.
      */
@@ -220,22 +225,12 @@ struct MeshPeerQueue
 struct MeshFlowControl
 {
   /**
-   * Peer
-   */
-  struct MeshPeer *peer;
-
-  /**
-   * Transmission queue to core DLL head
-   */
-  struct MeshPeerQueue *queue_head;
-
-  /**
-   * Transmission queue to core DLL tail
+   * Connection this controls.
    */
-  struct MeshPeerQueue *queue_tail;
+  struct MeshConnection *c;
 
   /**
-   * How many messages are in the queue to this peer.
+   * How many messages are in the queue on this connection.
    */
   unsigned int queue_n;
 
@@ -244,11 +239,6 @@ struct MeshFlowControl
    */
   unsigned int queue_max;
 
-  /**
-   * Handle for queued transmissions
-   */
-  struct GNUNET_CORE_TransmitHandle *core_transmit;
-
   /**
    * ID of the last packet sent towards the peer.
    */
@@ -296,11 +286,6 @@ struct MeshPeer
      */
   struct GNUNET_TIME_Absolute last_contact;
 
-    /**
-     * Number of attempts to reconnect so far
-     */
-  int n_reconnect_attempts;
-
     /**
      * Paths to reach the peer, ordered by ascending hop count
      */
@@ -322,10 +307,29 @@ struct MeshPeer
   struct MeshTunnel2 *tunnel;
 
     /**
-     * Flow control information for direct traffic.
+     * Connections that go through this peer, indexed by tid;
      */
-  struct MeshFlowControl *fc;
+  struct GNUNET_CONTAINER_MultiHashMap *connections;
+
+    /**
+     * Handle for queued transmissions
+     */
+  struct GNUNET_CORE_TransmitHandle *core_transmit;
+
+  /**
+   * Transmission queue to core DLL head
+   */
+  struct MeshPeerQueue *queue_head;
+  
+  /**
+   * Transmission queue to core DLL tail
+   */
+  struct MeshPeerQueue *queue_tail;
 
+  /**
+   * How many messages are in the queue to this peer.
+   */
+  unsigned int queue_n;
 };
 
 
@@ -373,15 +377,10 @@ struct MeshChannelReliability
   struct MeshReliableMessage        *tail_sent;
 
     /**
-     * Messages pending
+     * Messages pending to send.
      */
   unsigned int                      n_sent;
 
-    /**
-     * Next MID to use.
-     */
-  uint32_t                          mid_sent;
-
     /**
      * DLL of messages received out of order.
      */
@@ -389,9 +388,14 @@ struct MeshChannelReliability
   struct MeshReliableMessage        *tail_recv;
 
     /**
-     * Next MID expected.
+     * Messages received.
+     */
+  unsigned int                      n_recv;
+
+    /**
+     * Can we send data to the client?
      */
-  uint32_t                          mid_recv;
+  int client_ready;
 
     /**
      * Task to resend/poll in case no ACK is received.
@@ -432,18 +436,43 @@ struct MeshChannel
   uint32_t port;
 
     /**
-     * Local tunnel number ( >= GNUNET_MESH_LOCAL_CHANNEL_ID_CLI or 0 )
+     * Global channel number ( < GNUNET_MESH_LOCAL_CHANNEL_ID_CLI)
      */
-  MESH_ChannelNumber id;
+  MESH_ChannelNumber gid;
+
+    /**
+     * Local tunnel number for root (owner) client.
+     * ( >= GNUNET_MESH_LOCAL_CHANNEL_ID_CLI or 0 )
+     */
+  MESH_ChannelNumber lid_root;
 
     /**
      * Local tunnel number for local destination clients (incoming number)
-     * ( >= GNUNET_MESH_LOCAL_CHANNEL_ID_SERV or 0). All clients share the same
-     * number.
+     * ( >= GNUNET_MESH_LOCAL_CHANNEL_ID_SERV or 0).
+     */
+  MESH_ChannelNumber lid_dest;
+
+    /**
+     * Next MID to use for fwd traffic.
+     */
+  uint32_t mid_send_fwd;
+
+    /**
+     * Next MID expected for fwd traffic.
+     */
+  uint32_t mid_recv_fwd;
+
+    /**
+     * Next MID to use for bck traffic.
      */
-  MESH_ChannelNumber id_dest;
+  uint32_t mid_send_bck;
 
     /**
+     * Next MID expected for bck traffic.
+     */
+  uint32_t mid_recv_bck;
+
+  /**
      * Is the tunnel bufferless (minimum latency)?
      */
   int nobuffer;
@@ -461,12 +490,12 @@ struct MeshChannel
     /**
      * Client owner of the tunnel, if any
      */
-  struct MeshClient *owner;
+  struct MeshClient *root;
 
     /**
      * Client destination of the tunnel, if any.
      */
-  struct MeshClient *client;
+  struct MeshClient *dest;
 
     /**
      * Flag to signal the destruction of the channel.
@@ -507,6 +536,16 @@ struct MeshConnection
    */
   struct MeshTunnel2 *t;
 
+  /**
+   * Flow control information for traffic fwd.
+   */
+  struct MeshFlowControl fwd_fc;
+
+  /**
+   * Flow control information for traffic bck.
+   */
+  struct MeshFlowControl bck_fc;
+
   /**
    * Connection number.
    */
@@ -579,12 +618,12 @@ struct MeshTunnel2
   /**
    * Local peer ephemeral public key
    */
-  struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *my_eph;
+  struct GNUNET_CRYPTO_EccPublicKey *my_eph;
 
   /**
    * Remote peer's public key.
    */
-  struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *peers_eph;
+  struct GNUNET_CRYPTO_EccPublicKey *peers_eph;
 
   /**
    * Encryption ("our") key.
@@ -614,12 +653,12 @@ struct MeshTunnel2
   struct MeshChannel *channel_tail;
 
   /**
-   * Channel ID for the next created tunnel.
+   * Channel ID for the next created channel.
    */
   MESH_ChannelNumber next_chid;
 
   /**
-   * Channel ID for the next incoming tunnel.
+   * Channel ID for the next incoming channel.
    */
   MESH_ChannelNumber next_local_chid;
 
@@ -832,7 +871,7 @@ static struct GNUNET_CRYPTO_EccPrivateKey *my_private_key;
 /**
  * Own public key.
  */
-static struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded my_public_key;
+static struct GNUNET_CRYPTO_EccPublicKey my_public_key;
 
 /**
  * All ports clients of this peer have opened.
@@ -924,15 +963,15 @@ path_add_to_peers (struct MeshPeerPath *p, int confirmed);
 
 
 /**
- * Search for a channel by global ID using full PeerIdentities.
+ * Search for a tunnel by global ID using full PeerIdentities.
  *
- * @param oid owner of the tunnel.
- * @param tid global tunnel number.
+ * @param t Tunnel containing the channel.
+ * @param chid Public channel number.
  *
- * @return tunnel handler, NULL if doesn't exist.
+ * @return channel handler, NULL if doesn't exist
  */
 static struct MeshChannel *
-channel_get (const struct GNUNET_PeerIdentity *oid, MESH_ChannelNumber tid);
+channel_get (struct MeshTunnel2 *t, MESH_ChannelNumber chid);
 
 
 /**
@@ -1059,21 +1098,6 @@ static void
 queue_destroy (struct MeshPeerQueue *queue, int clear_cls);
 
 
-/**
- * @brief Get the next transmittable message from the queue.
- *
- * This will be the head, except in the case of being a data packet
- * not allowed by the destination peer.
- *
- * @param peer Destination peer.
- *
- * @return The next viable MeshPeerQueue element to send to that peer.
- *         NULL when there are no transmittable messages.
- */
-struct MeshPeerQueue *
-queue_get_next (const struct MeshPeer *peer);
-
-
 /**
  * Core callback to write a queued packet to core buffer
  *
@@ -1241,6 +1265,22 @@ connection_get_next_hop (struct MeshConnection *c)
 }
 
 
+/**
+ * Get the hop in a connection.
+ *
+ * @param c Connection.
+ * @param fwd Next hop?
+ *
+ * @return Next peer in the connection. 
+ */
+static struct MeshPeer *
+connection_get_hop (struct MeshConnection *c, int fwd)
+{
+  if (fwd)
+    return connection_get_next_hop (c);
+  return connection_get_prev_hop (c);
+}
+
 /**
  * Check if client has registered with the service and has not disconnected
  *
@@ -1256,8 +1296,7 @@ client_get (struct GNUNET_SERVER_Client *client)
 
 
 /**
- * Deletes a tunnel from a client (either owner or destination). To be used on
- * tunnel destroy.
+ * Deletes a tunnel from a client (either owner or destination).
  *
  * @param c Client whose tunnel to delete.
  * @param ch Channel which should be deleted.
@@ -1267,17 +1306,17 @@ client_delete_channel (struct MeshClient *c, struct MeshChannel *ch)
 {
   int res;
 
-  if (c == ch->owner)
+  if (c == ch->root)
   {
     res = GNUNET_CONTAINER_multihashmap32_remove (c->own_channels,
-                                                  ch->id, ch);
+                                                  ch->lid_root, ch);
     if (GNUNET_YES != res)
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "client_delete_channel owner KO\n");
   }
-  if (c == ch->client)
+  if (c == ch->dest)
   {
     res = GNUNET_CONTAINER_multihashmap32_remove (c->incoming_channels,
-                                                  ch->id_dest, ch);
+                                                  ch->lid_dest, ch);
     if (GNUNET_YES != res)
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "client_delete_tunnel client KO\n");
   }
@@ -1295,18 +1334,18 @@ send_local_channel_create (struct MeshChannel *ch)
   struct GNUNET_MESH_ChannelMessage msg;
   struct MeshTunnel2 *t = ch->t;
 
-  if (NULL == ch->client)
+  if (NULL == ch->dest)
     return;
   msg.header.size = htons (sizeof (msg));
   msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_CREATE);
-  msg.channel_id = htonl (ch->id_dest);
+  msg.channel_id = htonl (ch->lid_dest);
   msg.port = htonl (ch->port);
   msg.opt = 0;
   msg.opt |= GNUNET_YES == ch->reliable ? GNUNET_MESH_OPTION_RELIABLE : 0;
   msg.opt |= GNUNET_YES == ch->nobuffer ? GNUNET_MESH_OPTION_NOBUFFER : 0;
   msg.opt = htonl (msg.opt);
   GNUNET_PEER_resolve (t->peer->id, &msg.peer);
-  GNUNET_SERVER_notification_context_unicast (nc, ch->client->handle,
+  GNUNET_SERVER_notification_context_unicast (nc, ch->dest->handle,
                                               &msg.header, GNUNET_NO);
 }
 
@@ -1323,7 +1362,7 @@ send_local_channel_destroy (struct MeshChannel *ch, int fwd)
   struct GNUNET_MESH_ChannelMessage msg;
   struct MeshClient *c;
 
-  c = fwd ? ch->client : ch->owner;
+  c = fwd ? ch->dest : ch->root;
   if (NULL == c)
   {
     GNUNET_break (0);
@@ -1331,7 +1370,7 @@ send_local_channel_destroy (struct MeshChannel *ch, int fwd)
   }
   msg.header.size = htons (sizeof (msg));
   msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_DESTROY);
-  msg.channel_id = htonl (fwd ? ch->id_dest : ch->id);
+  msg.channel_id = htonl (fwd ? ch->lid_dest : ch->lid_root);
   msg.port = htonl (0);
   memset (&msg.peer, 0, sizeof (msg.peer));
   msg.opt = htonl (0);
@@ -1356,7 +1395,7 @@ send_local_ack (struct MeshChannel *ch,
 
   msg.header.size = htons (sizeof (msg));
   msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_ACK);
-  msg.channel_id = htonl (is_fwd ? ch->id : ch->id_dest);
+  msg.channel_id = htonl (is_fwd ? ch->lid_root : ch->lid_dest);
   GNUNET_SERVER_notification_context_unicast (nc,
                                               c->handle,
                                               &msg.header,
@@ -1396,27 +1435,25 @@ tunnel_get_connection (struct MeshTunnel2 *t, int fwd)
 {
   struct MeshConnection *c;
   struct MeshConnection *best;
-  struct MeshPeer *peer;
+  struct MeshFlowControl *fc;
   unsigned int lowest_q;
 
-
-  peer = NULL;
   best = NULL;
   lowest_q = UINT_MAX;
   for (c = t->connection_head; NULL != c; c = c->next)
   {
     if (MESH_CONNECTION_READY == c->state)
     {
-      peer = fwd ? connection_get_next_hop (c) : connection_get_prev_hop (c);
-      if (NULL == peer->fc)
+      fc = fwd ? &c->fwd_fc : &c->bck_fc;
+      if (NULL == fc)
       {
         GNUNET_break (0);
         continue;
       }
-      if (peer->fc->queue_n < lowest_q)
+      if (fc->queue_n < lowest_q)
       {
         best = c;
-        lowest_q = peer->fc->queue_n;
+        lowest_q = fc->queue_n;
       }
     }
   }
@@ -1424,6 +1461,29 @@ tunnel_get_connection (struct MeshTunnel2 *t, int fwd)
 }
 
 
+/**
+ * Get the total buffer space for a tunnel
+ */
+static unsigned int
+tunnel_get_buffer (struct MeshTunnel2 *t, int fwd)
+{
+  struct MeshConnection *c;
+  struct MeshFlowControl *fc;
+  unsigned int buffer;
+
+  for (buffer = 0, c = t->connection_head; NULL != c; c = c->next)
+  {
+    if (c->state != MESH_CONNECTION_READY)
+      continue;
+
+    fc = fwd ? &c->fwd_fc : &c->bck_fc;
+    buffer += fc->last_ack_recv - fc->last_pid_sent;
+  }
+
+  return buffer;
+}
+
+
 /**
  * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME 
  * Encrypt data with the tunnel key.
@@ -1485,7 +1545,7 @@ send_prebuilt_message_connection (const struct GNUNET_MessageHeader *message,
   size_t size;
   uint16_t type;
 
-  neighbor = fwd ? connection_get_next_hop (c) : connection_get_prev_hop (c);
+  neighbor = connection_get_hop (c, fwd);
   if (NULL == neighbor)
   {
     GNUNET_break (0);
@@ -1594,41 +1654,6 @@ send_prebuilt_message_channel (const struct GNUNET_MessageHeader *message,
 }
 
 
-/**
- * Sends an already built message directly to a peer.
- * Message does must not belong to a connection or channel.
- *
- * @param message Message to send. Function makes a copy of it.
- * @param peer Tunnel on which this message is transmitted.
- */
-static void
-send_prebuilt_message_peer (const struct GNUNET_MessageHeader *message,
-                            struct MeshPeer *peer)
-{
-  void *data;
-  size_t size;
-  uint16_t type;
-
-  if (NULL == peer)
-  {
-    GNUNET_break (0);
-    return;
-  }
-
-  size = ntohs (message->size);
-  data = GNUNET_malloc (size);
-  memcpy (data, message, size);
-  type = ntohs(message->type);
-
-  queue_add (data,
-             type,
-             size,
-             peer,
-             NULL,
-             NULL);
-}
-
-
 /**
  * Sends a CREATE CONNECTION message for a path to a peer.
  * Changes the connection and tunnel states if necessary.
@@ -1686,21 +1711,24 @@ send_connection_ack (struct MeshConnection *connection)
 
 
 /**
- * Build an ACK message and queue it to send to the given peer.
+ * Build a hop-by-hop ACK message and queue it to send for the given connection.
  * 
- * @param peer Peer to whom send the ACK.
+ * @param c Which connection to send the hop-by-hop ACK.
  * @param ack Value of the ACK.
+ * @param fwd Is this fwd?
  */
 static void
-send_ack (struct MeshPeer *peer, uint32_t ack)
+send_ack (struct MeshConnection *c, uint32_t ack, int fwd)
 {
   struct GNUNET_MESH_ACK msg;
 
   msg.header.size = htons (sizeof (msg));
   msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_ACK);
   msg.ack = htonl (ack);
+  msg.tid = c->t->id;
+  msg.cid = htonl (c->id);
 
-  send_prebuilt_message_peer (&msg.header, peer);
+  send_prebuilt_message_connection (&msg.header, c, NULL, fwd);
 }
 
 
@@ -1815,23 +1843,140 @@ send_core_connection_ack (void *cls, size_t size, void *buf)
 
 
 /**
- * Iterator over all the peers to remove the oldest not-used entry.
+ * Destroy the peer_info and free any allocated resources linked to it
+ *
+ * @param peer The peer_info to destroy.
+ *
+ * @return GNUNET_OK on success
+ */
+static int
+peer_destroy (struct MeshPeer *peer)
+{
+  struct GNUNET_PeerIdentity id;
+  struct MeshPeerPath *p;
+  struct MeshPeerPath *nextp;
+
+  GNUNET_PEER_resolve (peer->id, &id);
+  GNUNET_PEER_change_rc (peer->id, -1);
+
+  if (GNUNET_YES !=
+      GNUNET_CONTAINER_multihashmap_remove (peers, &id.hashPubKey, peer))
+  {
+    GNUNET_break (0);
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "removing peer %s, not in hashmap\n", GNUNET_i2s (&id));
+  }
+  if (NULL != peer->dhtget)
+  {
+    GNUNET_DHT_get_stop (peer->dhtget);
+  }
+  p = peer->path_head;
+  while (NULL != p)
+  {
+    nextp = p->next;
+    GNUNET_CONTAINER_DLL_remove (peer->path_head, peer->path_tail, p);
+    path_destroy (p);
+    p = nextp;
+  }
+  tunnel_destroy_empty (peer->tunnel);
+  GNUNET_free (peer);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Returns if peer is used (has a tunnel, is neighbor).
+ *
+ * @peer Peer to check.
+ *
+ * @return GNUNET_YES if peer is in use.
+ */
+static int
+peer_is_used (struct MeshPeer *peer)
+{
+  struct MeshPeerPath *p;
+
+  if (NULL != peer->tunnel)
+    return GNUNET_YES;
+
+  for (p = peer->path_head; NULL != p; p = p->next)
+  {
+    if (p->length < 3)
+      return GNUNET_YES;
+  }
+  return GNUNET_NO;
+}
+
+/**
+ * Iterator over all the peers to get the oldest timestamp.
  *
  * @param cls Closure (unsued).
  * @param key ID of the peer.
  * @param value Peer_Info of the peer.
+ */
+static int
+peer_get_oldest (void *cls,
+                 const struct GNUNET_HashCode *key,
+                 void *value)
+{
+  struct MeshPeer *p = value;
+  struct GNUNET_TIME_Absolute *abs = cls;
+
+  /* Don't count active peers */
+  if (GNUNET_YES == peer_is_used (p))
+    return GNUNET_YES;
+
+  if (abs->abs_value_us < p->last_contact.abs_value_us)
+    abs->abs_value_us = p->last_contact.abs_value_us;
+
+  return GNUNET_YES;
+}
+
+
+/**
+ * Iterator over all the peers to remove the oldest entry.
  *
- * FIXME implement
+ * @param cls Closure (unsued).
+ * @param key ID of the peer.
+ * @param value Peer_Info of the peer.
  */
 static int
 peer_timeout (void *cls,
               const struct GNUNET_HashCode *key,
               void *value)
 {
+  struct MeshPeer *p = value;
+  struct GNUNET_TIME_Absolute *abs = cls;
+
+  if (p->last_contact.abs_value_us == abs->abs_value_us &&
+      GNUNET_NO == peer_is_used (p))
+  {
+    peer_destroy (p);
+    return GNUNET_NO;
+  }
   return GNUNET_YES;
 }
 
 
+/**
+ * Delete oldest unused peer.
+ */
+static void
+peer_delete_oldest (void)
+{
+  struct GNUNET_TIME_Absolute abs;
+
+  abs = GNUNET_TIME_UNIT_FOREVER_ABS;
+
+  GNUNET_CONTAINER_multihashmap_iterate (peers,
+                                         &peer_get_oldest,
+                                         &abs);
+  GNUNET_CONTAINER_multihashmap_iterate (peers,
+                                         &peer_timeout,
+                                         &abs);
+}
+
+
 /**
  * Retrieve the MeshPeer stucture associated with the peer, create one
  * and insert it in the appropriate structures if the peer is not known yet.
@@ -1851,9 +1996,7 @@ peer_get (const struct GNUNET_PeerIdentity *peer_id)
     peer = GNUNET_new (struct MeshPeer);
     if (GNUNET_CONTAINER_multihashmap_size (peers) > max_peers)
     {
-      GNUNET_CONTAINER_multihashmap_iterate (peers,
-                                             &peer_timeout,
-                                             NULL);
+      peer_delete_oldest ();
     }
     GNUNET_CONTAINER_multihashmap_put (peers, &peer_id->hashPubKey, peer,
                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
@@ -1956,6 +2099,49 @@ peer_get_best_path (const struct MeshPeer *peer)
   return best_p;
 }
 
+static int
+queue_is_sendable (struct MeshPeerQueue *q)
+{
+  struct MeshFlowControl *fc;
+
+  /* Is PID-independent? */
+  switch (q->type)
+  {
+    case GNUNET_MESSAGE_TYPE_MESH_ACK:
+    case GNUNET_MESSAGE_TYPE_MESH_POLL:
+      return GNUNET_YES;
+  }
+
+  /* Is PID allowed? */
+  fc = q->fwd ? &q->c->fwd_fc : &q->c->bck_fc;
+  if (GMC_is_pid_bigger (fc->last_ack_recv, fc->last_pid_sent))
+    return GNUNET_YES;
+
+  return GNUNET_NO;
+}
+
+
+/**
+ * Get first sendable message.
+ *
+ * @param peer The destination peer.
+ *
+ * @return Best current known path towards the peer, if any.
+ */
+static struct MeshPeerQueue *
+peer_get_first_message (const struct MeshPeer *peer)
+{
+  struct MeshPeerQueue *q;
+
+  for (q = peer->queue_head; NULL != q; q = q->next)
+  {
+    if (queue_is_sendable (q))
+      return q;
+  }
+
+  return NULL;
+}
+
 
 /**
  * Try to establish a new connection to this peer in the given tunnel.
@@ -2009,31 +2195,62 @@ peer_connect (struct MeshPeer *peer)
 
 
 /**
- * @brief Re-initiate traffic to this peer if necessary.
+ * Get the first transmittable message for a connection.
  *
- * Check if there is traffic queued towards this peer
- * and the core transmit handle is NULL (traffic was stalled).
- * If so, call core tmt rdy.
+ * @param c Connection.
+ * @param fwd Is this FWD?
  *
- * @param peer_id Short ID of peer to which initiate traffic.
+ * @return First transmittable message.
  */
-static void
-peer_unlock_queue (GNUNET_PEER_Id peer_id)
+static struct MeshPeerQueue *
+connection_get_first_message (struct MeshConnection *c, int fwd)
 {
-  struct MeshPeer *peer;
   struct MeshPeerQueue *q;
-  size_t size;
+  struct MeshPeer *p;
 
-  peer = peer_get_short (peer_id);
-  if (NULL != peer->fc->core_transmit)
-    return; /* Already unlocked */
+  p = connection_get_hop (c, fwd);
 
-  q = queue_get_next (peer);
-  if (NULL == q)
+  for (q = p->queue_head; NULL != q; q = q->next)
+  {
+    if (q->c != c)
+      continue;
+    if (queue_is_sendable (q))
+      return q;
+  }
+
+  return NULL;
+}
+
+
+
+/**
+ * @brief Re-initiate traffic on this connection if necessary.
+ *
+ * Check if there is traffic queued towards this peer
+ * and the core transmit handle is NULL (traffic was stalled).
+ * If so, call core tmt rdy.
+ *
+ * @param c Connection on which initiate traffic.
+ * @param fwd Is this about fwd traffic?
+ */
+static void
+connection_unlock_queue (struct MeshConnection *c, int fwd)
+{
+  struct MeshPeer *peer;
+  struct MeshPeerQueue *q;
+  size_t size;
+
+  peer = connection_get_hop (c, fwd);
+
+  if (NULL != peer->core_transmit)
+    return; /* Already unlocked */
+
+  q = connection_get_first_message (c, fwd);
+  if (NULL == q)
     return; /* Nothing to transmit */
 
   size = q->size;
-  peer->fc->core_transmit =
+  peer->core_transmit =
       GNUNET_CORE_notify_transmit_ready (core_handle,
                                          GNUNET_NO,
                                          0,
@@ -2046,42 +2263,44 @@ peer_unlock_queue (GNUNET_PEER_Id peer_id)
 
 
 /**
- * Cancel all transmissions towards a neighbor that belong to
- * a certain connection.
+ * Cancel all transmissions that belong to a certain connection.
  *
- * @param peer Neighbor to whom cancel the transmissions.
  * @param c Connection which to cancel.
+ * @param fwd Cancel fwd traffic?
  */
 static void
-peer_cancel_queues (struct MeshPeer *peer, struct MeshConnection *c)
+connection_cancel_queues (struct MeshConnection *c, int fwd)
 {
   struct MeshPeerQueue *q;
   struct MeshPeerQueue *next;
   struct MeshFlowControl *fc;
+  struct MeshPeer *peer;
 
-  if (NULL == peer || NULL == peer->fc)
+  if (NULL == c)
   {
     GNUNET_break (0);
     return;
   }
-  fc = peer->fc;
-  for (q = fc->queue_head; NULL != q; q = next)
+  fc = fwd ? &c->fwd_fc : &c->bck_fc;
+  peer = connection_get_hop (c, fwd);
+
+  for (q = peer->queue_head; NULL != q; q = next)
   {
     next = q->next;
     if (q->c == c)
     {
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "peer_cancel_queue %s\n",
+                  "connection_cancel_queue %s\n",
                   GNUNET_MESH_DEBUG_M2S (q->type));
       queue_destroy (q, GNUNET_YES);
     }
   }
-  if (NULL == fc->queue_head)
+  if (NULL == peer->queue_head)
   {
-    if (NULL != fc->core_transmit)
+    if (NULL != peer->core_transmit)
     {
-      GNUNET_CORE_notify_transmit_ready_cancel (fc->core_transmit);
-      fc->core_transmit = NULL;
+      GNUNET_CORE_notify_transmit_ready_cancel (peer->core_transmit);
+      peer->core_transmit = NULL;
     }
     if (GNUNET_SCHEDULER_NO_TASK != fc->poll_task)
     {
@@ -2092,48 +2311,6 @@ peer_cancel_queues (struct MeshPeer *peer, struct MeshConnection *c)
 }
 
 
-/**
- * Destroy the peer_info and free any allocated resources linked to it
- *
- * @param peer The peer_info to destroy.
- *
- * @return GNUNET_OK on success
- */
-static int
-peer_destroy (struct MeshPeer *peer)
-{
-  struct GNUNET_PeerIdentity id;
-  struct MeshPeerPath *p;
-  struct MeshPeerPath *nextp;
-
-  GNUNET_PEER_resolve (peer->id, &id);
-  GNUNET_PEER_change_rc (peer->id, -1);
-
-  if (GNUNET_YES !=
-      GNUNET_CONTAINER_multihashmap_remove (peers, &id.hashPubKey, peer))
-  {
-    GNUNET_break (0);
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                "removing peer %s, not in hashmap\n", GNUNET_i2s (&id));
-  }
-  if (NULL != peer->dhtget)
-  {
-    GNUNET_DHT_get_stop (peer->dhtget);
-  }
-  p = peer->path_head;
-  while (NULL != p)
-  {
-    nextp = p->next;
-    GNUNET_CONTAINER_DLL_remove (peer->path_head, peer->path_tail, p);
-    path_destroy (p);
-    p = nextp;
-  }
-  tunnel_destroy_empty (peer->tunnel);
-  GNUNET_free (peer);
-  return GNUNET_OK;
-}
-
-
 /**
  * Remove all paths that rely on a direct connection between p1 and p2
  * from the peer itself and notify all tunnels about it.
@@ -2301,18 +2478,18 @@ peer_add_path_to_origin (struct MeshPeer *peer_info,
 
 
 /**
- * Function called if the connection to the peer has been stalled for a while,
- * possibly due to a missed ACK. Poll the peer about its ACK status.
+ * Function called if a connection has been stalled for a while,
+ * possibly due to a missed ACK. Poll the neighbor about its ACK status.
  *
  * @param cls Closure (poll ctx).
  * @param tc TaskContext.
  */
 static void
-peer_poll (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+connection_poll (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct MeshFlowControl *fc = cls;
   struct GNUNET_MESH_Poll msg;
-  struct MeshPeer *peer;
+  struct MeshConnection *c;
 
   fc->poll_task = GNUNET_SCHEDULER_NO_TASK;
   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
@@ -2320,20 +2497,19 @@ peer_poll (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
     return;
   }
 
+  c = fc->c;
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " *** Polling!\n");
-  peer = fc->peer;
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " *** peer: %s!\n", 
-                GNUNET_i2s (GNUNET_PEER_resolve2 (peer->id)));
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " *** connection %s[%X]\n", 
+              GNUNET_i2s (GNUNET_PEER_resolve2 (c->t->peer->id)), c->id);
 
   msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_POLL);
   msg.header.size = htons (sizeof (msg));
   msg.pid = htonl (fc->last_pid_sent);
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " *** pid (%u)!\n", fc->last_pid_sent);
-  send_prebuilt_message_peer (&msg.header, peer);
+  send_prebuilt_message_connection (&msg.header, c, NULL, fc == &c->fwd_fc);
   fc->poll_time = GNUNET_TIME_STD_BACKOFF (fc->poll_time);
   fc->poll_task = GNUNET_SCHEDULER_add_delayed (fc->poll_time,
-                                                &peer_poll, fc);
+                                                &connection_poll, fc);
 }
 
 
@@ -2472,37 +2648,29 @@ channel_get_by_local_id (struct MeshClient *c, MESH_ChannelNumber chid)
   return GNUNET_CONTAINER_multihashmap32_get (c->own_channels, chid);
 }
 
-
 /**
- * Search for a tunnel by global ID using PEER_ID
+ * Search for a tunnel by global ID using full PeerIdentities.
  *
- * @param pi owner of the tunnel
- * @param tid global tunnel number
+ * @param t Tunnel containing the channel.
+ * @param chid Public channel number.
  *
- * @return tunnel handler, NULL if doesn't exist
+ * @return channel handler, NULL if doesn't exist
  */
 static struct MeshChannel *
-channel_get_by_pi (GNUNET_PEER_Id pi, MESH_ChannelNumber tid)
+channel_get (struct MeshTunnel2 *t, MESH_ChannelNumber chid)
 {
-//   struct GNUNET_HashCode hash;
+  struct MeshChannel *ch;
 
-//   return GNUNET_CONTAINER_multihashmap_get (tunnels, &hash); FIXME
-  return NULL;
-}
+  if (NULL == t)
+    return NULL;
 
+  for (ch = t->channel_head; NULL != ch; ch = ch->next)
+  {
+    if (ch->gid == chid)
+      break;
+  }
 
-/**
- * Search for a tunnel by global ID using full PeerIdentities
- *
- * @param oid owner of the tunnel
- * @param tid global tunnel number
- *
- * @return tunnel handler, NULL if doesn't exist
- */
-static struct MeshChannel *
-channel_get (const struct GNUNET_PeerIdentity *oid, MESH_ChannelNumber tid)
-{
-  return channel_get_by_pi (GNUNET_PEER_search (oid), tid);
+  return ch;
 }
 
 
@@ -2552,20 +2720,30 @@ connection_change_state (struct MeshConnection* c,
 static void
 channel_add_client (struct MeshChannel *ch, struct MeshClient *c)
 {
-  if (NULL != ch->client)
+  struct MeshTunnel2 *t = ch->t;
+
+  if (NULL != ch->dest)
   {
     GNUNET_break(0);
     return;
   }
+
+  /* Assign local id as destination */
+  while (NULL != channel_get_by_local_id (c, t->next_local_chid))
+    t->next_local_chid = (t->next_local_chid + 1) | GNUNET_MESH_LOCAL_CHANNEL_ID_SERV;
+  ch->lid_dest = t->next_local_chid++;
+  t->next_local_chid = t->next_local_chid | GNUNET_MESH_LOCAL_CHANNEL_ID_SERV;
+
+  /* Store in client's hashmap */
   if (GNUNET_OK !=
       GNUNET_CONTAINER_multihashmap32_put (c->incoming_channels,
-                                           ch->id_dest, ch,
+                                           ch->lid_dest, ch,
                                            GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
   {
     GNUNET_break (0);
     return;
   }
-  ch->client = c;
+  ch->dest = c;
 }
 
 
@@ -2647,7 +2825,7 @@ tunnel_notify_connection_broken (struct MeshTunnel2* t,
 
 /**
  * Send an end-to-end FWD ACK message for the most recent in-sequence payload.
- * 
+ *
  * @param ch Channel this is about.
  * @param fwd Is for FWD traffic? (ACK dest->owner)
  */
@@ -2658,6 +2836,7 @@ channel_send_data_ack (struct MeshChannel *ch, int fwd)
   struct MeshChannelReliability *rel;
   struct MeshReliableMessage *copy;
   uint64_t mask;
+  uint32_t *mid;
   unsigned int delta;
 
   if (GNUNET_NO == ch->reliable)
@@ -2665,20 +2844,21 @@ channel_send_data_ack (struct MeshChannel *ch, int fwd)
     GNUNET_break (0);
     return;
   }
-  rel = fwd ? ch->bck_rel  : ch->fwd_rel;
+  rel = fwd ? ch->bck_rel       : ch->fwd_rel;
+  mid = fwd ? &ch->mid_recv_fwd : &ch->mid_recv_bck;
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "send_data_ack for %u\n",
-              rel->mid_recv - 1);
+              *mid - 1);
 
   msg.header.type = htons (fwd ? GNUNET_MESSAGE_TYPE_MESH_UNICAST_ACK :
                                  GNUNET_MESSAGE_TYPE_MESH_TO_ORIG_ACK);
   msg.header.size = htons (sizeof (msg));
-  msg.chid = htonl (ch->id);
-  msg.mid = htonl (rel->mid_recv - 1);
+  msg.chid = htonl (ch->gid);
+  msg.mid = htonl (*mid - 1);
   msg.futures = 0;
   for (copy = rel->head_recv; NULL != copy; copy = copy->next)
   {
-    delta = copy->mid - rel->mid_recv;
+    delta = copy->mid - *mid;
     if (63 < delta)
       break;
     mask = 0x1LL << delta;
@@ -2697,8 +2877,8 @@ channel_send_data_ack (struct MeshChannel *ch, int fwd)
 /**
  * Send an ACK informing the predecessor about the available buffer space.
  *
- * Note that although the name is fwd_ack, the FWD mean forward *traffic*,
- * the ACK itself goes "back" (towards root).
+ * Note that for fwd ack, the FWD mean forward *traffic* (root->dest),
+ * the ACK itself goes "back" (dest->root).
  *
  * @param c Connection on which to send the ACK.
  * @param fwd Is this FWD ACK? (Going dest->owner)
@@ -2708,15 +2888,11 @@ connection_send_ack (struct MeshConnection *c, int fwd)
 {
   struct MeshFlowControl *next_fc;
   struct MeshFlowControl *prev_fc;
-  struct MeshPeer *next;
-  struct MeshPeer *prev;
   uint32_t ack;
   int delta;
 
-  next    = fwd ? connection_get_next_hop (c) : connection_get_prev_hop (c);
-  prev    = fwd ? connection_get_prev_hop (c) : connection_get_next_hop (c);
-  next_fc = next->fc;
-  prev_fc = prev->fc;
+  next_fc = fwd ? &c->fwd_fc : &c->bck_fc;
+  prev_fc = fwd ? &c->bck_fc : &c->fwd_fc;
 
   /* Check if we need to transmit the ACK */
   if (prev_fc->last_ack_sent - prev_fc->last_pid_recv > 3)
@@ -2743,7 +2919,7 @@ connection_send_ack (struct MeshConnection *c, int fwd)
   }
 
   prev_fc->last_ack_sent = ack;
-  send_ack (prev, ack);
+  send_ack (c, ack, fwd);
 }
 
 
@@ -2900,14 +3076,15 @@ channel_send_client_data (struct MeshChannel *ch,
                           int fwd)
 {
   if (fwd)
-    channel_send_client_to_tid (ch, msg, ch->client, ch->id_dest);
+    channel_send_client_to_tid (ch, msg, ch->dest, ch->lid_dest);
   else
-    channel_send_client_to_tid (ch, msg, ch->owner, ch->id);
+    channel_send_client_to_tid (ch, msg, ch->root, ch->lid_root);
 }
 
 
 /**
- * Send up to 64 buffered messages to the client for in order delivery.
+ * Send a buffered message to the client, for in order delivery or
+ * as result of client ACK.
  *
  * @param ch Channel on which to empty the message buffer.
  * @param c Client to send to.
@@ -2920,35 +3097,38 @@ channel_send_client_buffered_data (struct MeshChannel *ch,
                                    struct MeshChannelReliability *rel)
 {
   struct MeshReliableMessage *copy;
-  struct MeshReliableMessage *next;
+  uint32_t *mid;
 
-  if (GNUNET_NO == ch->reliable)
+  if (GNUNET_NO == rel->client_ready)
   {
-    GNUNET_break (0);
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "client not ready\n");
     return;
   }
 
+  mid = rel == ch->bck_rel ? &ch->mid_recv_fwd : &ch->mid_recv_bck;
+
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "send_buffered_data\n");
-  for (copy = rel->head_recv; NULL != copy; copy = next)
+  copy = rel->head_recv;
+  if (NULL != copy)
   {
-    next = copy->next;
-    if (copy->mid == rel->mid_recv)
+    if (copy->mid == *mid || GNUNET_NO == ch->reliable)
     {
       struct GNUNET_MESH_Data *msg = (struct GNUNET_MESH_Data *) &copy[1];
 
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                   " have %u! now expecting %u\n",
-                  copy->mid, rel->mid_recv + 1);
+                  copy->mid, *mid + 1);
       channel_send_client_data (ch, msg, (rel == ch->bck_rel));
-      rel->mid_recv++;
+      rel->n_recv--;
+      *mid = *mid + 1;
       GNUNET_CONTAINER_DLL_remove (rel->head_recv, rel->tail_recv, copy);
       GNUNET_free (copy);
     }
     else
     {
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  " don't have %u, next is %u\n",
-                  rel->mid_recv,
+                  " reliable && don't have %u, next is %u\n",
+                  *mid,
                   copy->mid);
       return;
     }
@@ -2958,8 +3138,9 @@ channel_send_client_buffered_data (struct MeshChannel *ch,
 
 
 /**
- * We have received a message out of order, buffer it until we receive
- * the missing one and we can feed the rest to the client.
+ * We have received a message out of order, or the client is not ready.
+ * Buffer it until we receive an ACK from the client or the missing
+ * message from the channel.
  *
  * @param msg Message to buffer.
  * @param rel Reliability data to the corresponding direction.
@@ -2982,6 +3163,8 @@ channel_rel_add_buffered_data (const struct GNUNET_MESH_Data *msg,
   copy->rel = rel;
   memcpy (&copy[1], msg, size);
 
+  rel->n_recv++;
+
   // FIXME do something better than O(n), although n < 64...
   // FIXME start from the end (most messages are the latest ones)
   for (prev = rel->head_recv; NULL != prev; prev = prev->next)
@@ -3016,9 +3199,9 @@ rel_message_free (struct MeshReliableMessage *copy)
 
   rel = copy->rel;
   time = GNUNET_TIME_absolute_get_duration (copy->timestamp);
-  rel->expected_delay.rel_value *= 7;
-  rel->expected_delay.rel_value += time.rel_value;
-  rel->expected_delay.rel_value /= 8;
+  rel->expected_delay.rel_value_us *= 7;
+  rel->expected_delay.rel_value_us += time.rel_value_us;
+  rel->expected_delay.rel_value_us /= 8;
   rel->n_sent--;
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "!!! Freeing %u\n", copy->mid);
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "    n_sent %u\n", rel->n_sent);
@@ -3151,10 +3334,10 @@ channel_retransmit_message (void *cls,
   struct MeshChannelReliability *rel = cls;
   struct MeshReliableMessage *copy;
   struct MeshPeerQueue *q;
-  struct MeshPeer *pi;
   struct MeshChannel *ch;
   struct MeshConnection *c;
   struct GNUNET_MESH_Data *payload;
+  struct MeshPeer *hop;
   int fwd;
 
   rel->retry_task = GNUNET_SCHEDULER_NO_TASK;
@@ -3181,9 +3364,9 @@ channel_retransmit_message (void *cls,
    */
   payload = (struct GNUNET_MESH_Data *) &copy[1];
   fwd = (rel == ch->fwd_rel);
-  c = tunnel_get_connection(ch->t, fwd);
-  pi = connection_get_next_hop (c);
-  for (q = pi->fc->queue_head; NULL != q; q = q->next)
+  c = tunnel_get_connection (ch->t, fwd);
+  hop = connection_get_hop (c, fwd);
+  for (q = hop->queue_head; NULL != q; q = q->next)
   {
     if (ntohs (payload->header.type) == q->type && ch == q->ch)
     {
@@ -3214,6 +3397,57 @@ channel_retransmit_message (void *cls,
 }
 
 
+
+/**
+ * Send ACK on one or more connections due to buffer space to the client.
+ */
+static void
+channel_send_ack (struct MeshChannel *ch, uint32_t buffer, int fwd)
+{
+  struct MeshTunnel2 *t = ch->t;
+  struct MeshConnection *c;
+  struct MeshFlowControl *fc;
+  uint32_t allowed;
+  uint32_t to_allow;
+  unsigned int cs;
+
+  /* Count connections, how many messages are already allowed */
+  for (cs = 0, allowed = 0, c = t->connection_head; NULL != c; c = c->next)
+  {
+    fc = fwd ? &c->fwd_fc : &c->bck_fc;
+    if (GMC_is_pid_bigger(fc->last_pid_recv, fc->last_ack_sent))
+    {
+      GNUNET_break (0);
+      continue;
+    }
+    allowed += fc->last_ack_sent - fc->last_pid_recv;
+    cs++;
+  }
+
+  /* Make sure there is no overflow */
+  if (allowed > buffer)
+  {
+    GNUNET_break (0);
+    return;
+  }
+
+  /* Authorize connections to send more data */
+  to_allow = buffer - allowed;
+  for (c = t->connection_head; NULL != c && to_allow > 0; c = c->next)
+  {
+    fc = fwd ? &c->fwd_fc : &c->bck_fc;
+    if (fc->last_ack_sent - fc->last_pid_recv > 64 / 3)
+    {
+      continue;
+    }
+    send_ack (c, fc->last_ack_sent + 1, fwd);
+    to_allow--;
+  }
+
+  GNUNET_break (to_allow == 0);
+}
+
+
 /**
  * Send keepalive packets for a connection.
  *
@@ -3305,7 +3539,7 @@ connection_fwd_keepalive (void *cls, const struct GNUNET_SCHEDULER_TaskContext *
   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
     return;
 
-  connection_keepalive (c, GNUNET_YES);
+  connection_maintain (c, GNUNET_YES);
   c->fwd_maintenance_task = GNUNET_SCHEDULER_add_delayed (refresh_connection_time,
                                                           &connection_fwd_keepalive,
                                                           c);
@@ -3321,7 +3555,7 @@ connection_bck_keepalive (void *cls, const struct GNUNET_SCHEDULER_TaskContext *
   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
     return;
 
-  connection_keepalive (c, GNUNET_NO);
+  connection_maintain (c, GNUNET_NO);
   c->bck_maintenance_task = GNUNET_SCHEDULER_add_delayed (refresh_connection_time,
                                                           &connection_bck_keepalive,
                                                           c);
@@ -3353,6 +3587,7 @@ connection_send_destroy (struct MeshConnection *c)
 
   send_prebuilt_message_connection (&msg.header, c, NULL, GNUNET_YES);
   send_prebuilt_message_connection (&msg.header, c, NULL, GNUNET_NO);
+  c->destroy = GNUNET_YES;
 }
 
 
@@ -3372,21 +3607,32 @@ channel_send_destroy (struct MeshChannel *ch)
 
   msg.header.size = htons (sizeof (msg));
   msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_CHANNEL_DESTROY);
-  msg.chid = htonl (ch->id);
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "  sending tunnel destroy for channel %s:%X\n",
               GNUNET_i2s (GNUNET_PEER_resolve2 (ch->t->peer->id)),
-              ch->id);
+              ch->gid);
 
-  if (NULL != ch->owner)
+  if (NULL != ch->root)
+  {
+    msg.chid = htonl (ch->lid_root);
     send_local_channel_destroy (ch, GNUNET_NO);
+  }
   else
+  {
+    msg.chid = htonl (ch->gid);
     send_prebuilt_message_channel (&msg.header, ch, GNUNET_NO);
+  }
 
-  if (NULL != ch->client)
+  if (NULL != ch->dest)
+  {
+    msg.chid = htonl (ch->lid_dest);
     send_local_channel_destroy (ch, GNUNET_YES);
+  }
   else
+  {
+    msg.chid = htonl (ch->gid);
     send_prebuilt_message_channel (&msg.header, ch, GNUNET_YES);
+  }
 }
 
 
@@ -3396,7 +3642,7 @@ channel_send_destroy (struct MeshChannel *ch)
  * @param tid Tunnel ID.
  */
 static struct MeshTunnel2 *
-tunnel_new (struct GNUNET_HashCode *tid)
+tunnel_new (const struct GNUNET_HashCode *tid)
 {
   struct MeshTunnel2 *t;
 
@@ -3422,7 +3668,7 @@ tunnel_new (struct GNUNET_HashCode *tid)
  * @param tid Tunnel ID.
  */
 static struct MeshTunnel2 *
-tunnel_get (struct GNUNET_HashCode *tid)
+tunnel_get (const struct GNUNET_HashCode *tid)
 {
   return GNUNET_CONTAINER_multihashmap_get (tunnels, tid);
 }
@@ -3442,6 +3688,24 @@ tunnel_add_connection (struct MeshTunnel2 *t, struct MeshConnection *c)
 }
 
 
+/**
+ * Initialize a Flow Control structure to the initial state.
+ * 
+ * @param fc Flow Control structure to initialize.
+ */
+static void
+fc_init (struct MeshFlowControl *fc)
+{
+  fc->last_pid_sent = (uint32_t) -1; /* Next (expected) = 0 */
+  fc->last_pid_recv = (uint32_t) -1;
+  fc->last_ack_sent = (uint32_t) -1; /* No traffic allowed yet */
+  fc->last_ack_recv = (uint32_t) -1;
+  fc->poll_task = GNUNET_SCHEDULER_NO_TASK;
+  fc->poll_time = GNUNET_TIME_UNIT_SECONDS;
+  fc->queue_n = 0;
+}
+
+
 /**
  * Create a connection.
  *
@@ -3467,6 +3731,8 @@ connection_new (struct GNUNET_HashCode *tid, uint32_t cid)
 
   c = GNUNET_new (struct MeshConnection);
   c->id = cid;
+  fc_init (&c->fwd_fc);
+  fc_init (&c->bck_fc);
   tunnel_add_connection (t, c);
 
   return c;
@@ -3480,7 +3746,7 @@ connection_new (struct GNUNET_HashCode *tid, uint32_t cid)
  * @param cid Connection ID.
  */
 static struct MeshConnection *
-connection_get (struct GNUNET_HashCode *tid, uint32_t cid)
+connection_get (const struct GNUNET_HashCode *tid, uint32_t cid)
 {
   struct MeshConnection *c;
   struct MeshTunnel2 *t;
@@ -3502,8 +3768,6 @@ connection_get (struct GNUNET_HashCode *tid, uint32_t cid)
 static void
 connection_destroy (struct MeshConnection *c)
 {
-  struct MeshPeer *peer;
-
   if (NULL == c)
     return;
 
@@ -3511,12 +3775,8 @@ connection_destroy (struct MeshConnection *c)
               GNUNET_i2s (GNUNET_PEER_resolve2 (c->t->peer->id)),
               c->id);
 
-  peer = connection_get_next_hop (c);
-  if (NULL != peer)
-    peer_cancel_queues (peer, c);
-  peer = connection_get_prev_hop (c);
-  if (NULL != peer)
-    peer_cancel_queues (peer, c);
+  connection_cancel_queues (c, GNUNET_YES);
+  connection_cancel_queues (c, GNUNET_NO);
 
   if (GNUNET_SCHEDULER_NO_TASK != c->fwd_maintenance_task)
     GNUNET_SCHEDULER_cancel (c->fwd_maintenance_task);
@@ -3540,7 +3800,7 @@ tunnel_destroy (struct MeshTunnel2 *t)
     return;
 
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "destroying tunnel %s\n",
-              GNUNET_i2s (GNUNET_PEER_resolve2 (c->t->peer->id)));
+              GNUNET_i2s (GNUNET_PEER_resolve2 (t->peer->id)));
 
   if (GNUNET_YES != GNUNET_CONTAINER_multihashmap_remove (tunnels, &t->id, t))
     GNUNET_break (0);
@@ -3597,24 +3857,6 @@ tunnel_destroy_if_empty (struct MeshTunnel2 *t)
 }
 
 
-/**
- * Initialize a Flow Control structure to the initial state.
- * 
- * @param fc Flow Control structure to initialize.
- */
-static void
-fc_init (struct MeshFlowControl *fc)
-{
-  fc->last_pid_sent = (uint32_t) -1; /* Next (expected) = 0 */
-  fc->last_pid_recv = (uint32_t) -1;
-  fc->last_ack_sent = (uint32_t) -1; /* No traffic allowed yet */
-  fc->last_ack_recv = (uint32_t) -1;
-  fc->poll_task = GNUNET_SCHEDULER_NO_TASK;
-  fc->poll_time = GNUNET_TIME_UNIT_SECONDS;
-  fc->queue_n = 0;
-}
-
-
 /**
  * Destroy a channel and free all resources.
  * 
@@ -3628,22 +3870,22 @@ channel_destroy (struct MeshChannel *ch)
   if (NULL == ch)
     return;
 
-  c = ch->owner;
+  c = ch->root;
   if (NULL != c)
   {
     if (GNUNET_YES != GNUNET_CONTAINER_multihashmap32_remove (c->own_channels,
-                                                              c->id, ch))
+                                                              ch->lid_root, ch))
     {
       GNUNET_break (0);
     }
   }
 
-  c = ch->client;
+  c = ch->dest;
   if (NULL != c)
   {
     if (GNUNET_YES !=
         GNUNET_CONTAINER_multihashmap32_remove (c->incoming_channels,
-                                                ch->id_dest, ch))
+                                                ch->lid_dest, ch))
     {
       GNUNET_break (0);
     }
@@ -3663,35 +3905,42 @@ channel_destroy (struct MeshChannel *ch)
 
 /**
  * Create a new channel.
- * 
- * @param owner Clients that owns the channel, NULL for foreign channels.
- * @param id Channel Number for the channel, for the owner point of view.
- * 
+ *
+ * @param t Tunnel this channel is in.
+ * @param owner Client that owns the channel, NULL for foreign channels.
+ * @param lid_root Local ID for root client.
+ *
  * @return A new initialized channel. NULL on error.
  */
 static struct MeshChannel *
-channel_new (struct MeshClient *owner,
-             MESH_ChannelNumber id)
+channel_new (struct MeshTunnel2 *t,
+             struct MeshClient *owner, MESH_ChannelNumber lid_root)
 {
   struct MeshChannel *ch;
 
-  if (NULL == owner)
-    return NULL;
-
   ch = GNUNET_new (struct MeshChannel);
-  ch->owner = owner;
-  ch->id = id;
+  ch->root = owner;
+  ch->lid_root = lid_root;
+  ch->t = t;
 
   GNUNET_STATISTICS_update (stats, "# channels", 1, GNUNET_NO);
 
-  if (GNUNET_OK !=
-      GNUNET_CONTAINER_multihashmap32_put (owner->own_channels, id, ch,
-                                           GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
+  if (NULL != owner)
   {
-    GNUNET_break (0);
-    channel_destroy (ch);
-    GNUNET_SERVER_receive_done (owner->handle, GNUNET_SYSERR);
-    return NULL;
+    while (NULL != channel_get_by_local_id (owner, t->next_chid))
+      t->next_chid = (t->next_chid + 1) & ~GNUNET_MESH_LOCAL_CHANNEL_ID_CLI;
+    ch->gid = t->next_chid;
+    t->next_chid = (t->next_chid + 1) & ~GNUNET_MESH_LOCAL_CHANNEL_ID_CLI;
+
+    if(GNUNET_OK !=
+       GNUNET_CONTAINER_multihashmap32_put (owner->own_channels, lid_root, ch,
+                                           GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
+    {
+      GNUNET_break (0);
+      channel_destroy (ch);
+      GNUNET_SERVER_receive_done (owner->handle, GNUNET_SYSERR);
+      return NULL;
+    }
   }
 
   return ch;
@@ -3733,18 +3982,18 @@ channel_destroy_iterator (void *cls,
   struct MeshTunnel2 *t;
 
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              " Channel %X / %X destroy, due to client %u shutdown.\n",
-              ch->id, ch->id_dest, c->id);
+              " Channel %X (%X / %X) destroy, due to client %u shutdown.\n",
+              ch->gid, ch->lid_root, ch->lid_dest, c->id);
 
-  if (c == ch->client)
+  if (c == ch->dest)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " Client %u is destination.\n", c->id);
-    ch->client = NULL;
+    ch->dest = NULL;
   }
-  if (c == ch->owner)
+  if (c == ch->root)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " Client %u is owner.\n", c->id);
-    ch->owner = NULL;
+    ch->root = NULL;
   }
 
   t = ch->t;
@@ -3882,6 +4131,42 @@ connection_reset_timeout (struct MeshConnection *c, int fwd)
 }
 
 
+/**
+ * Iterator to notify all connections of a broken link. Mark connections
+ * to destroy after all traffic has been sent.
+ *
+ * @param cls Closure (peer disconnected).
+ * @param key Current key code (tid).
+ * @param value Value in the hash map (connection).
+ *
+ * @return GNUNET_YES if we should continue to iterate,
+ *         GNUNET_NO if not.
+ */
+static int
+connection_broken (void *cls,
+                   const struct GNUNET_HashCode *key,
+                   void *value)
+{
+  struct MeshPeer *peer = cls;
+  struct MeshConnection *c = value;
+  struct GNUNET_MESH_ConnectionBroken msg;
+  int fwd;
+
+  fwd = peer == connection_get_prev_hop (c);
+  connection_cancel_queues (c, !fwd);
+
+  msg.header.size = htons (sizeof (struct GNUNET_MESH_ConnectionBroken));
+  msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_CONNECTION_BROKEN);
+  msg.cid = htonl (c->id);
+  msg.tid = c->t->id;
+  msg.peer1 = my_full_id;
+  msg.peer2 = *GNUNET_PEER_resolve2 (peer->id);
+  send_prebuilt_message_connection (&msg.header, c, NULL, fwd);
+  c->destroy = GNUNET_YES;
+
+  return GNUNET_YES;
+}
+
 /******************************************************************************/
 /****************      MESH NETWORK HANDLER HELPERS     ***********************/
 /******************************************************************************/
@@ -3896,9 +4181,14 @@ connection_reset_timeout (struct MeshConnection *c, int fwd)
 static void
 queue_destroy (struct MeshPeerQueue *queue, int clear_cls)
 {
+  struct MeshPeer *peer;
   struct MeshFlowControl *fc;
+  int fwd;
+
+  fwd = queue->fwd;
+  peer = queue->peer;
+  fc = fwd ? &queue->c->fwd_fc : &queue->c->bck_fc;
 
-  fc = queue->peer->fc;
   if (GNUNET_YES == clear_cls)
   {
     switch (queue->type)
@@ -3928,36 +4218,38 @@ queue_destroy (struct MeshPeerQueue *queue, int clear_cls)
     }
     GNUNET_free_non_null (queue->cls);
   }
-  GNUNET_CONTAINER_DLL_remove (fc->queue_head, fc->queue_tail, queue);
+  GNUNET_CONTAINER_DLL_remove (peer->queue_head, peer->queue_tail, queue);
 
   fc->queue_n--;
+  peer->queue_n--;
+  if (NULL != queue->c)
+  {
+    queue->c->pending_messages--;
+    if (NULL != queue->c->t)
+    {
+      queue->c->t->pending_messages--;
+    }
+  }
 
   GNUNET_free (queue);
 }
 
+
 static size_t
 queue_send (void *cls, size_t size, void *buf)
 {
   struct MeshPeer *peer = cls;
-  const struct GNUNET_PeerIdentity *dst_id;
+  struct MeshFlowControl *fc;
+  struct MeshConnection *c;
   struct GNUNET_MessageHeader *msg;
   struct MeshPeerQueue *queue;
   struct MeshTunnel2 *t;
-  struct MeshFlowControl *fc;
-  struct MeshConnection *c;
+  const struct GNUNET_PeerIdentity *dst_id;
   size_t data_size;
   uint32_t pid;
   uint16_t type;
   int fwd;
 
-  fc = peer->fc;
-  if (NULL == fc)
-  {
-    GNUNET_break (0);
-    return 0;
-  }
-  fc->core_transmit = NULL;
-
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "* Queue send\n");
 
   if (NULL == buf || 0 == size)
@@ -3965,14 +4257,19 @@ queue_send (void *cls, size_t size, void *buf)
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "* Buffer size 0.\n");
     return 0;
   }
-  queue = fc->queue_head;
 
-  /* Queue has no traffic */
+  /* Initialize */
+  queue = peer_get_first_message (peer);
   if (NULL == queue)
   {
     GNUNET_break (0); /* Core tmt_rdy should've been canceled */
     return 0;
   }
+  queue->peer->core_transmit = NULL;
+  c = queue->c;
+  fwd = queue->fwd;
+  fc = fwd ? &c->fwd_fc : &c->bck_fc;
+
 
   dst_id = GNUNET_PEER_resolve2 (peer->id);
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "*   towards %s\n", GNUNET_i2s (dst_id));
@@ -3980,7 +4277,7 @@ queue_send (void *cls, size_t size, void *buf)
   if (queue->size > size)
   {
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "*   not enough room, reissue\n");
-      fc->core_transmit =
+      peer->core_transmit =
           GNUNET_CORE_notify_transmit_ready (core_handle,
                                              GNUNET_NO,
                                              0,
@@ -3993,7 +4290,6 @@ queue_send (void *cls, size_t size, void *buf)
   }
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "*   size ok\n");
 
-  c = queue->c;
   t = (NULL != c) ? c->t : NULL;
   type = 0;
 
@@ -4037,8 +4333,6 @@ queue_send (void *cls, size_t size, void *buf)
       data_size = 0;
   }
 
-  fc->queue_n--;
-
   if (0 < drop_percent &&
       GNUNET_CRYPTO_random_u32(GNUNET_CRYPTO_QUALITY_WEAK, 101) < drop_percent)
   {
@@ -4051,12 +4345,9 @@ queue_send (void *cls, size_t size, void *buf)
   queue_destroy (queue, GNUNET_NO);
 
   /* Send ACK if needed, after accounting for sent ID in fc->queue_n */
-  fwd = GNUNET_NO;
   switch (type)
   {
     case GNUNET_MESSAGE_TYPE_MESH_FWD:
-      fwd = GNUNET_YES;
-      /* fall through */
     case GNUNET_MESSAGE_TYPE_MESH_BCK:
       pid = ntohl ( ((struct GNUNET_MESH_Encrypted *) buf)->pid );
       fc->last_pid_sent = pid;
@@ -4067,12 +4358,12 @@ queue_send (void *cls, size_t size, void *buf)
   }
 
   /* If more data in queue, send next */
-  queue = fc->queue_head;
+  queue = peer_get_first_message (peer);
   if (NULL != queue)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "*   more data!\n");
-    if (NULL == fc->core_transmit) {
-      peer->fc->core_transmit =
+    if (NULL == peer->core_transmit) {
+      peer->core_transmit =
           GNUNET_CORE_notify_transmit_ready(core_handle,
                                             0,
                                             0,
@@ -4091,7 +4382,7 @@ queue_send (void *cls, size_t size, void *buf)
     {
       GNUNET_log (GNUNET_ERROR_TYPE_INFO, "*   %s starting poll timeout\n");
       fc->poll_task =
-          GNUNET_SCHEDULER_add_delayed (fc->poll_time, &peer_poll, fc);
+          GNUNET_SCHEDULER_add_delayed (fc->poll_time, &connection_poll, fc);
     }
   }
   else
@@ -4135,8 +4426,11 @@ queue_add (void *cls, uint16_t type, size_t size,
   struct MeshPeerQueue *queue;
   struct MeshFlowControl *fc;
   int priority;
+  int fwd;
+
+  fwd = (dst == connection_get_next_hop (c));
+  fc = fwd ? &c->fwd_fc : &c->bck_fc;
 
-  fc = dst->fc;
   if (NULL == fc)
   {
     GNUNET_break (0);
@@ -4150,8 +4444,8 @@ queue_add (void *cls, uint16_t type, size_t size,
     priority = 100;
 
   if (NULL != ch &&
-      ( (NULL != ch->owner && GNUNET_MESSAGE_TYPE_MESH_FWD == type) ||
-        (NULL != ch->client && GNUNET_MESSAGE_TYPE_MESH_BCK == type) ))
+      ( (NULL != ch->root && GNUNET_MESSAGE_TYPE_MESH_FWD == type) ||
+        (NULL != ch->dest && GNUNET_MESSAGE_TYPE_MESH_BCK == type) ))
     priority = 50;
 
   if (fc->queue_n >= fc->queue_max && 0 == priority)
@@ -4165,11 +4459,10 @@ queue_add (void *cls, uint16_t type, size_t size,
     return; /* Drop this message */
   }
 
-  fc->queue_n++;
   if (GMC_is_pid_bigger(fc->last_pid_sent + 1, fc->last_ack_recv) &&
       GNUNET_SCHEDULER_NO_TASK == fc->poll_task)
     fc->poll_task = GNUNET_SCHEDULER_add_delayed (fc->poll_time,
-                                                  &peer_poll,
+                                                  &connection_poll,
                                                   dst);
   queue = GNUNET_malloc (sizeof (struct MeshPeerQueue));
   queue->cls = cls;
@@ -4178,14 +4471,15 @@ queue_add (void *cls, uint16_t type, size_t size,
   queue->peer = dst;
   queue->c = c;
   queue->ch = ch;
+  queue->fwd = fwd;
   if (100 <= priority)
-    GNUNET_CONTAINER_DLL_insert (fc->queue_head, fc->queue_tail, queue);
+    GNUNET_CONTAINER_DLL_insert (dst->queue_head, dst->queue_tail, queue);
   else
-    GNUNET_CONTAINER_DLL_insert_tail (fc->queue_head, fc->queue_tail, queue);
+    GNUNET_CONTAINER_DLL_insert_tail (dst->queue_head, dst->queue_tail, queue);
 
-  if (NULL == fc->core_transmit)
+  if (NULL == dst->core_transmit)
   {
-    fc->core_transmit =
+    dst->core_transmit =
         GNUNET_CORE_notify_transmit_ready (core_handle,
                                            0,
                                            0,
@@ -4197,6 +4491,8 @@ queue_add (void *cls, uint16_t type, size_t size,
   }
   c->pending_messages++;
   c->t->pending_messages++;
+  fc->queue_n++;
+  dst->queue_n++;
 }
 
 
@@ -4206,24 +4502,222 @@ queue_add (void *cls, uint16_t type, size_t size,
 
 
 /**
- * Core handler for connection creation.
+ * Generic handler for mesh network payload traffic.
  *
- * @param cls Closure (unused).
- * @param peer Sender (neighbor).
- * @param message Message.
+ * @param t Tunnel on which we got this message.
+ * @param message Unencryted data message.
+ * @param fwd Is this FWD traffic? GNUNET_YES : GNUNET_NO;
  *
  * @return GNUNET_OK to keep the connection open,
  *         GNUNET_SYSERR to close it (signal serious error)
  */
 static int
-handle_mesh_connection_create (void *cls,
-                               const struct GNUNET_PeerIdentity *peer,
-                               const struct GNUNET_MessageHeader *message)
+handle_data (struct MeshTunnel2 *t, const struct GNUNET_MESH_Data *msg, int fwd)
 {
-  struct GNUNET_MESH_ConnectionCreate *msg;
-  struct GNUNET_PeerIdentity *id;
-  struct GNUNET_HashCode *tid;
-  struct MeshPeerPath *path;
+  struct MeshChannelReliability *rel;
+  struct MeshChannel *ch;
+  struct MeshClient *c;
+  uint32_t mid;
+  uint32_t *mid_recv;
+  uint16_t type;
+  size_t size;
+
+  /* Check size */
+  size = ntohs (msg->header.size);
+  if (size <
+      sizeof (struct GNUNET_MESH_Data) +
+      sizeof (struct GNUNET_MessageHeader))
+  {
+    GNUNET_break (0);
+    return GNUNET_OK;
+  }
+  type = ntohs (msg->header.type);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "got a %s message\n",
+              GNUNET_MESH_DEBUG_M2S (type));
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " payload of type %s\n",
+              GNUNET_MESH_DEBUG_M2S (ntohs (msg[1].header.type)));
+
+  /* Check channel */
+  ch = channel_get (t, ntohl (msg->chid));
+  if (NULL == ch)
+  {
+    GNUNET_STATISTICS_update (stats, "# data on unknown channel", 1, GNUNET_NO);
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "WARNING channel unknown\n");
+    return GNUNET_OK;
+  }
+
+  /*  Initialize FWD/BCK data */
+  c        = fwd ? ch->dest          : ch->root;
+  rel      = fwd ? ch->bck_rel       : ch->fwd_rel;
+  mid_recv = fwd ? &ch->mid_recv_fwd : &ch->mid_recv_bck;
+
+  if (NULL == c)
+  {
+    GNUNET_break (0);
+    return GNUNET_OK;
+  }
+
+  tunnel_change_state (t, MESH_TUNNEL_READY);
+
+  GNUNET_STATISTICS_update (stats, "# data received", 1, GNUNET_NO);
+
+  mid = ntohl (msg->mid);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " mid %u\n", mid);
+
+  if (GNUNET_NO == ch->reliable ||
+      ( !GMC_is_pid_bigger (*mid_recv, mid) &&
+        GMC_is_pid_bigger (*mid_recv + 64, mid) ) )
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "!!! RECV %u\n", mid);
+    if (GNUNET_YES == ch->reliable)
+    {
+      /* Is this the exact next expected messasge? */
+      if (mid == *mid_recv)
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "as expected\n");
+        *mid_recv = *mid_recv + 1;
+        channel_send_client_data (ch, msg, fwd);
+        channel_send_client_buffered_data (ch, c, rel);
+      }
+      else
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "save for later\n");
+        channel_rel_add_buffered_data (msg, rel);
+      }
+    }
+    else /* Tunnel unreliable, send to clients directly */
+    {
+      channel_send_client_data (ch, msg, fwd);
+    }
+  }
+  else
+  {
+    GNUNET_break_op (0);
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                " MID %u not expected (%u - %u), dropping!\n",
+                mid, *mid_recv, *mid_recv + 64);
+  }
+
+  channel_send_data_ack (ch, fwd);
+  return GNUNET_OK;
+}
+
+/**
+ * Handler for mesh network traffic end-to-end ACKs.
+ *
+ * @param t Tunnel on which we got this message.
+ * @param message Data message.
+ * @param fwd Is this a fwd ACK? (dest->orig)
+ *
+ * @return GNUNET_OK to keep the connection open,
+ *         GNUNET_SYSERR to close it (signal serious error)
+ */
+static int
+handle_data_ack (struct MeshTunnel2 *t,
+                 const struct GNUNET_MESH_DataACK *msg, int fwd)
+{
+  struct MeshChannelReliability *rel;
+  struct MeshReliableMessage *copy;
+  struct MeshReliableMessage *next;
+  struct MeshChannel *ch;
+  uint32_t ack;
+  uint16_t type;
+  int work;
+
+  type = ntohs (msg->header.type);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a %s message!\n",
+              GNUNET_MESH_DEBUG_M2S (type));
+  ch = channel_get (t, ntohl (msg->chid));
+  if (NULL == ch)
+  {
+    GNUNET_STATISTICS_update (stats, "# ack on unknown channel", 1, GNUNET_NO);
+    return GNUNET_OK;
+  }
+  ack = ntohl (msg->mid);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "!!! %s ACK %u\n",
+              (GNUNET_YES == fwd) ? "FWD" : "BCK", ack);
+
+  if (GNUNET_YES == fwd)
+  {
+    rel = ch->fwd_rel;
+  }
+  else
+  {
+    rel = ch->bck_rel;
+  }
+  if (NULL == rel)
+  {
+    return GNUNET_OK;
+  }
+
+  for (work = GNUNET_NO, copy = rel->head_sent; copy != NULL; copy = next)
+  {
+    if (GMC_is_pid_bigger (copy->mid, ack))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "!!!  head %u, out!\n", copy->mid);
+      channel_rel_free_sent (rel, msg);
+      break;
+    }
+    work = GNUNET_YES;
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "!!!  id %u\n", copy->mid);
+    next = copy->next;
+    rel_message_free (copy);
+  }
+  /* ACK client if needed */
+//   channel_send_ack (t, type, GNUNET_MESSAGE_TYPE_MESH_UNICAST_ACK == type);
+
+  /* If some message was free'd, update the retransmission delay*/
+  if (GNUNET_YES == work)
+  {
+    if (GNUNET_SCHEDULER_NO_TASK != rel->retry_task)
+    {
+      GNUNET_SCHEDULER_cancel (rel->retry_task);
+      if (NULL == rel->head_sent)
+      {
+        rel->retry_task = GNUNET_SCHEDULER_NO_TASK;
+      }
+      else
+      {
+        struct GNUNET_TIME_Absolute new_target;
+        struct GNUNET_TIME_Relative delay;
+
+        delay = GNUNET_TIME_relative_multiply (rel->retry_timer,
+                                               MESH_RETRANSMIT_MARGIN);
+        new_target = GNUNET_TIME_absolute_add (rel->head_sent->timestamp,
+                                               delay);
+        delay = GNUNET_TIME_absolute_get_remaining (new_target);
+        rel->retry_task =
+            GNUNET_SCHEDULER_add_delayed (delay,
+                                          &channel_retransmit_message,
+                                          rel);
+      }
+    }
+    else
+      GNUNET_break (0);
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Core handler for connection creation.
+ *
+ * @param cls Closure (unused).
+ * @param peer Sender (neighbor).
+ * @param message Message.
+ *
+ * @return GNUNET_OK to keep the connection open,
+ *         GNUNET_SYSERR to close it (signal serious error)
+ */
+static int
+handle_mesh_connection_create (void *cls,
+                               const struct GNUNET_PeerIdentity *peer,
+                               const struct GNUNET_MessageHeader *message)
+{
+  struct GNUNET_MESH_ConnectionCreate *msg;
+  struct GNUNET_PeerIdentity *id;
+  struct GNUNET_HashCode *tid;
+  struct MeshPeerPath *path;
   struct MeshPeer *dest_peer;
   struct MeshPeer *orig_peer;
   struct MeshConnection *c;
@@ -4438,29 +4932,32 @@ handle_mesh_connection_broken (void *cls, const struct GNUNET_PeerIdentity *peer
 /**
  * Core handler for tunnel destruction
  *
- * @param cls closure
- * @param message message
- * @param peer peer identity this notification is about
+ * @param cls Closure (unused).
+ * @param peer Peer identity of sending neighbor.
+ * @param message Message.
  *
  * @return GNUNET_OK to keep the connection open,
  *         GNUNET_SYSERR to close it (signal serious error)
  */
 static int
-handle_mesh_tunnel_destroy (void *cls, const struct GNUNET_PeerIdentity *peer,
-                            const struct GNUNET_MessageHeader *message)
+handle_mesh_connection_destroy (void *cls,
+                                const struct GNUNET_PeerIdentity *peer,
+                                const struct GNUNET_MessageHeader *message)
 {
-  struct GNUNET_MESH_TunnelDestroy *msg;
-  struct MeshTunnel *t;
+  struct GNUNET_MESH_ConnectionDestroy *msg;
+  struct MeshConnection *c;
+  GNUNET_PEER_Id id;
+  int fwd;
 
-  msg = (struct GNUNET_MESH_TunnelDestroy *) message;
+  msg = (struct GNUNET_MESH_ConnectionDestroy *) message;
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Got a TUNNEL DESTROY packet from %s\n",
+              "Got a CONNECTION DESTROY message from %s\n",
               GNUNET_i2s (peer));
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "  for tunnel %s [%u]\n",
-              GNUNET_i2s (&msg->oid), ntohl (msg->tid));
-  t = channel_get (&msg->oid, ntohl (msg->tid));
-  if (NULL == t)
+              "  for connection %s[%X]\n",
+              GNUNET_h2s (&msg->tid), ntohl (msg->cid));
+  c = connection_get (&msg->tid, ntohl (msg->cid));
+  if (NULL == c)
   {
     /* Probably already got the message from another path,
      * destroyed the tunnel and retransmitted to children.
@@ -4470,197 +4967,283 @@ handle_mesh_tunnel_destroy (void *cls, const struct GNUNET_PeerIdentity *peer,
                               1, GNUNET_NO);
     return GNUNET_OK;
   }
-  if (t->local_tid_dest >= GNUNET_MESH_LOCAL_CHANNEL_ID_SERV)
+  id = GNUNET_PEER_search (peer);
+  if (id == connection_get_prev_hop (c)->id)
+    fwd = GNUNET_YES;
+  else if (id == connection_get_next_hop (c)->id)
+    fwd = GNUNET_NO;
+  else
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "INCOMING TUNNEL %X %X\n",
-                t->local_tid, t->local_tid_dest);
+    GNUNET_break_op (0);
+    return GNUNET_OK;
   }
-  if (GNUNET_PEER_search (peer) == t->prev_hop)
+  send_prebuilt_message_connection (message, c, NULL, fwd);
+  c->destroy = GNUNET_YES;
+
+  return GNUNET_OK;
+}
+
+/**
+ * Handler for channel create messages.
+ *
+ * @param t Tunnel this channel is to be created in.
+ * @param msg Message.
+ * @param fwd Is this FWD traffic? GNUNET_YES : GNUNET_NO;
+ *
+ * @return GNUNET_OK to keep the connection open,
+ *         GNUNET_SYSERR to close it (signal serious error)
+ */
+static int
+handle_channel_create (struct MeshTunnel2 *t,
+                       struct GNUNET_MESH_ChannelCreate *msg,
+                       int fwd)
+{
+  MESH_ChannelNumber chid;
+  struct MeshChannel *ch;
+  struct MeshClient *c;
+
+  /* Check message size */
+  if (ntohs (msg->header.size) != sizeof (struct GNUNET_MESH_ChannelCreate))
   {
-    // TODO check owner's signature
-    // TODO add owner's signatue to tunnel for retransmission
-    peer_cancel_queues (t->prev_hop, t);
-    GNUNET_PEER_change_rc (t->prev_hop, -1);
-    t->prev_hop = 0;
+    GNUNET_break_op (0);
+    return GNUNET_OK;
   }
-  else if (GNUNET_PEER_search (peer) == t->next_hop)
+
+  /* Check if channel exists */
+  chid = ntohl (msg->chid);
+  ch = channel_get (t, chid);
+  if (NULL != ch)
   {
-    // TODO check dest's signature
-    // TODO add dest's signatue to tunnel for retransmission
-    peer_cancel_queues (t->next_hop, t);
-    GNUNET_PEER_change_rc (t->next_hop, -1);
-    t->next_hop = 0;
+    /* Probably a retransmission, safe to ignore */
+    return GNUNET_OK;
   }
-  else
+
+  /* Find a destination client */
+  c = GNUNET_CONTAINER_multihashmap32_get (ports, msg->port);
+  if (NULL == c)
+  {
+    /* TODO send reject */
+    return GNUNET_OK;
+  }
+
+  /* Create channel */
+  ch = channel_new (t, NULL, 0);
+  channel_set_options (ch, ntohl (msg->opt));
+  channel_add_client (ch, c);
+  if (GNUNET_YES == ch->reliable)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "!!! Reliable\n");
+    ch->bck_rel = GNUNET_malloc (sizeof (struct MeshChannelReliability));
+    ch->bck_rel->ch = ch;
+    ch->bck_rel->expected_delay = MESH_RETRANSMIT_TIME;
+  }
+
+  send_local_channel_create (ch);
+
+  return GNUNET_OK;
+}
+
+
+/**
+ * Handler for channel destroy messages.
+ *
+ * @param t Tunnel this channel is to be destroyed of.
+ * @param msg Message.
+ * @param fwd Is this FWD traffic? GNUNET_YES : GNUNET_NO;
+ *
+ * @return GNUNET_OK to keep the connection open,
+ *         GNUNET_SYSERR to close it (signal serious error)
+ */
+static int
+handle_channel_destroy (struct MeshTunnel2 *t,
+                        struct GNUNET_MESH_ChannelDestroy *msg,
+                        int fwd)
+{
+  MESH_ChannelNumber chid;
+  struct MeshChannel *ch;
+
+  /* Check message size */
+  if (ntohs (msg->header.size) != sizeof (struct GNUNET_MESH_ChannelDestroy))
   {
     GNUNET_break_op (0);
-    // TODO check both owner AND destination's signature to see which matches
-    // TODO restransmit in appropriate direction
     return GNUNET_OK;
   }
-  tunnel_destroy_empty (t);
 
-  // TODO: add timeout to destroy the tunnel anyway
+  /* Check if channel exists */
+  chid = ntohl (msg->chid);
+  ch = channel_get (t, chid);
+  if (NULL == ch)
+  {
+    /* Probably a retransmission, safe to ignore */
+    return GNUNET_OK;
+  }
+
+  send_local_channel_destroy (ch, fwd);
+  channel_destroy (ch);
+
   return GNUNET_OK;
 }
 
 
 /**
- * Generic handler for mesh network payload traffic.
+ * Generic handler for mesh network encrypted traffic.
  *
  * @param peer Peer identity this notification is about.
- * @param message Data message.
+ * @param message Encrypted message.
  * @param fwd Is this FWD traffic? GNUNET_YES : GNUNET_NO;
  *
  * @return GNUNET_OK to keep the connection open,
  *         GNUNET_SYSERR to close it (signal serious error)
  */
 static int
-handle_mesh_data (const struct GNUNET_PeerIdentity *peer,
-                  const struct GNUNET_MessageHeader *message,
-                  int fwd)
+handle_mesh_encrypted (const struct GNUNET_PeerIdentity *peer,
+                       const struct GNUNET_MESH_Encrypted *msg,
+                       int fwd)
 {
-  struct GNUNET_MESH_Data *msg;
+  struct MeshConnection *c;
+  struct MeshTunnel2 *t;
+  struct MeshPeer *neighbor;
   struct MeshFlowControl *fc;
-  struct MeshChannelReliability *rel;
-  struct MeshTunnel *t;
-  struct MeshClient *c;
-  GNUNET_PEER_Id hop;
   uint32_t pid;
   uint32_t ttl;
   uint16_t type;
   size_t size;
 
   /* Check size */
-  size = ntohs (message->size);
+  size = ntohs (msg->header.size);
   if (size <
-      sizeof (struct GNUNET_MESH_Data) +
+      sizeof (struct GNUNET_MESH_Encrypted) +
       sizeof (struct GNUNET_MessageHeader))
   {
     GNUNET_break (0);
     return GNUNET_OK;
   }
-  type =ntohs (message->type);
+  type = ntohs (msg->header.type);
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "got a %s message from %s\n",
               GNUNET_MESH_DEBUG_M2S (type), GNUNET_i2s (peer));
-  msg = (struct GNUNET_MESH_Data *) message;
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " payload of type %s\n",
-              GNUNET_MESH_DEBUG_M2S (ntohs (msg[1].header.type)));
-  /* Check tunnel */
-  t = channel_get (&msg->oid, ntohl (msg->tid));
-  if (NULL == t)
+
+  /* Check connection */
+  c = connection_get (&msg->tid, ntohl (msg->cid));
+  if (NULL == c)
   {
-    /* TODO notify back: we don't know this tunnel */
-    GNUNET_STATISTICS_update (stats, "# data on unknown tunnel", 1, GNUNET_NO);
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "WARNING tunnel unknown\n");
+    GNUNET_STATISTICS_update (stats, "# unknown connection", 1, GNUNET_NO);
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "WARNING connection unknown\n");
     return GNUNET_OK;
   }
+  t = c->t;
+  fc = fwd ? &c->fwd_fc : &c->bck_fc;
 
-  /*  Initialize FWD/BCK data */
+  /* Check if origin is as expected */
+  neighbor = connection_get_hop (c, fwd);
+  if (peer_get (peer)->id != neighbor->id)
+  {
+    GNUNET_break_op (0);
+    return GNUNET_OK;
+  }
+
+  /* Check PID */
   pid = ntohl (msg->pid);
-  fc =  fwd ? &t->prev_fc : &t->next_fc;
-  c =   fwd ? t->client   : t->owner;
-  rel = fwd ? t->bck_rel  : t->fwd_rel;
-  hop = fwd ? t->next_hop : t->prev_hop;
   if (GMC_is_pid_bigger (pid, fc->last_ack_sent))
   {
-    GNUNET_STATISTICS_update (stats, "# unsolicited data", 1, GNUNET_NO);
+    GNUNET_STATISTICS_update (stats, "# unsolicited message", 1, GNUNET_NO);
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                 "WARNING Received PID %u, (prev %u), ACK %u\n",
                 pid, fc->last_pid_recv, fc->last_ack_sent);
     return GNUNET_OK;
   }
-  if (NULL != c)
-    tunnel_change_state (t, MESH_TUNNEL_READY);
-  tunnel_reset_timeout (t, fwd);
-  if (NULL != c)
+  if (GNUNET_NO == GMC_is_pid_bigger (pid, fc->last_pid_recv))
   {
-    /* TODO signature verification */
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  it's for us! sending to client\n");
-    GNUNET_STATISTICS_update (stats, "# data received", 1, GNUNET_NO);
-    if (GMC_is_pid_bigger (pid, fc->last_pid_recv))
-    {
-      uint32_t mid;
+    GNUNET_STATISTICS_update (stats, "# duplicate PID", 1, GNUNET_NO);
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                " Pid %u not expected (%u+), dropping!\n",
+                pid, fc->last_pid_recv + 1);
+    return GNUNET_OK;
+  }
+  if (MESH_CONNECTION_SENT == c->state)
+    connection_change_state (c, MESH_CONNECTION_READY);
+  connection_reset_timeout (c, fwd);
+  fc->last_pid_recv = pid;
 
-      mid = ntohl (msg->mid);
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  " pid %u (mid %u) not seen yet\n", pid, mid);
-      fc->last_pid_recv = pid;
+  /* Is this message for us? */
+  if (NULL != c->t->channel_head)
+  {
+    size_t dsize = size - sizeof (struct GNUNET_MESH_Encrypted);
+    char cbuf[dsize];
+    struct GNUNET_MessageHeader *msgh;
 
-      if (GNUNET_NO == t->reliable ||
-          ( !GMC_is_pid_bigger (rel->mid_recv, mid) &&
-            GMC_is_pid_bigger (rel->mid_recv + 64, mid) ) )
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                    "!!! RECV %u\n", ntohl (msg->mid));
-        if (GNUNET_YES == t->reliable)
-        {
-          /* Is this the exact next expected messasge? */
-          if (mid == rel->mid_recv)
-          {
-            GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "as expected\n");
-            rel->mid_recv++;
-            tunnel_send_client_data (t, msg, fwd);
-            tunnel_send_client_buffered_data (t, c, rel);
-          }
-          else
-          {
-            GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "save for later\n");
-            tunnel_add_buffered_data (t, msg, rel);
-          }
-        }
-        else /* Tunnel unreliable, send to clients directly */
-        {
-          tunnel_send_client_data (t, msg, fwd);
-        }
-      }
-      else
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                    " MID %u not expected (%u - %u), dropping!\n",
-                    ntohl (msg->mid), rel->mid_recv, rel->mid_recv + 64);
-      }
-    }
-    else
+    /* TODO signature verification */
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  it's for us!\n");
+    GNUNET_STATISTICS_update (stats, "# messages received", 1, GNUNET_NO);
+
+    fc->last_pid_recv = pid;
+    tunnel_decrypt (t, cbuf, &msg[1], dsize, msg->iv, fwd);
+    msgh = (struct GNUNET_MessageHeader *) cbuf;
+    switch (ntohs (msgh->type))
     {
-//       GNUNET_STATISTICS_update (stats, "# duplicate PID", 1, GNUNET_NO);
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  " Pid %u not expected (%u+), dropping!\n",
-                  pid, fc->last_pid_recv + 1);
+      case GNUNET_MESSAGE_TYPE_MESH_UNICAST_ACK:
+        if (GNUNET_YES == fwd)
+          return handle_data_ack (t, (struct GNUNET_MESH_DataACK *) msgh,
+                                  GNUNET_YES);
+        GNUNET_break_op (0);
+        break;
+      case GNUNET_MESSAGE_TYPE_MESH_TO_ORIG_ACK:
+        if (GNUNET_NO == fwd)
+          return handle_data_ack (t, (struct GNUNET_MESH_DataACK *) msgh,
+                                  GNUNET_YES);
+        GNUNET_break_op (0);
+        break;
+      case GNUNET_MESSAGE_TYPE_MESH_UNICAST:
+        if (GNUNET_YES == fwd)
+          handle_data (t, (struct GNUNET_MESH_Data *) msgh, GNUNET_YES);
+        GNUNET_break_op (0);
+        break;
+      case GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN:
+        if (GNUNET_NO == fwd)
+          handle_data (t, (struct GNUNET_MESH_Data *) msgh, GNUNET_NO);
+        GNUNET_break_op (0);
+        break;
+      case GNUNET_MESSAGE_TYPE_MESH_CHANNEL_CREATE:
+        return handle_channel_create (t,
+                                      (struct GNUNET_MESH_ChannelCreate *) msgh,
+                                      fwd);
+        break;
+      case GNUNET_MESSAGE_TYPE_MESH_CHANNEL_DESTROY:
+        return handle_channel_destroy (t,
+                                       (struct GNUNET_MESH_ChannelDestroy *)
+                                       msgh,
+                                       fwd);
+        break;
+      default:
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                    "end-to-end message not known (%u)\n",
+                    ntohs (msgh->type));
     }
-    tunnel_send_ack (t, type, fwd);
+
+    connection_send_ack (c, fwd);
     return GNUNET_OK;
   }
-  fc->last_pid_recv = pid;
-  if (0 == hop)
-  {
-    GNUNET_STATISTICS_update (stats, "# data on dying tunnel", 1, GNUNET_NO);
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "data on dying tunnel %s[%X]\n",
-                GNUNET_PEER_resolve2 (t->id.oid), ntohl (msg->tid));
-    return GNUNET_OK; /* Next hop has destoyed the tunnel, drop */
-  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  not for us, retransmitting...\n");
   ttl = ntohl (msg->ttl);
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "   ttl: %u\n", ttl);
   if (ttl == 0)
   {
     GNUNET_STATISTICS_update (stats, "# TTL drops", 1, GNUNET_NO);
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, " TTL is 0, DROPPING!\n");
-    tunnel_send_ack (t, GNUNET_MESSAGE_TYPE_MESH_ACK, fwd);
+    connection_send_ack (c, fwd);
     return GNUNET_OK;
   }
+  GNUNET_STATISTICS_update (stats, "# messages forwarded", 1, GNUNET_NO);
+
+  send_prebuilt_message_connection (&msg->header, c, NULL, fwd);
+  connection_send_ack (c, fwd);
 
-  if (myid != hop)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  not for us, retransmitting...\n");
-    send_prebuilt_message (message, hop, t);
-    GNUNET_STATISTICS_update (stats, "# unicast forwarded", 1, GNUNET_NO);
-  }
   return GNUNET_OK;
 }
 
 
 /**
- * Core handler for mesh network traffic going from the origin to a peer
+ * Core handler for mesh network traffic going orig->dest.
  *
  * @param cls Closure (unused).
  * @param message Message received.
@@ -4670,14 +5253,16 @@ handle_mesh_data (const struct GNUNET_PeerIdentity *peer,
  *         GNUNET_SYSERR to close it (signal serious error)
  */
 static int
-handle_mesh_unicast (void *cls, const struct GNUNET_PeerIdentity *peer,
+handle_mesh_fwd (void *cls, const struct GNUNET_PeerIdentity *peer,
                      const struct GNUNET_MessageHeader *message)
 {
-  return handle_mesh_data (peer, message, GNUNET_YES);
+  return handle_mesh_encrypted (peer,
+                                (struct GNUNET_MESH_Encrypted *)message,
+                                GNUNET_YES);
 }
 
 /**
- * Core handler for mesh network traffic towards the owner of a tunnel.
+ * Core handler for mesh network traffic going dest->orig.
  *
  * @param cls Closure (unused).
  * @param message Message received.
@@ -4687,129 +5272,15 @@ handle_mesh_unicast (void *cls, const struct GNUNET_PeerIdentity *peer,
  *         GNUNET_SYSERR to close it (signal serious error)
  */
 static int
-handle_mesh_to_orig (void *cls, const struct GNUNET_PeerIdentity *peer,
+handle_mesh_bck (void *cls, const struct GNUNET_PeerIdentity *peer,
                      const struct GNUNET_MessageHeader *message)
 {
-  return handle_mesh_data (peer, message, GNUNET_NO);
+  return handle_mesh_encrypted (peer,
+                                (struct GNUNET_MESH_Encrypted *)message,
+                                GNUNET_NO);
 }
 
 
-/**
- * Core handler for mesh network traffic end-to-end ACKs.
- *
- * @param cls Closure.
- * @param message Message.
- * @param peer Peer identity this notification is about.
- *
- * @return GNUNET_OK to keep the connection open,
- *         GNUNET_SYSERR to close it (signal serious error)
- */
-static int
-handle_mesh_data_ack (void *cls, const struct GNUNET_PeerIdentity *peer,
-                      const struct GNUNET_MessageHeader *message)
-{
-  struct GNUNET_MESH_DataACK *msg;
-  struct MeshChannelReliability *rel;
-  struct MeshReliableMessage *copy;
-  struct MeshReliableMessage *next;
-  struct MeshTunnel *t;
-  GNUNET_PEER_Id id;
-  uint32_t ack;
-  uint16_t type;
-  int work;
-
-  type = ntohs (message->type);
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a %s message from %s!\n",
-              GNUNET_MESH_DEBUG_M2S (type), GNUNET_i2s (peer));
-  msg = (struct GNUNET_MESH_DataACK *) message;
-
-  t = channel_get (&msg->oid, ntohl (msg->tid));
-  if (NULL == t)
-  {
-    /* TODO notify that we dont know this tunnel (whom)? */
-    GNUNET_STATISTICS_update (stats, "# ack on unknown tunnel", 1, GNUNET_NO);
-    return GNUNET_OK;
-  }
-  ack = ntohl (msg->mid);
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  ACK %u\n", ack);
-
-  /* Is this a forward or backward ACK? */
-  id = GNUNET_PEER_search (peer);
-  if (t->next_hop == id && GNUNET_MESSAGE_TYPE_MESH_UNICAST_ACK == type)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  FWD ACK\n");
-    if (NULL == t->owner)
-    {
-      send_prebuilt_message (message, t->prev_hop, t);
-      return GNUNET_OK;
-    }
-    rel = t->fwd_rel;
-  }
-  else if (t->prev_hop == id && GNUNET_MESSAGE_TYPE_MESH_TO_ORIG_ACK == type)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  BCK ACK\n");
-    if (NULL == t->client)
-    {
-      send_prebuilt_message (message, t->next_hop, t);
-      return GNUNET_OK;
-    }
-    rel = t->bck_rel;
-  }
-  else
-  {
-    GNUNET_break_op (0);
-    return GNUNET_OK;
-  }
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "!!! ACK %u\n", ack);
-  for (work = GNUNET_NO, copy = rel->head_sent; copy != NULL; copy = next)
-  {
-   if (GMC_is_pid_bigger (copy->mid, ack))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "!!!  head %u, out!\n", copy->mid);
-      tunnel_free_sent_reliable (t, msg, rel);
-      break;
-    }
-    work = GNUNET_YES;
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "!!!  id %u\n", copy->mid);
-    next = copy->next;
-    tunnel_free_reliable_message (copy);
-  }
-  /* Once buffers have been free'd, send ACK */
-  tunnel_send_ack (t, type, GNUNET_MESSAGE_TYPE_MESH_UNICAST_ACK == type);
-
-  /* If some message was free'd, update the retransmission delay*/
-  if (GNUNET_YES == work)
-  {
-    if (GNUNET_SCHEDULER_NO_TASK != rel->retry_task)
-    {
-      GNUNET_SCHEDULER_cancel (rel->retry_task);
-      if (NULL == rel->head_sent)
-      {
-        rel->retry_task = GNUNET_SCHEDULER_NO_TASK;
-      }
-      else
-      {
-        struct GNUNET_TIME_Absolute new_target;
-        struct GNUNET_TIME_Relative delay;
-
-        delay = GNUNET_TIME_relative_multiply (rel->retry_timer,
-                                               MESH_RETRANSMIT_MARGIN);
-        new_target = GNUNET_TIME_absolute_add (rel->head_sent->timestamp,
-                                               delay);
-        delay = GNUNET_TIME_absolute_get_remaining (new_target);
-        rel->retry_task =
-            GNUNET_SCHEDULER_add_delayed (delay,
-                                          &tunnel_retransmit_message,
-                                          rel);
-      }
-    }
-    else
-      GNUNET_break (0);
-  }
-  return GNUNET_OK;
-}
-
 /**
  * Core handler for mesh network traffic point-to-point acks.
  *
@@ -4825,7 +5296,7 @@ handle_mesh_ack (void *cls, const struct GNUNET_PeerIdentity *peer,
                  const struct GNUNET_MessageHeader *message)
 {
   struct GNUNET_MESH_ACK *msg;
-  struct MeshTunnel *t;
+  struct MeshConnection *c;
   struct MeshFlowControl *fc;
   GNUNET_PEER_Id id;
   uint32_t ack;
@@ -4834,28 +5305,29 @@ handle_mesh_ack (void *cls, const struct GNUNET_PeerIdentity *peer,
               GNUNET_i2s (peer));
   msg = (struct GNUNET_MESH_ACK *) message;
 
-  t = channel_get (&msg->oid, ntohl (msg->tid));
+  c = connection_get (&msg->tid, ntohl (msg->cid));
 
-  if (NULL == t)
+  if (NULL == c)
   {
-    /* TODO notify that we dont know this tunnel (whom)? */
-    GNUNET_STATISTICS_update (stats, "# ack on unknown tunnel", 1, GNUNET_NO);
+    GNUNET_STATISTICS_update (stats, "# ack on unknown connection", 1,
+                              GNUNET_NO);
     return GNUNET_OK;
   }
-  ack = ntohl (msg->pid);
+
+  ack = ntohl (msg->ack);
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  ACK %u\n", ack);
 
   /* Is this a forward or backward ACK? */
   id = GNUNET_PEER_search (peer);
-  if (t->next_hop == id)
+  if (connection_get_next_hop (c)->id == id)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  FWD ACK\n");
-    fc = &t->next_fc;
+    fc = &c->fwd_fc;
   }
-  else if (t->prev_hop == id)
+  else if (connection_get_prev_hop (c)->id == id)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  BCK ACK\n");
-    fc = &t->prev_fc;
+    fc = &c->bck_fc;
   }
   else
   {
@@ -4863,6 +5335,7 @@ handle_mesh_ack (void *cls, const struct GNUNET_PeerIdentity *peer,
     return GNUNET_OK;
   }
 
+  /* Cancel polling if the ACK is bigger than before. */
   if (GNUNET_SCHEDULER_NO_TASK != fc->poll_task &&
       GMC_is_pid_bigger (ack, fc->last_ack_recv))
   {
@@ -4872,10 +5345,7 @@ handle_mesh_ack (void *cls, const struct GNUNET_PeerIdentity *peer,
   }
 
   fc->last_ack_recv = ack;
-  peer_unlock_queue (id);
-  tunnel_change_state (t, MESH_TUNNEL_READY);
-
-  tunnel_send_ack (t, GNUNET_MESSAGE_TYPE_MESH_ACK, t->next_hop == id);
+  connection_unlock_queue (c, fc == &c->fwd_fc);
 
   return GNUNET_OK;
 }
@@ -4896,55 +5366,49 @@ handle_mesh_poll (void *cls, const struct GNUNET_PeerIdentity *peer,
                   const struct GNUNET_MessageHeader *message)
 {
   struct GNUNET_MESH_Poll *msg;
-  struct MeshTunnel *t;
+  struct MeshConnection *c;
   struct MeshFlowControl *fc;
   GNUNET_PEER_Id id;
   uint32_t pid;
-  uint32_t old;
 
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a POLL packet from %s!\n",
               GNUNET_i2s (peer));
 
   msg = (struct GNUNET_MESH_Poll *) message;
 
-  t = channel_get (&msg->oid, ntohl (msg->tid));
+  c = connection_get (&msg->tid, ntohl (msg->cid));
 
-  if (NULL == t)
+  if (NULL == c)
   {
-    /* TODO notify that we dont know this tunnel (whom)? */
-    GNUNET_STATISTICS_update (stats, "# poll on unknown tunnel", 1, GNUNET_NO);
+    GNUNET_STATISTICS_update (stats, "# poll on unknown connection", 1,
+                              GNUNET_NO);
     GNUNET_break_op (0);
     return GNUNET_OK;
   }
 
   /* Is this a forward or backward ACK? */
   id = GNUNET_PEER_search (peer);
-  pid = ntohl (msg->pid);
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  PID %u\n", pid);
-  if (t->next_hop == id)
+  if (connection_get_next_hop (c)->id == id)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  from FWD\n");
-    fc = &t->next_fc;
-    old = fc->last_pid_recv;
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  FWD ACK\n");
+    fc = &c->fwd_fc;
   }
-  else if (t->prev_hop == id)
+  else if (connection_get_prev_hop (c)->id == id)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  from BCK\n");
-    fc = &t->prev_fc;
-    old = fc->last_pid_recv;
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  BCK ACK\n");
+    fc = &c->bck_fc;
   }
   else
   {
-    GNUNET_break (0);
+    GNUNET_break_op (0);
     return GNUNET_OK;
   }
 
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  was %u\n", fc->last_pid_recv);
+  pid = ntohl (msg->pid);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  PID %u, OLD %u\n",
+              pid, fc->last_pid_recv);
   fc->last_pid_recv = pid;
-  tunnel_send_ack (t, GNUNET_MESSAGE_TYPE_MESH_POLL, t->prev_hop == id);
-
-  if (GNUNET_YES == t->reliable)
-    fc->last_pid_recv = old;
+  connection_send_ack (c, fc == &c->fwd_fc);
 
   return GNUNET_OK;
 }
@@ -4965,40 +5429,45 @@ static int
 handle_mesh_keepalive (void *cls, const struct GNUNET_PeerIdentity *peer,
                        const struct GNUNET_MessageHeader *message)
 {
-  struct GNUNET_MESH_TunnelKeepAlive *msg;
-  struct MeshTunnel *t;
-  struct MeshClient *c;
-  GNUNET_PEER_Id hop;
+  struct GNUNET_MESH_ConnectionKeepAlive *msg;
+  struct MeshConnection *c;
+  struct MeshPeer *neighbor;
   int fwd;
 
-  msg = (struct GNUNET_MESH_TunnelKeepAlive *) message;
+  msg = (struct GNUNET_MESH_ConnectionKeepAlive *) message;
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "got a keepalive packet from %s\n",
               GNUNET_i2s (peer));
 
-  t = channel_get (&msg->oid, ntohl (msg->tid));
-  if (NULL == t)
+  c = connection_get (&msg->tid, ntohl (msg->cid));
+  if (NULL == c)
   {
-    /* TODO notify that we dont know that tunnel */
-    GNUNET_STATISTICS_update (stats, "# keepalive on unknown tunnel", 1,
+    GNUNET_STATISTICS_update (stats, "# keepalive on unknown connection", 1,
                               GNUNET_NO);
     return GNUNET_OK;
   }
 
   fwd = GNUNET_MESSAGE_TYPE_MESH_FWD_KEEPALIVE == ntohs (message->type) ? 
         GNUNET_YES : GNUNET_NO;
-  c   = fwd ? t->client   : t->owner;
-  hop = fwd ? t->next_hop : t->prev_hop;
 
-  if (NULL != c)
-    tunnel_change_state (t, MESH_TUNNEL_READY);
-  tunnel_reset_timeout (t, fwd);
-  if (NULL != c || 0 == hop || myid == hop)
+  /* Check if origin is as expected */
+  neighbor = connection_get_hop (c, fwd);
+  if (peer_get (peer)->id != neighbor->id)
+  {
+    GNUNET_break_op (0);
+    return GNUNET_OK;
+  }
+
+  connection_change_state (c, MESH_CONNECTION_READY);
+  connection_reset_timeout (c, fwd);
+
+  if (NULL != c->t->channel_head)
     return GNUNET_OK;
 
   GNUNET_STATISTICS_update (stats, "# keepalives forwarded", 1, GNUNET_NO);
-  send_prebuilt_message (message, hop, t);
+  send_prebuilt_message_connection (message, c, NULL, fwd);
+
   return GNUNET_OK;
-  }
+}
 
 
 
@@ -5011,23 +5480,19 @@ static struct GNUNET_CORE_MessageHandler core_handlers[] = {
   {&handle_mesh_connection_ack, GNUNET_MESSAGE_TYPE_MESH_CONNECTION_ACK,
     sizeof (struct GNUNET_MESH_ConnectionACK)},
   {&handle_mesh_connection_broken, GNUNET_MESSAGE_TYPE_MESH_CONNECTION_BROKEN,
-   sizeof (struct GNUNET_MESH_ConnectionBroken)},
-  {&handle_mesh_tunnel_destroy, GNUNET_MESSAGE_TYPE_MESH_TUNNEL_DESTROY,
-   sizeof (struct GNUNET_MESH_TunnelDestroy)},
-  {&handle_mesh_unicast, GNUNET_MESSAGE_TYPE_MESH_UNICAST, 0},
-  {&handle_mesh_to_orig, GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN, 0},
-  {&handle_mesh_data_ack, GNUNET_MESSAGE_TYPE_MESH_UNICAST_ACK,
-    sizeof (struct GNUNET_MESH_DataACK)},
-  {&handle_mesh_data_ack, GNUNET_MESSAGE_TYPE_MESH_TO_ORIG_ACK,
-    sizeof (struct GNUNET_MESH_DataACK)},
+    sizeof (struct GNUNET_MESH_ConnectionBroken)},
+  {&handle_mesh_connection_destroy, GNUNET_MESSAGE_TYPE_MESH_CONNECTION_DESTROY,
+    sizeof (struct GNUNET_MESH_ConnectionDestroy)},
   {&handle_mesh_keepalive, GNUNET_MESSAGE_TYPE_MESH_FWD_KEEPALIVE,
-    sizeof (struct GNUNET_MESH_TunnelKeepAlive)},
+    sizeof (struct GNUNET_MESH_ConnectionKeepAlive)},
   {&handle_mesh_keepalive, GNUNET_MESSAGE_TYPE_MESH_BCK_KEEPALIVE,
-    sizeof (struct GNUNET_MESH_TunnelKeepAlive)},
+    sizeof (struct GNUNET_MESH_ConnectionKeepAlive)},
   {&handle_mesh_ack, GNUNET_MESSAGE_TYPE_MESH_ACK,
     sizeof (struct GNUNET_MESH_ACK)},
   {&handle_mesh_poll, GNUNET_MESSAGE_TYPE_MESH_POLL,
     sizeof (struct GNUNET_MESH_Poll)},
+  {&handle_mesh_fwd, GNUNET_MESSAGE_TYPE_MESH_FWD, 0},
+  {&handle_mesh_bck, GNUNET_MESSAGE_TYPE_MESH_BCK, 0},
   {NULL, 0, 0}
 };
 
@@ -5108,6 +5573,7 @@ handle_local_client_connect (void *cls, struct GNUNET_SERVER_Client *client)
     return;
   c = GNUNET_malloc (sizeof (struct MeshClient));
   c->handle = client;
+  c->id = next_client_id++; /* overflow not important: just for debug */
   GNUNET_SERVER_client_keep (client);
   GNUNET_SERVER_client_set_user_context (client, c);
   GNUNET_CONTAINER_DLL_insert (clients_head, clients_tail, c);
@@ -5141,18 +5607,18 @@ handle_local_client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
                 c->id, c);
     GNUNET_SERVER_client_drop (c->handle);
     c->shutting_down = GNUNET_YES;
-    if (NULL != c->own_tunnels)
+    if (NULL != c->own_channels)
     {
-      GNUNET_CONTAINER_multihashmap32_iterate (c->own_tunnels,
-                                               &tunnel_destroy_iterator, c);
-      GNUNET_CONTAINER_multihashmap32_destroy (c->own_tunnels);
+      GNUNET_CONTAINER_multihashmap32_iterate (c->own_channels,
+                                               &channel_destroy_iterator, c);
+      GNUNET_CONTAINER_multihashmap32_destroy (c->own_channels);
     }
 
-    if (NULL != c->incoming_tunnels)
+    if (NULL != c->incoming_channels)
     {
-      GNUNET_CONTAINER_multihashmap32_iterate (c->incoming_tunnels,
-                                               &tunnel_destroy_iterator, c);
-      GNUNET_CONTAINER_multihashmap32_destroy (c->incoming_tunnels);
+      GNUNET_CONTAINER_multihashmap32_iterate (c->incoming_channels,
+                                               &channel_destroy_iterator, c);
+      GNUNET_CONTAINER_multihashmap32_destroy (c->incoming_channels);
     }
 
     if (NULL != c->ports)
@@ -5163,9 +5629,9 @@ handle_local_client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
     }
     next = c->next;
     GNUNET_CONTAINER_DLL_remove (clients_head, clients_tail, c);
+    GNUNET_STATISTICS_update (stats, "# clients", -1, GNUNET_NO);
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  client free (%p)\n", c);
     GNUNET_free (c);
-    GNUNET_STATISTICS_update (stats, "# clients", -1, GNUNET_NO);
     c = next;
   }
   else
@@ -5209,7 +5675,6 @@ handle_local_new_client (void *cls, struct GNUNET_SERVER_Client *client,
 
   /* Initialize new client structure */
   c = GNUNET_SERVER_client_get_user_context (client, struct MeshClient);
-  c->id = next_client_id++; /* overflow not important: just for debug */
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  client id %u\n", c->id);
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  client has %u ports\n", size);
   if (size > 0)
@@ -5286,7 +5751,7 @@ handle_local_channel_create (void *cls, struct GNUNET_SERVER_Client *client,
               GNUNET_i2s (&msg->peer), ntohl (msg->port));
   chid = ntohl (msg->channel_id);
 
-  /* Sanity check for duplicate tunnel IDs */
+  /* Sanity check for duplicate channel IDs */
   if (NULL != channel_get_by_local_id (c, chid))
   {
     GNUNET_break (0);
@@ -5296,37 +5761,35 @@ handle_local_channel_create (void *cls, struct GNUNET_SERVER_Client *client,
 
   peer = peer_get (&msg->peer);
   if (NULL == peer->tunnel)
-    peer->tunnel = tunnel_new ();
+  {
+    struct GNUNET_HashCode tid;
+
+    GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_NONCE, &tid);
+    peer->tunnel = tunnel_new (&tid);
+  }
   t = peer->tunnel;
 
   /* Create channel */
-  while (NULL != channel_get_by_pi (myid, next_tid))
-    next_tid = (next_tid + 1) & ~GNUNET_MESH_LOCAL_CHANNEL_ID_CLI;
-  t = tunnel_new (myid, next_tid, c, chid);
-  next_tid = (next_tid + 1) & ~GNUNET_MESH_LOCAL_CHANNEL_ID_CLI;
-  if (NULL == t)
+  ch = channel_new (t, c, chid);
+  if (NULL == ch)
   {
     GNUNET_break (0);
     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
     return;
   }
-  t->port = ntohl (msg->port);
-  tunnel_set_options (t, ntohl (msg->opt));
-  if (GNUNET_YES == t->reliable)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "!!! Reliable\n");
-    t->fwd_rel = GNUNET_malloc (sizeof (struct MeshChannelReliability));
-    t->fwd_rel->t = t;
-    t->fwd_rel->expected_delay = MESH_RETRANSMIT_TIME;
-  }
+  ch->port = ntohl (msg->port);
+  channel_set_options (ch, ntohl (msg->opt));
 
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CREATED TUNNEL %s[%x]:%u (%x)\n",
-              GNUNET_i2s (&my_full_id), t->id.tid, t->port, t->local_tid);
+  /* In unreliable channels, we'll use the DLL to buffer data for the root */
+  ch->fwd_rel = GNUNET_malloc (sizeof (struct MeshChannelReliability));
+  ch->fwd_rel->ch = ch;
+  ch->fwd_rel->expected_delay = MESH_RETRANSMIT_TIME;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CREATED CHANNEL %s[%x]:%u (%x)\n",
+              GNUNET_h2s (&t->id), ch->gid, ch->port, ch->lid_root);
+  peer_connect (peer);
+  /* FIXME send create channel */
 
-  peer_info = peer_get (&msg->peer);
-  peer_add_tunnel (peer_info, t);
-  peer_connect (peer_info, t);
-  tunnel_reset_timeout (t, GNUNET_YES);
   GNUNET_SERVER_receive_done (client, GNUNET_OK);
   return;
 }
@@ -5343,13 +5806,14 @@ static void
 handle_local_channel_destroy (void *cls, struct GNUNET_SERVER_Client *client,
                              const struct GNUNET_MessageHeader *message)
 {
-  struct GNUNET_MESH_ChannelMessage *tunnel_msg;
+  struct GNUNET_MESH_ChannelMessage *msg;
   struct MeshClient *c;
-  struct MeshTunnel *t;
-  MESH_ChannelNumber tid;
+  struct MeshChannel *ch;
+  struct MeshTunnel2 *t;
+  MESH_ChannelNumber chid;
 
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Got a DESTROY TUNNEL from client!\n");
+              "Got a DESTROY CHANNEL from client!\n");
 
   /* Sanity check for client registration */
   if (NULL == (c = client_get (client)))
@@ -5368,40 +5832,40 @@ handle_local_channel_destroy (void *cls, struct GNUNET_SERVER_Client *client,
     return;
   }
 
-  tunnel_msg = (struct GNUNET_MESH_ChannelMessage *) message;
+  msg = (struct GNUNET_MESH_ChannelMessage *) message;
 
   /* Retrieve tunnel */
-  tid = ntohl (tunnel_msg->channel_id);
-  t = channel_get_by_local_id (c, tid);
-  if (NULL == t)
+  chid = ntohl (msg->channel_id);
+  ch = channel_get_by_local_id (c, chid);
+  if (NULL == ch)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "  tunnel %X not found\n", tid);
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "  channel %X not found\n", chid);
     GNUNET_break (0);
     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
     return;
   }
 
   /* Cleanup after the tunnel */
-  client_delete_tunnel (c, t);
-  if (c == t->client && GNUNET_MESH_LOCAL_CHANNEL_ID_SERV <= tid)
+  client_delete_channel (c, ch);
+  if (c == ch->dest && GNUNET_MESH_LOCAL_CHANNEL_ID_SERV <= chid)
   {
-    t->client = NULL;
+    ch->dest = NULL;
   }
-  else if (c == t->owner && GNUNET_MESH_LOCAL_CHANNEL_ID_SERV > tid)
+  else if (c == ch->root && GNUNET_MESH_LOCAL_CHANNEL_ID_SERV > chid)
   {
-    peer_remove_tunnel (peer_get_short (t->dest), t);
-    t->owner = NULL;
+    ch->root = NULL;
   }
   else 
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "  tunnel %X client %p (%p, %p)\n",
-                tid, c, t->owner, t->client);
+                "  channel %X client %p (%p, %p)\n",
+                chid, c, ch->root, ch->dest);
     GNUNET_break (0);
   }
 
-  /* The tunnel will be destroyed when the last message is transmitted. */
-  tunnel_destroy_empty (t);
+  t = ch->t;
+  channel_destroy (ch);
+  tunnel_destroy_if_empty (t);
 
   GNUNET_SERVER_receive_done (client, GNUNET_OK);
   return;
@@ -5419,12 +5883,12 @@ static void
 handle_local_data (void *cls, struct GNUNET_SERVER_Client *client,
                    const struct GNUNET_MessageHeader *message)
 {
-  struct GNUNET_MESH_LocalData *data_msg;
+  struct GNUNET_MESH_LocalData *msg;
   struct MeshClient *c;
-  struct MeshTunnel *t;
-  struct MeshFlowControl *fc;
-  MESH_ChannelNumber tid;
+  struct MeshChannel *ch;
+  MESH_ChannelNumber chid;
   size_t size;
+  int fwd;
 
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Got data from a client!\n");
@@ -5438,7 +5902,7 @@ handle_local_data (void *cls, struct GNUNET_SERVER_Client *client,
   }
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  by client %u\n", c->id);
 
-  data_msg = (struct GNUNET_MESH_LocalData *) message;
+  msg = (struct GNUNET_MESH_LocalData *) message;
 
   /* Sanity check for message size */
   size = ntohs (message->size) - sizeof (struct GNUNET_MESH_LocalData);
@@ -5449,24 +5913,25 @@ handle_local_data (void *cls, struct GNUNET_SERVER_Client *client,
     return;
   }
 
-  /* Tunnel exists? */
-  tid = ntohl (data_msg->tid);
-  t = channel_get_by_local_id (c, tid);
-  if (NULL == t)
+  /* Channel exists? */
+  chid = ntohl (msg->id);
+  fwd = chid < GNUNET_MESH_LOCAL_CHANNEL_ID_SERV;
+  ch = channel_get_by_local_id (c, chid);
+  if (NULL == ch)
   {
     GNUNET_break (0);
     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
     return;
   }
 
-  /* Is the client in the tunnel? */
-  if ( !( (tid < GNUNET_MESH_LOCAL_CHANNEL_ID_SERV &&
-           t->owner &&
-           t->owner->handle == client)
+  /* Is the client in the channel? */
+  if ( !( (fwd &&
+           ch->root &&
+           ch->root->handle == client)
          ||
-          (tid >= GNUNET_MESH_LOCAL_CHANNEL_ID_SERV &&
-           t->client && 
-           t->client->handle == client) ) )
+          (!fwd &&
+           ch->dest && 
+           ch->dest->handle == client) ) )
   {
     GNUNET_break (0);
     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
@@ -5479,18 +5944,19 @@ handle_local_data (void *cls, struct GNUNET_SERVER_Client *client,
   {
     struct GNUNET_MESH_Data *payload;
     char cbuf[sizeof(struct GNUNET_MESH_Data) + size];
+    uint32_t *mid;
 
-    fc = tid < GNUNET_MESH_LOCAL_CHANNEL_ID_SERV ? &t->prev_fc : &t->next_fc;
-    if (GNUNET_YES == t->reliable)
+    mid = fwd ? &ch->mid_send_fwd : &ch->mid_send_bck;
+    if (GNUNET_YES == ch->reliable)
     {
       struct MeshChannelReliability *rel;
       struct MeshReliableMessage *copy;
 
-      rel = (tid < GNUNET_MESH_LOCAL_CHANNEL_ID_SERV) ? t->fwd_rel : t->bck_rel;
+      rel = fwd ? ch->fwd_rel       : ch->bck_rel;
       copy = GNUNET_malloc (sizeof (struct MeshReliableMessage)
                             + sizeof(struct GNUNET_MESH_Data)
                             + size);
-      copy->mid = rel->mid_sent++;
+      copy->mid = *mid;
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "!!! DATA %u\n", copy->mid);
       copy->timestamp = GNUNET_TIME_absolute_get ();
       copy->rel = rel;
@@ -5504,33 +5970,29 @@ handle_local_data (void *cls, struct GNUNET_SERVER_Client *client,
                                            MESH_RETRANSMIT_MARGIN);
         rel->retry_task =
             GNUNET_SCHEDULER_add_delayed (rel->retry_timer,
-                                          &tunnel_retransmit_message,
+                                          &channel_retransmit_message,
                                           rel);
       }
       payload = (struct GNUNET_MESH_Data *) &copy[1];
-      payload->mid = htonl (copy->mid);
     }
     else
     {
       payload = (struct GNUNET_MESH_Data *) cbuf;
-      payload->mid = htonl (fc->last_pid_recv + 1);
     }
-    memcpy (&payload[1], &data_msg[1], size);
+    payload->mid = htonl (*mid);
+    *mid = *mid + 1;
+    memcpy (&payload[1], &msg[1], size);
     payload->header.size = htons (sizeof (struct GNUNET_MESH_Data) + size);
-    payload->header.type = htons (tid < GNUNET_MESH_LOCAL_CHANNEL_ID_SERV ?
+    payload->header.type = htons (chid < GNUNET_MESH_LOCAL_CHANNEL_ID_SERV ?
                                   GNUNET_MESSAGE_TYPE_MESH_UNICAST :
                                   GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN);
-    GNUNET_PEER_resolve(t->id.oid, &payload->oid);;
-    payload->tid = htonl (t->id.tid);
-    payload->ttl = htonl (default_ttl);
-    payload->pid = htonl (fc->last_pid_recv + 1);
+    payload->chid = htonl (ch->gid);
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                 "  calling generic handler...\n");
-    if (tid < GNUNET_MESH_LOCAL_CHANNEL_ID_SERV)
-      handle_mesh_unicast (NULL, &my_full_id, &payload->header);
-    else
-      handle_mesh_to_orig (NULL, &my_full_id, &payload->header);
+    send_prebuilt_message_channel (&payload->header, ch, fwd);
   }
+  if (tunnel_get_buffer (ch->t, fwd) > 0)
+    send_local_ack (ch, c, fwd);
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "receive done OK\n");
   GNUNET_SERVER_receive_done (client, GNUNET_OK);
 
@@ -5550,11 +6012,14 @@ handle_local_ack (void *cls, struct GNUNET_SERVER_Client *client,
                   const struct GNUNET_MessageHeader *message)
 {
   struct GNUNET_MESH_LocalAck *msg;
-  struct MeshTunnel *t;
+  struct MeshChannelReliability *rel;
+  struct MeshChannel *ch;
   struct MeshClient *c;
-  MESH_ChannelNumber tid;
+  MESH_ChannelNumber chid;
+  int fwd;
 
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a local ACK\n");
+
   /* Sanity check for client registration */
   if (NULL == (c = client_get (client)))
   {
@@ -5566,32 +6031,25 @@ handle_local_ack (void *cls, struct GNUNET_SERVER_Client *client,
 
   msg = (struct GNUNET_MESH_LocalAck *) message;
 
-  /* Tunnel exists? */
-  tid = ntohl (msg->channel_id);
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  on tunnel %X\n", tid);
-  t = channel_get_by_local_id (c, tid);
-  if (NULL == t)
+  /* Channel exists? */
+  chid = ntohl (msg->channel_id);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  on channel %X\n", chid);
+  ch = channel_get_by_local_id (c, chid);
+  if (NULL == ch)
   {
     GNUNET_break (0);
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Tunnel %X unknown.\n", tid);
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Channel %X unknown.\n", chid);
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "  for client %u.\n", c->id);
     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
     return;
   }
 
-  /* Does client own tunnel? I.E: Is this an ACK for BCK traffic? */
-  if (tid < GNUNET_MESH_LOCAL_CHANNEL_ID_SERV)
-  {
-    /* The client owns the tunnel, ACK is for data to_origin, send BCK ACK. */
-    t->prev_fc.last_ack_recv++;
-    tunnel_send_ack (t, GNUNET_MESSAGE_TYPE_MESH_LOCAL_ACK, GNUNET_NO);
-  }
-  else
-  {
-    /* The client doesn't own the tunnel, this ACK is for FWD traffic. */
-    t->next_fc.last_ack_recv++;
-    tunnel_send_ack (t, GNUNET_MESSAGE_TYPE_MESH_LOCAL_ACK, GNUNET_YES);
-  }
+  fwd = chid < GNUNET_MESH_LOCAL_CHANNEL_ID_SERV;
+  rel = fwd ? ch->fwd_rel : ch->bck_rel;
+
+  rel->client_ready = GNUNET_YES;
+  channel_send_client_buffered_data (ch, c, rel);
+  channel_send_ack (ch, 64 - rel->n_recv, fwd);
 
   GNUNET_SERVER_receive_done (client, GNUNET_OK);
 
@@ -5599,7 +6057,6 @@ handle_local_ack (void *cls, struct GNUNET_SERVER_Client *client,
 }
 
 
-
 /**
  * Iterator over all tunnels to send a monitoring client info about each tunnel.
  *
@@ -5619,7 +6076,7 @@ monitor_all_tunnels_iterator (void *cls,
   struct GNUNET_MESH_LocalMonitor *msg;
 
   msg = GNUNET_malloc (sizeof(struct GNUNET_MESH_LocalMonitor));
-  msg->channel_id = htonl (ch->id);
+  msg->channel_id = htonl (ch->gid);
   msg->header.size = htons (sizeof (struct GNUNET_MESH_LocalMonitor));
   msg->header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_INFO_TUNNELS);
 
@@ -5697,7 +6154,8 @@ handle_local_show_tunnel (void *cls, struct GNUNET_SERVER_Client *client,
               c->id,
               &msg->owner,
               ntohl (msg->channel_id));
-  ch = channel_get (&msg->owner, ntohl (msg->channel_id));
+//   ch = channel_get (&msg->owner, ntohl (msg->channel_id));
+  ch = NULL; // FIXME
   if (NULL == ch)
   {
     /* We don't know the tunnel */
@@ -5762,13 +6220,13 @@ static struct GNUNET_SERVER_MessageHandler client_handlers[] = {
 static void
 core_connect (void *cls, const struct GNUNET_PeerIdentity *peer)
 {
-  struct MeshPeer *peer_info;
+  struct MeshPeer *pi;
   struct MeshPeerPath *path;
 
   DEBUG_CONN ("Peer connected\n");
   DEBUG_CONN ("     %s\n", GNUNET_i2s (&my_full_id));
-  peer_info = peer_get (peer);
-  if (myid == peer_info->id)
+  pi = peer_get (peer);
+  if (myid == pi->id)
   {
     DEBUG_CONN ("     (self)\n");
     path = path_new (1);
@@ -5777,19 +6235,15 @@ core_connect (void *cls, const struct GNUNET_PeerIdentity *peer)
   {
     DEBUG_CONN ("     %s\n", GNUNET_i2s (peer));
     path = path_new (2);
-    path->peers[1] = peer_info->id;
-    GNUNET_PEER_change_rc (peer_info->id, 1);
+    path->peers[1] = pi->id;
+    GNUNET_PEER_change_rc (pi->id, 1);
     GNUNET_STATISTICS_update (stats, "# peers", 1, GNUNET_NO);
   }
   path->peers[0] = myid;
   GNUNET_PEER_change_rc (myid, 1);
-  peer_add_path (peer_info, path, GNUNET_YES);
-  if (NULL == peer_info->fc)
-  {
-    peer_info->fc = GNUNET_new (struct MeshFlowControl);
-    fc_init (peer_info->fc);
-    peer_info->fc->peer = peer_info;
-  }
+  peer_add_path (pi, path, GNUNET_YES);
+
+  pi->connections = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_YES);
   return;
 }
 
@@ -5804,9 +6258,6 @@ static void
 core_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
 {
   struct MeshPeer *pi;
-  struct MeshPeerQueue *q;
-  struct MeshPeerQueue *n;
-  struct MeshFlowControl *fc;
 
   DEBUG_CONN ("Peer disconnected\n");
   pi = GNUNET_CONTAINER_multihashmap_get (peers, &peer->hashPubKey);
@@ -5815,33 +6266,19 @@ core_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
     GNUNET_break (0);
     return;
   }
-  fc = pi->fc;
-  if (NULL != fc)
-  {
-    GNUNET_break (0);
-    return;
-  }
-  pi->fc = NULL;
 
-  q = fc->queue_head;
-  while (NULL != q)
-  {
-      n = q->next;
-      queue_destroy (q, GNUNET_YES);
-      q = n;
-  }
-  if (NULL != fc->core_transmit)
-    GNUNET_CORE_notify_transmit_ready_cancel (fc->core_transmit);
-  if (GNUNET_SCHEDULER_NO_TASK != fc->poll_task)
-    GNUNET_SCHEDULER_cancel (fc->poll_task);
+  peer_remove_path (pi, myid, pi->id);
 
-  peer_remove_path (pi, pi->id, myid);
+  GNUNET_CONTAINER_multihashmap_iterate (pi->connections,
+                                         connection_broken,
+                                         pi);
+  GNUNET_CONTAINER_multihashmap_destroy (pi->connections);
+  pi->connections = NULL;
   if (myid == pi->id)
   {
     DEBUG_CONN ("     (self)\n");
   }
   GNUNET_STATISTICS_update (stats, "# peers", -1, GNUNET_NO);
-  GNUNET_free (fc);
 
   return;
 }
@@ -5934,36 +6371,6 @@ shutdown_tunnel (void *cls, const struct GNUNET_HashCode * key, void *value)
   return GNUNET_YES;
 }
 
-/**
- * Iterator over peer hash map entries to destroy the tunnel during shutdown.
- *
- * @param cls closure
- * @param key current key code
- * @param value value in the hash map
- * @return GNUNET_YES if we should continue to iterate,
- *         GNUNET_NO if not.
- */
-static int
-shutdown_peer (void *cls, const struct GNUNET_HashCode * key, void *value)
-{
-  struct MeshPeer *p = value;
-  struct MeshPeerQueue *q;
-  struct MeshPeerQueue *n;
-
-  q = p->fc->queue_head;
-  while (NULL != q)
-  {
-      n = q->next;
-      if (q->peer == p)
-      {
-        queue_destroy(q, GNUNET_YES);
-      }
-      q = n;
-  }
-  peer_destroy (p);
-  return GNUNET_YES;
-}
-
 
 /**
  * Task run during shutdown.
@@ -5982,7 +6389,6 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
     core_handle = NULL;
   }
   GNUNET_CONTAINER_multihashmap_iterate (tunnels, &shutdown_tunnel, NULL);
-  GNUNET_CONTAINER_multihashmap_iterate (peers, &shutdown_peer, NULL);
   if (dht_handle != NULL)
   {
     GNUNET_DHT_disconnect (dht_handle);