- avoid infinite ACK loops when creating loopback channels
[oweals/gnunet.git] / src / mesh / gnunet-service-mesh_channel.c
index aa89730c374b41fa57b068542e49f31e27a05f66..170ba955e4742623ea9d4a39a5d6db59306d8957 100644 (file)
@@ -258,6 +258,11 @@ struct MeshChannel
  */
 extern struct GNUNET_STATISTICS_Handle *stats;
 
+/**
+ * Local peer own ID (memory efficient handle).
+ */
+extern GNUNET_PEER_Id myid;
+
 
 /******************************************************************************/
 /********************************   STATIC  ***********************************/
@@ -594,6 +599,27 @@ rel_message_free (struct MeshReliableMessage *copy)
 }
 
 
+/**
+ * Confirm we got a channel create.
+ *
+ * @param ch The channel to confirm.
+ * @param fwd Should we send a FWD ACK? (going dest->root)
+ */
+static void
+channel_send_ack (struct MeshChannel *ch, int fwd)
+{
+  struct GNUNET_MESH_ChannelManage msg;
+
+  msg.header.size = htons (sizeof (msg));
+  msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_CHANNEL_ACK);
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+              "  sending channel %s ack for channel %s\n",
+              fwd ? "FWD" : "BCK", GMCH_2s (ch));
+
+  msg.chid = htonl (ch->gid);
+  GMCH_send_prebuilt_message (&msg.header, ch, !fwd);
+}
+
 
 /**
  * Channel was ACK'd by remote peer, mark as ready and cancel retransmission.
@@ -626,7 +652,10 @@ channel_confirm (struct MeshChannel *ch, int fwd)
       /* TODO return? */
     }
   }
-  send_ack (NULL, ch, fwd);
+
+  /* In case of a FWD ACk (SYNACK) send a BCK ACK (ACK). */
+  if (fwd)
+    channel_send_ack (ch, !fwd);
 }
 
 
@@ -785,7 +814,8 @@ channel_destroy (struct MeshChannel *ch)
  */
 static struct MeshChannel *
 channel_new (struct MeshTunnel3 *t,
-             struct MeshClient *owner, MESH_ChannelNumber lid_root)
+             struct MeshClient *owner,
+             MESH_ChannelNumber lid_root)
 {
   struct MeshChannel *ch;
 
@@ -823,71 +853,6 @@ channel_set_options (struct MeshChannel *ch, uint32_t options)
 }
 
 
-
-/**
- * Confirm we got a channel create.
- *
- * @param ch The channel to confirm.
- * @param fwd Should we send the ACK fwd?
- */
-static void
-channel_send_ack (struct MeshChannel *ch, int fwd)
-{
-  struct GNUNET_MESH_ChannelManage msg;
-
-  msg.header.size = htons (sizeof (msg));
-  msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_CHANNEL_ACK);
-  LOG (GNUNET_ERROR_TYPE_DEBUG,
-              "  sending channel %s ack for channel %s:%X\n",
-              fwd ? "FWD" : "BCK", GMT_2s (ch->t),
-              ch->gid);
-
-  msg.chid = htonl (ch->gid);
-  GMCH_send_prebuilt_message (&msg.header, ch, !fwd);
-}
-
-
-/**
- * Iterator for deleting each channel whose client endpoint disconnected.
- *
- * @param cls Closure (client that has disconnected).
- * @param key The local channel id (used to access the hashmap).
- * @param value The value stored at the key (channel to destroy).
- *
- * @return GNUNET_OK, keep iterating.
- */
-static int
-channel_destroy_iterator (void *cls,
-                          uint32_t key,
-                          void *value)
-{
-  struct MeshChannel *ch = value;
-  struct MeshClient *c = cls;
-  struct MeshTunnel3 *t;
-
-  LOG (GNUNET_ERROR_TYPE_DEBUG,
-              " Channel %X (%X / %X) destroy, due to client %s shutdown.\n",
-              ch->gid, ch->lid_root, ch->lid_dest, GML_2s (c));
-  GMCH_debug (ch);
-
-  if (c == ch->dest)
-  {
-    LOG (GNUNET_ERROR_TYPE_DEBUG, " Client %s is destination.\n", GML_2s (c));
-  }
-  if (c == ch->root)
-  {
-    LOG (GNUNET_ERROR_TYPE_DEBUG, " Client %s is owner.\n", GML_2s (c));
-  }
-
-  t = ch->t;
-  GMCH_send_destroy (ch);
-  channel_destroy (ch);
-  GMT_destroy_if_empty (t);
-
-  return GNUNET_OK;
-}
-
-
 /**
  * Handle a loopback message: call the appropriate handler for the message type.
  *
@@ -904,8 +869,8 @@ handle_loopback (struct MeshChannel *ch,
 
   type = ntohs (msgh->type);
   LOG (GNUNET_ERROR_TYPE_DEBUG,
-       "Loopback %s message!\n",
-       GNUNET_MESH_DEBUG_M2S (type));
+       "Loopback %s %s message!\n",
+       fwd ? "FWD" : "BCK", GNUNET_MESH_DEBUG_M2S (type));
 
   switch (type)
   {
@@ -919,8 +884,8 @@ handle_loopback (struct MeshChannel *ch,
       break;
 
     case GNUNET_MESSAGE_TYPE_MESH_CHANNEL_CREATE:
-       // FIXME store channel in loopback tunnel?
-      GMCH_handle_create ((struct GNUNET_MESH_ChannelCreate *) msgh,
+      GMCH_handle_create (ch->t,
+                          (struct GNUNET_MESH_ChannelCreate *) msgh,
                           fwd);
       break;
 
@@ -1093,6 +1058,16 @@ GMCH_send_data (struct MeshChannel *ch,
                 const struct GNUNET_MESH_Data *msg,
                 int fwd)
 {
+  if (GMCH_is_terminal (ch, fwd))
+  {
+    GML_send_data (fwd ? ch->dest : ch->root,
+                   msg,
+                   fwd ? ch->lid_dest : ch->lid_root);
+  }
+  else
+  {
+    GMT_send_prebuilt_message (&msg->header, ch->t, ch, fwd);
+  }
 }
 
 
@@ -1105,7 +1080,7 @@ GMCH_send_data (struct MeshChannel *ch,
  * @param fwd Is for FWD traffic? (ACK dest->owner)
  */
 void
