more stats
[oweals/gnunet.git] / src / core / gnunet-service-core.c
index f05715471b9a6db7ff1f65ecd7aaee7feea5959e..8bf6f0798914d92a6ab7a2b5abd7028ef514f344 100644 (file)
 #include "gnunet_peerinfo_service.h"
 #include "gnunet_protocols.h"
 #include "gnunet_signatures.h"
+#include "gnunet_statistics_service.h"
 #include "gnunet_transport_service.h"
 #include "core.h"
 
 
 #define DEBUG_HANDSHAKE GNUNET_NO
 
-#define DEBUG_CORE_QUOTA GNUNET_YES
+#define DEBUG_CORE_QUOTA GNUNET_NO
 
 /**
  * Receive and send buffer windows grow over time.  For
  */
 #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 +482,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.
    */
@@ -679,6 +690,11 @@ static struct GNUNET_SERVER_NotificationContext *notifier;
  */
 static struct Neighbour *neighbours;
 
+/**
+ * For creating statistics.
+ */
+static struct GNUNET_STATISTICS_Handle *stats;
+
 /**
  * Sum of all preferences among all neighbours.
  */
@@ -727,6 +743,7 @@ update_preference_sum (unsigned long long inc)
       preference_sum += n->current_preference;
       n = n->next;
     }    
+  GNUNET_STATISTICS_set (stats, gettext_noop ("# total peer preference"), preference_sum, GNUNET_NO);
 }
 
 
@@ -795,7 +812,14 @@ send_to_all_clients (const struct GNUNET_MessageHeader *msg,
   while (c != NULL)
     {
       if (0 != (c->options & options))
-       send_to_client (c, msg, can_drop);
+       {
+#if DEBUG_CORE_CLIENT
+         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                     "Sending message of type %u to client.\n",
+                     ntohs (msg->type));
+#endif
+         send_to_client (c, msg, can_drop);
+       }
       c = c->next;
     }
 }
@@ -875,24 +899,27 @@ handle_client_init (void *cls,
               "Sending `%s' message to client.\n", "INIT_REPLY");
 #endif
   send_to_client (c, &irm.header, GNUNET_NO);
-  /* notify new client about existing neighbours */
-  cnm.header.size = htons (sizeof (struct ConnectNotifyMessage));
-  cnm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT);
-  n = neighbours;
-  while (n != NULL)
+  if (0 != (c->options & GNUNET_CORE_OPTION_SEND_CONNECT))
     {
-      if (n->status == PEER_STATE_KEY_CONFIRMED)
+      /* notify new client about existing neighbours */
+      cnm.header.size = htons (sizeof (struct ConnectNotifyMessage));
+      cnm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT);
+      n = neighbours;
+      while (n != NULL)
        {
+         if (n->status == PEER_STATE_KEY_CONFIRMED)
+           {
 #if DEBUG_CORE_CLIENT
-         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                     "Sending `%s' message to client.\n", "NOTIFY_CONNECT");
+             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                         "Sending `%s' message to client.\n", "NOTIFY_CONNECT");
 #endif
-         cnm.distance = htonl (n->last_distance);
-         cnm.latency = GNUNET_TIME_relative_hton (n->last_latency);
-         cnm.peer = n->peer;
-         send_to_client (c, &cnm.header, GNUNET_NO);
+             cnm.distance = htonl (n->last_distance);
+             cnm.latency = GNUNET_TIME_relative_hton (n->last_latency);
+             cnm.peer = n->peer;
+             send_to_client (c, &cnm.header, GNUNET_NO);
+           }
+         n = n->next;
        }
-      n = n->next;
     }
   GNUNET_SERVER_receive_done (client, GNUNET_OK);
 }
@@ -1074,6 +1101,10 @@ 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);
+  if (n->status == PEER_STATE_KEY_CONFIRMED)
+    GNUNET_STATISTICS_update (stats, gettext_noop ("# peers connected"), -1, GNUNET_NO);
   GNUNET_free_non_null (n->public_key);
   GNUNET_free_non_null (n->pending_ping);
   GNUNET_free_non_null (n->pending_pong);
