+ GNUNET_PEER_resolve (id, &peer);
+ cinfo = tunnel_get_neighbor_fc (t, &peer);
+
+ if (cinfo->bck_ack != cinfo->pid &&
+ GNUNET_NO == GMC_is_pid_bigger (cinfo->bck_ack, cinfo->pid))
+ return;
+
+ cinfo->bck_ack++;
+ send_ack (t, &peer, cinfo->bck_ack);
+}
+
+
+/**
+ * @brief Send BCK ACKs to clients to allow them more to_origin traffic
+ *
+ * Iterates over all clients and sends BCK ACKs to the ones that need it.
+ *
+ * @param t Tunnel on which to send the BCK ACKs.
+ */
+static void
+tunnel_send_clients_bck_ack (struct MeshTunnel *t)
+{
+ unsigned int i;
+ unsigned int tunnel_delta;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " Sending BCK ACK to clients\n");
+
+ tunnel_delta = t->bck_ack - t->bck_pid;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " tunnel delta: %u\n", tunnel_delta);
+
+ /* Find client whom to allow to send to origin (with lowest buffer space) */
+ for (i = 0; i < t->nclients; i++)
+ {
+ struct MeshTunnelClientInfo *clinfo;
+ unsigned int delta;
+
+ clinfo = &t->clients_fc[i];
+ delta = clinfo->bck_ack - clinfo->bck_pid;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " client %u delta: %u\n",
+ t->clients[i]->id, delta);
+
+ if ((GNUNET_NO == t->nobuffer && tunnel_delta > delta) ||
+ (GNUNET_YES == t->nobuffer && 0 == delta))
+ {
+ uint32_t ack;
+
+ ack = clinfo->bck_pid;
+ ack += t->nobuffer ? 1 : tunnel_delta;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ " sending ack to client %u: %u\n",
+ t->clients[i]->id, ack);
+ send_local_ack (t, t->clients[i], ack);
+ clinfo->bck_ack = ack;
+ }
+ }
+}
+
+
+/**
+ * Send an ACK informing the children nodes and destination clients about
+ * the available buffer space.
+ * If buffering is off, send only on behalf of root (can be self).
+ * If buffering is on, send when sent to predecessor and buffer space is free.
+ * Note that although the name is bck_ack, the BCK mean backwards *traffic*,
+ * the ACK itself goes "forward" (towards children/clients).
+ *
+ * @param t Tunnel on which to send the ACK.
+ * @param type Type of message that triggered the ACK transmission.
+ */
+static void
+tunnel_send_bck_ack (struct MeshTunnel *t, uint16_t type)
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Sending BCK ACK on tunnel %u [%u] due to %s\n",
+ t->id.oid, t->id.tid, GNUNET_MESH_DEBUG_M2S(type));
+ /* Is it after data to_origin retransmission? */
+ switch (type)
+ {
+ case GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN:
+ if (GNUNET_YES == t->nobuffer)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ " Not sending ACK, nobuffer\n");
+ return;
+ }
+ break;
+ case GNUNET_MESSAGE_TYPE_MESH_ACK:
+ case GNUNET_MESSAGE_TYPE_MESH_LOCAL_ACK:
+ break;
+ default:
+ GNUNET_break (0);
+ }
+
+ tunnel_send_clients_bck_ack (t);
+ tree_iterate_children (t->tree, &tunnel_send_child_bck_ack, t);
+}
+
+
+/**
+ * @brief Re-initiate traffic to this peer 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 cls Closure (unused)
+ * @param peer_id Short ID of peer to which initiate traffic.
+ */
+static void
+peer_unlock_queue(void *cls, GNUNET_PEER_Id peer_id)
+{
+ struct MeshPeerInfo *peer;
+ struct GNUNET_PeerIdentity id;
+ struct MeshPeerQueue *q;
+ size_t size;
+
+ peer = peer_info_get_short(peer_id);
+ if (NULL != peer->core_transmit)
+ return;
+
+ q = queue_get_next(peer);
+ if (NULL == q)
+ {
+ /* Might br multicast traffic already sent to this particular peer but
+ * not to other children in this tunnel.
+ * This way t->queue_n would be > 0 but the queue of this particular peer
+ * would be empty.
+ */
+ return;
+ }
+ size = q->size;
+ GNUNET_PEER_resolve (peer->id, &id);
+ peer->core_transmit =
+ GNUNET_CORE_notify_transmit_ready(core_handle,
+ 0,
+ 0,
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ &id,
+ size,
+ &queue_send,
+ peer);
+ return;
+}
+
+
+/**
+ * @brief Allow transmission of FWD traffic on this tunnel
+ *
+ * Check if there is traffic queued towards any children
+ * and the core transmit handle is NULL, and if so, call core tmt rdy.
+ *
+ * @param t Tunnel on which to unlock FWD traffic.
+ */
+static void
+tunnel_unlock_fwd_queues (struct MeshTunnel *t)
+{
+ if (0 == t->fwd_queue_n)
+ return;
+
+ tree_iterate_children (t->tree, &peer_unlock_queue, NULL);
+}
+
+
+/**
+ * @brief Allow transmission of BCK traffic on this tunnel
+ *
+ * Check if there is traffic queued towards the root of the tree
+ * and the core transmit handle is NULL, and if so, call core tmt rdy.
+ *
+ * @param t Tunnel on which to unlock BCK traffic.
+ */
+static void
+tunnel_unlock_bck_queue (struct MeshTunnel *t)
+{
+ if (0 == t->bck_queue_n)
+ return;
+
+ peer_unlock_queue(NULL, tree_get_predecessor(t->tree));
+}
+
+
+/**
+ * Send a message to all peers in this tunnel that the tunnel is no longer
+ * valid.
+ *
+ * @param t The tunnel whose peers to notify.
+ */
+static void
+tunnel_send_destroy (struct MeshTunnel *t)
+{
+ struct GNUNET_MESH_TunnelDestroy msg;
+
+ msg.header.size = htons (sizeof (msg));
+ msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_TUNNEL_DESTROY);
+ GNUNET_PEER_resolve (t->id.oid, &msg.oid);
+ msg.tid = htonl (t->id.tid);
+ tunnel_send_multicast (t, &msg.header);
+}