-GMCH_send_ack (struct MeshChannel *ch, int fwd)
+GMCH_send_data_ack (struct MeshChannel *ch, int fwd)
 {
   struct GNUNET_MESH_DataACK msg;
   struct MeshChannelReliability *rel;
@@ -1204,7 +1179,7 @@ GMCH_handle_local_ack (struct MeshChannel *ch, int fwd)
 
   rel->client_ready = GNUNET_YES;
   send_client_buffered_data (ch, c, fwd);
-  send_ack (NULL, ch, fwd);
+  GMC_send_ack (NULL, ch, fwd);
 }
 
 
@@ -1272,34 +1247,29 @@ GMCH_handle_local_data (struct MeshChannel *ch,
  *
  * @param ch Channel.
  * @param c Client that requested the destruction (to avoid notifying him).
- * @param chid Channel ID used.
  */
 void
 GMCH_handle_local_destroy (struct MeshChannel *ch,
-                           struct MeshClient *c,
-                           MESH_ChannelNumber chid)
+                           struct MeshClient *c)
 {
   struct MeshTunnel3 *t;
 
   /* Cleanup after the tunnel */
-  GML_client_delete_channel (c, ch, chid);
-  if (c == ch->dest && GNUNET_MESH_LOCAL_CHANNEL_ID_SERV <= chid)
+  if (c == ch->dest)
   {
+    LOG (GNUNET_ERROR_TYPE_DEBUG, " Client %s is destination.\n", GML_2s (c));
+    GML_client_delete_channel (c, ch, ch->lid_dest);
     ch->dest = NULL;
   }
-  else if (c == ch->root && GNUNET_MESH_LOCAL_CHANNEL_ID_SERV > chid)
+  if (c == ch->root)
   {
+    LOG (GNUNET_ERROR_TYPE_DEBUG, " Client %s is owner.\n", GML_2s (c));
+    GML_client_delete_channel (c, ch, ch->lid_root);
     ch->root = NULL;
   }
-  else
-  {
-    LOG (GNUNET_ERROR_TYPE_ERROR,
-                "  channel %X client %p (%p, %p)\n",
-                chid, c, ch->root, ch->dest);
-    GNUNET_break (0);
-  }
 
   t = ch->t;
+  GMCH_send_destroy (ch);
   channel_destroy (ch);
   GMT_destroy_if_empty (t);
 }
