#include "gnunet_signatures.h"
#include "gnunet_statistics_service.h"
-#include "mesh_protocol_enc.h"
+#include "mesh_protocol.h"
#include "mesh_path.h"
#include "gnunet-service-mesh_tunnel.h"
#define LOG(level, ...) GNUNET_log_from(level,"mesh-tun",__VA_ARGS__)
-#define REKEY_WAIT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 30)
+#define REKEY_WAIT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 5)
/******************************************************************************/
/******************************** STRUCTS **********************************/
/**
* Queued messages, to transmit once tunnel gets connected.
*/
- struct MeshTunnelQueue *tq_head;
- struct MeshTunnelQueue *tq_tail;
+ struct MeshTunnelDelayed *tq_head;
+ struct MeshTunnelDelayed *tq_tail;
};
/**
- * Struct used to queue messages in a tunnel.
+ * Struct used to save messages in a non-ready tunnel to send once connected.
*/
-struct MeshTunnelQueue
+struct MeshTunnelDelayed
{
/**
* DLL
*/
- struct MeshTunnelQueue *next;
- struct MeshTunnelQueue *prev;
+ struct MeshTunnelDelayed *next;
+ struct MeshTunnelDelayed *prev;
/**
* Channel.
};
+/**
+ * Handle for messages queued but not yet sent.
+ */
+struct MeshTunnel3Queue
+{
+ /**
+ * Connection queue handle, to cancel if necessary.
+ */
+ struct MeshConnectionQueue *q;
+
+ /**
+ * Continuation to call once sent.
+ */
+ GMT_sent cont;
+
+ /**
+ * Closure for @c cont.
+ */
+ void *cont_cls;
+};
+
+
/******************************************************************************/
/******************************* GLOBALS ***********************************/
/******************************************************************************/
return "MESH_TUNNEL3_WAITING";
case MESH_TUNNEL3_KEY_SENT:
return "MESH_TUNNEL3_KEY_SENT";
- case MESH_TUNNEL3_PING_SENT:
- return "MESH_TUNNEL3_PING_SENT";
case MESH_TUNNEL3_READY:
return "MESH_TUNNEL3_READY";
case MESH_TUNNEL3_RECONNECTING:
* @return GNUNET_OK if message is fine, GNUNET_SYSERR otherwise.
*/
int
-check_ephemeral (struct MeshTunnel3 *t,
+check_ephemeral (struct MeshTunnel3 *t,
const struct GNUNET_MESH_KX_Ephemeral *msg)
{
/* Check message size */
{
struct GNUNET_CRYPTO_SymmetricInitializationVector siv;
- GNUNET_CRYPTO_symmetric_derive_iv (&siv, &t->e_key, &iv, sizeof (uint32_t), NULL);
+ GNUNET_CRYPTO_symmetric_derive_iv (&siv, &t->d_key, &iv, sizeof (uint32_t), NULL);
return GNUNET_CRYPTO_symmetric_decrypt (src, size, &t->d_key, &siv, dst);
}
static void
send_queued_data (struct MeshTunnel3 *t)
{
- struct MeshTunnelQueue *tq;
- struct MeshTunnelQueue *next;
+ struct MeshTunnelDelayed *tq;
+ struct MeshTunnelDelayed *next;
unsigned int room;
LOG (GNUNET_ERROR_TYPE_DEBUG,
- "GMT_send_queued_data on tunnel %s\n",
- GMT_2s (t));
+ "GMT_send_queued_data on tunnel %s\n",
+ GMT_2s (t));
if (GMT_is_loopback (t))
{
room--;
GNUNET_CONTAINER_DLL_remove (t->tq_head, t->tq_tail, tq);
GMCH_send_prebuilt_message ((struct GNUNET_MessageHeader *) &tq[1],
- tq->ch, GMCH_is_origin (tq->ch, GNUNET_YES));
+ tq->ch, GMCH_is_origin (tq->ch, GNUNET_YES),
+ GNUNET_NO);
GNUNET_free (tq);
}
struct MeshChannel *ch,
const struct GNUNET_MessageHeader *msg)
{
- struct MeshTunnelQueue *tq;
+ struct MeshTunnelDelayed *tq;
uint16_t size = ntohs (msg->size);
if (MESH_TUNNEL3_READY == t->state)
return;
}
- tq = GNUNET_malloc (sizeof (struct MeshTunnelQueue) + size);
+ tq = GNUNET_malloc (sizeof (struct MeshTunnelDelayed) + size);
tq->ch = ch;
memcpy (&tq[1], msg, size);
case GNUNET_MESSAGE_TYPE_MESH_KX_EPHEMERAL:
case GNUNET_MESSAGE_TYPE_MESH_KX_PING:
case GNUNET_MESSAGE_TYPE_MESH_KX_PONG:
- msg->cid = *GMC_get_id (c);
msg->reserved = htonl (0);
memcpy (&msg[1], message, size);
break;
}
fwd = GMC_is_origin (t->connection_head->c, GNUNET_YES);
- GMC_send_prebuilt_message (&msg->header, c, fwd);
+ /* TODO save handle and cancel in case of a unneeded retransmission */
+ GMC_send_prebuilt_message (&msg->header, c, fwd, NULL, NULL);
}
static void
send_ephemeral (struct MeshTunnel3 *t)
{
- kx_msg.sender_status = htonl (t->state);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __FUNCTION__);
+ kx_msg.sender_status = htonl (t->state);
send_kx (t, &kx_msg.header);
}
{
struct GNUNET_MESH_KX_Ping msg;
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __FUNCTION__);
msg.header.size = htons (sizeof (msg));
msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_KX_PING);
msg.iv = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX);
msg.target = *GMP_get_id (t->peer);
msg.nonce = t->kx_ctx->challenge;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " sending %u\n", msg.nonce);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " towards %s\n", GNUNET_i2s (&msg.target));
t_encrypt (t, &msg.target, &msg.target, ping_encryption_size(), msg.iv);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " e sending %u\n", msg.nonce);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " e towards %s\n", GNUNET_i2s (&msg.target));
send_kx (t, &msg.header);
}
{
struct GNUNET_MESH_KX_Pong msg;
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "%s()\n", __FUNCTION__);
msg.header.size = htons (sizeof (msg));
msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_KX_PONG);
msg.iv = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX);
msg.nonce = challenge;
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " sending %u\n", msg.nonce);
t_encrypt (t, &msg.nonce, &msg.nonce, sizeof (msg.nonce), msg.iv);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " e sending %u\n", msg.nonce);
send_kx (t, &msg.header);
}
t->rekey_task = GNUNET_SCHEDULER_NO_TASK;
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Re-key Tunnel\n");
if (NULL != tc && 0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
return;
- t->kx_ctx = GNUNET_new (struct MeshTunnelKXCtx);
- t->kx_ctx->challenge = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
- UINT32_MAX);
- t->kx_ctx->d_key_old = t->d_key;
+ if (NULL == t->kx_ctx)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " new kx ctx\n");
+ t->kx_ctx = GNUNET_new (struct MeshTunnelKXCtx);
+ t->kx_ctx->challenge = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
+ UINT32_MAX);
+ t->kx_ctx->d_key_old = t->d_key;
+ }
send_ephemeral (t);
- if (MESH_TUNNEL3_READY == t->state)
+ if (MESH_TUNNEL3_READY == t->state || MESH_TUNNEL3_REKEY == t->state)
{
send_ping (t);
t->state = MESH_TUNNEL3_REKEY;
LOG (GNUNET_ERROR_TYPE_DEBUG, "Unexpected state %u\n", t->state);
}
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " next call in %s\n",
+ GNUNET_STRINGS_relative_time_to_string (REKEY_WAIT, GNUNET_YES));
t->rekey_task = GNUNET_SCHEDULER_add_delayed (REKEY_WAIT, &rekey_tunnel, t);
}
}
+/**
+ * Called only on shutdown, destroy every tunnel.
+ *
+ * @param cls Closure (unused).
+ * @param key Current public key.
+ * @param value Value in the hash map (tunnel).
+ *
+ * @return #GNUNET_YES, so we should continue to iterate,
+ */
+static int
+destroy_iterator (void *cls,
+ const struct GNUNET_PeerIdentity *key,
+ void *value)
+{
+ struct MeshTunnel3 *t = value;
+
+ GMT_destroy (t);
+ return GNUNET_YES;
+}
+
+
/**
* Demultiplex data per channel and call appropriate channel handler.
*
* #GNUNET_NO if message is BCK on the respective channel (loopback)
* #GNUNET_SYSERR if message on a one-ended channel (remote)
*/
-void
+static void
handle_data (struct MeshTunnel3 *t,
const struct GNUNET_MESH_Data *msg,
int fwd)
* #GNUNET_NO if message is BCK on the respective channel (loopback)
* #GNUNET_SYSERR if message on a one-ended channel (remote)
*/
-void
+static void
handle_data_ack (struct MeshTunnel3 *t,
const struct GNUNET_MESH_DataACK *msg,
int fwd)
* @param t Tunnel on which the data came.
* @param msg Data message.
*/
-void
+static void
handle_ch_create (struct MeshTunnel3 *t,
const struct GNUNET_MESH_ChannelCreate *msg)
{
{
ch = GMCH_handle_create (t, msg);
}
- GMT_add_channel (t, ch);
+ if (NULL != ch)
+ GMT_add_channel (t, ch);
+}
+
+
+
+/**
+ * Handle channel NACK: check correctness and call channel handler for NACKs.
+ *
+ * @param t Tunnel on which the NACK came.
+ * @param msg NACK message.
+ */
+static void
+handle_ch_nack (struct MeshTunnel3 *t,
+ const struct GNUNET_MESH_ChannelManage *msg)
+{
+ struct MeshChannel *ch;
+ size_t size;
+
+ /* Check size */
+ size = ntohs (msg->header.size);
+ if (size != sizeof (struct GNUNET_MESH_ChannelManage))
+ {
+ GNUNET_break (0);
+ return;
+ }
+
+ /* Check channel */
+ ch = GMT_get_channel (t, ntohl (msg->chid));
+ if (NULL == ch)
+ {
+ GNUNET_STATISTICS_update (stats, "# channel NACK on unknown channel",
+ 1, GNUNET_NO);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "WARNING channel %u unknown\n",
+ ntohl (msg->chid));
+ return;
+ }
+
+ GMCH_handle_nack (ch);
}
* #GNUNET_NO if message is BCK on the respective channel (loopback)
* #GNUNET_SYSERR if message on a one-ended channel (remote)
*/
-void
+static void
handle_ch_ack (struct MeshTunnel3 *t,
const struct GNUNET_MESH_ChannelManage *msg,
int fwd)
* #GNUNET_NO if message is BCK on the respective channel (loopback)
* #GNUNET_SYSERR if message on a one-ended channel (remote)
*/
-void
+static void
handle_ch_destroy (struct MeshTunnel3 *t,
const struct GNUNET_MESH_ChannelManage *msg,
int fwd)
return;
}
derive_key_material (&km, &msg->ephemeral_key);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " km is %s\n", GNUNET_h2s (&km));
derive_symmertic (&t->e_key, &my_full_id, GMP_get_id (t->peer), &km);
derive_symmertic (&t->d_key, GMP_get_id (t->peer), &my_full_id, &km);
if (MESH_TUNNEL3_KEY_SENT == t->state)
{
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " our key was sent, send ping\n");
send_ping (t);
- t->state = MESH_TUNNEL3_PING_SENT;
+ t->state = MESH_TUNNEL3_REKEY;
}
}
{
struct GNUNET_MESH_KX_Ping res;
+ if (ntohs (msg->header.size) != sizeof (res))
+ {
+ GNUNET_break_op (0);
+ return;
+ }
+
LOG (GNUNET_ERROR_TYPE_DEBUG, " ping message\n");
- t_decrypt (t, &res.target, &msg->target, ping_encryption_size(), msg->iv);
- if (0 != memcmp (&my_full_id, &msg->target, sizeof (my_full_id)))
+ t_decrypt (t, &res.target, &msg->target, ping_encryption_size (), msg->iv);
+ if (0 != memcmp (&my_full_id, &res.target, sizeof (my_full_id)))
{
- GNUNET_break (0);
- LOG (GNUNET_ERROR_TYPE_DEBUG, " at %s\n", GNUNET_i2s (&my_full_id));
- LOG (GNUNET_ERROR_TYPE_DEBUG, " for %s\n", GNUNET_i2s (&msg->target));
+ GNUNET_break_op (0);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " e got %u\n", msg->nonce);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " e towards %s\n", GNUNET_i2s (&msg->target));
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " got %u\n", res.nonce);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " towards %s\n", GNUNET_i2s (&res.target));
return;
}
- send_pong (t, res.iv);
+ send_pong (t, res.nonce);
}
if (challenge != t->kx_ctx->challenge)
{
LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Wrong PONG challenge: %u. Expected: %u.\n",
- challenge, t->kx_ctx->challenge);
+ "Wrong PONG challenge: %u (e: %u). Expected: %u.\n",
+ challenge, msg->nonce, t->kx_ctx->challenge);
GNUNET_break_op (0);
return;
}
(struct GNUNET_MESH_ChannelCreate *) msgh);
break;
+ case GNUNET_MESSAGE_TYPE_MESH_CHANNEL_NACK:
+ handle_ch_nack (t,
+ (struct GNUNET_MESH_ChannelManage *) msgh);
+ break;
+
case GNUNET_MESSAGE_TYPE_MESH_CHANNEL_ACK:
handle_ch_ack (t,
(struct GNUNET_MESH_ChannelManage *) msgh,
GNUNET_SCHEDULER_cancel (rekey_task);
rekey_task = GNUNET_SCHEDULER_NO_TASK;
}
+ GNUNET_CONTAINER_multipeermap_iterate (tunnels, &destroy_iterator, NULL);
GNUNET_CONTAINER_multipeermap_destroy (tunnels);
}
{
struct MeshTChannel *aux;
+ GNUNET_assert (NULL != ch);
+
LOG (GNUNET_ERROR_TYPE_DEBUG, "Adding channel %p to tunnel %p\n", ch, t);
for (aux = t->channel_head; aux != NULL; aux = aux->next)
{
struct MeshTConnection *iter;
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Tunnel empty: destroying scheduled\n");
for (iter = t->connection_head; NULL != iter; iter = iter->next)
{
GMC_send_destroy (iter->c);
void
GMT_destroy (struct MeshTunnel3 *t)
{
- struct MeshTConnection *iter;
- struct MeshTConnection *next;
+ struct MeshTConnection *iter_c;
+ struct MeshTConnection *next_c;
+ struct MeshTChannel *iter_ch;
+ struct MeshTChannel *next_ch;
if (NULL == t)
return;
LOG (GNUNET_ERROR_TYPE_DEBUG, "destroying tunnel %s\n", GMP_2s (t->peer));
-// if (GNUNET_YES != GNUNET_CONTAINER_multihashmap_remove (tunnels, &t->id, t))
-// GNUNET_break (0);
+ GNUNET_break (GNUNET_YES ==
+ GNUNET_CONTAINER_multipeermap_remove (tunnels,
+ GMP_get_id (t->peer), t));
- for (iter = t->connection_head; NULL != iter; iter = next)
+ for (iter_c = t->connection_head; NULL != iter_c; iter_c = next_c)
{
- next = iter->next;
- GMC_destroy (iter->c);
+ next_c = iter_c->next;
+ GMC_destroy (iter_c->c);
+ }
+ for (iter_ch = t->channel_head; NULL != iter_ch; iter_ch = next_ch)
+ {
+ next_ch = iter_ch->next;
+ GMCH_destroy (iter_ch->ch);
+ /* Should only happen on shutdown, but it's ok. */
}
GNUNET_STATISTICS_update (stats, "# tunnels", -1, GNUNET_NO);
if (NULL != t->channel_head)
LOG (GNUNET_ERROR_TYPE_DEBUG, " head ch: %p\n", t->channel_head->ch);
- if (NULL == t)
- {
- GNUNET_break (0);
- return;
- }
-
/* Get buffer space */
buffer = GMT_get_connections_buffer (t);
if (0 == buffer)
unsigned int cs;
unsigned int buffer;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
"Tunnel send connection ACKs on %s\n",
GMT_2s (t));
}
+/**
+ * Callback called when a queued message is sent.
+ *
+ * Calculates the average time and connection packet tracking.
+ *
+ * @param cls Closure (TunnelQueue handle).
+ * @param c Connection this message was on.
+ * @param type Type of message sent.
+ * @param fwd Was this a FWD going message?
+ * @param size Size of the message.
+ */
+static void
+message_sent (void *cls,
+ struct MeshConnection *c,
+ struct MeshConnectionQueue *q,
+ uint16_t type, int fwd, size_t size)
+{
+ struct MeshTunnel3Queue *qt = cls;
+
+ GNUNET_assert (NULL != qt->cont);
+ qt->cont (qt->cont_cls, GMC_get_tunnel (c), qt, type, size);
+ GNUNET_free (qt);
+}
+
+
+/**
+ * Cancel a previously sent message while it's in the queue.
+ *
+ * ONLY can be called before the continuation given to the send function
+ * is called. Once the continuation is called, the message is no longer in the
+ * queue.
+ *
+ * @param q Handle to the queue.
+ */
+void
+GMT_cancel (struct MeshTunnel3Queue *q)
+{
+ GMC_cancel (q->q);
+ /* message_sent() will be called and free q */
+}
+
/**
* Sends an already built message on a tunnel, encrypting it and
* choosing the best connection.
* @param t Tunnel on which this message is transmitted.
* @param ch Channel on which this message is transmitted.
* @param fwd Is this a fwd message on @c ch?
+ * @param cont Continuation to call once message is really sent.
+ * @param cont_cls Closure for @c cont.
+ *
+ * @return Handle to cancel message. NULL if @c cont is NULL.
*/
-void
+struct MeshTunnel3Queue *
GMT_send_prebuilt_message (const struct GNUNET_MessageHeader *message,
struct MeshTunnel3 *t,
- struct MeshChannel *ch,
- int fwd)
+ struct MeshChannel *ch, int fwd,
+ GMT_sent cont, void *cont_cls)
{
+ struct MeshTunnel3Queue *q;
struct MeshConnection *c;
struct GNUNET_MESH_Encrypted *msg;
size_t size = ntohs (message->size);
if (MESH_TUNNEL3_READY != t->state)
{
queue_data (t, ch, message);
- return;
+ /* FIXME */
+ return NULL;
}
LOG (GNUNET_ERROR_TYPE_DEBUG, "GMT Send on Tunnel %s\n", GMT_2s (t));
{
LOG (GNUNET_ERROR_TYPE_DEBUG, " loopback!\n");
handle_decrypted (t, message, fwd);
- return;
+ /* FIXME: call cont? */
+ return NULL; /* Already delivered, cannot cancel */
}
iv = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX);
if (NULL == c)
{
GNUNET_break (GNUNET_YES == t->destroy);
- return;
+ return NULL;
}
type = ntohs (message->type);
switch (type)
{
case GNUNET_MESSAGE_TYPE_MESH_DATA:
+ case GNUNET_MESSAGE_TYPE_MESH_DATA_ACK:
case GNUNET_MESSAGE_TYPE_MESH_CHANNEL_CREATE:
case GNUNET_MESSAGE_TYPE_MESH_CHANNEL_DESTROY:
case GNUNET_MESSAGE_TYPE_MESH_CHANNEL_ACK:
}
fwd = GMC_is_origin (c, GNUNET_YES);
- GMC_send_prebuilt_message (&msg->header, c, fwd);
+
+ if (NULL == cont)
+ {
+ (void) GMC_send_prebuilt_message (&msg->header, c, fwd, NULL, NULL);
+ return NULL;
+ }
+ q = GNUNET_new (struct MeshTunnel3Queue); /* FIXME valgrind: leak*/
+ q->q = GMC_send_prebuilt_message (&msg->header, c, fwd, &message_sent, q);
+ q->cont = cont;
+ q->cont_cls = cont_cls;
+
+ return q;
}
/**
/**
- * Is the tunnel using this path already?
+ * Is the tunnel this path already?
*
* @param t Tunnel.
* @param p Path.
return "(NULL)";
return GMP_2s (t->peer);
-}
\ No newline at end of file
+}