keepalive PINGs
authorChristian Grothoff <christian@grothoff.org>
Wed, 10 Mar 2010 10:55:01 +0000 (10:55 +0000)
committerChristian Grothoff <christian@grothoff.org>
Wed, 10 Mar 2010 10:55:01 +0000 (10:55 +0000)
TODO
src/core/gnunet-service-core.c
src/include/gnunet_time_lib.h
src/util/time.c

diff --git a/TODO b/TODO
index b2f6166b5ac4e3c26c1100aa26f7a1650c10b584..bd58eaeec12db1aba9c9ab10fb9871076d281999 100644 (file)
--- a/TODO
+++ b/TODO
@@ -13,7 +13,6 @@ away), in order in which they will likely be done:
 * ARM [Safey]
 
 Urgent items (before announcing ng.gnunet.org):
-* core fails to do keepalive on idle connections => disconnect!
 * new webpage
   - run peer => have a 0.9.x hostlist
 => Deploy(able) development network
index f63ccec887a4351ccdf65273574c9ea78c728607..b13ea023bb2c59f294de15e7d0c6587c28adb085 100644 (file)
  */
 #define MAX_PONG_DELAY GNUNET_TIME_relative_multiply (MAX_PING_DELAY, 2)
 
+/**
+ * What is the minimum frequency for a PING message?
+ */
+#define MIN_PING_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
+
 /**
  * How often do we recalculate bandwidth quotas?
  */
@@ -476,6 +481,11 @@ struct Neighbour
    */
   GNUNET_SCHEDULER_TaskIdentifier quota_update_task;
 
+  /**
+   * ID of task used for sending keep-alive pings.
+   */
+  GNUNET_SCHEDULER_TaskIdentifier keep_alive_task;
+
   /**
    * ID of task used for cleaning up dead neighbour entries.
    */
@@ -1074,6 +1084,8 @@ free_neighbour (struct Neighbour *n)
     GNUNET_SCHEDULER_cancel (sched, n->quota_update_task);
   if (n->dead_clean_task != GNUNET_SCHEDULER_NO_TASK)
     GNUNET_SCHEDULER_cancel (sched, n->dead_clean_task);
+  if (n->keep_alive_task != GNUNET_SCHEDULER_NO_TASK)
+    GNUNET_SCHEDULER_cancel (sched, n->keep_alive_task);
   GNUNET_free_non_null (n->public_key);
   GNUNET_free_non_null (n->pending_ping);
   GNUNET_free_non_null (n->pending_pong);
@@ -1081,6 +1093,53 @@ free_neighbour (struct Neighbour *n)
 }
 
 
+/**
+ * Check if we have encrypted messages for the specified neighbour
+ * pending, and if so, check with the transport about sending them
+ * out.
+ *
+ * @param n neighbour to check.
+ */
+static void process_encrypted_neighbour_queue (struct Neighbour *n);
+
+
+/**
+ * Encrypt size bytes from in and write the result to out.  Use the
+ * key for outbound traffic of the given neighbour.
+ *
+ * @param n neighbour we are sending to
+ * @param iv initialization vector to use
+ * @param in ciphertext
+ * @param out plaintext
+ * @param size size of in/out
+ * @return GNUNET_OK on success
+ */
+static int
+do_encrypt (struct Neighbour *n,
+            const GNUNET_HashCode * iv,
+            const void *in, void *out, size_t size)
+{
+  if (size != (uint16_t) size)
+    {
+      GNUNET_break (0);
+      return GNUNET_NO;
+    }
+  GNUNET_assert (size ==
+                 GNUNET_CRYPTO_aes_encrypt (in,
+                                            (uint16_t) size,
+                                            &n->encrypt_key,
+                                            (const struct
+                                             GNUNET_CRYPTO_AesInitializationVector
+                                             *) iv, out));
+#if DEBUG_CORE
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Encrypted %u bytes for `%4s' using key %u\n", size,
+              GNUNET_i2s (&n->peer), n->encrypt_key.crc32);
+#endif
+  return GNUNET_OK;
+}
+
+
 /**
  * Consider freeing the given neighbour since we may not need
  * to keep it around anymore.
@@ -1091,6 +1150,68 @@ static void
 consider_free_neighbour (struct Neighbour *n);
 
 
+/**
+ * Task triggered when a neighbour entry is about to time out 
+ * (and we should prevent this by sending a PING).
+ *
+ * @param cls the 'struct Neighbour'
+ * @param tc scheduler context (not used)
+ */
+static void
+send_keep_alive (void *cls,
+                const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct Neighbour *n = cls;
+  struct GNUNET_TIME_Relative retry;
+  struct GNUNET_TIME_Relative left;
+  struct MessageEntry *me;
+  struct PingMessage pp;
+  struct PingMessage *pm;
+
+  n->keep_alive_task = GNUNET_SCHEDULER_NO_TASK;
+  /* send PING */
+  me = GNUNET_malloc (sizeof (struct MessageEntry) +
+                      sizeof (struct PingMessage));
+  me->deadline = GNUNET_TIME_relative_to_absolute (MAX_PING_DELAY);
+  me->priority = PING_PRIORITY;
+  me->size = sizeof (struct PingMessage);
+  n->encrypted_tail->next = me;
+  n->encrypted_tail = me;
+  pm = (struct PingMessage *) &me[1];
+  pm->header.size = htons (sizeof (struct PingMessage));
+  pm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_PING);
+  pp.challenge = htonl (n->ping_challenge);
+  pp.target = n->peer;
+#if DEBUG_CORE
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Encrypting `%s' and `%s' messages for `%4s'.\n",
+              "SET_KEY", "PING", GNUNET_i2s (&n->peer));
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Sending `%s' to `%4s' with challenge %u encrypted using key %u\n",
+              "PING",
+              GNUNET_i2s (&n->peer), n->ping_challenge, n->encrypt_key.crc32);
+#endif
+  do_encrypt (n,
+              &n->peer.hashPubKey,
+              &pp.challenge,
+              &pm->challenge,
+              sizeof (struct PingMessage) -
+              sizeof (struct GNUNET_MessageHeader));
+  process_encrypted_neighbour_queue (n);
+  /* reschedule PING job */
+  left = GNUNET_TIME_absolute_get_remaining (GNUNET_TIME_absolute_add (n->last_activity,
+                                                                      GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
+  retry = GNUNET_TIME_relative_max (GNUNET_TIME_relative_divide (left, 2),
+                                   MIN_PING_FREQUENCY);
+  n->keep_alive_task 
+    = GNUNET_SCHEDULER_add_delayed (sched, 
+                                   GNUNET_TIME_relative_divide (left, 2),
+                                   &send_keep_alive,
+                                   n);
+
+}
+
+
 /**
  * Task triggered when a neighbour entry might have gotten stale.
  *
@@ -1102,6 +1223,7 @@ consider_free_task (void *cls,
                    const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct Neighbour *n = cls;
+
   n->dead_clean_task = GNUNET_SCHEDULER_NO_TASK;
   consider_free_neighbour (n);
 }
@@ -1127,7 +1249,7 @@ consider_free_neighbour (struct Neighbour *n)
     return; /* no chance */
   
   left = GNUNET_TIME_absolute_get_remaining (GNUNET_TIME_absolute_add (n->last_activity,
-                                                                      MAX_PONG_DELAY));
+                                                                      GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT));
   if (left.value > 0)
     {
       if (n->dead_clean_task != GNUNET_SCHEDULER_NO_TASK)
@@ -1156,16 +1278,6 @@ consider_free_neighbour (struct Neighbour *n)
 }
 
 
