/**
- * Build a local ACK message and send it to a local client.
+ * Build a local ACK message and send it to a local client, if needed.
+ *
+ * If the client was already allowed to send data, do nothing.
*
* @param ch Channel on which to send the ACK.
* @param c Client to whom send the ACK.
- * @param fwd Set to GNUNET_YES for FWD ACK (dest->owner)
+ * @param fwd Set to GNUNET_YES for FWD ACK (dest->root)
*/
static void
send_local_ack (struct MeshChannel *ch, int fwd)
struct MeshChannelReliability *rel;
struct MeshClient *c;
- c = fwd ? ch->root : ch->dest;
+ c = fwd ? ch->root : ch->dest;
+ rel = fwd ? ch->root_rel : ch->dest_rel;
+
+ if (GNUNET_YES == rel->client_ready)
+ return; /* don't send double ACKs to client */
+
+ rel->client_ready = GNUNET_YES;
+
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"send local %s ack on %s:%X towards %p\n",
fwd ? "FWD" : "BCK", peer2s (ch->t->peer), ch->gid, c);
c->handle,
&msg.header,
GNUNET_NO);
- rel = fwd ? ch->root_rel : ch->dest_rel;
- rel->client_ready = GNUNET_YES;
+
}
/**
- * Send an end-to-end FWD ACK message for the most recent in-sequence payload.
+ * Send an end-to-end ACK message for the most recent in-sequence payload.
*
* If channel is not reliable, do nothing.
*
* the ACK itself goes "back" (dest->root).
*
* @param c Connection on which to send the ACK.
+ * @param buffer How much space free to advertise?
* @param fwd Is this FWD ACK? (Going dest->owner)
*/
static void
-connection_send_ack (struct MeshConnection *c, int fwd)
+connection_send_ack (struct MeshConnection *c, unsigned int buffer, int fwd)
{
struct MeshFlowControl *next_fc;
struct MeshFlowControl *prev_fc;
©->header, GNUNET_NO);
}
+
+/**
+ * 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 (MUST be of type MESH_DATA).
+ * @param rel Reliability data to the corresponding direction.
+ */
+static void
+channel_rel_add_buffered_data (const struct GNUNET_MESH_Data *msg,
+ struct MeshChannelReliability *rel)
+{
+ struct MeshReliableMessage *copy;
+ struct MeshReliableMessage *prev;
+ uint32_t mid;
+ uint16_t size;
+
+ size = ntohs (msg->header.size);
+ mid = ntohl (msg->mid);
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "add_buffered_data %u\n", mid);
+
+ copy = GNUNET_malloc (sizeof (*copy) + size);
+ copy->mid = mid;
+ copy->rel = rel;
+ memcpy (©[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)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " prev %u\n", prev->mid);
+ if (GMC_is_pid_bigger (prev->mid, mid))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " bingo!\n");
+ GNUNET_CONTAINER_DLL_insert_before (rel->head_recv, rel->tail_recv,
+ prev, copy);
+ return;
+ }
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " insert at tail!\n");
+ GNUNET_CONTAINER_DLL_insert_tail (rel->head_recv, rel->tail_recv, copy);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "add_buffered_data END\n");
+}
+
+
/**
* Modify the data message ID from global to local and send to client.
*
int fwd)
{
if (fwd)
- channel_send_client_to_tid (ch, msg, ch->dest, ch->lid_dest);
+ {
+ if (ch->dest_rel->client_ready)
+ channel_send_client_to_tid (ch, msg, ch->dest, ch->lid_dest);
+ else
+ channel_rel_add_buffered_data (msg, ch->dest_rel);
+ }
else
- channel_send_client_to_tid (ch, msg, ch->root, ch->lid_root);
+ {
+ if (ch->root_rel->client_ready)
+ channel_send_client_to_tid (ch, msg, ch->root, ch->lid_root);
+ else
+ channel_rel_add_buffered_data (msg, ch->root_rel);
+ }
}
}
-/**
- * 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 (MUST be of type MESH_DATA).
- * @param rel Reliability data to the corresponding direction.
- */
-static void
-channel_rel_add_buffered_data (const struct GNUNET_MESH_Data *msg,
- struct MeshChannelReliability *rel)
-{
- struct MeshReliableMessage *copy;
- struct MeshReliableMessage *prev;
- uint32_t mid;
- uint16_t size;
-
- size = ntohs (msg->header.size);
- mid = ntohl (msg->mid);
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "add_buffered_data %u\n", mid);
-
- copy = GNUNET_malloc (sizeof (*copy) + size);
- copy->mid = mid;
- copy->rel = rel;
- memcpy (©[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)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " prev %u\n", prev->mid);
- if (GMC_is_pid_bigger (prev->mid, mid))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " bingo!\n");
- GNUNET_CONTAINER_DLL_insert_before (rel->head_recv, rel->tail_recv,
- prev, copy);
- return;
- }
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " insert at tail!\n");
- GNUNET_CONTAINER_DLL_insert_tail (rel->head_recv, rel->tail_recv, copy);
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "add_buffered_data END\n");
-}
-
-
/**
* Destroy a reliable message after it has been acknowledged, either by
* direct mid ACK or bitfield. Updates the appropriate data structures and
}
-/**
- * Send an ACK to a client if needed.
- *
- * @param ch Channel this is regarding.
- * @param fwd Is this about fwd traffic? (ACK goes the opposite direction).
- */
-static void
-channel_send_client_ack (struct MeshChannel *ch, int fwd)
-{
- struct MeshChannelReliability *rel;
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Channel send client %s ack on %s:%X\n",
- fwd ? "FWD" : "BCK", peer2s (ch->t->peer), ch->gid);
-
- /* Check for buffer space */
- if (0 >= tunnel_get_buffer (ch->t, fwd))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " No buffer :(\n");
- return;
- }
-
- /* Send ACK (fwd indicates traffic to be ACK'd) to client */
- rel = fwd ? ch->root_rel : ch->dest_rel;
- if (GNUNET_NO == rel->client_ready)
- send_local_ack (ch, fwd);
- else
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client was ready\n");
-}
-
-
/**
* Send ACK on one or more connections due to buffer space to the client.
*
* @param fwd Is this in for FWD traffic? (ACK goes dest->root)
*/
static void
-channel_send_connection_ack (struct MeshChannel *ch, int fwd)
+channel_send_connections_ack (struct MeshChannel *ch,
+ unsigned int buffer,
+ int fwd)
{
struct MeshTunnel2 *t = ch->t;
struct MeshConnection *c;
struct MeshFlowControl *fc;
- struct MeshChannelReliability *rel;
uint32_t allowed;
uint32_t to_allow;
+ uint32_t allow_per_connection;
unsigned int cs;
- uint32_t buffer;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Channel send connection %s ack on %s:%X\n",
fwd ? "FWD" : "BCK", peer2s (ch->t->peer), ch->gid);
- /* Check */
- rel = fwd ? ch->root_rel : ch->dest_rel;
- if (NULL == rel)
- {
- GNUNET_break (0);
- return;
- }
- buffer = 64 - rel->n_sent;
-
/* Count connections, how many messages are already allowed */
for (cs = 0, allowed = 0, c = t->connection_head; NULL != c; c = c->next)
{
/* Authorize connections to send more data */
to_allow = buffer - allowed;
+
for (c = t->connection_head; NULL != c && to_allow > 0; c = c->next)
{
+ allow_per_connection = to_allow/cs;
+ to_allow -= allow_per_connection;
+ cs--;
fc = fwd ? &c->fwd_fc : &c->bck_fc;
if (fc->last_ack_sent - fc->last_pid_recv > 64 / 3)
{
continue;
}
- connection_send_ack (c, fwd);
- to_allow--;
+ connection_send_ack (c, allow_per_connection, fwd);
}
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Channel send connection %s ack on %s:%X\n",
+ fwd ? "FWD" : "BCK", peer2s (ch->t->peer), ch->gid);
GNUNET_break (to_allow == 0);
}
+/**
+ * Get free buffer space towards the client on a specific channel.
+ *
+ * @param ch Channel.
+ * @param fwd Is query about FWD traffic?
+ *
+ * @return Free buffer space [0 - 64]
+ */
+static unsigned int
+channel_get_buffer (struct MeshChannel *ch, int fwd)
+{
+ struct MeshChannelReliability *rel;
+
+ rel = fwd ? ch->dest_rel : ch->root_rel;
+
+ return (64 - rel->n_recv);
+}
+
+
+/**
+ * Get free buffer space in a connection.
+ *
+ * @param c Connection.
+ * @param fwd Is query about FWD traffic?
+ *
+ * @return Free buffer space [0 - max_msgs_queue/max_connections]
+ */
+static unsigned int
+connection_get_buffer (struct MeshConnection *c, int fwd)
+{
+ struct MeshFlowControl *fc;
+
+ fc = fwd ? &c->fwd_fc : &c->bck_fc;
+
+ return (fc->queue_max - fc->queue_n);
+}
+
+
/**
* Send an ACK on the appropriate connection/channel, depending on
* the direction and the position of the peer.
static void
send_ack (struct MeshConnection *c, struct MeshChannel *ch, int fwd)
{
- if (NULL == ch)
+ unsigned int buffer;
+
+ if (NULL == c || connection_is_terminal (c, fwd))
{
- connection_send_ack (c, fwd);
- return;
+ GNUNET_assert (NULL != ch);
+ buffer = channel_get_buffer (ch, fwd);
+ }
+ else
+ {
+ GNUNET_assert (NULL != c);
+ buffer = connection_get_buffer (c, fwd);
+ }
+
+ if (NULL == c)
+ {
+ GNUNET_assert (NULL != ch);
+ channel_send_connections_ack (ch, buffer, fwd);
+ }
+ else if (connection_is_origin (c, fwd))
+ {
+ if (0 < buffer)
+ {
+ GNUNET_assert (NULL != ch);
+ send_local_ack (ch, fwd);
+ }
+ }
+ else
+ {
+ connection_send_ack (c, buffer, fwd);
}
- channel_send_connection_ack (ch, fwd);
}
case GNUNET_MESSAGE_TYPE_MESH_BCK:
pid = ntohl ( ((struct GNUNET_MESH_Encrypted *) buf)->pid );
fc->last_pid_sent = pid;
- if (NULL != ch)
- channel_send_client_ack (ch, fwd);
- else
- connection_send_ack (c, fwd);
+ send_ack (c, ch, fwd);
break;
default:
break;
* @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)
+ * @return channel which this message was on.
*/
-static int
+static struct MeshChannel *
handle_data (struct MeshTunnel2 *t, const struct GNUNET_MESH_Data *msg, int fwd)
{
struct MeshChannelReliability *rel;
sizeof (struct GNUNET_MessageHeader))
{
GNUNET_break (0);
- return GNUNET_OK;
+ return NULL;
}
type = ntohs (msg->header.type);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "got a %s message\n",
{
GNUNET_STATISTICS_update (stats, "# data on unknown channel", 1, GNUNET_NO);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "WARNING channel unknown\n");
- return GNUNET_OK;
+ return NULL;
}
/* Initialize FWD/BCK data */
if (NULL == c)
{
GNUNET_break (0);
- return GNUNET_OK;
+ return NULL;
}
tunnel_change_state (t, MESH_TUNNEL_READY);
}
channel_send_data_ack (ch, fwd);
- return GNUNET_OK;
+ return ch;
}
/**
* @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)
+ * @return channel this message was on.
*/
-static int
+static struct MeshChannel *
handle_data_ack (struct MeshTunnel2 *t,
const struct GNUNET_MESH_DataACK *msg, int fwd)
{
if (NULL == ch)
{
GNUNET_STATISTICS_update (stats, "# ack on unknown channel", 1, GNUNET_NO);
- return GNUNET_OK;
+ return NULL;
}
ack = ntohl (msg->mid);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "!!! %s ACK %u\n",
}
if (NULL == rel)
{
- return GNUNET_OK;
+ GNUNET_break (0);
+ return NULL;
}
for (work = GNUNET_NO, copy = rel->head_sent; copy != NULL; copy = next)
else
GNUNET_break (0);
}
- return GNUNET_OK;
+ return ch;
}
* @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)
+ * @return channel this message was on.
*/
-static int
+static struct MeshChannel *
handle_channel_create (struct MeshTunnel2 *t,
struct GNUNET_MESH_ChannelCreate *msg,
int fwd)
if (ntohs (msg->header.size) != sizeof (struct GNUNET_MESH_ChannelCreate))
{
GNUNET_break_op (0);
- return GNUNET_OK;
+ return NULL;
}
/* Check if channel exists */
{
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " duplicate CC!!\n");
channel_send_ack (ch, !fwd);
- return GNUNET_OK;
+ return NULL;
}
}
else
{
/* TODO send reject */
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " no client has port registered\n");
- return GNUNET_OK;
+ /* TODO free ch */
+ return NULL;
}
channel_add_client (ch, c);
send_local_channel_create (ch);
channel_send_ack (ch, !fwd);
- return GNUNET_OK;
+ return ch;
}
* @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)
+ * @return channel this message was on.
*/
-static int
+static struct MeshChannel *
handle_channel_ack (struct MeshTunnel2 *t,
struct GNUNET_MESH_ChannelManage *msg,
int fwd)
if (ntohs (msg->header.size) != sizeof (struct GNUNET_MESH_ChannelManage))
{
GNUNET_break_op (0);
- return GNUNET_OK;
+ return NULL;
}
/* Check if channel exists */
{
GNUNET_break_op (0);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " channel %u unknown!!\n", chid);
- return GNUNET_OK;
+ return NULL;
}
channel_confirm (ch, !fwd);
- return GNUNET_OK;
+ return ch;
}
* @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)
+ * @return channel this message was on.
*/
-static int
+static struct MeshChannel *
handle_channel_destroy (struct MeshTunnel2 *t,
struct GNUNET_MESH_ChannelManage *msg,
int fwd)
if (ntohs (msg->header.size) != sizeof (struct GNUNET_MESH_ChannelManage))
{
GNUNET_break_op (0);
- return GNUNET_OK;
+ return NULL;
}
/* Check if channel exists */
if (NULL == ch)
{
/* Probably a retransmission, safe to ignore */
- return GNUNET_OK;
+ return NULL;
}
send_local_channel_destroy (ch, fwd);
channel_destroy (ch);
- return GNUNET_OK;
+ return ch;
}
size_t dsize = size - sizeof (struct GNUNET_MESH_Encrypted);
char cbuf[dsize];
struct GNUNET_MessageHeader *msgh;
- int r;
+ struct MeshChannel *ch;
/* TODO signature verification */
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " message for us!\n");
{
case GNUNET_MESSAGE_TYPE_MESH_DATA:
/* Don't send hop ACK, wait for client to ACK */
- return handle_data (t, (struct GNUNET_MESH_Data *) msgh, fwd);
+ ch = handle_data (t, (struct GNUNET_MESH_Data *) msgh, fwd);
+ break;
case GNUNET_MESSAGE_TYPE_MESH_DATA_ACK:
- r = handle_data_ack (t, (struct GNUNET_MESH_DataACK *) msgh, fwd);
+ ch = handle_data_ack (t, (struct GNUNET_MESH_DataACK *) msgh, fwd);
break;
case GNUNET_MESSAGE_TYPE_MESH_CHANNEL_CREATE:
- r = handle_channel_create (t,
- (struct GNUNET_MESH_ChannelCreate *) msgh,
- fwd);
+ ch = handle_channel_create (t,
+ (struct GNUNET_MESH_ChannelCreate *) msgh,
+ fwd);
break;
case GNUNET_MESSAGE_TYPE_MESH_CHANNEL_ACK:
- r = handle_channel_ack (t,
- (struct GNUNET_MESH_ChannelManage *) msgh,
- fwd);
+ ch = handle_channel_ack (t,
+ (struct GNUNET_MESH_ChannelManage *) msgh,
+ fwd);
break;
case GNUNET_MESSAGE_TYPE_MESH_CHANNEL_DESTROY:
- r = handle_channel_destroy (t,
- (struct GNUNET_MESH_ChannelManage *) msgh,
- fwd);
+ ch = handle_channel_destroy (t,
+ (struct GNUNET_MESH_ChannelManage *) msgh,
+ fwd);
break;
default:
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"end-to-end message not known (%u)\n",
ntohs (msgh->type));
- r = GNUNET_OK;
+ ch = NULL;
}
- connection_send_ack (c, fwd);
- return r;
+ send_ack (c, ch, fwd);
+ return GNUNET_OK;
}
/* Message not for us: forward to next hop */
{
GNUNET_STATISTICS_update (stats, "# TTL drops", 1, GNUNET_NO);
GNUNET_log (GNUNET_ERROR_TYPE_WARNING, " TTL is 0, DROPPING!\n");
- connection_send_ack (c, fwd);
+ connection_send_ack (c, connection_get_buffer (c, fwd), fwd);
return GNUNET_OK;
}
GNUNET_STATISTICS_update (stats, "# messages forwarded", 1, GNUNET_NO);
struct MeshFlowControl *fc;
GNUNET_PEER_Id id;
uint32_t pid;
+ int fwd;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "\n\n");
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a POLL packet from %s!\n",
return GNUNET_OK;
}
- /* Is this a forward or backward ACK? */
+ /* Is this a forward or backward ACK?
+ * Note: a poll should never be needed in a loopback case,
+ * since there is no possiblility of packet loss there, so
+ * this way of discerining FWD/BCK should not be a problem.
+ */
id = GNUNET_PEER_search (peer);
if (connection_get_next_hop (c)->id == id)
{
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " PID %u, OLD %u\n",
pid, fc->last_pid_recv);
fc->last_pid_recv = pid;
- connection_send_ack (c, fc == &c->fwd_fc);
+ fwd = fc == &c->fwd_fc;
+ connection_send_ack (c, connection_get_buffer(c, fwd), fwd);
return GNUNET_OK;
}