fix shutdown sequence, start to handle loopback
authorChristian Grothoff <christian@grothoff.org>
Mon, 23 Jan 2017 10:14:37 +0000 (11:14 +0100)
committerChristian Grothoff <christian@grothoff.org>
Mon, 23 Jan 2017 10:14:37 +0000 (11:14 +0100)
src/cadet/gnunet-service-cadet-new.c
src/cadet/gnunet-service-cadet-new.h
src/cadet/gnunet-service-cadet-new_channel.c
src/cadet/gnunet-service-cadet-new_channel.h
src/cadet/gnunet-service-cadet-new_tunnels.c

index bb251c3075b4505bb8e2cff3d4edb24641e1e89d..c9a2fb437fd30bc0d1c10dd99aaf1d11e23eaeb3 100644 (file)
@@ -131,7 +131,7 @@ struct GNUNET_PeerIdentity my_full_id;
 struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key;
 
 /**
- * Signal that shutdown is happening: prevent recover measures.
+ * Signal that shutdown is happening: prevent recovery measures.
  */
 int shutting_down;
 
@@ -356,17 +356,11 @@ destroy_paths_now (void *cls,
 
 
 /**
- * Task run during shutdown.
- *
- * @param cls unused
+ * Shutdown everything once the clients have disconnected.
  */
 static void
-shutdown_task (void *cls)
+shutdown_rest ()
 {
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Shutting down\n");
-  shutting_down = GNUNET_YES;
-  GCO_shutdown ();
   if (NULL != stats)
   {
     GNUNET_STATISTICS_destroy (stats,
@@ -413,6 +407,23 @@ shutdown_task (void *cls)
 }
 
 
+/**
+ * Task run during shutdown.
+ *
+ * @param cls unused
+ */
+static void
+shutdown_task (void *cls)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Shutting down\n");
+  shutting_down = GNUNET_YES;
+  GCO_shutdown ();
+  if (NULL == clients_head)
+    shutdown_rest ();
+}
+
+
 /**
  * We had a remote connection @a value to port @a port before
  * client @a cls opened port @a port.  Bind them now.
@@ -608,7 +619,8 @@ handle_channel_destroy (void *cls,
                  GNUNET_CONTAINER_multihashmap32_remove (c->channels,
                                                          ntohl (msg->ccn.channel_of_client),
                                                          ch));
-  GCCH_channel_local_destroy (ch);
+  GCCH_channel_local_destroy (ch,
+                              c);
   GNUNET_SERVICE_client_continue (c->client);
 }
 
@@ -1196,10 +1208,8 @@ channel_destroy_iterator (void *cls,
                  GNUNET_CONTAINER_multihashmap32_remove (c->channels,
                                                          key,
                                                          ch));
-  if (key < GNUNET_CADET_LOCAL_CHANNEL_ID_CLI)
-    GCCH_channel_local_destroy (ch);
-  else
-    GCCH_channel_incoming_destroy (ch);
+  GCCH_channel_local_destroy (ch,
+                              c);
   return GNUNET_OK;
 }
 
@@ -1275,6 +1285,9 @@ client_disconnect_cb (void *cls,
                             -1,
                             GNUNET_NO);
   GNUNET_free (c);
+  if ( (NULL == clients_head) &&
+       (GNUNET_YES == shutting_down) )
+    shutdown_rest ();
 }
 
 
index cb289e9c326edf6849b0433d4f960df086664005..4a76c578b4ac018a8254b1f6ef75aaa1b8901f5f 100644 (file)
@@ -225,6 +225,11 @@ extern unsigned long long ratchet_messages;
  */
 extern struct GNUNET_TIME_Relative ratchet_time;
 
+/**
+ * Signal that shutdown is happening: prevent recovery measures.
+ */
+extern int shutting_down;
+
 
 /**
  * Send a message to a client.
index f45c5e5ce985b14ed507b09b389a823a0993230c..87073219249c54f610b09cbf977c8f0a990636e7 100644 (file)
@@ -301,6 +301,11 @@ struct CadetChannel
    */
   int out_of_order;
 
+  /**
+   * Is this channel a loopback channel, where the destination is us again?
+   */
+  int is_loopback;
+
   /**
    * Flag to signal the destruction of the channel.  If this is set to
    * #GNUNET_YES the channel will be destroyed once the queue is
@@ -523,11 +528,38 @@ GCCH_channel_local_new (struct CadetClient *owner,
   ch->owner = owner;
   ch->ccn = ccn;
   ch->port = *port;
-  ch->t = GCP_get_tunnel (destination,
-                          GNUNET_YES);
-  ch->retry_time = CADET_INITIAL_RETRANSMIT_TIME;
-  ch->ctn = GCT_add_channel (ch->t,
-                             ch);
+  if (0 == memcmp (&my_full_id,
+                   GCP_get_id (destination),
+                   sizeof (struct GNUNET_PeerIdentity)))
+  {
+    ch->is_loopback = GNUNET_YES;
+    ch->dest = GNUNET_CONTAINER_multihashmap_get (open_ports,
+                                                  port);
+    if (NULL == ch->dest)
+    {
+      /* port closed, wait for it to possibly open */
+      (void) GNUNET_CONTAINER_multihashmap_put (loose_channels,
+                                                port,
+                                                ch,
+                                                GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+      LOG (GNUNET_ERROR_TYPE_DEBUG,
+           "Created loose incoming loopback channel to port %s\n",
+           GNUNET_h2s (&ch->port));
+    }
+    else
+    {
+      GCCH_bind (ch,
+                 ch->dest);
+    }
+  }
+  else
+  {
+    ch->t = GCP_get_tunnel (destination,
+                            GNUNET_YES);
+    ch->retry_time = CADET_INITIAL_RETRANSMIT_TIME;
+    ch->ctn = GCT_add_channel (ch->t,
+                               ch);
+  }
   GNUNET_STATISTICS_update (stats,
                             "# channels",
                             1,
@@ -537,7 +569,7 @@ GCCH_channel_local_new (struct CadetClient *owner,
        GNUNET_h2s (port),
        GCP_2s (destination),
        GSC_2s (owner),
-       GCT_2s (ch->t));
+       (GNUNET_YES == ch->is_loopback) ? "loopback" : GCT_2s (ch->t));
   return ch;
 }
 