@@ -1081,6 +1112,54 @@ 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));
+  GNUNET_STATISTICS_update (stats, gettext_noop ("# bytes encrypted"), size, GNUNET_NO);
+#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 +1170,70 @@ 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);
+  GNUNET_CONTAINER_DLL_insert_after (n->encrypted_head,
+                                    n->encrypted_tail,
+                                    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, 
+                                   retry,
+                                   &send_keep_alive,
+                                   n);
+
+}
+
+
 /**
  * Task triggered when a neighbour entry might have gotten stale.
  *
@@ -1102,6 +1245,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 +1271,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)
@@ -1152,20 +1296,11 @@ consider_free_neighbour (struct Neighbour *n)
     prev->next = n->next;
   GNUNET_assert (neighbour_count > 0);
   neighbour_count--;
+  GNUNET_STATISTICS_set (stats, gettext_noop ("# active neighbours"), neighbour_count, GNUNET_NO);
   free_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
@@ -1320,6 +1455,7 @@ do_decrypt (struct Neighbour *n,
       GNUNET_break (0);
       return GNUNET_SYSERR;
     }
+  GNUNET_STATISTICS_update (stats, gettext_noop ("# bytes decrypted"), size, GNUNET_NO);
 #if DEBUG_CORE
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Decrypted %u bytes from `%4s' using key %u\n",
@@ -1329,43 +1465,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)
@@ -1593,9 +1692,11 @@ batch_message (struct Neighbour *n,
   *retry_time = GNUNET_TIME_UNIT_FOREVER_REL;
   if (0 == select_messages (n, size, retry_time))
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+#if DEBUG_CORE
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                   "No messages selected, will try again in %llu ms\n",
                   retry_time->value);
+#endif
       return 0;
     }
   ntm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_OUTBOUND);
@@ -1750,9 +1851,11 @@ set_key_retry_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct Neighbour *n = cls;
 
+#if DEBUG_CORE
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Retrying key transmission to `%4s'\n",
              GNUNET_i2s (&n->peer));
+#endif
   n->retry_set_key_task = GNUNET_SCHEDULER_NO_TASK;
   n->set_key_retry_frequency =
     GNUNET_TIME_relative_multiply (n->set_key_retry_frequency, 2);
@@ -1959,6 +2062,7 @@ create_neighbour (const struct GNUNET_PeerIdentity *pid)
   n->next = neighbours;
   neighbours = n;
   neighbour_count++;
+  GNUNET_STATISTICS_set (stats, gettext_noop ("# active neighbours"), neighbour_count, GNUNET_NO);
   n->peer = *pid;
   GNUNET_CRYPTO_aes_create_session_key (&n->encrypt_key);
   now = GNUNET_TIME_absolute_get ();
@@ -2159,6 +2263,7 @@ handle_client_request_connect (void *cls,
   if ( (n->is_connected) ||
        (n->th != NULL) )
     return; /* already connected, or at least trying */