@@ -1312,8 +1282,10 @@ GMCH_handle_local_destroy (struct MeshChannel *ch,
  *
  * @param c Client that requested the creation (will be the root).
  * @param msg Create Channel message.
+ *
+ * @return GNUNET_OK if everything went fine, GNUNET_SYSERR otherwise.
  */
-void
+int
 GMCH_handle_local_create (struct MeshClient *c,
                           struct GNUNET_MESH_ChannelMessage *msg)
 {
@@ -1330,15 +1302,14 @@ GMCH_handle_local_create (struct MeshClient *c,
   if (NULL != GML_channel_get (c, chid))
   {
     GNUNET_break (0);
-    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
-    return;
+    return GNUNET_SYSERR;
   }
 
   peer = GMP_get (&msg->peer);
   GMP_add_tunnel (peer);
-  t = GMP_get_tunnel(peer);
+  t = GMP_get_tunnel (peer);
 
-  if (GMP_get_short_id(peer) == myid)
+  if (GMP_get_short_id (peer) == myid)
   {
     GMT_change_state (t, MESH_TUNNEL3_READY);
   }
@@ -1352,7 +1323,7 @@ GMCH_handle_local_create (struct MeshClient *c,
   if (NULL == ch)
   {
     GNUNET_break (0);
-    return;
+    return GNUNET_SYSERR;
   }
   ch->port = ntohl (msg->port);
   channel_set_options (ch, ntohl (msg->opt));
@@ -1362,8 +1333,7 @@ GMCH_handle_local_create (struct MeshClient *c,
   ch->root_rel->ch = ch;
   ch->root_rel->expected_delay = MESH_RETRANSMIT_TIME;
 
-  LOG (GNUNET_ERROR_TYPE_DEBUG, "CREATED CHANNEL %s[%x]:%u (%x)\n",
-              peer2s (t->peer), ch->gid, ch->port, ch->lid_root);
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "CREATED CHANNEL %s\n", GMCH_2s (ch));
 
   /* Send create channel */
   {
@@ -1377,6 +1347,7 @@ GMCH_handle_local_create (struct MeshClient *c,
 
     GMT_queue_data (t, ch, &msgcc.header, GNUNET_YES);
   }
+  return GNUNET_OK;
 }
 
 /**
@@ -1446,7 +1417,7 @@ GMCH_handle_data (struct MeshChannel *ch,
                 mid, rel->mid_recv, rel->mid_recv + 64);
   }
 
-  GMCH_send_ack (ch, fwd);
+  GMCH_send_data_ack (ch, fwd);
 }
 
 
@@ -1537,11 +1508,13 @@ GMCH_handle_data_ack (struct MeshChannel *ch,
 /**
  * Handler for channel create messages.
  *
+ * @param t Tunnel this channel will be in.
  * @param msg Message.
  * @param fwd Is this FWD traffic? GNUNET_YES : GNUNET_NO;
  */
 struct MeshChannel *
-GMCH_handle_create (const struct GNUNET_MESH_ChannelCreate *msg,
+GMCH_handle_create (struct MeshTunnel3 *t,
+                    const struct GNUNET_MESH_ChannelCreate *msg,
                     int fwd)
 {
   MESH_ChannelNumber chid;
@@ -1551,9 +1524,13 @@ GMCH_handle_create (const struct GNUNET_MESH_ChannelCreate *msg,
 
   chid = ntohl (msg->chid);
 
-  /* Create channel */
-  ch = channel_new (NULL, NULL, 0); /* FIXME pass t */
-  ch->gid = chid;
+  ch = GMT_get_channel (t, chid);
+  if (NULL == ch)
+  {
+    /* Create channel */
+    ch = channel_new (t, NULL, 0);
+    ch->gid = chid;
+  }
   channel_set_options (ch, ntohl (msg->opt));
 
   /* Find a destination client */
@@ -1573,7 +1550,8 @@ GMCH_handle_create (const struct GNUNET_MESH_ChannelCreate *msg,
     LOG (GNUNET_ERROR_TYPE_DEBUG, "!!! Reliable\n");
 
   GMCH_send_create (ch);
-  GMCH_send_ack (ch, fwd);
+  channel_send_ack (ch, fwd);
+  GMCH_send_data_ack (ch, fwd);
 
   if (GNUNET_NO == ch->dest_rel->client_ready)
   {
@@ -1613,14 +1591,19 @@ GMCH_handle_destroy (struct MeshChannel *ch,
                      const struct GNUNET_MESH_ChannelManage *msg,
                      int fwd)
 {
+  struct MeshTunnel3 *t;
+
+  GMCH_debug (ch);
   if ( (fwd && NULL == ch->dest) || (!fwd && NULL == ch->root) )
   {
     /* Not for us (don't destroy twice a half-open loopback channel) */
     return;
   }
 
+  t = ch->t;
   GMCH_send_destroy (ch);
   channel_destroy (ch);
+  GMT_destroy_if_empty (t);
 }
 
 
@@ -1643,8 +1626,8 @@ void
 GMCH_send_prebuilt_message (const struct GNUNET_MessageHeader *message,
                             struct MeshChannel *ch, int fwd)
 {
-  LOG (GNUNET_ERROR_TYPE_DEBUG, "Send on Channel %s:%X %s\n",
-       GMT_2s (ch->t), ch->gid, fwd ? "FWD" : "BCK");
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "Send %s on channel %s\n",
+       fwd ? "FWD" : "BCK", GMCH_2s (ch));
   LOG (GNUNET_ERROR_TYPE_DEBUG, "  %s\n",
        GNUNET_MESH_DEBUG_M2S (ntohs (message->type)));
 
@@ -1656,3 +1639,25 @@ GMCH_send_prebuilt_message (const struct GNUNET_MessageHeader *message,
 
   GMT_send_prebuilt_message (message, ch->t, ch, fwd);
 }
+
+
+/**
+ * Get the static string for identification of the channel.
+ *
+ * @param ch Channel.
+ *
+ * @return Static string with the channel IDs.
+ */
+const char *
+GMCH_2s (const struct MeshChannel *ch)
+{
+  static char buf[64];
+
+  if (NULL == ch)
+    return "(NULL Channel)";
+
+  sprintf (buf, "%s:%X (%X / %X)",
+           GMT_2s (ch->t), ch->gid, ch->lid_root, ch->lid_dest);
+
+  return buf;
+}