@@ -791,11 +823,18 @@ GCCH_bind (struct CadetChannel *ch,
                       &ch->port,
                       options);
   ch->mid_recv.mid = htonl (1); /* The CONNECT counts as message 0! */
-
-  /* notify other peer that we accepted the connection */
-  ch->retry_control_task
-    = GNUNET_SCHEDULER_add_now (&send_open_ack,
-                                ch);
+  if (GNUNET_YES == ch->is_loopback)
+  {
+    ch->state = CADET_CHANNEL_OPEN_SENT;
+    GCCH_handle_channel_open_ack (ch);
+  }
+  else
+  {
+    /* notify other peer that we accepted the connection */
+    ch->retry_control_task
+      = GNUNET_SCHEDULER_add_now (&send_open_ack,
+                                  ch);
+  }
   /* give client it's initial supply of ACKs */
   env = GNUNET_MQ_msg (tcm,
                        GNUNET_MESSAGE_TYPE_CADET_LOCAL_CHANNEL_CREATE);
@@ -812,17 +851,27 @@ GCCH_bind (struct CadetChannel *ch,
 
 
 /**
- * Destroy locally created channel.  Called by the
- * local client, so no need to tell the client.
+ * Destroy locally created channel.  Called by the local client, so no
+ * need to tell the client.
  *
  * @param ch channel to destroy
+ * @param c client that caused the destruction
  */
 void
-GCCH_channel_local_destroy (struct CadetChannel *ch)
+GCCH_channel_local_destroy (struct CadetChannel *ch,
+                            struct CadetClient *c)
 {
   LOG (GNUNET_ERROR_TYPE_DEBUG,
-       "Local client asks for destruction of %s which it initiated\n",
+       "%s asks for destruction of %s\n",
+       GSC_2s (c),
        GCCH_2s (ch));
+  GNUNET_assert (NULL != c);
+  if (c == ch->owner)
+    ch->owner = NULL;
+  else if (c == ch->dest)
+    ch->dest = NULL;
+  else
+    GNUNET_assert (0);
   if (GNUNET_YES == ch->destroy)
   {
     /* other end already destroyed, with the local client gone, no need
@@ -830,9 +879,12 @@ GCCH_channel_local_destroy (struct CadetChannel *ch)
     channel_destroy (ch);
     return;
   }
-  if (NULL != ch->head_sent)
+  if ( (NULL != ch->head_sent) ||
+       (NULL != ch->owner) ||
+       (NULL != ch->dest) )
   {
-    /* allow send queue to train first */
+    /* Wait for other end to destroy us as well,
+       and otherwise allow send queue to be transmitted first */
     ch->destroy = GNUNET_YES;
     return;
   }
@@ -840,39 +892,7 @@ GCCH_channel_local_destroy (struct CadetChannel *ch)
   if (CADET_CHANNEL_NEW != ch->state)
     GCT_send_channel_destroy (ch->t,
                               ch->ctn);
-  /* Now finish our clean up */
-  channel_destroy (ch);
-}
-
-
-/**
- * Destroy channel that was incoming.  Called by the
- * local client, so no need to tell the client.
- *
- * @param ch channel to destroy
- */
-void
-GCCH_channel_incoming_destroy (struct CadetChannel *ch)
-{
-  LOG (GNUNET_ERROR_TYPE_DEBUG,
-       "Local client asks for destruction of %s which it accepted\n",
-       GCCH_2s (ch));
-  if (GNUNET_YES == ch->destroy)
-  {
-    /* other end already destroyed, with the remote client gone, no need
-       to finish transmissions, just destroy immediately. */
-    channel_destroy (ch);
-    return;
-  }
-  if (NULL != ch->head_recv)
-  {
-    /* allow local client to see all data first */
-    ch->destroy = GNUNET_YES;
-    return;
-  }
   /* Nothing left to do, just finish destruction */
-  GCT_send_channel_destroy (ch->t,
-                            ch->ctn);
   channel_destroy (ch);
 }
 
@@ -980,6 +1000,12 @@ GCCH_handle_channel_plaintext_data (struct CadetChannel *ch,
        "Receicved %u bytes of application data on %s\n",
        (unsigned int) payload_size,
        GCCH_2s (ch));
+  if (GNUNET_YES == ch->is_loopback)
+  {
+    GNUNET_break (0); // FIXME: not implemented
+    return;
+  }
+
   env = GNUNET_MQ_msg_extra (ld,
                              payload_size,
                              GNUNET_MESSAGE_TYPE_CADET_LOCAL_DATA);
@@ -1054,6 +1080,7 @@ GCCH_handle_channel_plaintext_data_ack (struct CadetChannel *ch,
 {
   struct CadetReliableMessage *crm;
 
+  GNUNET_break (GNUNET_NO == ch->is_loopback);
   if (GNUNET_NO == ch->reliable)
   {
     /* not expecting ACKs on unreliable channel, odd */
@@ -1104,9 +1131,23 @@ GCCH_handle_channel_plaintext_data_ack (struct CadetChannel *ch,
 void
 GCCH_handle_remote_destroy (struct CadetChannel *ch)
 {
+  GNUNET_break (GNUNET_NO == ch->is_loopback);
   LOG (GNUNET_ERROR_TYPE_DEBUG,
        "Received remote channel DESTROY for %s\n",
        GCCH_2s (ch));
+  if (GNUNET_YES == ch->destroy)
+  {
+    /* Local client already gone, this is instant-death. */
+    channel_destroy (ch);
+    return;
+  }
+  if (NULL != ch->head_recv)
+  {
+    LOG (GNUNET_ERROR_TYPE_WARNING,
+         "Lost end of transmission due to remote shutdown on channel %s\n",
+         GCCH_2s (ch));
+    /* FIXME: change API to notify client about truncated transmission! */
+  }
   ch->destroy = GNUNET_YES;
   GSC_handle_remote_channel_destroy ((NULL != ch->owner) ? ch->owner : ch->dest,
                                      ch->ccn,
@@ -1290,6 +1331,12 @@ GCCH_handle_local_data (struct CadetChannel *ch,
   }
   ch->pending_messages++;
 
+  if (GNUNET_YES == ch->is_loopback)
+  {
+    GNUNET_break (0); // fIXME: not implemented
+    return GNUNET_SYSERR;
+  }
+
   /* Everything is correct, send the message. */
   crm = GNUNET_malloc (sizeof (*crm) + buf_len);
   crm->ch = ch;
index 3a931499e16e62034966522702396083866f9128..a473fd3e4e501e3944ff23f8080878c6831e2591 100644 (file)
@@ -113,9 +113,11 @@ GCCH_bind (struct CadetChannel *ch,
  * local client, so no need to tell the client.
  *
  * @param ch channel to destroy
+ * @param c client that caused the destruction
  */
 void
-GCCH_channel_local_destroy (struct CadetChannel *ch);
+GCCH_channel_local_destroy (struct CadetChannel *ch,
+                            struct CadetClient *c);
 
 
 /**
@@ -149,16 +151,6 @@ GCCH_channel_incoming_new (struct CadetTunnel *t,
                            uint32_t options);
 
 
-/**
- * Destroy channel that was incoming.  Called by the
- * local client, so no need to tell the client.
- *
- * @param ch channel to destroy
- */
-void
-GCCH_channel_incoming_destroy (struct CadetChannel *ch);
-
-
 /**
  * We got a #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN message again for
  * this channel.  If the binding was successful, (re)transmit the
index 20ab8f4e9487c1a0538e45684af5e8644dc1ea83..b85f1b321113f8490f71a2ceefefe81d00cfd9a6 100644 (file)
@@ -1623,6 +1623,26 @@ GCT_remove_channel (struct CadetTunnel *t,
 }
 
 
+/**
+ * Destroy remaining channels during shutdown.
+ *
+ * @param cls the `struct CadetTunnel` of the channel
+ * @param key key of the channel
+ * @param value the `struct CadetChannel`
+ * @return #GNUNET_OK (continue to iterate)
+ */
+static int
+destroy_remaining_channels (void *cls,
+                            uint32_t key,
+                            void *value)
+{
+  struct CadetChannel *ch = value;
+
+  GCCH_handle_remote_destroy (ch);
+  return GNUNET_OK;
+}
+
+
 /**
  * Destroys the tunnel @a t now, without delay. Used during shutdown.
  *
@@ -1631,6 +1651,10 @@ GCT_remove_channel (struct CadetTunnel *t,
 void
 GCT_destroy_tunnel_now (struct CadetTunnel *t)
 {
+  GNUNET_assert (GNUNET_YES == shutting_down);
+  GNUNET_CONTAINER_multihashmap32_iterate (t->channels,
+                                           &destroy_remaining_channels,
+                                           t);
   GNUNET_assert (0 ==
                  GNUNET_CONTAINER_multihashmap32_size (t->channels));
   if (NULL != t->destroy_task)