+  GNUNET_STATISTICS_update (stats, gettext_noop ("# connection requests received"), 1, GNUNET_NO);
 #if DEBUG_CORE
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Core received `%s' request for `%4s', will try to establish connection\n",
@@ -2223,10 +2328,18 @@ process_hello_retry_send_key (void *cls,
       n->pitr = NULL;
       if (n->public_key != NULL)
        {
+         GNUNET_STATISTICS_update (stats,
+                                   gettext_noop ("# SETKEY messages deferred (need public key)"), 
+                                   -1, 
+                                   GNUNET_NO);
          send_key (n);
        }
       else
        {
+         GNUNET_STATISTICS_update (stats,
+                                   gettext_noop ("# Delayed connecting due to lack of public key"),
+                                   1,
+                                   GNUNET_NO);      
          if (GNUNET_SCHEDULER_NO_TASK == n->retry_set_key_task)
            n->retry_set_key_task
              = GNUNET_SCHEDULER_add_delayed (sched,
@@ -2261,6 +2374,10 @@ process_hello_retry_send_key (void *cls,
     GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
   if (GNUNET_OK != GNUNET_HELLO_get_key (hello, n->public_key))
     {
+      GNUNET_STATISTICS_update (stats,
+                               gettext_noop ("# Error extracting public key from HELLO"),
+                               1,
+                               GNUNET_NO);      
       GNUNET_free (n->public_key);
       n->public_key = NULL;
 #if DEBUG_CORE
@@ -2357,8 +2474,10 @@ send_key (struct Neighbour *n)
   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;
+  GNUNET_CONTAINER_DLL_insert_after (n->encrypted_head,
+                                    n->encrypted_tail,
+                                    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);
@@ -2507,6 +2626,7 @@ handle_ping (struct Neighbour *n, const struct PingMessage *m)
               "Target of `%s' request is `%4s'.\n",
               "PING", GNUNET_i2s (&t.target));
 #endif
+  GNUNET_STATISTICS_update (stats, gettext_noop ("# ping messages decrypted"), 1, GNUNET_NO);
   if (0 != memcmp (&t.target,
                    &my_identity, sizeof (struct GNUNET_PeerIdentity)))
     {
@@ -2628,7 +2748,7 @@ handle_pong (struct Neighbour *n,
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                   "Confirmed key via `%s' message for peer `%4s'\n",
                   "PONG", GNUNET_i2s (&n->peer));
-#endif
+#endif      
       if (n->retry_set_key_task != GNUNET_SCHEDULER_NO_TASK)
         {
           GNUNET_SCHEDULER_cancel (sched, n->retry_set_key_task);
@@ -2641,9 +2761,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);
@@ -2700,6 +2827,7 @@ handle_set_key (struct Neighbour *n, const struct SetKeyMessage *m)
                                         0,
                                         GNUNET_TIME_UNIT_MINUTES,
                                         &process_hello_retry_handle_set_key, n);
+      GNUNET_STATISTICS_update (stats, gettext_noop ("# SETKEY messages deferred (need public key)"), 1, GNUNET_NO);
       return;
     }
   if (0 != memcmp (&m->target,
@@ -2707,8 +2835,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) !=
@@ -2736,7 +2865,8 @@ handle_set_key (struct Neighbour *n, const struct SetKeyMessage *m)
       return;
     }
 #if DEBUG_CORE
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Decrypting key material.\n");
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
+             "Decrypting key material.\n");
 #endif  
   if ((GNUNET_CRYPTO_rsa_decrypt (my_private_key,
                                   &m->encrypted_key,
@@ -3082,10 +3212,12 @@ handle_encrypted_message (struct Neighbour *n,
   /* process decrypted message(s) */
   if (n->bw_out_external_limit.value__ != pt->inbound_bw_limit.value__)
     {
+#if DEBUG_CORE_SET_QUOTA
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                  "Received %u b/s as new inbound limit for peer `%4s'\n",
                  (unsigned int) ntohl (pt->inbound_bw_limit.value__),
                  GNUNET_i2s (&n->peer));
+#endif
       n->bw_out_external_limit = pt->inbound_bw_limit;
       n->bw_out = GNUNET_BANDWIDTH_value_min (n->bw_out_external_limit,
                                              n->bw_out_internal_limit);
@@ -3099,6 +3231,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);
 }
@@ -3156,6 +3295,7 @@ handle_transport_receive (void *cls,
           GNUNET_break_op (0);
           return;
         }
+      GNUNET_STATISTICS_update (stats, gettext_noop ("# session keys received"), 1, GNUNET_NO);
       handle_set_key (n, (const struct SetKeyMessage *) message);
       break;
     case GNUNET_MESSAGE_TYPE_CORE_ENCRYPTED_MESSAGE:
@@ -3169,6 +3309,13 @@ handle_transport_receive (void *cls,
           (n->status != PEER_STATE_KEY_CONFIRMED))
         {
           GNUNET_break_op (0);
+         /* blacklist briefly (?); might help recover (?) */
+         GNUNET_TRANSPORT_blacklist (sched, cfg,
+                                     &n->peer, 
+                                     GNUNET_TIME_UNIT_SECONDS,
+                                     GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
+                                                                    5),
+                                     NULL, NULL);
           return;
         }
       handle_encrypted_message (n, (const struct EncryptedMessage *) message);
@@ -3179,6 +3326,7 @@ handle_transport_receive (void *cls,
           GNUNET_break_op (0);
           return;
         }
+      GNUNET_STATISTICS_update (stats, gettext_noop ("# ping messages received"), 1, GNUNET_NO);
       if ((n->status != PEER_STATE_KEY_RECEIVED) &&
           (n->status != PEER_STATE_KEY_CONFIRMED))
         {
@@ -3200,6 +3348,7 @@ handle_transport_receive (void *cls,
           GNUNET_break_op (0);
           return;
         }
+      GNUNET_STATISTICS_update (stats, gettext_noop ("# pong messages received"), 1, GNUNET_NO);
       if ( (n->status != PEER_STATE_KEY_RECEIVED) &&
           (n->status != PEER_STATE_KEY_CONFIRMED) )
         {
@@ -3225,7 +3374,17 @@ handle_transport_receive (void *cls,
       now = GNUNET_TIME_absolute_get ();
       n->last_activity = now;
       if (!up)
-        n->time_established = now;
+       {
+         GNUNET_STATISTICS_update (stats, gettext_noop ("# peers connected"), 1, GNUNET_NO);
+         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);
     }
 }
 
@@ -3324,7 +3483,6 @@ handle_transport_notify_connect (void *cls,
                                 unsigned int distance)
 {
   struct Neighbour *n;
-  struct GNUNET_TIME_Absolute now;
   struct ConnectNotifyMessage cnm;
 
   n = find_neighbour (peer);
@@ -3341,7 +3499,6 @@ handle_transport_notify_connect (void *cls,
     {
       n = create_neighbour (peer);
     }
-  now = GNUNET_TIME_absolute_get ();
   n->is_connected = GNUNET_YES;      
   n->last_latency = latency;
   n->last_distance = distance;
@@ -3429,12 +3586,15 @@ cleaning_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
       neighbour_count--;
       free_neighbour (n);
     }
+  GNUNET_STATISTICS_set (stats, gettext_noop ("# active neighbours"), neighbour_count, GNUNET_NO);
   GNUNET_SERVER_notification_context_destroy (notifier);
   notifier = NULL;
   while (NULL != (c = clients))
     handle_client_disconnect (NULL, c->client_handle);
   if (my_private_key != NULL)
     GNUNET_CRYPTO_rsa_key_free (my_private_key);
+  if (stats != NULL)
+    GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
 }
 
 
@@ -3504,6 +3664,7 @@ run (void *cls,
                                         &handle_transport_notify_connect,
                                         &handle_transport_notify_disconnect);
   GNUNET_assert (NULL != transport);
+  stats = GNUNET_STATISTICS_create (sched, "core", cfg);
   GNUNET_SCHEDULER_add_delayed (sched,
                                 GNUNET_TIME_UNIT_FOREVER_REL,
                                 &cleaning_task, NULL);