- fix ACK direction
[oweals/gnunet.git] / src / mesh / gnunet-service-mesh_local.c
index 9714034ba44f1f9c4e237952c41e176adcc2e396..9b868124a1d96b3f2d5c4dfbafd9151d2ed6386d 100644 (file)
@@ -28,7 +28,6 @@
 #include "mesh_protocol_enc.h" // GNUNET_MESH_Data is shared
 
 #include "gnunet-service-mesh_local.h"
-#include "gnunet-service-mesh_tunnel.h"
 
 #define LOG(level, ...) GNUNET_log_from(level,"mesh-loc",__VA_ARGS__)
 
@@ -58,11 +57,16 @@ struct MeshClient
      */
   struct GNUNET_CONTAINER_MultiHashMap32 *own_channels;
 
-   /**
+    /**
      * Tunnels this client has accepted, indexed by incoming local id
      */
   struct GNUNET_CONTAINER_MultiHashMap32 *incoming_channels;
 
+    /**
+     * Channel ID for the next incoming channel.
+     */
+  MESH_ChannelNumber next_chid;
+
     /**
      * Handle to communicate with the client
      */
@@ -175,17 +179,44 @@ handle_client_connect (void *cls, struct GNUNET_SERVER_Client *client)
 {
   struct MeshClient *c;
 
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "client connected: %p\n", client);
   if (NULL == client)
     return;
-  c = GNUNET_malloc (sizeof (struct MeshClient));
+  c = GNUNET_new (struct MeshClient);
   c->handle = client;
   c->id = next_client_id++; /* overflow not important: just for debug */
+  c->next_chid = GNUNET_MESH_LOCAL_CHANNEL_ID_SERV;
   GNUNET_SERVER_client_keep (client);
   GNUNET_SERVER_client_set_user_context (client, c);
   GNUNET_CONTAINER_DLL_insert (clients_head, clients_tail, c);
 }
 
 
+/**
+ * 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;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+              " Channel %s destroy, due to client %s shutdown.\n",
+              GMCH_2s (ch), GML_2s (c));
+
+  GMCH_handle_local_destroy (ch, c);
+  return GNUNET_OK;
+}
+
 /**
  * Handler for client disconnection
  *
@@ -205,7 +236,7 @@ handle_client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
     return;
   }
 
-  c = client_get (client);
+  c = GML_client_get (client);
   if (NULL != c)
   {
     LOG (GNUNET_ERROR_TYPE_DEBUG, "matching client found (%u, %p)\n",
@@ -323,17 +354,12 @@ static void
 handle_channel_create (void *cls, struct GNUNET_SERVER_Client *client,
                        const struct GNUNET_MessageHeader *message)
 {
-  struct GNUNET_MESH_ChannelMessage *msg;
-  struct MeshPeer *peer;
-  struct MeshTunnel2 *t;
-  struct MeshChannel *ch;
   struct MeshClient *c;
-  MESH_ChannelNumber chid;
 
   LOG (GNUNET_ERROR_TYPE_DEBUG, "new channel requested\n");
 
   /* Sanity check for client registration */
-  if (NULL == (c = client_get (client)))
+  if (NULL == (c = GML_client_get (client)))
   {
     GNUNET_break (0);
     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
@@ -349,66 +375,13 @@ handle_channel_create (void *cls, struct GNUNET_SERVER_Client *client,
     return;
   }
 
-  msg = (struct GNUNET_MESH_ChannelMessage *) message;
-  LOG (GNUNET_ERROR_TYPE_DEBUG, "  towards %s:%u\n",
-              GNUNET_i2s (&msg->peer), ntohl (msg->port));
-  chid = ntohl (msg->channel_id);
-
-  /* Sanity check for duplicate channel IDs */
-  if (NULL != channel_get_by_local_id (c, chid))
-  {
-    GNUNET_break (0);
-    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
-    return;
-  }
-
-  peer = peer_get (&msg->peer);
-  if (NULL == peer->tunnel)
-  {
-    peer->tunnel = tunnel_new ();
-    peer->tunnel->peer = peer;
-    if (peer->id == myid)
-    {
-      tunnel_change_state (peer->tunnel, MESH_TUNNEL_READY);
-    }
-    else
-    {
-      peer_connect (peer);
-    }
-  }
-  t = peer->tunnel;
-
-  /* Create channel */
-  ch = channel_new (t, c, chid);
-  if (NULL == ch)
+  if (GNUNET_OK !=
+      GMCH_handle_local_create (c,
+                                (struct GNUNET_MESH_ChannelMessage *) message))
   {
-    GNUNET_break (0);
     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
     return;
   }
-  ch->port = ntohl (msg->port);
-  channel_set_options (ch, ntohl (msg->opt));
-
-  /* In unreliable channels, we'll use the DLL to buffer BCK data */
-  ch->root_rel = GNUNET_new (struct MeshChannelReliability);
-  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);
-
-  /* Send create channel */
-  {
-    struct GNUNET_MESH_ChannelCreate msgcc;
-
-    msgcc.header.size = htons (sizeof (msgcc));
-    msgcc.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_CHANNEL_CREATE);
-    msgcc.chid = htonl (ch->gid);
-    msgcc.port = msg->port;
-    msgcc.opt = msg->opt;
-
-    GMT_queue_data (t, ch, &msgcc.header, GNUNET_YES);
-  }
 
   GNUNET_SERVER_receive_done (client, GNUNET_OK);
   return;