-/**
- * Check if we have encrypted messages for the specified neighbour
- * pending, and if so, check with the transport about sending them
- * out.
- *
- * @param n neighbour to check.
- */
-static void process_encrypted_neighbour_queue (struct Neighbour *n);
-
-
 /**
  * Function called when the transport service is ready to
  * receive an encrypted message for the respective peer
@@ -1329,43 +1441,6 @@ do_decrypt (struct Neighbour *n,
 }
 
 
-/**
- * Encrypt size bytes from in and write the result to out.  Use the
- * key for outbound traffic of the given neighbour.
- *
- * @param n neighbour we are sending to
- * @param iv initialization vector to use
- * @param in ciphertext
- * @param out plaintext
- * @param size size of in/out
- * @return GNUNET_OK on success
- */
-static int
-do_encrypt (struct Neighbour *n,
-            const GNUNET_HashCode * iv,
-            const void *in, void *out, size_t size)
-{
-  if (size != (uint16_t) size)
-    {
-      GNUNET_break (0);
-      return GNUNET_NO;
-    }
-  GNUNET_assert (size ==
-                 GNUNET_CRYPTO_aes_encrypt (in,
-                                            (uint16_t) size,
-                                            &n->encrypt_key,
-                                            (const struct
-                                             GNUNET_CRYPTO_AesInitializationVector
-                                             *) iv, out));
-#if DEBUG_CORE
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Encrypted %u bytes for `%4s' using key %u\n", size,
-              GNUNET_i2s (&n->peer), n->encrypt_key.crc32);
-#endif
-  return GNUNET_OK;
-}
-
-
 /**
  * Select messages for transmission.  This heuristic uses a combination
  * of earliest deadline first (EDF) scheduling (with bounded horizon)
@@ -2645,9 +2720,16 @@ handle_pong (struct Neighbour *n,
       cnm.peer = n->peer;
       send_to_all_clients (&cnm.header, GNUNET_YES, GNUNET_CORE_OPTION_SEND_CONNECT);
       process_encrypted_neighbour_queue (n);
-      break;
+      /* fall-through! */
     case PEER_STATE_KEY_CONFIRMED:
-      /* duplicate PONG? */
+      n->last_activity = GNUNET_TIME_absolute_get ();
+      if (n->keep_alive_task != GNUNET_SCHEDULER_NO_TASK)
+       GNUNET_SCHEDULER_cancel (sched, n->keep_alive_task);
+      n->keep_alive_task 
+       = GNUNET_SCHEDULER_add_delayed (sched, 
+                                       GNUNET_TIME_relative_divide (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, 2),
+                                       &send_keep_alive,
+                                       n);
       break;
     default:
       GNUNET_break (0);
@@ -2711,8 +2793,9 @@ handle_set_key (struct Neighbour *n, const struct SetKeyMessage *m)
                   sizeof (struct GNUNET_PeerIdentity)))
     {
       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                 _("Received `%s' message that was not for me.  Ignoring.\n"),
-                 "SET_KEY");
+                 _("Received `%s' message that was for `%s', not for me.  Ignoring.\n"),
+                 "SET_KEY",
+                 GNUNET_i2s (&m->target));
       return;
     }
   if ((ntohl (m->purpose.size) !=
@@ -3106,6 +3189,13 @@ handle_encrypted_message (struct Neighbour *n,
                                  NULL, NULL); 
     }
   n->last_activity = GNUNET_TIME_absolute_get ();
+  if (n->keep_alive_task != GNUNET_SCHEDULER_NO_TASK)
+    GNUNET_SCHEDULER_cancel (sched, n->keep_alive_task);
+  n->keep_alive_task 
+    = GNUNET_SCHEDULER_add_delayed (sched, 
+                                   GNUNET_TIME_relative_divide (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, 2),
+                                   &send_keep_alive,
+                                   n);
   off = sizeof (struct EncryptedMessage);
   deliver_messages (n, buf, size, off);
 }
