- fix #3091
[oweals/gnunet.git] / src / mesh / gnunet-service-mesh_channel.c
index 7ee1250207067dc18f10f58bd953e007d43c0eae..e7fc0323b9a1ed3e5956c154ee3c5b2151ce2902 100644 (file)
@@ -54,7 +54,7 @@ enum MeshChannelState
   MESH_CHANNEL_SENT,
 
   /**
-   * Connection confirmed, ready to carry traffic..
+   * Connection confirmed, ready to carry traffic.
    */
   MESH_CHANNEL_READY,
 };
@@ -143,6 +143,11 @@ struct MeshChannelReliability
      */
   int                               client_ready;
 
+  /**
+   * Can the client send data to us?
+   */
+  int                               client_allowed;
+
     /**
      * Task to resend/poll in case no ACK is received.
      */
@@ -464,11 +469,32 @@ send_client_ack (struct MeshChannel *ch, int fwd)
     GNUNET_break (0);
     return;
   }
-  if (GNUNET_YES == rel->client_ready)
+
+  if (GNUNET_YES == rel->client_allowed)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG, "  already allowed\n");
     return;
+  }
+  rel->client_allowed = GNUNET_YES;
 
   GML_send_ack (fwd ? ch->root : ch->dest, fwd ? ch->lid_root : ch->lid_dest);
-  rel->client_ready = GNUNET_YES;
+}
+
+
+/**
+ * Notify the root that the destination rejected the channel.
+ *
+ * @param ch Rejected channel.
+ */
+static void
+send_client_nack (struct MeshChannel *ch)
+{
+  if (NULL == ch->root)
+  {
+    GNUNET_break (0);
+    return;
+  }
+  GML_send_nack (ch->root, ch->lid_root);
 }
 
 
@@ -705,11 +731,32 @@ channel_send_ack (struct MeshChannel *ch, int fwd)
 }
 
 
+/**
+ * Notify that a channel create didn't succeed.
+ *
+ * @param ch The channel to reject.
+ */
+static void
+channel_send_nack (struct MeshChannel *ch)
+{
+  struct GNUNET_MESH_ChannelManage msg;
+
+  msg.header.size = htons (sizeof (msg));
+  msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_CHANNEL_NACK);
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "  sending channel NACK for channel %s\n",
+       GMCH_2s (ch));
+
+  msg.chid = htonl (ch->gid);
+  GMCH_send_prebuilt_message (&msg.header, ch, GNUNET_NO);
+}
+
+
 /**
  * Channel was ACK'd by remote peer, mark as ready and cancel retransmission.
  *
  * @param ch Channel to mark as ready.
- * @param fwd Was the ACK message sent fwd? (dest->root, SYNACK)
+ * @param fwd Was the ACK message a FWD ACK? (dest->root, SYNACK)
  */
 static void
 channel_confirm (struct MeshChannel *ch, int fwd)
@@ -724,6 +771,7 @@ channel_confirm (struct MeshChannel *ch, int fwd)
   ch->state = MESH_CHANNEL_READY;
 
   rel = fwd ? ch->root_rel : ch->dest_rel;
+  rel->client_ready = GNUNET_YES;
   for (copy = rel->head_sent; NULL != copy; copy = next)
   {
     struct GNUNET_MessageHeader *msg;
@@ -880,6 +928,15 @@ channel_set_options (struct MeshChannel *ch, uint32_t options)
                  GNUNET_YES : GNUNET_NO;
 }
 
+static int
+is_loopback (const struct MeshChannel *ch)
+{
+  if (NULL != ch->t)
+    return GMT_is_loopback (ch->t);
+
+  return (NULL != ch->root && NULL != ch->dest);
+}
+
 
 /**
  * Handle a loopback message: call the appropriate handler for the message type.
@@ -913,8 +970,7 @@ handle_loopback (struct MeshChannel *ch,
 
     case GNUNET_MESSAGE_TYPE_MESH_CHANNEL_CREATE:
       GMCH_handle_create (ch->t,
-                          (struct GNUNET_MESH_ChannelCreate *) msgh,
-                          fwd);
+                          (struct GNUNET_MESH_ChannelCreate *) msgh);
       break;
 
     case GNUNET_MESSAGE_TYPE_MESH_CHANNEL_ACK:
@@ -923,6 +979,10 @@ handle_loopback (struct MeshChannel *ch,
                        fwd);
       break;
 
+    case GNUNET_MESSAGE_TYPE_MESH_CHANNEL_NACK:
+      GMCH_handle_nack (ch);
+      break;
+
     case GNUNET_MESSAGE_TYPE_MESH_CHANNEL_DESTROY:
       GMCH_handle_destroy (ch,
                            (struct GNUNET_MESH_ChannelManage *) msgh,
@@ -998,13 +1058,32 @@ GMCH_get_buffer (struct MeshChannel *ch, int fwd)
 }
 
 
+/**
+ * Get flow control status of end point: is client allow to send?
+ *
+ * @param ch Channel.
+ * @param fwd Is query about FWD traffic? (Request root status).
+ *
+ * @return #GNUNET_YES if client is allowed to send us data.
+ */
+int
+GMCH_get_allowed (struct MeshChannel *ch, int fwd)
+{
+  struct MeshChannelReliability *rel;
+
+  rel = fwd ? ch->root_rel : ch->dest_rel;
+
+  return rel->client_allowed;
+}
+
+
 /**
  * Is the root client for this channel on this peer?
  *
  * @param ch Channel.
  * @param fwd Is this for fwd traffic?
  *
- * @return GNUNET_YES in case it is.
+ * @return #GNUNET_YES in case it is.
  */
 int
 GMCH_is_origin (struct MeshChannel *ch, int fwd)
