-improve UDP logging
[oweals/gnunet.git] / src / core / gnunet-service-core_kx.c
index 79155a660bb4b1a5932ec7306e6ad5e5db9430fc..fc242b171dc6862385771011542ea13efd271654 100644 (file)
@@ -1,6 +1,6 @@
 /*
      This file is part of GNUnet.
-     (C) 2009-2013 Christian Grothoff (and other contributing authors)
+     Copyright (C) 2009-2013 Christian Grothoff (and other contributing authors)
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
@@ -82,12 +82,12 @@ struct EphemeralKeyMessage
   struct GNUNET_MessageHeader header;
 
   /**
-   * Status of the sender (should be in "enum PeerStateMachine"), nbo.
+   * Status of the sender (should be in `enum PeerStateMachine`), nbo.
    */
   int32_t sender_status GNUNET_PACKED;
 
   /**
-   * An ECC signature of the 'origin' asserting the validity of
+   * An ECC signature of the @e origin_identity asserting the validity of
    * the given ephemeral key.
    */
   struct GNUNET_CRYPTO_EddsaSignature signature;
@@ -230,7 +230,7 @@ GNUNET_NETWORK_STRUCT_END
 
 
 /**
- * Number of bytes (at the beginning) of "struct EncryptedMessage"
+ * Number of bytes (at the beginning) of `struct EncryptedMessage`
  * that are NOT encrypted.
  */
 #define ENCRYPTED_HEADER_SIZE (offsetof(struct EncryptedMessage, sequence_number))
@@ -289,6 +289,11 @@ struct GSC_KeyExchangeInfo
    */
   struct GNUNET_TIME_Absolute timeout;
 
+  /**
+   * What was the last timeout we informed our monitors about?
+   */
+  struct GNUNET_TIME_Absolute last_notify_timeout;
+
   /**
    * At what frequency are we currently re-trying SET_KEY messages?
    */
@@ -297,12 +302,12 @@ struct GSC_KeyExchangeInfo
   /**
    * ID of task used for re-trying SET_KEY and PING message.
    */
-  GNUNET_SCHEDULER_TaskIdentifier retry_set_key_task;
+  struct GNUNET_SCHEDULER_Task * retry_set_key_task;
 
   /**
    * ID of task used for sending keep-alive pings.
    */
-  GNUNET_SCHEDULER_TaskIdentifier keep_alive_task;
+  struct GNUNET_SCHEDULER_Task * keep_alive_task;
 
   /**
    * Bit map indicating which of the 32 sequence numbers before the last
@@ -368,7 +373,7 @@ static struct GSC_KeyExchangeInfo *kx_tail;
  * Task scheduled for periodic re-generation (and thus rekeying) of our
  * ephemeral key.
  */
-static GNUNET_SCHEDULER_TaskIdentifier rekey_task;
+static struct GNUNET_SCHEDULER_Task * rekey_task;
 
 /**
  * Notification context for all monitors.
@@ -380,7 +385,7 @@ static struct GNUNET_SERVER_NotificationContext *nc;
  * Inform the given monitor about the KX state of
  * the given peer.
  *
- * @param mc monitor to inform
+ * @param client client to inform
  * @param kx key exchange state to inform about
  */
 static void
@@ -401,6 +406,22 @@ monitor_notify (struct GNUNET_SERVER_Client *client,
 }
 
 