@@ -3233,6 +3323,13 @@ handle_transport_receive (void *cls,
       n->last_activity = now;
       if (!up)
         n->time_established = now;
+      if (n->keep_alive_task != GNUNET_SCHEDULER_NO_TASK)
+       GNUNET_SCHEDULER_cancel (sched, n->keep_alive_task);
+      n->keep_alive_task 
+       = GNUNET_SCHEDULER_add_delayed (sched, 
+                                       GNUNET_TIME_relative_divide (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, 2),
+                                       &send_keep_alive,
+                                       n);
     }
 }
 
index 8336a04fd17879a8cc4c7a3bd07a312495459c88..fa443a4632cff80a6914f0fd7c1a987928e5a8d4 100644 (file)
@@ -204,6 +204,20 @@ struct GNUNET_TIME_Relative GNUNET_TIME_relative_min (struct
                                                      struct
                                                      GNUNET_TIME_Relative t2);
 
+
+/**
+ * Return the maximum of two relative time values.
+ *
+ * @param t1 first timestamp
+ * @param t2 other timestamp
+ * @return timestamp that is larger
+ */
+struct GNUNET_TIME_Relative GNUNET_TIME_relative_max (struct
+                                                     GNUNET_TIME_Relative
+                                                     t1,
+                                                     struct
+                                                     GNUNET_TIME_Relative t2);
+
 /**
  * Return the minimum of two absolute time values.
  *
@@ -313,6 +327,19 @@ struct GNUNET_TIME_Relative GNUNET_TIME_relative_multiply (struct
                                                            unsigned int
                                                            factor);
 
+/**
+ * Divide relative time by a given factor.
+ *
+ * @param rel some duration
+ * @param factor integer to divide by
+ * @return FOREVER if rel=FOREVER or factor==0; otherwise rel/factor
+ */
+struct GNUNET_TIME_Relative GNUNET_TIME_relative_divide (struct
+                                                        GNUNET_TIME_Relative
+                                                        rel,
+                                                        unsigned int
+                                                        factor);
+
 /**
  * Add relative times together.
  *
index 36a3c8631df2dc1a7bbf1083f7bb4b7991be9a9d..ce2f9517f7ba362d8d6641b689574ef0cdf0dfd0 100644 (file)
@@ -137,6 +137,22 @@ GNUNET_TIME_relative_min (struct
 }
 
 
+/**
+ * Return the maximum of two relative time values.
+ *
+ * @param t1 first timestamp
+ * @param t2 other timestamp
+ * @return timestamp that is larger
+ */
+struct GNUNET_TIME_Relative
+GNUNET_TIME_relative_max (struct
+                          GNUNET_TIME_Relative
+                          t1, struct GNUNET_TIME_Relative t2)
+{
+  return (t1.value > t2.value) ? t1 : t2;
+}
+
+
 
 /**
  * Return the minimum of two relative time values.
@@ -276,6 +292,26 @@ GNUNET_TIME_relative_multiply (struct GNUNET_TIME_Relative rel,
 }
 
 
+/**
+ * Divide relative time by a given factor.
+ *
+ * @param rel some duration
+ * @param factor integer to divide by
+ * @return FOREVER if rel=FOREVER or factor==0; otherwise rel/factor
+ */
+struct GNUNET_TIME_Relative
+GNUNET_TIME_relative_divide (struct GNUNET_TIME_Relative rel,
+                            unsigned int factor)
+{
+  struct GNUNET_TIME_Relative ret;
+  if ( (factor == 0) ||
+       (rel.value == GNUNET_TIME_UNIT_FOREVER_REL.value) )
+    return GNUNET_TIME_UNIT_FOREVER_REL;
+  ret.value = rel.value / (unsigned long long) factor;
+  return ret;
+}
+
+
 /**
  * Calculate the estimate time of arrival/completion 
  * for an operation.