@@ -429,14 +402,13 @@ handle_channel_destroy (void *cls, struct GNUNET_SERVER_Client *client,
   struct GNUNET_MESH_ChannelMessage *msg;
   struct MeshClient *c;
   struct MeshChannel *ch;
-  struct MeshTunnel2 *t;
   MESH_ChannelNumber chid;
 
   LOG (GNUNET_ERROR_TYPE_DEBUG,
               "Got a DESTROY CHANNEL from client!\n");
 
   /* Sanity check for client registration */
-  if (NULL == (c = client_get (client)))
+  if (NULL == (c = GML_client_get (client)))
   {
     GNUNET_break (0);
     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
@@ -456,7 +428,7 @@ handle_channel_destroy (void *cls, struct GNUNET_SERVER_Client *client,
 
   /* Retrieve tunnel */
   chid = ntohl (msg->channel_id);
-  ch = channel_get_by_local_id (c, chid);
+  ch = GML_channel_get (c, chid);
   if (NULL == ch)
   {
     LOG (GNUNET_ERROR_TYPE_ERROR, "  channel %X not found\n", chid);
@@ -465,27 +437,7 @@ handle_channel_destroy (void *cls, struct GNUNET_SERVER_Client *client,
     return;
   }
 
-  /* Cleanup after the tunnel */
-  client_delete_channel (c, ch);
-  if (c == ch->dest && GNUNET_MESH_LOCAL_CHANNEL_ID_SERV <= chid)
-  {
-    ch->dest = NULL;
-  }
-  else if (c == ch->root && GNUNET_MESH_LOCAL_CHANNEL_ID_SERV > chid)
-  {
-    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;
-  channel_destroy (ch);
-  tunnel_destroy_if_empty (t);
+  GMCH_handle_local_destroy (ch, c);
 
   GNUNET_SERVER_receive_done (client, GNUNET_OK);
   return;
@@ -506,7 +458,6 @@ handle_data (void *cls, struct GNUNET_SERVER_Client *client,
   struct GNUNET_MESH_LocalData *msg;
   struct MeshClient *c;
   struct MeshChannel *ch;
-  struct MeshChannelReliability *rel;
   MESH_ChannelNumber chid;
   size_t size;
   int fwd;
@@ -515,7 +466,7 @@ handle_data (void *cls, struct GNUNET_SERVER_Client *client,
               "Got data from a client!\n");
 
   /* Sanity check for client registration */
-  if (NULL == (c = client_get (client)))
+  if (NULL == (c = GML_client_get (client)))
   {
     GNUNET_break (0);
     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
@@ -537,7 +488,7 @@ handle_data (void *cls, struct GNUNET_SERVER_Client *client,
   /* Channel exists? */
   chid = ntohl (msg->id);
   fwd = chid < GNUNET_MESH_LOCAL_CHANNEL_ID_SERV;
-  ch = channel_get_by_local_id (c, chid);
+  ch = GML_channel_get (c, chid);
   if (NULL == ch)
   {
     GNUNET_break (0);
@@ -545,44 +496,14 @@ handle_data (void *cls, struct GNUNET_SERVER_Client *client,
     return;
   }
 
-  /* Is the client in the channel? */
-  if ( !( (fwd &&
-           ch->root &&
-           ch->root->handle == client)
-         ||
-          (!fwd &&
-           ch->dest &&
-           ch->dest->handle == client) ) )
+  if (GNUNET_OK !=
+      GMCH_handle_local_data (ch, c,
+                              (struct GNUNET_MessageHeader *)&msg[1], fwd))
   {
-    GNUNET_break (0);
     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
     return;
   }
 
-  rel = fwd ? ch->root_rel : ch->dest_rel;
-  rel->client_ready = GNUNET_NO;
-
-  /* Ok, everything is correct, send the message. */
-  {
-    struct GNUNET_MESH_Data *payload;
-    uint16_t p2p_size = sizeof(struct GNUNET_MESH_Data) + size;
-    unsigned char cbuf[p2p_size];
-
-    payload = (struct GNUNET_MESH_Data *) cbuf;
-    payload->mid = htonl (rel->mid_send);
-    rel->mid_send++;
-    memcpy (&payload[1], &msg[1], size);
-    payload->header.size = htons (p2p_size);
-    payload->header.type = htons (GNUNET_MESSAGE_TYPE_MESH_DATA);
-    payload->chid = htonl (ch->gid);
-    LOG (GNUNET_ERROR_TYPE_DEBUG, "  sending on channel...\n");
-    send_prebuilt_message_channel (&payload->header, ch, fwd);
-
-    if (GNUNET_YES == ch->reliable)
-      channel_save_copy (ch, &payload->header, fwd);
-  }
-  if (tunnel_get_buffer (ch->t, fwd) > 0)
-    send_local_ack (ch, fwd);
   LOG (GNUNET_ERROR_TYPE_DEBUG, "receive done OK\n");
   GNUNET_SERVER_receive_done (client, GNUNET_OK);
 
@@ -602,7 +523,6 @@ handle_ack (void *cls, struct GNUNET_SERVER_Client *client,
             const struct GNUNET_MessageHeader *message)
 {
   struct GNUNET_MESH_LocalAck *msg;
-  struct MeshChannelReliability *rel;
   struct MeshChannel *ch;
   struct MeshClient *c;
   MESH_ChannelNumber chid;
@@ -611,7 +531,7 @@ handle_ack (void *cls, struct GNUNET_SERVER_Client *client,
   LOG (GNUNET_ERROR_TYPE_DEBUG, "Got a local ACK\n");
 
   /* Sanity check for client registration */
-  if (NULL == (c = client_get (client)))
+  if (NULL == (c = GML_client_get (client)))
   {
     GNUNET_break (0);
     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
@@ -624,7 +544,7 @@ handle_ack (void *cls, struct GNUNET_SERVER_Client *client,
   /* Channel exists? */
   chid = ntohl (msg->channel_id);
   LOG (GNUNET_ERROR_TYPE_DEBUG, "  on channel %X\n", chid);
-  ch = channel_get_by_local_id (c, chid);
+  ch = GML_channel_get (c, chid);
   LOG (GNUNET_ERROR_TYPE_DEBUG, "   -- ch %p\n", ch);
   if (NULL == ch)
   {
@@ -635,15 +555,11 @@ handle_ack (void *cls, struct GNUNET_SERVER_Client *client,
     return;
   }
 
-  /* If client is root, the ACK is going FWD, therefore this is "BCK". */
-  /* If client is dest, the ACK is going BCK, therefore this is "FWD" */
+  /* If client is root, the ACK is going FWD, therefore this is "BCK ACK". */
+  /* If client is dest, the ACK is going BCK, therefore this is "FWD ACK" */
   fwd = chid >= GNUNET_MESH_LOCAL_CHANNEL_ID_SERV;
-  rel = fwd ? ch->dest_rel : ch->root_rel;
-
-  rel->client_ready = GNUNET_YES;
-  channel_send_client_buffered_data (ch, c, fwd);
-  send_ack (NULL, ch, fwd);
 
+  GMCH_handle_local_ack (ch, fwd);
   GNUNET_SERVER_receive_done (client, GNUNET_OK);
 
   return;
@@ -697,7 +613,7 @@ handle_get_tunnels (void *cls, struct GNUNET_SERVER_Client *client,
   struct MeshClient *c;
 
   /* Sanity check for client registration */
-  if (NULL == (c = client_get (client)))
+  if (NULL == (c = GML_client_get (client)))
   {
     GNUNET_break (0);
     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
@@ -734,7 +650,7 @@ handle_show_tunnel (void *cls, struct GNUNET_SERVER_Client *client,
   struct MeshChannel *ch;
 
   /* Sanity check for client registration */
-  if (NULL == (c = client_get (client)))
+  if (NULL == (c = GML_client_get (client)))
   {
     GNUNET_break (0);
     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
@@ -810,6 +726,7 @@ static struct GNUNET_SERVER_MessageHandler client_handlers[] = {
 void
 GML_init (struct GNUNET_SERVER_Handle *handle)
 {
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "init\n");
   server_handle = handle;
   GNUNET_SERVER_suspend (server_handle);
   ports = GNUNET_CONTAINER_multihashmap32_create (32);
@@ -849,6 +766,97 @@ GML_shutdown (void)
 }
 
 
+/**
+ * Get a chennel from a client
+ *
+ * @param c the client to check
+ * @param chid Channel ID, must be local (> 0x800...)
+ *
+ * @return non-NULL if channel exists in the clients lists
+ */
+struct MeshChannel *
+GML_channel_get (struct MeshClient *c, MESH_ChannelNumber chid)
+{
+  if (0 == (chid & GNUNET_MESH_LOCAL_CHANNEL_ID_CLI))
+  {
+    GNUNET_break_op (0);
+    LOG (GNUNET_ERROR_TYPE_DEBUG, "CHID %X not a local chid\n", chid);
+    return NULL;
+  }
+  if (chid >= GNUNET_MESH_LOCAL_CHANNEL_ID_SERV)
+    return GNUNET_CONTAINER_multihashmap32_get (c->incoming_channels, chid);
+  return GNUNET_CONTAINER_multihashmap32_get (c->own_channels, chid);
+}
+
+
+/**
+ * Add a channel to a client
+ *
+ * @param client Client.
+ * @param chid Channel ID.
+ * @param ch Channel.
+ */
+void
+GML_channel_add (struct MeshClient *client,
+                 uint32_t chid,
+                 struct MeshChannel *ch)
+{
+  if (chid >= GNUNET_MESH_LOCAL_CHANNEL_ID_SERV)
+    GNUNET_CONTAINER_multihashmap32_put (client->incoming_channels, chid, ch,
+                                         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
+  else if (chid >= GNUNET_MESH_LOCAL_CHANNEL_ID_CLI)
+    GNUNET_CONTAINER_multihashmap32_put (client->own_channels, chid, ch,
+                                         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
+  else
+    GNUNET_break (0);
+}
+
+
+/**
+ * Remove a channel from a client
+ *
+ * @param client Client.
+ * @param chid Channel ID.
+ * @param ch Channel.
+ */
+void
+GML_channel_remove (struct MeshClient *client,
+                    uint32_t chid,
+                    struct MeshChannel *ch)
+{
+  if (GNUNET_MESH_LOCAL_CHANNEL_ID_SERV <= chid)
+    GNUNET_CONTAINER_multihashmap32_remove (client->incoming_channels, chid, ch);
+  else if (GNUNET_MESH_LOCAL_CHANNEL_ID_CLI <= chid)
+    GNUNET_CONTAINER_multihashmap32_remove (client->own_channels, chid, ch);
+  else
+    GNUNET_break (0);
+}
+
+
+/**
+ * Get the tunnel's next free local channel ID.
+ *
+ * @param c Client.
+ *
+ * @return LID of a channel free to use.
+ */
+MESH_ChannelNumber
+GML_get_next_chid (struct MeshClient *c)
+{
+  MESH_ChannelNumber chid;
+
+  while (NULL != GML_channel_get (c, c->next_chid))
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG, "Channel %u exists...\n", c->next_chid);
+    c->next_chid = (c->next_chid + 1) | GNUNET_MESH_LOCAL_CHANNEL_ID_SERV;
+  }
+  chid = c->next_chid;
+  c->next_chid = (c->next_chid + 1) | GNUNET_MESH_LOCAL_CHANNEL_ID_SERV;
+
+  return chid;
+}
+
+
 /**
  * Check if client has registered with the service and has not disconnected
  *
@@ -862,31 +870,51 @@ GML_client_get (struct GNUNET_SERVER_Client *client)
   return GNUNET_SERVER_client_get_user_context (client, struct MeshClient);
 }
 
+/**
+ * Find a client that has opened a port
+ *
+ * @param port Port to check.
+ *
+ * @return non-NULL if a client has the port.
+ */
+struct MeshClient *
+GML_client_get_by_port (uint32_t port)
+{
+  return GNUNET_CONTAINER_multihashmap32_get (ports, port);
+}
+
 
 /**
- * Deletes a tunnel from a client (either owner or destination).
+ * Deletes a channel from a client (either owner or destination).
  *
  * @param c Client whose tunnel to delete.
  * @param ch Channel which should be deleted.
+ * @param id Channel ID.
  */
 void
-GML_client_delete_channel (struct MeshClient *c, struct MeshChannel *ch)
+GML_client_delete_channel (struct MeshClient *c,
+                           struct MeshChannel *ch,
+                           MESH_ChannelNumber id)
 {
   int res;
 
-  if (c == ch->root)
+  if (GNUNET_MESH_LOCAL_CHANNEL_ID_SERV <= id)
   {
-    res = GNUNET_CONTAINER_multihashmap32_remove (c->own_channels,
-                                                  ch->lid_root, ch);
+    res = GNUNET_CONTAINER_multihashmap32_remove (c->incoming_channels,
+                                                  id, ch);
     if (GNUNET_YES != res)
-      LOG (GNUNET_ERROR_TYPE_DEBUG, "client_delete_channel owner KO\n");
+      LOG (GNUNET_ERROR_TYPE_DEBUG, "client_delete_channel dest KO\n");
   }
-  if (c == ch->dest)
+  else if (GNUNET_MESH_LOCAL_CHANNEL_ID_CLI <= id)
   {
-    res = GNUNET_CONTAINER_multihashmap32_remove (c->incoming_channels,
-                                                  ch->lid_dest, ch);
+    res = GNUNET_CONTAINER_multihashmap32_remove (c->own_channels,
+                                                  id, ch);
     if (GNUNET_YES != res)
-      LOG (GNUNET_ERROR_TYPE_DEBUG, "client_delete_tunnel client KO\n");
+      LOG (GNUNET_ERROR_TYPE_DEBUG, "client_delete_tunnel root KO\n");
+  }
+  else
+  {
+    GNUNET_break (0);
   }
 }
 
@@ -895,39 +923,21 @@ GML_client_delete_channel (struct MeshClient *c, struct MeshChannel *ch)
  *
  * 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->root)
+ * @param id Channel ID to use
  */
 void
-GML_send_ack (struct MeshChannel *ch, int fwd)
+GML_send_ack (struct MeshClient *c, MESH_ChannelNumber id)
 {
   struct GNUNET_MESH_LocalAck msg;
-  struct MeshChannelReliability *rel;
-  struct MeshClient *c;
-
-  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;
 
   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);
+              "send local %s ack on %X towards %p\n",
+              id < GNUNET_MESH_LOCAL_CHANNEL_ID_SERV ? "FWD" : "BCK", id, c);
 
-  if (NULL == c
-      || ( fwd && (0 == ch->lid_root || c != ch->root))
-      || (!fwd && (0 == ch->lid_dest || c != ch->dest)) )
-  {
-    GNUNET_break (0);
-    return;
-  }
   msg.header.size = htons (sizeof (msg));
   msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_ACK);
-  msg.channel_id = htonl (fwd ? ch->lid_root : ch->lid_dest);
+  msg.channel_id = htonl (id);
   GNUNET_SERVER_notification_context_unicast (nc,
                                               c->handle,
                                               &msg.header,
@@ -944,7 +954,7 @@ GML_send_ack (struct MeshChannel *ch, int fwd)
 void
 GML_send_channel_create (struct MeshClient *c,
                          uint32_t id, uint32_t port, uint32_t opt,
-                         struct GNUNET_PeerIdentity *peer)
+                         const struct GNUNET_PeerIdentity *peer)
 {
   struct GNUNET_MESH_ChannelMessage msg;
 
@@ -989,11 +999,11 @@ GML_send_channel_destroy (struct MeshClient *c, uint32_t id)
 /**
  * Modify the mesh message ID from global to local and send to client.
  *
- * @param msg Message to modify and send.
  * @param c Client to send to.
- * @param tid Tunnel ID to use (c can be both owner and client).
+ * @param msg Message to modify and send.
+ * @param id Channel ID to use (c can be both owner and client).
  */
-static void
+void
 GML_send_data (struct MeshClient *c,
                const struct GNUNET_MESH_Data *msg,
                MESH_ChannelNumber id)
@@ -1022,4 +1032,18 @@ GML_send_data (struct MeshClient *c,
 }
 
 
+/**
+ * Get the static string to represent a client.
+ *
+ * @param c Client.
+ *
+ * @return Static string for the client.
+ */
+const char *
+GML_2s (const struct MeshClient *c)
+{
+  static char buf[32];
 
+  sprintf (buf, "%u", c->id);
+  return buf;
+}