@@ -1022,7 +1101,7 @@ GMCH_is_origin (struct MeshChannel *ch, int fwd)
  * @param ch Channel.
  * @param fwd Is this for fwd traffic?
  *
- * @return GNUNET_YES in case it is.
+ * @return #GNUNET_YES in case it is.
  */
 int
 GMCH_is_terminal (struct MeshChannel *ch, int fwd)
@@ -1057,18 +1136,32 @@ GMCH_send_create (struct MeshChannel *ch)
 
 /**
  * Notify a client that the channel is no longer valid.
- * FIXME send on tunnel if some client == NULL?
  *
  * @param ch Channel that is destroyed.
  */
 void
 GMCH_send_destroy (struct MeshChannel *ch)
 {
+  struct GNUNET_MESH_ChannelManage msg;
+
+  msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_CHANNEL_DESTROY);
+  msg.header.size = htons (sizeof (msg));
+  msg.chid = htonl (ch->gid);
+
+  /* If root is not NULL, notify.
+   * If it's NULL, check lid_root. When a local destroy comes in, root 
+   * is set to NULL but lid_root is left untouched. In this case, do nothing,
+   * the client is the one who reuqested the channel to be destroyed.
+   */
   if (NULL != ch->root)
     GML_send_channel_destroy (ch->root, ch->lid_root);
+  else if (0 == ch->lid_root)
+    GMCH_send_prebuilt_message (&msg.header, ch, GNUNET_NO);
 
   if (NULL != ch->dest)
     GML_send_channel_destroy (ch->dest, ch->lid_dest);
+  else if (0 == ch->lid_dest)
+    GMCH_send_prebuilt_message (&msg.header, ch, GNUNET_YES);
 }
 
 
@@ -1153,6 +1246,22 @@ GMCH_send_data_ack (struct MeshChannel *ch, int fwd)
 }
 
 
+/**
+ * Allow a client to send us more data, in case it was choked.
+ *
+ * @param ch Channel.
+ * @param fwd Is this about FWD traffic? (Root client).
+ */
+void
+GMCH_allow_client (struct MeshChannel *ch, int fwd)
+{
+  if (MESH_CHANNEL_READY != ch->state)
+    return;
+
+  send_client_ack (ch, fwd);
+}
+
+
 /**
  * Log channel info.
  *
@@ -1195,7 +1304,7 @@ GMCH_debug (struct MeshChannel *ch)
  * Mark client as ready and send him any buffered data we could have for him.
  *
  * @param ch Channel.
- * @param fwd Is this a "FWD ACK"? (FWD ACKs are sent by root and go BCK)
+ * @param fwd Is this a "FWD ACK"? (FWD ACKs are sent by dest and go BCK)
  */
 void
 GMCH_handle_local_ack (struct MeshChannel *ch, int fwd)
@@ -1208,7 +1317,17 @@ GMCH_handle_local_ack (struct MeshChannel *ch, int fwd)
 
   rel->client_ready = GNUNET_YES;
   send_client_buffered_data (ch, c, fwd);
-  GMC_send_ack (NULL, ch, fwd);
+  if (is_loopback (ch))
+  {
+    unsigned int buffer;
+
+    buffer = GMCH_get_buffer (ch, fwd);
+    if (0 < buffer)
+      GMCH_allow_client (ch, fwd);
+
+    return;
+  }
+  GMT_send_connection_acks (ch->t);
 }
 
 
@@ -1220,6 +1339,8 @@ GMCH_handle_local_ack (struct MeshChannel *ch, int fwd)
  * in the tunnel.
  *
  * @param ch Channel.
