+ * Get the state of a tunnel.
+ *
+ * @param t Tunnel.
+ *
+ * @return Tunnel's state.
+ */
+enum MeshTunnel3State
+GMT_get_state (struct MeshTunnel3 *t)
+{
+ if (NULL == t)
+ return (enum MeshTunnel3State) -1;
+ return t->state;
+}
+
+/**
+ * Get the total buffer space for a tunnel.
+ *
+ * If terminal, use the biggest channel buffer (or 64) if no channel exists.
+ * If not terminal, use the sum of all connection buffers.
+ *
+ * @param t Tunnel.
+ * @param fwd Is this for FWD traffic?
+ *
+ * @return Buffer space offered by all entities (c/ch) in the tunnel.
+ */
+unsigned int
+GMT_get_buffer (struct MeshTunnel3 *t, int fwd)
+{
+ struct MeshTConnection *iter;
+ unsigned int buffer;
+
+ iter = t->connection_head;
+ buffer = 0;
+
+ /* If terminal, return biggest channel buffer */
+ if (NULL == iter || GMC_is_terminal (iter->c, fwd))
+ {
+ struct MeshTChannel *iter_ch;
+ unsigned int ch_buf;
+
+ if (NULL == t->channel_head)
+ {
+ /* Probably getting buffer for a channel create/handshake. */
+ return 64;
+ }
+
+ for (iter_ch = t->channel_head; NULL != iter_ch; iter_ch = iter_ch->next)
+ {
+ ch_buf = GMCH_get_buffer (iter_ch->ch, fwd);
+ if (ch_buf > buffer)
+ buffer = ch_buf;
+ }
+ return buffer;
+ }
+
+ /* If not terminal, return sum of connection buffers */
+ while (NULL != iter)
+ {
+ if (GMC_get_state (iter->c) != MESH_CONNECTION_READY)
+ {
+ iter = iter->next;
+ continue;
+ }
+
+ buffer += GMC_get_buffer (iter->c, fwd);
+ iter = iter->next;
+ }
+
+ return buffer;
+}
+
+
+/**
+ * Get the tunnel's destination.
+ *
+ * @param t Tunnel.
+ *
+ * @return ID of the destination peer.
+ */
+const struct GNUNET_PeerIdentity *
+GMT_get_destination (struct MeshTunnel3 *t)
+{
+ return GMP_get_id (t->peer);
+}
+
+
+/**
+ * Get the tunnel's next free global channel ID.
+ *
+ * @param t Tunnel.
+ *
+ * @return GID of a channel free to use.
+ */
+MESH_ChannelNumber
+GMT_get_next_chid (struct MeshTunnel3 *t)
+{
+ MESH_ChannelNumber chid;
+
+ while (NULL != GMT_get_channel (t, t->next_chid))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Channel %u exists...\n", t->next_chid);
+ t->next_chid = (t->next_chid + 1) & ~GNUNET_MESH_LOCAL_CHANNEL_ID_CLI;
+ }
+ chid = t->next_chid;
+ t->next_chid = (t->next_chid + 1) & ~GNUNET_MESH_LOCAL_CHANNEL_ID_CLI;
+
+ return chid;
+}
+
+
+/**
+ * Send ACK on one or more channels due to buffer in connections..
+ *
+ * @param t Channel which has some free buffer space.
+ * @param fwd Is this for FWD traffic? (ACK goes to root)
+ */
+void
+GMT_unchoke_channels (struct MeshTunnel3 *t, int fwd)
+{
+ struct MeshTChannel *iter;
+ unsigned int buffer;
+ unsigned int channels = GMT_count_channels (t);
+ unsigned int choked_n;
+ struct MeshChannel *choked[channels];
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "GMT_unchoke_channels on %s\n", GMT_2s (t));
+
+ if (NULL == t)
+ {
+ GNUNET_break (0);
+ return;
+ }
+
+ /* Get buffer space */
+ buffer = GMT_get_buffer (t, fwd);
+ if (0 == buffer)
+ {
+ return;
+ }
+
+ /* Count and remember choked channels */
+ choked_n = 0;
+ for (iter = t->channel_head; NULL != iter; iter = iter->next)
+ {
+ if (GNUNET_NO == GMCH_get_allowed (iter->ch, fwd))
+ {
+ choked[choked_n++] = iter->ch;
+ }
+ }
+
+ /* Unchoke random channels */
+ while (0 < buffer && 0 < choked_n)
+ {
+ unsigned int r = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
+ choked_n);
+ GMCH_allow_client (choked[r], fwd);
+ choked_n--;
+ buffer--;
+ choked[r] = choked[choked_n];
+ }
+}
+
+
+/**
+ * Send ACK on one or more connections due to buffer space to the client.
+ *
+ * Iterates all connections of the tunnel and sends ACKs appropriately.
+ *
+ * @param t Tunnel.
+ * @param fwd Is this in for FWD traffic? (ACK goes dest->root)
+ */
+void
+GMT_send_acks (struct MeshTunnel3 *t, int fwd)
+{
+ struct MeshTConnection *iter;
+ uint32_t allowed;
+ uint32_t to_allow;
+ uint32_t allow_per_connection;
+ unsigned int cs;
+ unsigned int buffer;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Tunnel send %s ACKs on %s\n",
+ fwd ? "FWD" : "BCK", GMT_2s (t));
+
+ if (NULL == t)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ if (NULL == t->channel_head ||
+ GNUNET_NO == GMCH_is_origin (t->channel_head->ch, !fwd))
+ {
+ GNUNET_break (0);
+ return;
+ }
+
+ buffer = GMT_get_buffer (t, fwd);
+
+ /* Count connections, how many messages are already allowed */
+ cs = GMT_count_connections (t);
+ for (allowed = 0, iter = t->connection_head; NULL != iter; iter = iter->next)
+ {
+ allowed += GMC_get_allowed (iter->c, fwd);
+ }
+
+ /* Make sure there is no overflow */
+ if (allowed > buffer)
+ {
+ GNUNET_break (0);
+ return;
+ }
+
+ /* Authorize connections to send more data */
+ to_allow = buffer; /* - allowed; */
+
+ for (iter = t->connection_head; NULL != iter && to_allow > 0; iter = iter->next)
+ {
+ allow_per_connection = to_allow/cs;
+ to_allow -= allow_per_connection;
+ cs--;
+ if (GMC_get_allowed (iter->c, fwd) > 64 / 3)
+ {
+ continue;
+ }
+ GMC_allow (iter->c, buffer, fwd);
+ }
+
+ GNUNET_break (to_allow == 0);
+}
+
+
+/**
+ * Sends an already built message on a tunnel, GMT_encrypting it and
+ * choosing the best connection.