+/**
+ * Calculate seed value we should use for a message.
+ *
+ * @param kx key exchange context
+ */
+static uint32_t
+calculate_seed (struct GSC_KeyExchangeInfo *kx)
+{
+  /* Note: may want to make this non-random and instead
+     derive from key material to avoid having an undetectable
+     side-channel */
+  return htonl (GNUNET_CRYPTO_random_u32
+               (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX));
+}
+
+
 /**
  * Inform all monitors about the KX state of the given peer.
  *
@@ -419,6 +440,7 @@ monitor_notify_all (struct GSC_KeyExchangeInfo *kx)
   GNUNET_SERVER_notification_context_broadcast (nc,
                                                 &msg.header,
                                                 GNUNET_NO);
+  kx->last_notify_timeout = kx->timeout;
 }
 
 
@@ -431,7 +453,8 @@ monitor_notify_all (struct GSC_KeyExchangeInfo *kx)
  */
 static void
 derive_auth_key (struct GNUNET_CRYPTO_AuthKey *akey,
-                 const struct GNUNET_CRYPTO_SymmetricSessionKey *skey, uint32_t seed)
+                 const struct GNUNET_CRYPTO_SymmetricSessionKey *skey,
+                 uint32_t seed)
 {
   static const char ctx[] = "authentication key";
 
@@ -628,7 +651,7 @@ set_key_retry_task (void *cls,
 {
   struct GSC_KeyExchangeInfo *kx = cls;
 
-  kx->retry_set_key_task = GNUNET_SCHEDULER_NO_TASK;
+  kx->retry_set_key_task = NULL;
   kx->set_key_retry_frequency = GNUNET_TIME_STD_BACKOFF (kx->set_key_retry_frequency);
   GNUNET_assert (GNUNET_CORE_KX_STATE_DOWN != kx->status);
   send_key (kx);
@@ -650,8 +673,7 @@ setup_fresh_ping (struct GSC_KeyExchangeInfo *kx)
   pm = &kx->ping;
   pm->header.size = htons (sizeof (struct PingMessage));
   pm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_PING);
-  pm->iv_seed =
-      GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX);
+  pm->iv_seed = calculate_seed (kx);
   derive_iv (&iv, &kx->encrypt_key, pm->iv_seed, &kx->peer);
   pp.challenge = kx->ping_challenge;
   pp.target = kx->peer;
@@ -724,15 +746,15 @@ GSC_KX_stop (struct GSC_KeyExchangeInfo *kx)
   GSC_SESSIONS_end (&kx->peer);
   GNUNET_STATISTICS_update (GSC_stats, gettext_noop ("# key exchanges stopped"),
                             1, GNUNET_NO);
-  if (kx->retry_set_key_task != GNUNET_SCHEDULER_NO_TASK)
+  if (kx->retry_set_key_task != NULL)
   {
     GNUNET_SCHEDULER_cancel (kx->retry_set_key_task);
-    kx->retry_set_key_task = GNUNET_SCHEDULER_NO_TASK;
+    kx->retry_set_key_task = NULL;
   }
-  if (kx->keep_alive_task != GNUNET_SCHEDULER_NO_TASK)
+  if (kx->keep_alive_task != NULL)
   {
     GNUNET_SCHEDULER_cancel (kx->keep_alive_task);
-    kx->keep_alive_task = GNUNET_SCHEDULER_NO_TASK;
+    kx->keep_alive_task = NULL;
   }
   kx->status = GNUNET_CORE_KX_PEER_DISCONNECT;
   monitor_notify_all (kx);
@@ -751,7 +773,8 @@ GSC_KX_stop (struct GSC_KeyExchangeInfo *kx)
 static void
 send_ping (struct GSC_KeyExchangeInfo *kx)
 {
-  GSC_NEIGHBOURS_transmit (&kx->peer, &kx->ping.header,
+  GSC_NEIGHBOURS_transmit (&kx->peer,
+                           &kx->ping.header,
                            MIN_PING_FREQUENCY);
 }
 
@@ -821,13 +844,15 @@ GSC_KX_handle_ephemeral_key (struct GSC_KeyExchangeInfo *kx,
         (GNUNET_CORE_KX_STATE_REKEY_SENT == kx->status) ) &&
        (end_t.abs_value_us <= kx->foreign_key_expires.abs_value_us) )
   {
-    GNUNET_STATISTICS_update (GSC_stats, gettext_noop ("# old ephemeral keys ignored"),
+    GNUNET_STATISTICS_update (GSC_stats,
+                              gettext_noop ("# old ephemeral keys ignored"),
                              1, GNUNET_NO);
     return;
   }
   start_t = GNUNET_TIME_absolute_ntoh (m->creation_time);
 
-  GNUNET_STATISTICS_update (GSC_stats, gettext_noop ("# ephemeral keys received"),
+  GNUNET_STATISTICS_update (GSC_stats,
+                            gettext_noop ("# ephemeral keys received"),
                             1, GNUNET_NO);
 
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -884,11 +909,17 @@ GSC_KX_handle_ephemeral_key (struct GSC_KeyExchangeInfo *kx,
     break;
   case GNUNET_CORE_KX_STATE_KEY_SENT:
     /* fine, need to send our key after updating our status, see below */
+    GSC_SESSIONS_reinit (&kx->peer);
     break;
   case GNUNET_CORE_KX_STATE_KEY_RECEIVED:
+    /* other peer already got our key, but typemap did go down */
+    GSC_SESSIONS_reinit (&kx->peer);
+    break;
   case GNUNET_CORE_KX_STATE_UP:
+    /* other peer already got our key, typemap NOT down */
+    break;
   case GNUNET_CORE_KX_STATE_REKEY_SENT:
-    /* other peer already got our key */
+    /* other peer already got our key, typemap NOT down */
     break;
   default:
     GNUNET_break (0);
@@ -898,7 +929,7 @@ GSC_KX_handle_ephemeral_key (struct GSC_KeyExchangeInfo *kx,
   switch (kx->status)
   {
   case GNUNET_CORE_KX_STATE_DOWN:
-    GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == kx->keep_alive_task);
+    GNUNET_assert (NULL == kx->keep_alive_task);
     kx->status = GNUNET_CORE_KX_STATE_KEY_RECEIVED;
     monitor_notify_all (kx);
     if (GNUNET_CORE_KX_STATE_KEY_SENT == sender_status)
@@ -906,7 +937,7 @@ GSC_KX_handle_ephemeral_key (struct GSC_KeyExchangeInfo *kx,
     send_ping (kx);
     break;
   case GNUNET_CORE_KX_STATE_KEY_SENT:
-    GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == kx->keep_alive_task);
+    GNUNET_assert (NULL == kx->keep_alive_task);
     kx->status = GNUNET_CORE_KX_STATE_KEY_RECEIVED;
     monitor_notify_all (kx);
     if (GNUNET_CORE_KX_STATE_KEY_SENT == sender_status)
@@ -914,7 +945,7 @@ GSC_KX_handle_ephemeral_key (struct GSC_KeyExchangeInfo *kx,
     send_ping (kx);
     break;
   case GNUNET_CORE_KX_STATE_KEY_RECEIVED:
-    GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == kx->keep_alive_task);
+    GNUNET_assert (NULL == kx->keep_alive_task);
     if (GNUNET_CORE_KX_STATE_KEY_SENT == sender_status)
       send_key (kx);
     send_ping (kx);
@@ -1011,15 +1042,15 @@ GSC_KX_handle_ping (struct GSC_KeyExchangeInfo *kx,
   tx.target = t.target;
   tp.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_PONG);
   tp.header.size = htons (sizeof (struct PongMessage));
-  tp.iv_seed =
-      GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX);
+  tp.iv_seed = calculate_seed (kx);
   derive_pong_iv (&iv, &kx->encrypt_key, tp.iv_seed, t.challenge, &kx->peer);
   do_encrypt (kx, &iv, &tx.challenge, &tp.challenge,
               sizeof (struct PongMessage) - ((void *) &tp.challenge -
                                              (void *) &tp));
   GNUNET_STATISTICS_update (GSC_stats, gettext_noop ("# PONG messages created"),
                             1, GNUNET_NO);
-  GSC_NEIGHBOURS_transmit (&kx->peer, &tp.header,
+  GSC_NEIGHBOURS_transmit (&kx->peer,
+                           &tp.header,
                            GNUNET_TIME_UNIT_FOREVER_REL /* FIXME: timeout */ );
 }
 
@@ -1038,7 +1069,7 @@ send_keep_alive (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   struct GNUNET_TIME_Relative retry;
   struct GNUNET_TIME_Relative left;
 
-  kx->keep_alive_task = GNUNET_SCHEDULER_NO_TASK;
+  kx->keep_alive_task = NULL;
   left = GNUNET_TIME_absolute_get_remaining (kx->timeout);
   if (0 == left.rel_value_us)
   {
@@ -1057,7 +1088,8 @@ send_keep_alive (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
                             gettext_noop ("# keepalive messages sent"), 1,
                             GNUNET_NO);
   setup_fresh_ping (kx);
-  GSC_NEIGHBOURS_transmit (&kx->peer, &kx->ping.header,
+  GSC_NEIGHBOURS_transmit (&kx->peer,
+                           &kx->ping.header,
                            kx->set_key_retry_frequency);
   retry =
       GNUNET_TIME_relative_max (GNUNET_TIME_relative_divide (left, 2),
@@ -1077,11 +1109,20 @@ send_keep_alive (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 static void
 update_timeout (struct GSC_KeyExchangeInfo *kx)
 {
+  struct GNUNET_TIME_Relative delta;
+
   kx->timeout =
       GNUNET_TIME_relative_to_absolute
       (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
-  monitor_notify_all (kx);
-  if (kx->keep_alive_task != GNUNET_SCHEDULER_NO_TASK)
+  delta = GNUNET_TIME_absolute_get_difference (kx->last_notify_timeout,
+                                               kx->timeout);
+  if (delta.rel_value_us > 5LL * 1000LL * 1000LL)
+  {
+    /* we only notify monitors about timeout changes if those
+       are bigger than the threshold (5s) */
+    monitor_notify_all (kx);
+  }
+  if (kx->keep_alive_task != NULL)
     GNUNET_SCHEDULER_cancel (kx->keep_alive_task);
   kx->keep_alive_task =
       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_divide
@@ -1168,13 +1209,14 @@ GSC_KX_handle_pong (struct GSC_KeyExchangeInfo *kx,
                 "PONG", GNUNET_i2s (&t.target), (unsigned int) t.challenge);
     return;
   }
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received PONG from `%s'\n",
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Received PONG from `%s'\n",
               GNUNET_i2s (&kx->peer));
   /* no need to resend key any longer */
-  if (GNUNET_SCHEDULER_NO_TASK != kx->retry_set_key_task)
+  if (NULL != kx->retry_set_key_task)
   {
     GNUNET_SCHEDULER_cancel (kx->retry_set_key_task);
-    kx->retry_set_key_task = GNUNET_SCHEDULER_NO_TASK;
+    kx->retry_set_key_task = NULL;
   }
   switch (kx->status)
   {
@@ -1192,7 +1234,7 @@ GSC_KX_handle_pong (struct GSC_KeyExchangeInfo *kx,
     kx->status = GNUNET_CORE_KX_STATE_UP;
     monitor_notify_all (kx);
     GSC_SESSIONS_create (&kx->peer, kx);
-    GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == kx->keep_alive_task);
+    GNUNET_assert (NULL == kx->keep_alive_task);
     update_timeout (kx);
     break;
   case GNUNET_CORE_KX_STATE_UP:
@@ -1227,10 +1269,10 @@ static void
 send_key (struct GSC_KeyExchangeInfo *kx)
 {
   GNUNET_assert (GNUNET_CORE_KX_STATE_DOWN != kx->status);
-  if (GNUNET_SCHEDULER_NO_TASK != kx->retry_set_key_task)
+  if (NULL != kx->retry_set_key_task)
   {
      GNUNET_SCHEDULER_cancel (kx->retry_set_key_task);
-     kx->retry_set_key_task = GNUNET_SCHEDULER_NO_TASK;
+     kx->retry_set_key_task = NULL;
   }
   /* always update sender status in SET KEY message */
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -1238,7 +1280,8 @@ send_key (struct GSC_KeyExchangeInfo *kx)
               GNUNET_i2s (&kx->peer),
              kx->status);
   current_ekm.sender_status = htonl ((int32_t) (kx->status));
-  GSC_NEIGHBOURS_transmit (&kx->peer, &current_ekm.header,
+  GSC_NEIGHBOURS_transmit (&kx->peer,
+                           &current_ekm.header,
                            kx->set_key_retry_frequency);
   kx->retry_set_key_task =
       GNUNET_SCHEDULER_add_delayed (kx->set_key_retry_frequency,
@@ -1251,11 +1294,12 @@ send_key (struct GSC_KeyExchangeInfo *kx)
  *
  * @param kx key exchange context
  * @param payload payload of the message
- * @param payload_size number of bytes in 'payload'
+ * @param payload_size number of bytes in @a payload
  */
 void
 GSC_KX_encrypt_and_transmit (struct GSC_KeyExchangeInfo *kx,
-                             const void *payload, size_t payload_size)
+                             const void *payload,
+                             size_t payload_size)
 {
   size_t used = payload_size + sizeof (struct EncryptedMessage);
   char pbuf[used];              /* plaintext */
@@ -1266,10 +1310,8 @@ GSC_KX_encrypt_and_transmit (struct GSC_KeyExchangeInfo *kx,
   struct GNUNET_CRYPTO_AuthKey auth_key;
 
   ph = (struct EncryptedMessage *) pbuf;
-  ph->iv_seed =
-      htonl (GNUNET_CRYPTO_random_u32
-             (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX));
   ph->sequence_number = htonl (++kx->last_sequence_number_sent);
+  ph->iv_seed = calculate_seed (kx);
   ph->reserved = 0;
   ph->timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
   memcpy (&ph[1], payload, payload_size);
@@ -1290,7 +1332,8 @@ GSC_KX_encrypt_and_transmit (struct GSC_KeyExchangeInfo *kx,
                   ph->iv_seed);
   GNUNET_CRYPTO_hmac (&auth_key, &em->sequence_number,
                       used - ENCRYPTED_HEADER_SIZE, &em->hmac);
-  GSC_NEIGHBOURS_transmit (&kx->peer, &em->header,
+  GSC_NEIGHBOURS_transmit (&kx->peer,
+                           &em->header,
                            GNUNET_TIME_UNIT_FOREVER_REL);
 }
 
@@ -1359,10 +1402,10 @@ GSC_KX_handle_encrypted_message (struct GSC_KeyExchangeInfo *kx,
                               gettext_noop ("# sessions terminated by key expiration"),
                               1, GNUNET_NO);
     GSC_SESSIONS_end (&kx->peer);
-    if (GNUNET_SCHEDULER_NO_TASK != kx->keep_alive_task)
+    if (NULL != kx->keep_alive_task)
     {
       GNUNET_SCHEDULER_cancel (kx->keep_alive_task);
-      kx->keep_alive_task = GNUNET_SCHEDULER_NO_TASK;
+      kx->keep_alive_task = NULL;
     }
     kx->status = GNUNET_CORE_KX_STATE_KEY_SENT;
     monitor_notify_all (kx);
@@ -1509,6 +1552,9 @@ deliver_message (void *cls,
   case GNUNET_MESSAGE_TYPE_CORE_COMPRESSED_TYPE_MAP:
     GSC_SESSIONS_set_typemap (dmc->peer, m);
     return GNUNET_OK;
+  case GNUNET_MESSAGE_TYPE_CORE_CONFIRM_TYPE_MAP:
+    GSC_SESSIONS_confirm_typemap (dmc->peer, m);
+    return GNUNET_OK;
   default:
     GSC_CLIENTS_deliver_message (dmc->peer, m,
                                  ntohs (m->size),
@@ -1638,10 +1684,10 @@ GSC_KX_init (struct GNUNET_CRYPTO_EddsaPrivateKey *pk,
 void
 GSC_KX_done ()
 {
-  if (GNUNET_SCHEDULER_NO_TASK != rekey_task)
+  if (NULL != rekey_task)
   {
     GNUNET_SCHEDULER_cancel (rekey_task);
-    rekey_task = GNUNET_SCHEDULER_NO_TASK;
+    rekey_task = NULL;
   }
   if (NULL != my_ephemeral_key)
   {