+ * @param c Client which sent the data.
+ * @param message Message.
  * @param fwd Is this a FWD data?
  *
  * @return GNUNET_OK if everything goes well, GNUNET_SYSERR in case of en error.
@@ -1249,6 +1370,8 @@ GMCH_handle_local_data (struct MeshChannel *ch,
 
   rel = fwd ? ch->root_rel : ch->dest_rel;
 
+  rel->client_allowed = GNUNET_NO;
+
   /* Ok, everything is correct, send the message. */
   payload = (struct GNUNET_MESH_Data *) cbuf;
   payload->mid = htonl (rel->mid_send);
@@ -1258,11 +1381,19 @@ GMCH_handle_local_data (struct MeshChannel *ch,
   payload->header.type = htons (GNUNET_MESSAGE_TYPE_MESH_DATA);
   payload->chid = htonl (ch->gid);
   LOG (GNUNET_ERROR_TYPE_DEBUG, "  sending on channel...\n");
-  GMCH_send_prebuilt_message (&payload->header, ch, fwd);
-
   if (GNUNET_YES == ch->reliable)
     channel_save_copy (ch, &payload->header, fwd);
-  if (GMT_get_buffer (ch->t, fwd) > 0)
+  GMCH_send_prebuilt_message (&payload->header, ch, fwd);
+
+  if (is_loopback (ch))
+  {
+    if (GMCH_get_buffer (ch, fwd) > 0);
+      send_client_ack (ch, fwd);
+
+    return GNUNET_OK;
+  }
+
+  if (GMT_get_connections_buffer (ch->t) > 0)
   {
     send_client_ack (ch, fwd);
   }
@@ -1376,17 +1507,21 @@ GMCH_handle_local_create (struct MeshClient *c,
     msgcc.port = msg->port;
     msgcc.opt = msg->opt;
 
-    GMT_queue_data (t, ch, &msgcc.header, GNUNET_YES);
+    GMT_send_prebuilt_message (&msgcc.header, t, ch, GNUNET_YES);
   }
   return GNUNET_OK;
 }
 
+
 /**
  * Handler for mesh network payload traffic.
  *
  * @param ch Channel for the message.
- * @param message Unencryted data message.
- * @param fwd Is this FWD traffic? GNUNET_YES : GNUNET_NO;
+ * @param msg Unencryted data message.
+ * @param fwd Is this message fwd? This only is meaningful in loopback channels.
+ *            #GNUNET_YES if message is FWD on the respective channel (loopback)
+ *            #GNUNET_NO if message is BCK on the respective channel (loopback)
+ *            #GNUNET_SYSERR if message on a one-ended channel (remote)
  */
 void
 GMCH_handle_data (struct MeshChannel *ch,
@@ -1397,6 +1532,18 @@ GMCH_handle_data (struct MeshChannel *ch,
   struct MeshClient *c;
   uint32_t mid;
 
+  /* If this is a remote (non-loopback) channel, find 'fwd'. */
+  if (GNUNET_SYSERR == fwd)
+  {
+    if (is_loopback (ch))
+    {
+      /* It is a loopback channel after all... */
+      GNUNET_break (0);
+      return;
+    }
+    fwd = (NULL != ch->dest) ? GNUNET_YES : GNUNET_NO;
+  }
+
   /*  Initialize FWD/BCK data */
   c   = fwd ? ch->dest     : ch->root;
   rel = fwd ? ch->dest_rel : ch->root_rel;
@@ -1455,9 +1602,12 @@ GMCH_handle_data (struct MeshChannel *ch,
 /**
  * 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)
+ * @param ch Channel on which we got this message.
+ * @param msg Data message.
+ * @param fwd Is this message fwd? This only is meaningful in loopback channels.
+ *            #GNUNET_YES if message is FWD on the respective channel (loopback)
+ *            #GNUNET_NO if message is BCK on the respective channel (loopback)
+ *            #GNUNET_SYSERR if message on a one-ended channel (remote)
  */
 void
 GMCH_handle_data_ack (struct MeshChannel *ch,
@@ -1470,9 +1620,21 @@ GMCH_handle_data_ack (struct MeshChannel *ch,
   uint32_t ack;
   int work;
 
+  /* If this is a remote (non-loopback) channel, find 'fwd'. */
+  if (GNUNET_SYSERR == fwd)
+  {
+    if (is_loopback (ch))
+    {
+      /* It is a loopback channel after all... */
+      GNUNET_break (0);
+      return;
+    }
+    fwd = (NULL != ch->dest) ? GNUNET_YES : GNUNET_NO;
+  }
+
   ack = ntohl (msg->mid);
   LOG (GNUNET_ERROR_TYPE_DEBUG, "!!! %s ACK %u\n",
-              (GNUNET_YES == fwd) ? "FWD" : "BCK", ack);
+       (GNUNET_YES == fwd) ? "FWD" : "BCK", ack);
 
   if (GNUNET_YES == fwd)
   {
@@ -1539,14 +1701,14 @@ GMCH_handle_data_ack (struct MeshChannel *ch,
 /**
  * Handler for channel create messages.
  *
+ * Does not have fwd parameter because it's always 'FWD': channel is incoming.
+ *
  * @param t Tunnel this channel will be in.
- * @param msg Message.
- * @param fwd Is this FWD traffic? GNUNET_YES : GNUNET_NO;
+ * @param msg Channel crate message.
  */
 struct MeshChannel *
 GMCH_handle_create (struct MeshTunnel3 *t,
-                    const struct GNUNET_MESH_ChannelCreate *msg,
-                    int fwd)
+                    const struct GNUNET_MESH_ChannelCreate *msg)
 {
   MESH_ChannelNumber chid;
   struct MeshChannel *ch;
@@ -1572,7 +1734,15 @@ GMCH_handle_create (struct MeshTunnel3 *t,
   {
     /* TODO send reject */
     LOG (GNUNET_ERROR_TYPE_DEBUG, "  no client has port registered\n");
-    channel_destroy (ch);
+    if (is_loopback (ch))
+    {
+      channel_send_nack (ch);
+    }
+    else
+    {
+      channel_send_nack (ch);
+      channel_destroy (ch);
+    }
     return NULL;
   }
   else
@@ -1587,24 +1757,54 @@ GMCH_handle_create (struct MeshTunnel3 *t,
     LOG (GNUNET_ERROR_TYPE_DEBUG, "!!! Not Reliable\n");
 
   GMCH_send_create (ch);
-  channel_send_ack (ch, fwd);
+  channel_send_ack (ch, GNUNET_YES);
 
   return ch;
 }
 
 
+/**
+ * Handler for channel NACK messages.
+ *
+ * NACK messages always go dest -> root, no need for 'fwd' or 'msg' parameter.
+ *
+ * @param ch Channel.
+ */
+void
+GMCH_handle_nack (struct MeshChannel *ch)
+{
+  send_client_nack (ch);
+  channel_destroy (ch);
+}
+
+
 /**
  * Handler for channel ack messages.
  *
  * @param ch Channel.
  * @param msg Message.
- * @param fwd Is this FWD traffic? GNUNET_YES : GNUNET_NO;
+ * @param fwd Is this message fwd? This only is meaningful in loopback channels.
+ *            #GNUNET_YES if message is FWD on the respective channel (loopback)
+ *            #GNUNET_NO if message is BCK on the respective channel (loopback)
+ *            #GNUNET_SYSERR if message on a one-ended channel (remote)
  */
 void
 GMCH_handle_ack (struct MeshChannel *ch,
                  const struct GNUNET_MESH_ChannelManage *msg,
                  int fwd)
 {
+  /* If this is a remote (non-loopback) channel, find 'fwd'. */
+  if (GNUNET_SYSERR == fwd)
+  {
+    if (is_loopback (ch))
+    {
+      /* It is a loopback channel after all... */
+      GNUNET_break (0);
+      return;
+    }
+    fwd = (NULL != ch->dest) ? GNUNET_YES : GNUNET_NO;
+  }
+
   channel_confirm (ch, !fwd);
 }
 
@@ -1614,7 +1814,10 @@ GMCH_handle_ack (struct MeshChannel *ch,
  *
  * @param ch Channel to be destroyed of.
  * @param msg Message.
- * @param fwd Is this FWD traffic? GNUNET_YES : GNUNET_NO;
+ * @param fwd Is this message fwd? This only is meaningful in loopback channels.
+ *            #GNUNET_YES if message is FWD on the respective channel (loopback)
+ *            #GNUNET_NO if message is BCK on the respective channel (loopback)
+ *            #GNUNET_SYSERR if message on a one-ended channel (remote)
  */
 void
 GMCH_handle_destroy (struct MeshChannel *ch,
@@ -1623,6 +1826,18 @@ GMCH_handle_destroy (struct MeshChannel *ch,
 {
   struct MeshTunnel3 *t;
 
+  /* If this is a remote (non-loopback) channel, find 'fwd'. */
+  if (GNUNET_SYSERR == fwd)
+  {
+    if (is_loopback (ch))
+    {
+      /* It is a loopback channel after all... */
+      GNUNET_break (0);
+      return;
+    }
+    fwd = (NULL != ch->dest) ? GNUNET_YES : GNUNET_NO;
+  }
+
   GMCH_debug (ch);
   if ( (fwd && NULL == ch->dest) || (!fwd && NULL == ch->root) )
   {