-
/*
This file is part of GNUnet.
Copyright (C) 2013, 2017 GNUnet e.V.
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
-
/**
* @file cadet/gnunet-service-cadet-new_tunnels.c
* @brief Information we track per tunnel.
* @author Christian Grothoff
*
* FIXME:
+ * - check KX estate machine -- make sure it is never stuck!
+ * - clean up KX logic, including adding sender authentication
+ * - implement connection management (evaluate, kill old ones,
+ * search for new ones)
* - when managing connections, distinguish those that
* have (recently) had traffic from those that were
* never ready (or not recently)
- * - clean up KX logic!
*/
#include "platform.h"
#include "gnunet_util_lib.h"
+#include "gnunet_statistics_service.h"
#include "gnunet_signatures.h"
-#include "cadet_protocol.h"
-#include "cadet_path.h"
#include "gnunet-service-cadet-new.h"
+#include "cadet_protocol.h"
#include "gnunet-service-cadet-new_channel.h"
#include "gnunet-service-cadet-new_connection.h"
#include "gnunet-service-cadet-new_tunnels.h"
};
-/**
- * Entry in list of connections used by tunnel, with metadata.
- */
-struct CadetTConnection
-{
- /**
- * Next in DLL.
- */
- struct CadetTConnection *next;
-
- /**
- * Prev in DLL.
- */
- struct CadetTConnection *prev;
-
- /**
- * Connection handle.
- */
- struct CadetConnection *cc;
-
- /**
- * Tunnel this connection belongs to.
- */
- struct CadetTunnel *t;
-
- /**
- * Creation time, to keep oldest connection alive.
- */
- struct GNUNET_TIME_Absolute created;
-
- /**
- * Connection throughput, to keep fastest connection alive.
- */
- uint32_t throughput;
-};
-
-
/**
* Struct used to save messages in a non-ready tunnel to send once connected.
*/
struct CadetTunnelAxolotl ax;
/**
- * State of the tunnel connectivity.
+ * Task scheduled if there are no more channels using the tunnel.
*/
- enum CadetTunnelCState cstate;
+ struct GNUNET_SCHEDULER_Task *destroy_task;
/**
- * State of the tunnel encryption.
+ * Task to trim connections if too many are present.
*/
- enum CadetTunnelEState estate;
+ struct GNUNET_SCHEDULER_Task *maintain_connections_task;
+
+ /**
+ * Task to trigger KX.
+ */
+ struct GNUNET_SCHEDULER_Task *kx_task;
+
+ /**
+ * Tokenizer for decrypted messages.
+ */
+ struct GNUNET_MessageStreamTokenizer *mst;
/**
- * Task to start the rekey process.
+ * Dispatcher for decrypted messages only (do NOT use for sending!).
*/
- struct GNUNET_SCHEDULER_Task *rekey_task;
+ struct GNUNET_MQ_Handle *mq;
/**
* DLL of connections that are actively used to reach the destination peer.
/**
* Channels inside this tunnel. Maps
- * `struct GCT_ChannelTunnelNumber` to a `struct CadetChannel`.
+ * `struct GNUNET_CADET_ChannelTunnelNumber` to a `struct CadetChannel`.
*/
struct GNUNET_CONTAINER_MultiHashMap32 *channels;
/**
* Channel ID for the next created channel in this tunnel.
*/
- struct GCT_ChannelTunnelNumber next_chid;
+ struct GNUNET_CADET_ChannelTunnelNumber next_ctn;
/**
* Queued messages, to transmit once tunnel gets connected.
*/
struct CadetTunnelQueueEntry *tq_tail;
+
/**
- * Task scheduled if there are no more channels using the tunnel.
+ * Ephemeral message in the queue (to avoid queueing more than one).
*/
- struct GNUNET_SCHEDULER_Task *destroy_task;
+ struct CadetConnectionQueue *ephm_hKILL;
/**
- * Task to trim connections if too many are present.
+ * Pong message in the queue.
*/
- struct GNUNET_SCHEDULER_Task *maintain_connections_task;
+ struct CadetConnectionQueue *pong_hKILL;
/**
- * Ephemeral message in the queue (to avoid queueing more than one).
+ * How long do we wait until we retry the KX?
*/
- struct CadetConnectionQueue *ephm_hKILL;
+ struct GNUNET_TIME_Relative kx_retry_delay;
/**
- * Pong message in the queue.
+ * When do we try the next KX?
*/
- struct CadetConnectionQueue *pong_hKILL;
+ struct GNUNET_TIME_Absolute next_kx_attempt;
/**
* Number of connections in the @e connection_head DLL.
* Number of entries in the @e tq_head DLL.
*/
unsigned int tq_len;
+
+ /**
+ * State of the tunnel encryption.
+ */
+ enum CadetTunnelEState estate;
+
};
}
+/**
+ * Get string description for tunnel encryption state.
+ *
+ * @param es Tunnel state.
+ *
+ * @return String representation.
+ */
+static const char *
+estate2s (enum CadetTunnelEState es)
+{
+ static char buf[32];
+
+ switch (es)
+ {
+ case CADET_TUNNEL_KEY_UNINITIALIZED:
+ return "CADET_TUNNEL_KEY_UNINITIALIZED";
+ case CADET_TUNNEL_KEY_SENT:
+ return "CADET_TUNNEL_KEY_SENT";
+ case CADET_TUNNEL_KEY_PING:
+ return "CADET_TUNNEL_KEY_PING";
+ case CADET_TUNNEL_KEY_OK:
+ return "CADET_TUNNEL_KEY_OK";
+ case CADET_TUNNEL_KEY_REKEY:
+ return "CADET_TUNNEL_KEY_REKEY";
+ default:
+ SPRINTF (buf, "%u (UNKNOWN STATE)", es);
+ return buf;
+ }
+}
+
+
/**
* Return the peer to which this tunnel goes.
*
}
+/**
+ * Lookup a channel by its @a ctn.
+ *
+ * @param t tunnel to look in
+ * @param ctn number of channel to find
+ * @return NULL if channel does not exist
+ */
+struct CadetChannel *
+lookup_channel (struct CadetTunnel *t,
+ struct GNUNET_CADET_ChannelTunnelNumber ctn)
+{
+ return GNUNET_CONTAINER_multihashmap32_get (t->channels,
+ ntohl (ctn.cn));
+}
+
+
/**
* Count all created connections of a tunnel. Not necessarily ready connections!
*
/**
- * Get the connectivity state of a tunnel.
- *
- * @param t Tunnel.
+ * Find first connection that is ready in the list of
+ * our connections. Picks ready connections round-robin.
*
- * @return Tunnel's connectivity state.
+ * @param t tunnel to search
+ * @return NULL if we have no connection that is ready
*/
-enum CadetTunnelCState
-GCT_get_cstate (struct CadetTunnel *t)
+static struct CadetTConnection *
+get_ready_connection (struct CadetTunnel *t)
{
- return t->cstate;
+ for (struct CadetTConnection *pos = t->connection_head;
+ NULL != pos;
+ pos = pos->next)
+ if (GNUNET_YES == pos->is_ready)
+ {
+ if (pos != t->connection_tail)
+ {
+ /* move 'pos' to the end, so we try other ready connections
+ first next time (round-robin, modulo availability) */
+ GNUNET_CONTAINER_DLL_remove (t->connection_head,
+ t->connection_tail,
+ pos);
+ GNUNET_CONTAINER_DLL_insert_tail (t->connection_head,
+ t->connection_tail,
+ pos);
+ }
+ return pos;
+ }
+ return NULL;
}
}
+
+/**
+ * Called when either we have a new connection, or a new message in the
+ * queue, or some existing connection has transmission capacity. Looks
+ * at our message queue and if there is a message, picks a connection
+ * to send it on.
+ *
+ * @param t tunnel to process messages on
+ */
+static void
+trigger_transmissions (struct CadetTunnel *t);
+
+
/* ************************************** start core crypto ***************************** */
size_t size,
uint32_t iv,
const struct GNUNET_CRYPTO_SymmetricSessionKey *key,
- struct GNUNET_CADET_Hash *hmac)
+ struct GNUNET_ShortHashCode *hmac)
{
static const char ctx[] = "cadet authentication key";
struct GNUNET_CRYPTO_AuthKey auth_key;
*/
static void
t_h_encrypt (struct CadetTunnel *t,
- struct GNUNET_CADET_Encrypted *msg)
+ struct GNUNET_CADET_TunnelEncryptedMessage *msg)
{
struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
struct CadetTunnelAxolotl *ax;
*/
static void
t_h_decrypt (struct CadetTunnel *t,
- const struct GNUNET_CADET_Encrypted *src,
- struct GNUNET_CADET_Encrypted *dst)
+ const struct GNUNET_CADET_TunnelEncryptedMessage *src,
+ struct GNUNET_CADET_TunnelEncryptedMessage *dst)
{
struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
struct CadetTunnelAxolotl *ax;
static ssize_t
try_old_ax_keys (struct CadetTunnel *t,
void *dst,
- const struct GNUNET_CADET_Encrypted *src,
+ const struct GNUNET_CADET_TunnelEncryptedMessage *src,
size_t size)
{
struct CadetTunnelSkippedKey *key;
- struct GNUNET_CADET_Hash *hmac;
+ struct GNUNET_ShortHashCode *hmac;
struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
- struct GNUNET_CADET_Encrypted plaintext_header;
+ struct GNUNET_CADET_TunnelEncryptedMessage plaintext_header;
struct GNUNET_CRYPTO_SymmetricSessionKey *valid_HK;
size_t esize;
size_t res;
LOG (GNUNET_ERROR_TYPE_DEBUG,
"Trying skipped keys\n");
hmac = &plaintext_header.hmac;
- esize = size - sizeof (struct GNUNET_CADET_Encrypted);
+ esize = size - sizeof (struct GNUNET_CADET_TunnelEncryptedMessage);
/* Find a correct Header Key */
valid_HK = NULL;
return -1;
/* Should've been checked in -cadet_connection.c handle_cadet_encrypted. */
- GNUNET_assert (size > sizeof (struct GNUNET_CADET_Encrypted));
- len = size - sizeof (struct GNUNET_CADET_Encrypted);
+ GNUNET_assert (size > sizeof (struct GNUNET_CADET_TunnelEncryptedMessage));
+ len = size - sizeof (struct GNUNET_CADET_TunnelEncryptedMessage);
GNUNET_assert (len >= sizeof (struct GNUNET_MessageHeader));
/* Decrypt header */
static ssize_t
t_ax_decrypt_and_validate (struct CadetTunnel *t,
void *dst,
- const struct GNUNET_CADET_Encrypted *src,
+ const struct GNUNET_CADET_TunnelEncryptedMessage *src,
size_t size)
{
struct CadetTunnelAxolotl *ax;
- struct GNUNET_CADET_Hash msg_hmac;
+ struct GNUNET_ShortHashCode msg_hmac;
struct GNUNET_HashCode hmac;
- struct GNUNET_CADET_Encrypted plaintext_header;
+ struct GNUNET_CADET_TunnelEncryptedMessage plaintext_header;
uint32_t Np;
uint32_t PNp;
size_t esize; /* Size of encryped payload */
- esize = size - sizeof (struct GNUNET_CADET_Encrypted);
+ esize = size - sizeof (struct GNUNET_CADET_TunnelEncryptedMessage);
ax = &t->ax;
/* Try current HK */
}
+/**
+ * Change the tunnel encryption state.
+ * If the encryption state changes to OK, stop the rekey task.
+ *
+ * @param t Tunnel whose encryption state to change, or NULL.
+ * @param state New encryption state.
+ */
+void
+GCT_change_estate (struct CadetTunnel *t,
+ enum CadetTunnelEState state)
+{
+ enum CadetTunnelEState old = t->estate;
+
+ t->estate = state;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Tunnel %s estate changed from %d to %d\n",
+ GCT_2s (t),
+ old,
+ state);
+
+ if ( (CADET_TUNNEL_KEY_OK != old) &&
+ (CADET_TUNNEL_KEY_OK == t->estate) )
+ {
+ if (NULL != t->kx_task)
+ {
+ GNUNET_SCHEDULER_cancel (t->kx_task);
+ t->kx_task = NULL;
+ }
+ /* FIXME: schedule rekey task! */
+ }
+}
+
+
+/**
+ * Send a KX message.
+ *
+ * FIXME: does not take care of sender-authentication yet!
+ *
+ * @param t Tunnel on which to send it.
+ * @param force_reply Force the other peer to reply with a KX message.
+ */
+static void
+send_kx (struct CadetTunnel *t,
+ int force_reply)
+{
+ struct CadetTunnelAxolotl *ax = &t->ax;
+ struct CadetTConnection *ct;
+ struct CadetConnection *cc;
+ struct GNUNET_MQ_Envelope *env;
+ struct GNUNET_CADET_TunnelKeyExchangeMessage *msg;
+ enum GNUNET_CADET_KX_Flags flags;
+
+ ct = get_ready_connection (t);
+ if (NULL == ct)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Wanted to send KX on tunnel %s, but no connection is ready, deferring\n",
+ GCT_2s (t));
+ return;
+ }
+ cc = ct->cc;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Sending KX on tunnel %s using connection %s\n",
+ GCT_2s (t),
+ GCC_2s (ct->cc));
+
+ // GNUNET_assert (GNUNET_NO == GCT_is_loopback (t));
+ env = GNUNET_MQ_msg (msg,
+ GNUNET_MESSAGE_TYPE_CADET_TUNNEL_KX);
+ flags = GNUNET_CADET_KX_FLAG_NONE;
+ if (GNUNET_YES == force_reply)
+ flags |= GNUNET_CADET_KX_FLAG_FORCE_REPLY;
+ msg->flags = htonl (flags);
+ msg->cid = *GCC_get_id (cc);
+ GNUNET_CRYPTO_ecdhe_key_get_public (ax->kx_0,
+ &msg->ephemeral_key);
+ GNUNET_CRYPTO_ecdhe_key_get_public (ax->DHRs,
+ &msg->ratchet_key);
+ GCC_transmit (cc,
+ env);
+ t->kx_retry_delay = GNUNET_TIME_STD_BACKOFF (t->kx_retry_delay);
+ t->next_kx_attempt = GNUNET_TIME_relative_to_absolute (t->kx_retry_delay);
+ if (CADET_TUNNEL_KEY_UNINITIALIZED == t->estate)
+ GCT_change_estate (t,
+ CADET_TUNNEL_KEY_SENT);
+}
+
+
+/**
+ * Handle KX message.
+ *
+ * FIXME: sender-authentication in KX is missing!
+ *
+ * @param ct connection/tunnel combo that received encrypted message
+ * @param msg the key exchange message
+ */
+void
+GCT_handle_kx (struct CadetTConnection *ct,
+ const struct GNUNET_CADET_TunnelKeyExchangeMessage *msg)
+{
+ struct CadetTunnel *t = ct->t;
+ struct CadetTunnelAxolotl *ax = &t->ax;
+ struct GNUNET_HashCode key_material[3];
+ struct GNUNET_CRYPTO_SymmetricSessionKey keys[5];
+ const char salt[] = "CADET Axolotl salt";
+ const struct GNUNET_PeerIdentity *pid;
+ int am_I_alice;
+
+ pid = GCP_get_id (t->destination);
+ if (0 > GNUNET_CRYPTO_cmp_peer_identity (&my_full_id,
+ pid))
+ am_I_alice = GNUNET_YES;
+ else if (0 < GNUNET_CRYPTO_cmp_peer_identity (&my_full_id,
+ pid))
+ am_I_alice = GNUNET_NO;
+ else
+ {
+ GNUNET_break_op (0);
+ return;
+ }
+
+ if (0 != (GNUNET_CADET_KX_FLAG_FORCE_REPLY & ntohl (msg->flags)))
+ {
+ if (NULL != t->kx_task)
+ {
+ GNUNET_SCHEDULER_cancel (t->kx_task);
+ t->kx_task = NULL;
+ }
+ send_kx (t,
+ GNUNET_NO);
+ }
+
+ if (0 == memcmp (&ax->DHRr,
+ &msg->ratchet_key,
+ sizeof (msg->ratchet_key)))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ " known ratchet key, exit\n");
+ return;
+ }
+
+ ax->DHRr = msg->ratchet_key;
+
+ /* ECDH A B0 */
+ if (GNUNET_YES == am_I_alice)
+ {
+ GNUNET_CRYPTO_eddsa_ecdh (my_private_key, /* A */
+ &msg->ephemeral_key, /* B0 */
+ &key_material[0]);
+ }
+ else
+ {
+ GNUNET_CRYPTO_ecdh_eddsa (ax->kx_0, /* B0 */
+ &pid->public_key, /* A */
+ &key_material[0]);
+ }
+
+ /* ECDH A0 B */
+ if (GNUNET_YES == am_I_alice)
+ {
+ GNUNET_CRYPTO_ecdh_eddsa (ax->kx_0, /* A0 */
+ &pid->public_key, /* B */
+ &key_material[1]);
+ }
+ else
+ {
+ GNUNET_CRYPTO_eddsa_ecdh (my_private_key, /* A */
+ &msg->ephemeral_key, /* B0 */
+ &key_material[1]);
+
+
+ }
+
+ /* ECDH A0 B0 */
+ /* (This is the triple-DH, we could probably safely skip this,
+ as A0/B0 are already in the key material.) */
+ GNUNET_CRYPTO_ecc_ecdh (ax->kx_0, /* A0 or B0 */
+ &msg->ephemeral_key, /* B0 or A0 */
+ &key_material[2]);
+
+ /* KDF */
+ GNUNET_CRYPTO_kdf (keys, sizeof (keys),
+ salt, sizeof (salt),
+ &key_material, sizeof (key_material),
+ NULL);
+
+ if (0 == memcmp (&ax->RK,
+ &keys[0],
+ sizeof (ax->RK)))
+ {
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ " known handshake key, exit\n");
+ return;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Handling KX message for tunnel %s\n",
+ GCT_2s (t));
+
+ ax->RK = keys[0];
+ if (GNUNET_YES == am_I_alice)
+ {
+ ax->HKr = keys[1];
+ ax->NHKs = keys[2];
+ ax->NHKr = keys[3];
+ ax->CKr = keys[4];
+ ax->ratchet_flag = GNUNET_YES;
+ }
+ else
+ {
+ ax->HKs = keys[1];
+ ax->NHKr = keys[2];
+ ax->NHKs = keys[3];
+ ax->CKs = keys[4];
+ ax->ratchet_flag = GNUNET_NO;
+ ax->ratchet_allowed = GNUNET_NO;
+ ax->ratchet_counter = 0;
+ ax->ratchet_expiration
+ = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get(),
+ ratchet_time);
+ }
+ ax->PNs = 0;
+ ax->Nr = 0;
+ ax->Ns = 0;
+
+ switch (t->estate)
+ {
+ case CADET_TUNNEL_KEY_UNINITIALIZED:
+ GCT_change_estate (t,
+ CADET_TUNNEL_KEY_PING);
+ break;
+ case CADET_TUNNEL_KEY_SENT:
+ /* Got a response to us sending our key; now
+ we can start transmitting! */
+ GCT_change_estate (t,
+ CADET_TUNNEL_KEY_OK);
+ trigger_transmissions (t);
+ break;
+ case CADET_TUNNEL_KEY_PING:
+ /* Got a key yet again; need encrypted payload to advance */
+ break;
+ case CADET_TUNNEL_KEY_OK:
+ /* Did not expect a key, but so what. */
+ break;
+ case CADET_TUNNEL_KEY_REKEY:
+ /* Got a key yet again; need encrypted payload to advance */
+ break;
+ }
+}
+
+
/* ************************************** end core crypto ***************************** */
+/**
+ * Compute the next free channel tunnel number for this tunnel.
+ *
+ * @param t the tunnel
+ * @return unused number that can uniquely identify a channel in the tunnel
+ */
+static struct GNUNET_CADET_ChannelTunnelNumber
+get_next_free_ctn (struct CadetTunnel *t)
+{
+ struct GNUNET_CADET_ChannelTunnelNumber ret;
+ uint32_t ctn;
+
+ /* FIXME: this logic does NOT prevent both ends of the
+ channel from picking the same CTN!
+ Need to reserve one bit of the CTN for the
+ direction, i.e. which side established the connection! */
+ ctn = ntohl (t->next_ctn.cn);
+ while (NULL !=
+ GNUNET_CONTAINER_multihashmap32_get (t->channels,
+ ctn))
+ ctn++;
+ t->next_ctn.cn = htonl (ctn + 1);
+ ret.cn = ntohl (ctn);
+ return ret;
+}
+
+
/**
* Add a channel to a tunnel.
*
* @param ch Channel
* @return unique number identifying @a ch within @a t
*/
-struct GCT_ChannelTunnelNumber
+struct GNUNET_CADET_ChannelTunnelNumber
GCT_add_channel (struct CadetTunnel *t,
struct CadetChannel *ch)
{
- struct GCT_ChannelTunnelNumber ret;
- uint32_t chid;
+ struct GNUNET_CADET_ChannelTunnelNumber ctn;
- chid = ntohl (t->next_chid.channel_in_tunnel);
- while (NULL !=
- GNUNET_CONTAINER_multihashmap32_get (t->channels,
- chid))
- chid++;
+ ctn = get_next_free_ctn (t);
GNUNET_assert (GNUNET_YES ==
GNUNET_CONTAINER_multihashmap32_put (t->channels,
- chid,
+ ntohl (ctn.cn),
ch,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
- t->next_chid.channel_in_tunnel = htonl (chid + 1);
- ret.channel_in_tunnel = htonl (chid);
- return ret;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Adding channel %s to tunnel %s\n",
+ GCCH_2s (ch),
+ GCT_2s (t));
+ return ctn;
}
struct CadetTunnelQueueEntry *tqe;
t->destroy_task = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Destroying idle tunnel %s\n",
+ GCT_2s (t));
GNUNET_assert (0 == GNUNET_CONTAINER_multihashmap32_size (t->channels));
while (NULL != (ct = t->connection_head))
{
GNUNET_SCHEDULER_cancel (t->maintain_connections_task);
t->maintain_connections_task = NULL;
}
+ GNUNET_MST_destroy (t->mst);
+ GNUNET_MQ_destroy (t->mq);
GNUNET_free (t);
}
/**
- * A connection is ready for transmission. Looks at our message queue
- * and if there is a message, sends it out via the connection.
+ * Remove a channel from a tunnel.
*
- * @param cls the `struct CadetTConnection` that is ready
+ * @param t Tunnel.
+ * @param ch Channel
+ * @param ctn unique number identifying @a ch within @a t
+ */
+void
+GCT_remove_channel (struct CadetTunnel *t,
+ struct CadetChannel *ch,
+ struct GNUNET_CADET_ChannelTunnelNumber ctn)
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Removing channel %s from tunnel %s\n",
+ GCCH_2s (ch),
+ GCT_2s (t));
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multihashmap32_remove (t->channels,
+ ntohl (ctn.cn),
+ ch));
+ if (0 ==
+ GNUNET_CONTAINER_multihashmap32_size (t->channels))
+ {
+ t->destroy_task = GNUNET_SCHEDULER_add_delayed (IDLE_DESTROY_DELAY,
+ &destroy_tunnel,
+ t);
+ }
+}
+
+
+/**
+ * Destroys the tunnel @a t now, without delay. Used during shutdown.
+ *
+ * @param t tunnel to destroy
+ */
+void
+GCT_destroy_tunnel_now (struct CadetTunnel *t)
+{
+ GNUNET_assert (0 ==
+ GNUNET_CONTAINER_multihashmap32_size (t->channels));
+ GNUNET_SCHEDULER_cancel (t->destroy_task);
+ destroy_tunnel (t);
+}
+
+
+/**
+ * It's been a while, we should try to redo the KX, if we can.
+ *
+ * @param cls the `struct CadetTunnel` to do KX for.
*/
static void
-connection_ready_cb (void *cls)
+retry_kx (void *cls)
{
- struct CadetTConnection *ct = cls;
- struct CadetTunnel *t = ct->t;
- struct CadetTunnelQueueEntry *tq = t->tq_head;
+ struct CadetTunnel *t = cls;
- if (NULL == tq)
- return; /* no messages pending right now */
+ t->kx_task = NULL;
+ send_kx (t,
+ ( (CADET_TUNNEL_KEY_UNINITIALIZED == t->estate) ||
+ (CADET_TUNNEL_KEY_SENT == t->estate) )
+ ? GNUNET_YES
+ : GNUNET_NO);
+}
+
+
+/**
+ * Send normal payload from queue in @a t via connection @a ct.
+ * Does nothing if our payload queue is empty.
+ *
+ * @param t tunnel to send data from
+ * @param ct connection to use for transmission (is ready)
+ */
+static void
+try_send_normal_payload (struct CadetTunnel *t,
+ struct CadetTConnection *ct)
+{
+ struct CadetTunnelQueueEntry *tq;
+ GNUNET_assert (GNUNET_YES == ct->is_ready);
+ tq = t->tq_head;
+ if (NULL == tq)
+ {
+ /* no messages pending right now */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Not sending payload of tunnel %s on ready connection %s (nothing pending)\n",
+ GCT_2s (t),
+ GCC_2s (ct->cc));
+ return;
+ }
/* ready to send message 'tq' on tunnel 'ct' */
GNUNET_assert (t == tq->t);
GNUNET_CONTAINER_DLL_remove (t->tq_head,
tq);
if (NULL != tq->cid)
*tq->cid = *GCC_get_id (ct->cc);
+ ct->is_ready = GNUNET_NO;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Sending payload of tunnel %s on connection %s\n",
+ GCT_2s (t),
+ GCC_2s (ct->cc));
GCC_transmit (ct->cc,
tq->env);
- tq->cont (tq->cont_cls);
+ if (NULL != tq->cont)
+ tq->cont (tq->cont_cls);
GNUNET_free (tq);
}
+/**
+ * A connection is @a is_ready for transmission. Looks at our message
+ * queue and if there is a message, sends it out via the connection.
+ *
+ * @param cls the `struct CadetTConnection` that is @a is_ready
+ * @param is_ready #GNUNET_YES if connection are now ready,
+ * #GNUNET_NO if connection are no longer ready
+ */
+static void
+connection_ready_cb (void *cls,
+ int is_ready)
+{
+ struct CadetTConnection *ct = cls;
+ struct CadetTunnel *t = ct->t;
+
+ if (GNUNET_NO == is_ready)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Connection %s no longer ready for tunnel %s\n",
+ GCC_2s (ct->cc),
+ GCT_2s (t));
+ ct->is_ready = GNUNET_NO;
+ return;
+ }
+ ct->is_ready = GNUNET_YES;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Connection %s now ready for tunnel %s in state %s\n",
+ GCC_2s (ct->cc),
+ GCT_2s (t),
+ estate2s (t->estate));
+ switch (t->estate)
+ {
+ case CADET_TUNNEL_KEY_UNINITIALIZED:
+ send_kx (t,
+ GNUNET_YES);
+ break;
+ case CADET_TUNNEL_KEY_SENT:
+ case CADET_TUNNEL_KEY_PING:
+ /* opportunity to #retry_kx() starts now, schedule job */
+ if (NULL == t->kx_task)
+ {
+ t->kx_task
+ = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining (t->next_kx_attempt),
+ &retry_kx,
+ t);
+ }
+ break;
+ case CADET_TUNNEL_KEY_OK:
+ try_send_normal_payload (t,
+ ct);
+ break;
+ case CADET_TUNNEL_KEY_REKEY:
+ send_kx (t,
+ GNUNET_NO);
+ t->estate = CADET_TUNNEL_KEY_OK;
+ break;
+ }
+}
+
+
/**
* Called when either we have a new connection, or a new message in the
* queue, or some existing connection has transmission capacity. Looks
if (NULL == t->tq_head)
return; /* no messages pending right now */
- for (ct = t->connection_head;
- NULL != ct;
- ct = ct->next)
- if (GNUNET_YES == GCC_is_ready (ct->cc))
- break;
+ ct = get_ready_connection (t);
if (NULL == ct)
return; /* no connections ready */
- connection_ready_cb (ct);
+ try_send_normal_payload (t,
+ ct);
}
{
struct CadetTunnel *t = cls;
+ t->maintain_connections_task = NULL;
GNUNET_break (0); // FIXME: implement!
}
path,
ct,
&connection_ready_cb,
- t);
+ ct);
/* FIXME: schedule job to kill connection (and path?) if it takes
too long to get ready! (And track performance data on how long
other connections took with the tunnel!)
t->connection_tail,
ct);
t->num_connections++;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Found interesting path %s for tunnel %s, created connection %s\n",
+ GCPP_2s (path),
+ GCT_2s (t),
+ GCC_2s (ct->cc));
return GNUNET_YES;
}
}
+/**
+ * NOT IMPLEMENTED.
+ *
+ * @param cls the `struct CadetTunnel` for which we decrypted the message
+ * @param msg the message we received on the tunnel
+ */
+static void
+handle_plaintext_keepalive (void *cls,
+ const struct GNUNET_MessageHeader *msg)
+{
+ struct CadetTunnel *t = cls;
+
+ GNUNET_break (0); // FIXME
+}
+
+
+/**
+ * Check that @a msg is well-formed.
+ *
+ * @param cls the `struct CadetTunnel` for which we decrypted the message
+ * @param msg the message we received on the tunnel
+ * @return #GNUNET_OK (any variable-size payload goes)
+ */
+static int
+check_plaintext_data (void *cls,
+ const struct GNUNET_CADET_ChannelAppDataMessage *msg)
+{
+ return GNUNET_OK;
+}
+
+
+/**
+ * We received payload data for a channel. Locate the channel
+ * and process the data, or return an error if the channel is unknown.
+ *
+ * @param cls the `struct CadetTunnel` for which we decrypted the message
+ * @param msg the message we received on the tunnel
+ */
+static void
+handle_plaintext_data (void *cls,
+ const struct GNUNET_CADET_ChannelAppDataMessage *msg)
+{
+ struct CadetTunnel *t = cls;
+ struct CadetChannel *ch;
+
+ ch = lookup_channel (t,
+ msg->ctn);
+ if (NULL == ch)
+ {
+ /* We don't know about such a channel, might have been destroyed on our
+ end in the meantime, or never existed. Send back a DESTROY. */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Receicved %u bytes of application data for unknown channel %u, sending DESTROY\n",
+ (unsigned int) (ntohs (msg->header.size) - sizeof (*msg)),
+ ntohl (msg->ctn.cn));
+ GCT_send_channel_destroy (t,
+ msg->ctn);
+ return;
+ }
+ GCCH_handle_channel_plaintext_data (ch,
+ msg);
+}
+
+
+/**
+ * We received an acknowledgement for data we sent on a channel.
+ * Locate the channel and process it, or return an error if the
+ * channel is unknown.
+ *
+ * @param cls the `struct CadetTunnel` for which we decrypted the message
+ * @param ack the message we received on the tunnel
+ */
+static void
+handle_plaintext_data_ack (void *cls,
+ const struct GNUNET_CADET_ChannelDataAckMessage *ack)
+{
+ struct CadetTunnel *t = cls;
+ struct CadetChannel *ch;
+
+ ch = lookup_channel (t,
+ ack->ctn);
+ if (NULL == ch)
+ {
+ /* We don't know about such a channel, might have been destroyed on our
+ end in the meantime, or never existed. Send back a DESTROY. */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Receicved DATA_ACK for unknown channel %u, sending DESTROY\n",
+ ntohl (ack->ctn.cn));
+ GCT_send_channel_destroy (t,
+ ack->ctn);
+ return;
+ }
+ GCCH_handle_channel_plaintext_data_ack (ch,
+ ack);
+}
+
+
+/**
+ * We have received a request to open a channel to a port from
+ * another peer. Creates the incoming channel.
+ *
+ * @param cls the `struct CadetTunnel` for which we decrypted the message
+ * @param cc the message we received on the tunnel
+ */
+static void
+handle_plaintext_channel_open (void *cls,
+ const struct GNUNET_CADET_ChannelOpenMessage *cc)
+{
+ struct CadetTunnel *t = cls;
+ struct CadetChannel *ch;
+ struct GNUNET_CADET_ChannelTunnelNumber ctn;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Receicved channel OPEN on port %s from peer %s\n",
+ GNUNET_h2s (&cc->port),
+ GCP_2s (GCT_get_destination (t)));
+ ctn = get_next_free_ctn (t);
+ ch = GCCH_channel_incoming_new (t,
+ ctn,
+ &cc->port,
+ ntohl (cc->opt));
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap32_put (t->channels,
+ ntohl (ctn.cn),
+ ch,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+}
+
+
+/**
+ * Send a DESTROY message via the tunnel.
+ *
+ * @param t the tunnel to transmit over
+ * @param ctn ID of the channel to destroy
+ */
+void
+GCT_send_channel_destroy (struct CadetTunnel *t,
+ struct GNUNET_CADET_ChannelTunnelNumber ctn)
+{
+ struct GNUNET_CADET_ChannelManageMessage msg;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Sending DESTORY message for channel ID %u\n",
+ ntohl (ctn.cn));
+ msg.header.size = htons (sizeof (msg));
+ msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_DESTROY);
+ msg.reserved = htonl (0);
+ msg.ctn = ctn;
+ GCT_send (t,
+ &msg.header,
+ NULL,
+ NULL);
+}
+
+
+/**
+ * We have received confirmation from the target peer that the
+ * given channel could be established (the port is open).
+ * Tell the client.
+ *
+ * @param cls the `struct CadetTunnel` for which we decrypted the message
+ * @param cm the message we received on the tunnel
+ */
+static void
+handle_plaintext_channel_open_ack (void *cls,
+ const struct GNUNET_CADET_ChannelManageMessage *cm)
+{
+ struct CadetTunnel *t = cls;
+ struct CadetChannel *ch;
+
+ ch = lookup_channel (t,
+ cm->ctn);
+ if (NULL == ch)
+ {
+ /* We don't know about such a channel, might have been destroyed on our
+ end in the meantime, or never existed. Send back a DESTROY. */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received channel OPEN_ACK for unknown channel, sending DESTROY\n",
+ GCCH_2s (ch));
+ GCT_send_channel_destroy (t,
+ cm->ctn);
+ return;
+ }
+ GCCH_handle_channel_open_ack (ch);
+}
+
+
+/**
+ * We received a message saying that a channel should be destroyed.
+ * Pass it on to the correct channel.
+ *
+ * @param cls the `struct CadetTunnel` for which we decrypted the message
+ * @param cm the message we received on the tunnel
+ */
+static void
+handle_plaintext_channel_destroy (void *cls,
+ const struct GNUNET_CADET_ChannelManageMessage *cm)
+{
+ struct CadetTunnel *t = cls;
+ struct CadetChannel *ch;
+
+ ch = lookup_channel (t,
+ cm->ctn);
+ if (NULL == ch)
+ {
+ /* We don't know about such a channel, might have been destroyed on our
+ end in the meantime, or never existed. */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received channel DESTORY for unknown channel. Ignoring.\n",
+ GCCH_2s (ch));
+ return;
+ }
+ GCCH_handle_remote_destroy (ch);
+}
+
+
+/**
+ * Handles a message we decrypted, by injecting it into
+ * our message queue (which will do the dispatching).
+ *
+ * @param cls the `struct CadetTunnel` that got the message
+ * @param msg the message
+ * @return #GNUNET_OK (continue to process)
+ */
+static int
+handle_decrypted (void *cls,
+ const struct GNUNET_MessageHeader *msg)
+{
+ struct CadetTunnel *t = cls;
+
+ GNUNET_MQ_inject_message (t->mq,
+ msg);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called if we had an error processing
+ * an incoming decrypted message.
+ *
+ * @param cls the `struct CadetTunnel`
+ * @param error error code
+ */
+static void
+decrypted_error_cb (void *cls,
+ enum GNUNET_MQ_Error error)
+{
+ GNUNET_break_op (0);
+}
+
+
/**
* Create a tunnel to @a destionation. Must only be called
* from within #GCP_get_tunnel().
struct CadetTunnel *
GCT_create_tunnel (struct CadetPeer *destination)
{
+ struct GNUNET_MQ_MessageHandler handlers[] = {
+ GNUNET_MQ_hd_fixed_size (plaintext_keepalive,
+ GNUNET_MESSAGE_TYPE_CADET_CHANNEL_KEEPALIVE,
+ struct GNUNET_MessageHeader,
+ NULL),
+ GNUNET_MQ_hd_var_size (plaintext_data,
+ GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA,
+ struct GNUNET_CADET_ChannelAppDataMessage,
+ NULL),
+ GNUNET_MQ_hd_fixed_size (plaintext_data_ack,
+ GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA_ACK,
+ struct GNUNET_CADET_ChannelDataAckMessage,
+ NULL),
+ GNUNET_MQ_hd_fixed_size (plaintext_channel_open,
+ GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN,
+ struct GNUNET_CADET_ChannelOpenMessage,
+ NULL),
+ GNUNET_MQ_hd_fixed_size (plaintext_channel_open_ack,
+ GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK,
+ struct GNUNET_CADET_ChannelManageMessage,
+ NULL),
+ GNUNET_MQ_hd_fixed_size (plaintext_channel_destroy,
+ GNUNET_MESSAGE_TYPE_CADET_CHANNEL_DESTROY,
+ struct GNUNET_CADET_ChannelManageMessage,
+ NULL),
+ GNUNET_MQ_handler_end ()
+ };
struct CadetTunnel *t;
t = GNUNET_new (struct CadetTunnel);
+ new_ephemeral (t);
t->destination = destination;
t->channels = GNUNET_CONTAINER_multihashmap32_create (8);
(void) GCP_iterate_paths (destination,
t->maintain_connections_task
= GNUNET_SCHEDULER_add_now (&maintain_connections_cb,
t);
+ t->mq = GNUNET_MQ_queue_for_callbacks (NULL,
+ NULL,
+ NULL,
+ NULL,
+ handlers,
+ &decrypted_error_cb,
+ t);
+ t->mst = GNUNET_MST_create (&handle_decrypted,
+ t);
return t;
}
/**
- * Remove a channel from a tunnel.
+ * Add a @a connection to the @a tunnel.
*
- * @param t Tunnel.
- * @param ch Channel
- * @param gid unique number identifying @a ch within @a t
+ * @param t a tunnel
+ * @param cid connection identifer to use for the connection
+ * @param path path to use for the connection
*/
void
-GCT_remove_channel (struct CadetTunnel *t,
- struct CadetChannel *ch,
- struct GCT_ChannelTunnelNumber gid)
+GCT_add_inbound_connection (struct CadetTunnel *t,
+ const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid,
+ struct CadetPeerPath *path)
{
- GNUNET_assert (GNUNET_YES ==
- GNUNET_CONTAINER_multihashmap32_remove (t->channels,
- ntohl (gid.channel_in_tunnel),
- ch));
- if (0 ==
- GNUNET_CONTAINER_multihashmap32_size (t->channels))
+ struct CadetTConnection *ct;
+
+ ct = GNUNET_new (struct CadetTConnection);
+ ct->created = GNUNET_TIME_absolute_get ();
+ ct->t = t;
+ ct->cc = GCC_create_inbound (t->destination,
+ path,
+ ct,
+ cid,
+ &connection_ready_cb,
+ t);
+ /* FIXME: schedule job to kill connection (and path?) if it takes
+ too long to get ready! (And track performance data on how long
+ other connections took with the tunnel!)
+ => Note: to be done within 'connection'-logic! */
+ GNUNET_CONTAINER_DLL_insert (t->connection_head,
+ t->connection_tail,
+ ct);
+ t->num_connections++;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Tunnel %s has new connection %s\n",
+ GCT_2s (t),
+ GCC_2s (ct->cc));
+}
+
+
+/**
+ * Handle encrypted message.
+ *
+ * @param ct connection/tunnel combo that received encrypted message
+ * @param msg the encrypted message to decrypt
+ */
+void
+GCT_handle_encrypted (struct CadetTConnection *ct,
+ const struct GNUNET_CADET_TunnelEncryptedMessage *msg)
+{
+ struct CadetTunnel *t = ct->t;
+ uint16_t size = ntohs (msg->header.size);
+ char cbuf [size] GNUNET_ALIGN;
+ ssize_t decrypted_size;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Tunnel %s received %u bytes of encrypted data in state %d\n",
+ GCT_2s (t),
+ (unsigned int) size,
+ t->estate);
+
+ switch (t->estate)
{
- t->destroy_task = GNUNET_SCHEDULER_add_delayed (IDLE_DESTROY_DELAY,
- &destroy_tunnel,
- t);
+ case CADET_TUNNEL_KEY_UNINITIALIZED:
+ /* We did not even SEND our KX, how can the other peer
+ send us encrypted data? */
+ GNUNET_break_op (0);
+ return;
+ case CADET_TUNNEL_KEY_SENT:
+ /* We did not get the KX of the other peer, but that
+ might have been lost. Ask for KX again. */
+ GNUNET_STATISTICS_update (stats,
+ "# received encrypted without KX",
+ 1,
+ GNUNET_NO);
+ if (NULL != t->kx_task)
+ GNUNET_SCHEDULER_cancel (t->kx_task);
+ t->kx_task = GNUNET_SCHEDULER_add_now (&retry_kx,
+ t);
+ return;
+ case CADET_TUNNEL_KEY_PING:
+ /* Great, first payload, we might graduate to OK */
+ case CADET_TUNNEL_KEY_OK:
+ case CADET_TUNNEL_KEY_REKEY:
+ break;
+ }
+
+ GNUNET_STATISTICS_update (stats,
+ "# received encrypted",
+ 1,
+ GNUNET_NO);
+ decrypted_size = t_ax_decrypt_and_validate (t,
+ cbuf,
+ msg,
+ size);
+
+ if (-1 == decrypted_size)
+ {
+ GNUNET_break_op (0);
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ "Tunnel %s failed to decrypt and validate encrypted data\n",
+ GCT_2s (t));
+ GNUNET_STATISTICS_update (stats,
+ "# unable to decrypt",
+ 1,
+ GNUNET_NO);
+ return;
}
+ if (CADET_TUNNEL_KEY_PING == t->estate)
+ {
+ GCT_change_estate (t,
+ CADET_TUNNEL_KEY_OK);
+ trigger_transmissions (t);
+ }
+ /* The MST will ultimately call #handle_decrypted() on each message. */
+ GNUNET_break_op (GNUNET_OK ==
+ GNUNET_MST_from_buffer (t->mst,
+ cbuf,
+ decrypted_size,
+ GNUNET_YES,
+ GNUNET_NO));
}
struct CadetTunnelQueueEntry *tq;
uint16_t payload_size;
struct GNUNET_MQ_Envelope *env;
- struct GNUNET_CADET_Encrypted *ax_msg;
-
- /* FIXME: what about KX not yet being ready? (see "is_ready()" check in old code!) */
+ struct GNUNET_CADET_TunnelEncryptedMessage *ax_msg;
payload_size = ntohs (message->size);
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Encrypting %u bytes of for tunnel %s\n",
+ (unsigned int) payload_size,
+ GCT_2s (t));
env = GNUNET_MQ_msg_extra (ax_msg,
payload_size,
- GNUNET_MESSAGE_TYPE_CADET_ENCRYPTED);
+ GNUNET_MESSAGE_TYPE_CADET_TUNNEL_ENCRYPTED);
t_ax_encrypt (t,
&ax_msg[1],
message,
0,
&t->ax.HKs,
&ax_msg->hmac);
- // ax_msg->pid = htonl (GCC_get_pid (c, fwd)); // FIXME: connection flow-control not (re)implemented yet!
tq = GNUNET_malloc (sizeof (*tq));
tq->t = t;
tq->env = env;
- tq->cid = &ax_msg->cid;
+ tq->cid = &ax_msg->cid; /* will initialize 'ax_msg->cid' once we know the connection */
tq->cont = cont;
tq->cont_cls = cont_cls;
GNUNET_CONTAINER_DLL_insert_tail (t->tq_head,
}
-/**
- * Get string description for tunnel connectivity state.
- *
- * @param cs Tunnel state.
- *
- * @return String representation.
- */
-static const char *
-cstate2s (enum CadetTunnelCState cs)
-{
- static char buf[32];
-
- switch (cs)
- {
- case CADET_TUNNEL_NEW:
- return "CADET_TUNNEL_NEW";
- case CADET_TUNNEL_SEARCHING:
- return "CADET_TUNNEL_SEARCHING";
- case CADET_TUNNEL_WAITING:
- return "CADET_TUNNEL_WAITING";
- case CADET_TUNNEL_READY:
- return "CADET_TUNNEL_READY";
- case CADET_TUNNEL_SHUTDOWN:
- return "CADET_TUNNEL_SHUTDOWN";
- default:
- SPRINTF (buf, "%u (UNKNOWN STATE)", cs);
- return buf;
- }
-}
-
-
-/**
- * Get string description for tunnel encryption state.
- *
- * @param es Tunnel state.
- *
- * @return String representation.
- */
-static const char *
-estate2s (enum CadetTunnelEState es)
-{
- static char buf[32];
-
- switch (es)
- {
- case CADET_TUNNEL_KEY_UNINITIALIZED:
- return "CADET_TUNNEL_KEY_UNINITIALIZED";
- case CADET_TUNNEL_KEY_SENT:
- return "CADET_TUNNEL_KEY_SENT";
- case CADET_TUNNEL_KEY_PING:
- return "CADET_TUNNEL_KEY_PING";
- case CADET_TUNNEL_KEY_OK:
- return "CADET_TUNNEL_KEY_OK";
- case CADET_TUNNEL_KEY_REKEY:
- return "CADET_TUNNEL_KEY_REKEY";
- default:
- SPRINTF (buf, "%u (UNKNOWN STATE)", es);
- return buf;
- }
-}
-
-
#define LOG2(level, ...) GNUNET_log_from_nocheck(level,"cadet-tun",__VA_ARGS__)
return;
LOG2 (level,
- "TTT TUNNEL TOWARDS %s in cstate %s, estate %s tq_len: %u #cons: %u\n",
+ "TTT TUNNEL TOWARDS %s in estate %s tq_len: %u #cons: %u\n",
GCT_2s (t),
- cstate2s (t->cstate),
estate2s (t->estate),
t->tq_len,
t->num_connections);