+ struct MeshTunnelDelayed *tqd;
+
+ /**
+ * Continuation to call once sent.
+ */
+ GMT_sent cont;
+
+ /**
+ * Closure for @c cont.
+ */
+ void *cont_cls;
+};
+
+
+/******************************************************************************/
+/******************************* GLOBALS ***********************************/
+/******************************************************************************/
+
+/**
+ * Global handle to the statistics service.
+ */
+extern struct GNUNET_STATISTICS_Handle *stats;
+
+/**
+ * Local peer own ID (memory efficient handle).
+ */
+extern GNUNET_PEER_Id myid;
+
+/**
+ * Local peer own ID (full value).
+ */
+extern struct GNUNET_PeerIdentity my_full_id;
+
+
+/**
+ * Set of all tunnels, in order to trigger a new exchange on rekey.
+ * Indexed by peer's ID.
+ */
+static struct GNUNET_CONTAINER_MultiPeerMap *tunnels;
+
+/**
+ * Default TTL for payload packets.
+ */
+static unsigned long long default_ttl;
+
+/**
+ * Own private key.
+ */
+const static struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key;
+
+/**
+ * Own ephemeral private key.
+ */
+static struct GNUNET_CRYPTO_EcdhePrivateKey *my_ephemeral_key;
+
+/**
+ * Cached message used to perform a key exchange.
+ */
+static struct GNUNET_MESH_KX_Ephemeral kx_msg;
+
+/**
+ * Task to generate a new ephemeral key.
+ */
+static GNUNET_SCHEDULER_TaskIdentifier rekey_task;
+
+/**
+ * Rekey period.
+ */
+static struct GNUNET_TIME_Relative rekey_period;
+
+/******************************************************************************/
+/******************************** STATIC ***********************************/
+/******************************************************************************/
+
+/**
+ * Get string description for tunnel connectivity state.
+ *
+ * @param cs Tunnel state.
+ *
+ * @return String representation.
+ */
+static const char *
+cstate2s (enum MeshTunnel3CState cs)
+{
+ static char buf[128];
+
+ switch (cs)
+ {
+ case MESH_TUNNEL3_NEW:
+ return "MESH_TUNNEL3_NEW";
+ case MESH_TUNNEL3_SEARCHING:
+ return "MESH_TUNNEL3_SEARCHING";
+ case MESH_TUNNEL3_WAITING:
+ return "MESH_TUNNEL3_WAITING";
+ case MESH_TUNNEL3_READY:
+ return "MESH_TUNNEL3_READY";
+
+ default:
+ sprintf (buf, "%u (UNKNOWN STATE)", cs);
+ return buf;
+ }
+ return "";
+}
+
+
+/**
+ * Get string description for tunnel encryption state.
+ *
+ * @param es Tunnel state.
+ *
+ * @return String representation.
+ */
+static const char *
+estate2s (enum MeshTunnel3EState es)
+{
+ static char buf[128];
+
+ switch (es)
+ {
+ case MESH_TUNNEL3_KEY_UNINITIALIZED:
+ return "MESH_TUNNEL3_KEY_UNINITIALIZED";
+ case MESH_TUNNEL3_KEY_SENT:
+ return "MESH_TUNNEL3_KEY_SENT";
+ case MESH_TUNNEL3_KEY_PING:
+ return "MESH_TUNNEL3_KEY_PING";
+ case MESH_TUNNEL3_KEY_OK:
+ return "MESH_TUNNEL3_KEY_OK";
+
+ default:
+ sprintf (buf, "%u (UNKNOWN STATE)", es);
+ return buf;
+ }
+ return "";
+}
+
+
+/**
+ * @brief Check if tunnel is ready to send traffic.
+ *
+ * Tunnel must be connected and with encryption correctly set up.
+ *
+ * @param t Tunnel to check.
+ *
+ * @return #GNUNET_YES if ready, #GNUNET_NO otherwise
+ */
+static int
+is_ready (struct MeshTunnel3 *t)
+{
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " ready: cs=%s, es=%s\n",
+ cstate2s (t->cstate), estate2s (t->estate));
+ return (MESH_TUNNEL3_READY == t->cstate
+ && MESH_TUNNEL3_KEY_OK == t->estate)
+ || GMT_is_loopback (t);
+}
+
+
+/**
+ * Ephemeral key message purpose size.
+ *
+ * @return Size of the part of the ephemeral key message that must be signed.
+ */
+size_t
+ephemeral_purpose_size (void)
+{
+ return sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
+ sizeof (struct GNUNET_TIME_AbsoluteNBO) +
+ sizeof (struct GNUNET_TIME_AbsoluteNBO) +
+ sizeof (struct GNUNET_CRYPTO_EcdhePublicKey) +
+ sizeof (struct GNUNET_PeerIdentity);
+}
+
+
+/**
+ * Size of the encrypted part of a ping message.
+ *
+ * @return Size of the encrypted part of a ping message.
+ */
+size_t
+ping_encryption_size (void)
+{
+ return sizeof (struct GNUNET_PeerIdentity) + sizeof (uint32_t);
+}
+
+
+/**
+ * Get the channel's buffer. ONLY FOR NON-LOOPBACK CHANNELS!!
+ *
+ * @param tch Tunnel's channel handle.
+ *
+ * @return Amount of messages the channel can still buffer towards the client.
+ */
+static unsigned int
+get_channel_buffer (const struct MeshTChannel *tch)
+{
+ int fwd;
+
+ /* If channel is outgoing, is origin in the FWD direction and fwd is YES */
+ fwd = GMCH_is_origin (tch->ch, GNUNET_YES);
+
+ return GMCH_get_buffer (tch->ch, fwd);
+}
+
+
+/**
+ * Get the channel's allowance status.
+ *
+ * @param tch Tunnel's channel handle.
+ *
+ * @return #GNUNET_YES if we allowed the client to send data to us.
+ */
+static int
+get_channel_allowed (const struct MeshTChannel *tch)
+{
+ int fwd;
+
+ /* If channel is outgoing, is origin in the FWD direction and fwd is YES */
+ fwd = GMCH_is_origin (tch->ch, GNUNET_YES);
+
+ return GMCH_get_allowed (tch->ch, fwd);
+}
+
+
+/**
+ * Get the connection's buffer.
+ *
+ * @param tc Tunnel's connection handle.
+ *
+ * @return Amount of messages the connection can still buffer.
+ */
+static unsigned int
+get_connection_buffer (const struct MeshTConnection *tc)
+{
+ int fwd;
+
+ /* If connection is outgoing, is origin in the FWD direction and fwd is YES */
+ fwd = GMC_is_origin (tc->c, GNUNET_YES);
+
+ return GMC_get_buffer (tc->c, fwd);
+}
+
+
+/**
+ * Get the connection's allowance.
+ *
+ * @param tc Tunnel's connection handle.
+ *
+ * @return Amount of messages we have allowed the next peer to send us.
+ */
+static unsigned int
+get_connection_allowed (const struct MeshTConnection *tc)
+{
+ int fwd;
+
+ /* If connection is outgoing, is origin in the FWD direction and fwd is YES */
+ fwd = GMC_is_origin (tc->c, GNUNET_YES);
+
+ return GMC_get_allowed (tc->c, fwd);
+}
+
+
+/**
+ * Check that a ephemeral key message s well formed and correctly signed.
+ *
+ * @param t Tunnel on which the message came.
+ * @param msg The ephemeral key message.
+ *
+ * @return GNUNET_OK if message is fine, GNUNET_SYSERR otherwise.
+ */
+int
+check_ephemeral (struct MeshTunnel3 *t,
+ const struct GNUNET_MESH_KX_Ephemeral *msg)
+{
+ /* Check message size */
+ if (ntohs (msg->header.size) != sizeof (struct GNUNET_MESH_KX_Ephemeral))
+ return GNUNET_SYSERR;
+
+ /* Check signature size */
+ if (ntohl (msg->purpose.size) != ephemeral_purpose_size ())
+ return GNUNET_SYSERR;
+
+ /* Check origin */
+ if (0 != memcmp (&msg->origin_identity,
+ GMP_get_id (t->peer),
+ sizeof (struct GNUNET_PeerIdentity)))
+ return GNUNET_SYSERR;
+
+ /* Check signature */
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_MESH_KX,
+ &msg->purpose,
+ &msg->signature,
+ &msg->origin_identity.public_key))
+ return GNUNET_SYSERR;
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * Encrypt data with the tunnel key.
+ *
+ * @param t Tunnel whose key to use.
+ * @param dst Destination for the encrypted data.
+ * @param src Source of the plaintext. Can overlap with @c dst.
+ * @param size Size of the plaintext.
+ * @param iv Initialization Vector to use.
+ */
+static int
+t_encrypt (struct MeshTunnel3 *t,
+ void *dst, const void *src,
+ size_t size, uint32_t iv)
+{
+ struct GNUNET_CRYPTO_SymmetricInitializationVector siv;
+
+ GNUNET_CRYPTO_symmetric_derive_iv (&siv, &t->e_key, &iv, sizeof (uint32_t), NULL);
+ return GNUNET_CRYPTO_symmetric_encrypt (src, size, &t->e_key, &siv, dst);
+}
+
+
+/**
+ * Decrypt data with the tunnel key.
+ *
+ * @param t Tunnel whose key to use.
+ * @param dst Destination for the plaintext.
+ * @param src Source of the encrypted data. Can overlap with @c dst.
+ * @param size Size of the encrypted data.
+ * @param iv Initialization Vector to use.
+ */
+static int
+t_decrypt (struct MeshTunnel3 *t,
+ void *dst, const void *src,
+ size_t size, uint32_t iv)
+{
+ struct GNUNET_CRYPTO_SymmetricInitializationVector siv;
+
+ 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);
+}
+
+
+/**
+ * Create key material by doing ECDH on the local and remote ephemeral keys.
+ *
+ * @param key_material Where to store the key material.
+ * @param ephemeral_key Peer's public ephemeral key.
+ */
+void
+derive_key_material (struct GNUNET_HashCode *key_material,
+ const struct GNUNET_CRYPTO_EcdhePublicKey *ephemeral_key)
+{
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_ecc_ecdh (my_ephemeral_key,
+ ephemeral_key,
+ key_material))
+ {
+ GNUNET_break (0);
+ }
+}
+
+/**
+ * Create a symmetic key from the identities of both ends and the key material
+ * from ECDH.
+ *
+ * @param key Destination for the generated key.
+ * @param sender ID of the peer that will encrypt with @c key.
+ * @param receiver ID of the peer that will decrypt with @c key.
+ * @param key_material Hash created with ECDH with the ephemeral keys.
+ */
+void
+derive_symmertic (struct GNUNET_CRYPTO_SymmetricSessionKey *key,
+ const struct GNUNET_PeerIdentity *sender,
+ const struct GNUNET_PeerIdentity *receiver,
+ const struct GNUNET_HashCode *key_material)
+{
+ const char salt[] = "MESH kx salt";
+
+ GNUNET_CRYPTO_kdf (key, sizeof (struct GNUNET_CRYPTO_SymmetricSessionKey),
+ salt, sizeof (salt),
+ key_material, sizeof (struct GNUNET_HashCode),
+ sender, sizeof (struct GNUNET_PeerIdentity),
+ receiver, sizeof (struct GNUNET_PeerIdentity),
+ NULL);
+}
+
+/**
+ * Pick a connection on which send the next data message.
+ *
+ * @param t Tunnel on which to send the message.
+ *
+ * @return The connection on which to send the next message.
+ */
+static struct MeshConnection *
+tunnel_get_connection (struct MeshTunnel3 *t)
+{
+ struct MeshTConnection *iter;
+ struct MeshConnection *best;
+ unsigned int qn;
+ unsigned int lowest_q;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "tunnel_get_connection %s\n", GMP_2s (t->peer));
+ best = NULL;
+ lowest_q = UINT_MAX;
+ for (iter = t->connection_head; NULL != iter; iter = iter->next)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " connection %s: %u\n",
+ GMC_2s (iter->c), GMC_get_state (iter->c));
+ if (MESH_CONNECTION_READY == GMC_get_state (iter->c))
+ {
+ qn = GMC_get_qn (iter->c, GMC_is_origin (iter->c, GNUNET_YES));
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " q_n %u, \n", qn);
+ if (qn < lowest_q)
+ {
+ best = iter->c;
+ lowest_q = qn;
+ }
+ }
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " selected: connection %s\n", GMC_2s (best));
+ return best;
+}
+
+
+/**
+ * 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 q Connection queue handle (unused).
+ * @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);
+}
+
+
+/**
+ * Delete a queued message: either was sent or the channel was destroyed
+ * before the tunnel's key exchange had a chance to finish.
+ *
+ * @param tq Queue handle.
+ */
+static void
+unqueue_data (struct MeshTunnelDelayed *tq)
+{
+ GNUNET_CONTAINER_DLL_remove (tq->t->tq_head, tq->t->tq_tail, tq);
+ GNUNET_free (tq);
+}
+
+
+/**
+ * Cache a message to be sent once tunnel is online.
+ *
+ * @param t Tunnel to hold the message.
+ * @param msg Message itself (copy will be made).
+ */
+static struct MeshTunnelDelayed *
+queue_data (struct MeshTunnel3 *t, const struct GNUNET_MessageHeader *msg)
+{
+ struct MeshTunnelDelayed *tqd;
+ uint16_t size = ntohs (msg->size);
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "queue data on Tunnel %s\n", GMT_2s (t));
+
+ if (GNUNET_YES == is_ready (t))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+
+ tqd = GNUNET_malloc (sizeof (struct MeshTunnelDelayed) + size);
+
+ tqd->t = t;
+ memcpy (&tqd[1], msg, size);
+ GNUNET_CONTAINER_DLL_insert_tail (t->tq_head, t->tq_tail, tqd);
+ return tqd;
+}
+
+
+
+/**
+ * Sends an already built message on a tunnel, encrypting it and
+ * choosing the best connection.
+ *
+ * @param message Message to send. Function modifies it.
+ * @param t Tunnel on which this message is transmitted.
+ * @param force Force the tunnel to take the message (buffer overfill).
+ * @param cont Continuation to call once message is really sent.
+ * @param cont_cls Closure for @c cont.
+ * @param existing_q In case this a transmission of previously queued data,
+ * this should be TunnelQueue given to the client.
+ * Otherwise, NULL.
+ *
+ * @return Handle to cancel message. NULL if @c cont is NULL.
+ */
+static struct MeshTunnel3Queue *
+send_prebuilt_message (const struct GNUNET_MessageHeader *message,
+ struct MeshTunnel3 *t, int force,
+ GMT_sent cont, void *cont_cls,
+ struct MeshTunnel3Queue *existing_q)
+{
+ struct MeshTunnel3Queue *tq;
+ struct MeshConnection *c;
+ struct GNUNET_MESH_Encrypted *msg;
+ size_t size = ntohs (message->size);
+ char cbuf[sizeof (struct GNUNET_MESH_Encrypted) + size];
+ uint32_t iv;
+ uint16_t type;
+ int fwd;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "GMT Send on Tunnel %s\n", GMT_2s (t));
+
+ if (GNUNET_NO == is_ready (t))
+ {
+ struct MeshTunnelDelayed *tqd;
+ /* A non null existing_q indicates sending of queued data.
+ * Should only happen after tunnel becomes ready.
+ */
+ GNUNET_assert (NULL == existing_q);
+ tqd = queue_data (t, message);
+ if (NULL == cont)
+ return NULL;
+ tq = GNUNET_new (struct MeshTunnel3Queue);
+ tq->tqd = tqd;
+ tqd->tq = tq;
+ tq->cont = cont;
+ tq->cont_cls = cont_cls;
+ return tq;
+ }
+
+ GNUNET_assert (GNUNET_NO == GMT_is_loopback (t));
+
+ iv = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX);
+ msg = (struct GNUNET_MESH_Encrypted *) cbuf;
+ msg->header.type = htons (GNUNET_MESSAGE_TYPE_MESH_ENCRYPTED);
+ msg->iv = iv;
+ GNUNET_assert (t_encrypt (t, &msg[1], message, size, iv) == size);
+ msg->header.size = htons (sizeof (struct GNUNET_MESH_Encrypted) + size);
+ c = tunnel_get_connection (t);
+ if (NULL == c)
+ {
+ GNUNET_break (GNUNET_YES == t->destroy);
+ 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:
+ msg->cid = *GMC_get_id (c);
+ msg->ttl = htonl (default_ttl);
+ break;
+ default:
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "unkown type %s\n",
+ GM_m2s (type));
+ GNUNET_break (0);
+ }
+
+ fwd = GMC_is_origin (c, GNUNET_YES);
+
+ if (NULL == cont)
+ {
+ (void) GMC_send_prebuilt_message (&msg->header, c, fwd, force, NULL, NULL);
+ return NULL;
+ }
+ if (NULL == existing_q)
+ {
+ tq = GNUNET_new (struct MeshTunnel3Queue); /* FIXME valgrind: leak*/
+ }
+ else
+ {
+ tq = existing_q;
+ tq->tqd = NULL;
+ }
+ tq->cq = GMC_send_prebuilt_message (&msg->header, c, fwd, force,
+ &message_sent, tq);
+ tq->cont = cont;
+ tq->cont_cls = cont_cls;
+
+ return tq;
+}
+
+
+/**
+ * Send all cached messages that we can, tunnel is online.
+ *
+ * @param t Tunnel that holds the messages. Cannot be loopback.
+ */
+static void
+send_queued_data (struct MeshTunnel3 *t)
+{
+ struct MeshTunnelDelayed *tqd;
+ struct MeshTunnelDelayed *next;
+ unsigned int room;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "GMT_send_queued_data on tunnel %s\n",
+ GMT_2s (t));
+
+ if (GMT_is_loopback (t))
+ {
+ GNUNET_break (0);
+ return;
+ }
+
+ if (GNUNET_NO == is_ready (t))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " not ready yet: %s/%s\n",
+ estate2s (t->estate), cstate2s (t->cstate));
+ return;
+ }
+
+ room = GMT_get_connections_buffer (t);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " buffer space: %u\n", room);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " tq head: %p\n", t->tq_head);
+ for (tqd = t->tq_head; NULL != tqd && room > 0; tqd = next)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " sending queued data\n");
+ next = tqd->next;
+ room--;
+ send_prebuilt_message ((struct GNUNET_MessageHeader *) &tqd[1],
+ tqd->t, GNUNET_YES,
+ NULL != tqd->tq ? tqd->tq->cont : NULL,
+ NULL != tqd->tq ? tqd->tq->cont_cls : NULL,
+ tqd->tq);
+ unqueue_data (tqd);
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "GMT_send_queued_data end\n", GMP_2s (t->peer));
+}
+
+
+/**
+ * Sends key exchange message on a tunnel, choosing the best connection.
+ * Should not be called on loopback tunnels.
+ *
+ * @param t Tunnel on which this message is transmitted.
+ * @param message Message to send. Function modifies it.
+ */
+static void
+send_kx (struct MeshTunnel3 *t,
+ const struct GNUNET_MessageHeader *message)
+{
+ struct MeshConnection *c;
+ struct GNUNET_MESH_KX *msg;
+ size_t size = ntohs (message->size);
+ char cbuf[sizeof (struct GNUNET_MESH_KX) + size];
+ uint16_t type;
+ int fwd;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "GMT KX on Tunnel %s\n", GMT_2s (t));
+
+ /* Avoid loopback. */
+ if (GMT_is_loopback (t))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " loopback!\n");
+ GNUNET_break (0);
+ return;
+ }
+
+ /* Must have a connection. */
+ if (NULL == t->connection_head)
+ {
+ GNUNET_break (MESH_TUNNEL3_SEARCHING == t->cstate);
+ return;
+ }