#include "platform.h"
#include "gnunet_util_lib.h"
+#include "gnunet_statistics_service.h"
+
#include "mesh_protocol_enc.h"
#include "gnunet-service-mesh_tunnel.h"
#include "gnunet-service-mesh_connection.h"
#include "gnunet-service-mesh_channel.h"
+#include "gnunet-service-mesh_peer.h"
#include "mesh_path.h"
+#define LOG(level, ...) GNUNET_log_from(level,"mesh-tun",__VA_ARGS__)
-/**
- * All the states a tunnel can be in.
- */
-enum MeshTunnelState
-{
- /**
- * Uninitialized status, should never appear in operation.
- */
- MESH_TUNNEL_NEW,
-
- /**
- * Path to the peer not known yet
- */
- MESH_TUNNEL_SEARCHING,
-
- /**
- * Request sent, not yet answered.
- */
- MESH_TUNNEL_WAITING,
-
- /**
- * Peer connected and ready to accept data
- */
- MESH_TUNNEL_READY,
-
- /**
- * Peer connected previosly but not responding
- */
- MESH_TUNNEL_RECONNECTING
-};
-
+#define START_FUNCTION LOG(GNUNET_ERROR_TYPE_DEBUG, "%s start\n", __FUNCTION__)
+#define END_FUNCTION LOG(GNUNET_ERROR_TYPE_DEBUG, "%s end\n", __FUNCTION__)
/******************************************************************************/
/******************************** STRUCTS **********************************/
/******************************************************************************/
+struct MeshTChannel
+{
+ struct MeshTChannel *next;
+ struct MeshTChannel *prev;
+ struct MeshChannel *ch;
+};
+
+struct MeshTConnection
+{
+ struct MeshTConnection *next;
+ struct MeshTConnection *prev;
+ struct MeshConnection *c;
+};
+
/**
* Struct containing all information regarding a tunnel to a peer.
*/
-struct MeshTunnel2
+struct MeshTunnel3
{
/**
* Endpoint of the tunnel.
/**
* State of the tunnel.
*/
- enum MeshTunnelState state;
+ enum MeshTunnel3State state;
/**
* Local peer ephemeral private key
*/
- struct GNUNET_CRYPTO_EccPrivateKey *my_eph_key;
+ struct GNUNET_CRYPTO_EddsaPrivateKey *my_eph_key;
/**
* Local peer ephemeral public key
*/
- struct GNUNET_CRYPTO_EccPublicSignKey *my_eph;
+ struct GNUNET_CRYPTO_EddsaPublicKey *my_eph;
/**
* Remote peer's public key.
*/
- struct GNUNET_CRYPTO_EccPublicSignKey *peers_eph;
+ struct GNUNET_CRYPTO_EddsaPublicKey *peers_eph;
/**
* Encryption ("our") key.
/**
* Paths that are actively used to reach the destination peer.
*/
- struct MeshConnection *connection_head;
- struct MeshConnection *connection_tail;
+ struct MeshTConnection *connection_head;
+ struct MeshTConnection *connection_tail;
/**
* Next connection number.
/**
* Channels inside this tunnel.
*/
- struct MeshChannel *channel_head;
- struct MeshChannel *channel_tail;
+ struct MeshTChannel *channel_head;
+ struct MeshTChannel *channel_tail;
/**
* Channel ID for the next created channel.
*/
MESH_ChannelNumber next_chid;
- /**
- * Channel ID for the next incoming channel.
- */
- MESH_ChannelNumber next_local_chid;
-
/**
* Pending message count.
*/
/******************************************************************************/
/**
- * Default TTL for payload packets.
+ * Global handle to the statistics service.
*/
-static unsigned long long default_ttl;
+extern struct GNUNET_STATISTICS_Handle *stats;
/**
* Local peer own ID (memory efficient handle).
*/
-static GNUNET_PEER_Id my_short_id;
+extern GNUNET_PEER_Id myid;
/**
* Local peer own ID (full value).
*/
-const static struct GNUNET_PeerIdentity *my_full_id;
+extern struct GNUNET_PeerIdentity my_full_id;
+
+/**
+ * Default TTL for payload packets.
+ */
+static unsigned long long default_ttl;
/**
* Own private key.
*/
-const static struct GNUNET_CRYPTO_EccPrivateKey *my_private_key;
+const static struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key;
/******************************************************************************/
/******************************** STATIC ***********************************/
/******************************************************************************/
-
/**
* Get string description for tunnel state.
*
* @return String representation.
*/
static const char *
-GNUNET_MESH_DEBUG_TS2S (enum MeshTunnelState s)
+GMT_state2s (enum MeshTunnel3State s)
{
static char buf[128];
switch (s)
{
- case MESH_TUNNEL_NEW:
- return "MESH_TUNNEL_NEW";
- case MESH_TUNNEL_SEARCHING:
- return "MESH_TUNNEL_SEARCHING";
- case MESH_TUNNEL_WAITING:
- return "MESH_TUNNEL_WAITING";
- case MESH_TUNNEL_READY:
- return "MESH_TUNNEL_READY";
- case MESH_TUNNEL_RECONNECTING:
- return "MESH_TUNNEL_RECONNECTING";
+ 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";
+ case MESH_TUNNEL3_RECONNECTING:
+ return "MESH_TUNNEL3_RECONNECTING";
default:
sprintf (buf, "%u (UNKNOWN STATE)", s);
}
}
-
/**
* Pick a connection on which send the next data message.
*
* @return The connection on which to send the next message.
*/
static struct MeshConnection *
-tunnel_get_connection (struct MeshTunnel2 *t, int fwd)
+tunnel_get_connection (struct MeshTunnel3 *t, int fwd)
{
- struct MeshConnection *c;
+ struct MeshTConnection *iter;
struct MeshConnection *best;
- struct MeshFlowControl *fc;
+ unsigned int qn;
unsigned int lowest_q;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "tunnel_get_connection %s\n",
- peer2s (t->peer));
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "tunnel_get_connection %s\n", GMP_2s (t->peer));
best = NULL;
lowest_q = UINT_MAX;
- for (c = t->connection_head; NULL != c; c = c->next)
+ for (iter = t->connection_head; NULL != iter; iter = iter->next)
{
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " connection %s: %u\n",
- GNUNET_h2s (&c->id), c->state);
- if (MESH_CONNECTION_READY == c->state)
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " connection %s: %u\n",
+ GNUNET_h2s (GMC_get_id (iter->c)), GMC_get_state (iter->c));
+ if (MESH_CONNECTION_READY == GMC_get_state (iter->c))
{
- fc = fwd ? &c->fwd_fc : &c->bck_fc;
- if (NULL == fc)
- {
- GNUNET_break (0);
- continue;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " q_n %u, \n", fc->queue_n);
- if (fc->queue_n < lowest_q)
+ qn = GMC_get_qn (iter->c, fwd);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " q_n %u, \n", qn);
+ if (qn < lowest_q)
{
- best = c;
- lowest_q = fc->queue_n;
+ best = iter->c;
+ lowest_q = qn;
}
}
}
/**
- * Get the total buffer space for a tunnel.
+ * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME
+ * Encrypt data with the tunnel key.
*
- * @param t Tunnel.
- * @param fwd Is this for FWD traffic?
+ * @param t Tunnel whose key to use.
+ * @param dst Destination for the GMT_encrypted data.
+ * @param src Source of the plaintext.
+ * @param size Size of the plaintext.
+ * @param iv Initialization Vector to use.
+ * @param fwd Is this a fwd message?
+ */
+static void
+GMT_encrypt (struct MeshTunnel3 *t,
+ void *dst, const void *src,
+ size_t size, uint64_t iv, int fwd)
+{
+ memcpy (dst, src, size);
+}
+
+
+/**
+ * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME
+ * Decrypt data with the tunnel key.
*
- * @return Buffer space offered by all connections in the tunnel.
+ * @param t Tunnel whose key to use.
+ * @param dst Destination for the plaintext.
+ * @param src Source of the GMT_encrypted data.
+ * @param size Size of the GMT_encrypted data.
+ * @param iv Initialization Vector to use.
+ * @param fwd Is this a fwd message?
*/
-static unsigned int
-tunnel_get_buffer (struct MeshTunnel2 *t, int fwd)
+static void
+GMT_decrypt (struct MeshTunnel3 *t,
+ void *dst, const void *src,
+ size_t size, uint64_t iv, int fwd)
{
- struct MeshConnection *c;
- struct MeshFlowControl *fc;
- unsigned int buffer;
+ memcpy (dst, src, size);
+}
- c = t->connection_head;
- buffer = 0;
- /* If terminal, return biggest channel buffer */
- if (NULL == c || GMC_is_terminal (c, fwd))
+void
+handle_data (struct MeshTunnel3 *t,
+ const struct GNUNET_MESH_Data *msg,
+ int fwd)
+{
+ struct MeshChannel *ch;
+ uint16_t type;
+ size_t size;
+
+ /* Check size */
+ size = ntohs (msg->header.size);
+ if (size <
+ sizeof (struct GNUNET_MESH_Data) +
+ sizeof (struct GNUNET_MessageHeader))
{
- struct MeshChannel *ch;
- unsigned int ch_buf;
+ GNUNET_break (0);
+ return;
+ }
+ type = ntohs (msg->header.type);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "got a %s message\n",
+ GNUNET_MESH_DEBUG_M2S (type));
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " payload of type %s\n",
+ GNUNET_MESH_DEBUG_M2S (ntohs (msg[1].header.type)));
+
+ /* Check channel */
+ ch = GMT_get_channel (t, ntohl (msg->chid));
+ if (NULL == ch)
+ {
+ GNUNET_STATISTICS_update (stats, "# data on unknown channel",
+ 1, GNUNET_NO);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "WARNING channel %u unknown\n",
+ ntohl (msg->chid));
+ return;
+ }
- if (NULL == t->channel_head)
- return 64;
+ GMT_change_state (t, MESH_TUNNEL3_READY);
+ GMCH_handle_data (ch, msg, fwd);
+}
- for (ch = t->channel_head; NULL != ch; ch = ch->next)
- {
- ch_buf = GMCH_get_buffer (ch, fwd);
- if (ch_buf > buffer)
- buffer = ch_buf;
- }
- return buffer;
+void
+handle_data_ack (struct MeshTunnel3 *t,
+ const struct GNUNET_MESH_DataACK *msg,
+ int fwd)
+{
+ struct MeshChannel *ch;
+ size_t size;
+
+ /* Check size */
+ size = ntohs (msg->header.size);
+ if (size != sizeof (struct GNUNET_MESH_DataACK))
+ {
+ GNUNET_break (0);
+ return;
}
- /* If not terminal, return sum of connection buffers */
- while (NULL != c)
+ /* Check channel */
+ ch = GMT_get_channel (t, ntohl (msg->chid));
+ if (NULL == ch)
{
- if (c->state != MESH_CONNECTION_READY)
- {
- c = c->next;
- continue;
- }
+ GNUNET_STATISTICS_update (stats, "# data ack on unknown channel",
+ 1, GNUNET_NO);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "WARNING channel %u unknown\n",
+ ntohl (msg->chid));
+ return;
+ }
- fc = fwd ? &c->fwd_fc : &c->bck_fc;
- buffer += fc->queue_max - fc->queue_n;
- c = c->next;
+ GMCH_handle_data_ack (ch, msg, fwd);
+}
+
+void
+handle_ch_create (struct MeshTunnel3 *t,
+ const struct GNUNET_MESH_ChannelCreate *msg,
+ int fwd)
+{
+ ;
+
+ struct MeshTChannel *tch;
+ struct MeshChannel *ch;
+ size_t size;
+
+ /* Check size */
+ size = ntohs (msg->header.size);
+ if (size != sizeof (struct GNUNET_MESH_ChannelCreate))
+ {
+ GNUNET_break (0);
+ return;
}
- return buffer;
+ /* Check channel */
+ ch = GMT_get_channel (t, ntohl (msg->chid));
+ if (NULL != ch)
+ {
+ /* Probably a retransmission, safe to ignore */
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " already exists...\n");
+ }
+ else
+ {
+ ch = GMCH_handle_create (t, msg, fwd);
+ }
+
+ tch = GNUNET_new (struct MeshTChannel);
+ tch->ch = ch;
+ GNUNET_CONTAINER_DLL_insert (t->channel_head, t->channel_tail, tch);
+
+ ;
+}
+
+void
+handle_ch_ack (struct MeshTunnel3 *t,
+ const struct GNUNET_MESH_ChannelManage *msg,
+ int fwd)
+{
+ 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 ack on unknown channel",
+ 1, GNUNET_NO);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "WARNING channel %u unknown\n",
+ ntohl (msg->chid));
+ return;
+ }
+
+ GMCH_handle_ack (ch, msg, fwd);
+}
+
+void
+handle_ch_destroy (struct MeshTunnel3 *t,
+ const struct GNUNET_MESH_ChannelManage *msg,
+ int fwd)
+{
+ 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)
+ {
+ /* Probably a retransmission, safe to ignore */
+ return;
+ }
+
+ GMCH_handle_destroy (ch, msg, fwd);
}
/**
- * Send all cached messages that we can, tunnel is online.
+ * Demultiplex by message type and call appropriate handler for a message
+ * towards a channel of a local tunnel.
*
- * @param t Tunnel that holds the messages.
- * @param fwd Is this fwd?
+ * @param t Tunnel this message came on.
+ * @param msgh Message header.
+ * @param fwd Is this message fwd?
*/
static void
-tunnel_send_queued_data (struct MeshTunnel2 *t, int fwd)
+handle_decrypted (struct MeshTunnel3 *t,
+ const struct GNUNET_MessageHeader *msgh,
+ int fwd)
{
- struct MeshTunnelQueue *tq;
- struct MeshTunnelQueue *next;
- unsigned int room;
+ uint16_t type;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "tunnel_send_queued_data on tunnel %s\n",
- peer2s (t->peer));
- room = tunnel_get_buffer (t, fwd);
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " buffer space: %u\n", room);
- for (tq = t->tq_head; NULL != tq && room > 0; tq = next)
+ type = ntohs (msgh->type);
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Got a %s message!\n",
+ GNUNET_MESH_DEBUG_M2S (type));
+
+ switch (type)
{
- next = tq->next;
- room--;
- GNUNET_CONTAINER_DLL_remove (t->tq_head, t->tq_tail, tq);
- GMCH_send_prebuilt_message ((struct GNUNET_MessageHeader *) &tq[1],
- tq->ch, fwd);
+ case GNUNET_MESSAGE_TYPE_MESH_DATA:
+ /* Don't send hop ACK, wait for client to ACK */
+ handle_data (t, (struct GNUNET_MESH_Data *) msgh, fwd);
+ break;
- GNUNET_free (tq);
+ case GNUNET_MESSAGE_TYPE_MESH_DATA_ACK:
+ handle_data_ack (t, (struct GNUNET_MESH_DataACK *) msgh, fwd);
+ break;
+
+ case GNUNET_MESSAGE_TYPE_MESH_CHANNEL_CREATE:
+ handle_ch_create (t,
+ (struct GNUNET_MESH_ChannelCreate *) msgh,
+ fwd);
+ break;
+
+ case GNUNET_MESSAGE_TYPE_MESH_CHANNEL_ACK:
+ handle_ch_ack (t,
+ (struct GNUNET_MESH_ChannelManage *) msgh,
+ fwd);
+ break;
+
+ case GNUNET_MESSAGE_TYPE_MESH_CHANNEL_DESTROY:
+ handle_ch_destroy (t,
+ (struct GNUNET_MESH_ChannelManage *) msgh,
+ fwd);
+ break;
+
+ default:
+ GNUNET_break_op (0);
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "end-to-end message not known (%u)\n",
+ ntohs (msgh->type));
+ }
+}
+
+/******************************************************************************/
+/******************************** API ***********************************/
+/******************************************************************************/
+
+
+/**
+ * Decrypt and demultiplex by message type. Call appropriate handler
+ * for every message.
+ *
+ * @param t Tunnel this message came on.
+ * @param msg Encrypted message.
+ * @param fwd Is this message fwd?
+ */
+void
+GMT_handle_encrypted (struct MeshTunnel3 *t,
+ const struct GNUNET_MESH_Encrypted *msg,
+ int fwd)
+{
+ size_t size = ntohs (msg->header.size);
+ size_t payload_size = size - sizeof (struct GNUNET_MESH_Encrypted);
+ char cbuf[payload_size];
+ struct GNUNET_MessageHeader *msgh;
+ unsigned int off;
+
+ GMT_decrypt (t, cbuf, &msg[1], payload_size, msg->iv, fwd);
+ off = 0;
+ while (off < payload_size)
+ {
+ msgh = (struct GNUNET_MessageHeader *) &cbuf[off];
+ handle_decrypted (t, msgh, fwd);
+ off += ntohs (msgh->size);
}
}
* @param fwd Is this fwd?
*/
void
-GMT_queue_data (struct MeshTunnel2 *t,
+GMT_queue_data (struct MeshTunnel3 *t,
struct MeshChannel *ch,
struct GNUNET_MessageHeader *msg,
int fwd)
memcpy (&tq[1], msg, size);
GNUNET_CONTAINER_DLL_insert_tail (t->tq_head, t->tq_tail, tq);
- if (MESH_TUNNEL_READY == t->state)
- tunnel_send_queued_data (t, fwd);
+ if (MESH_TUNNEL3_READY == t->state)
+ GMT_send_queued_data (t, fwd);
}
+/**
+ * Send all cached messages that we can, tunnel is online.
+ *
+ * @param t Tunnel that holds the messages.
+ * @param fwd Is this fwd?
+ */
+void
+GMT_send_queued_data (struct MeshTunnel3 *t, int fwd)
+{
+ struct MeshTunnelQueue *tq;
+ struct MeshTunnelQueue *next;
+ unsigned int room;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "GMT_send_queued_data on tunnel %s\n",
+ GMT_2s (t));
+ room = GMT_get_buffer (t, fwd);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " buffer space: %u\n", room);
+ for (tq = t->tq_head; NULL != tq && room > 0; tq = next)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, " data on channel %s\n", GMCH_2s (tq->ch));
+ next = tq->next;
+ room--;
+ GNUNET_CONTAINER_DLL_remove (t->tq_head, t->tq_tail, tq);
+ GMCH_send_prebuilt_message ((struct GNUNET_MessageHeader *) &tq[1],
+ tq->ch, fwd);
+
+ GNUNET_free (tq);
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "GMT_send_queued_data end\n",
+ GMP_2s (t->peer));
+}
-/******************************************************************************/
-/******************************** API ***********************************/
-/******************************************************************************/
/**
* Initialize the tunnel subsystem.
*/
void
GMT_init (const struct GNUNET_CONFIGURATION_Handle *c,
- const struct GNUNET_PeerIdentity *id,
- const struct GNUNET_CRYPTO_EccPrivateKey *key)
+ const struct GNUNET_CRYPTO_EddsaPrivateKey *key)
{
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "init\n");
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_number (c, "MESH", "DEFAULT_TTL",
&default_ttl))
"MESH", "DEFAULT_TTL", "USING DEFAULT");
default_ttl = 64;
}
- my_full_id = id;
my_private_key = key;
- my_short_id = GNUNET_PEER_intern (my_full_id);
}
void
GMT_shutdown (void)
{
- GNUNET_PEER_change_rc (my_short_id, -1);
}
/**
* Create a tunnel.
+ *
+ * @param destination Peer this tunnel is towards.
*/
-struct MeshTunnel2 *
-GMT_new (void)
+struct MeshTunnel3 *
+GMT_new (struct MeshPeer *destination)
{
- struct MeshTunnel2 *t;
+ struct MeshTunnel3 *t;
- t = GNUNET_new (struct MeshTunnel2);
+ t = GNUNET_new (struct MeshTunnel3);
t->next_chid = 0;
- t->next_local_chid = GNUNET_MESH_LOCAL_CHANNEL_ID_SERV;
+ t->peer = destination;
// if (GNUNET_OK !=
// GNUNET_CONTAINER_multihashmap_put (tunnels, tid, t,
// GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
}
-
/**
* Change the tunnel state.
*
* @param state New state.
*/
void
-GMT_change_state (struct MeshTunnel2* t, enum MeshTunnelState state)
+GMT_change_state (struct MeshTunnel3* t, enum MeshTunnel3State state)
{
if (NULL == t)
return;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
"Tunnel %s state was %s\n",
- peer2s (t->peer),
- GNUNET_MESH_DEBUG_TS2S (t->state));
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ GMP_2s (t->peer),
+ GMT_state2s (t->state));
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
"Tunnel %s state is now %s\n",
- peer2s (t->peer),
- GNUNET_MESH_DEBUG_TS2S (state));
+ GMP_2s (t->peer),
+ GMT_state2s (state));
t->state = state;
+ if (MESH_TUNNEL3_READY == state && 3 <= GMT_count_connections (t))
+ {
+ GMP_stop_search (t->peer);
+ }
}
* @param c Connection.
*/
void
-GMT_add_connection (struct MeshTunnel2 *t, struct MeshConnection *c)
+GMT_add_connection (struct MeshTunnel3 *t, struct MeshConnection *c)
{
- struct MeshConnection *aux;
- c->t = t;
+ struct MeshTConnection *aux;
+
for (aux = t->connection_head; aux != NULL; aux = aux->next)
- if (aux == c)
+ if (aux->c == c)
return;
- GNUNET_CONTAINER_DLL_insert_tail (t->connection_head, t->connection_tail, c);
-}
-
+ aux = GNUNET_new (struct MeshTConnection);
+ aux->c = c;
+ GNUNET_CONTAINER_DLL_insert_tail (t->connection_head, t->connection_tail, aux);
+}
/**
- * Tunnel is empty: destroy it.
- *
- * Notifies all connections about the destruction.
+ * Remove a connection from a tunnel.
*
- * @param t Tunnel to destroy.
+ * @param t Tunnel.
+ * @param c Connection.
*/
void
-GMT_destroy_empty (struct MeshTunnel2 *t)
+GMT_remove_connection (struct MeshTunnel3 *t, struct MeshConnection *c)
{
- struct MeshConnection *c;
+ struct MeshTConnection *aux;
+
+ for (aux = t->connection_head; aux != NULL; aux = aux->next)
+ if (aux->c == c)
+ {
+ GNUNET_CONTAINER_DLL_remove (t->connection_head, t->connection_tail, aux);
+ GNUNET_free (aux);
+ return;
+ }
+}
+
+
+/**
+ * Add a channel to a tunnel.
+ *
+ * @param t Tunnel.
+ * @param ch Channel.
+ */
+void
+GMT_add_channel (struct MeshTunnel3 *t, struct MeshChannel *ch)
+{
+ struct MeshTChannel *aux;
+
+ for (aux = t->channel_head; aux != NULL; aux = aux->next)
+ if (aux->ch == ch)
+ return;
+
+ aux = GNUNET_new (struct MeshTChannel);
+ aux->ch = ch;
+ GNUNET_CONTAINER_DLL_insert_tail (t->channel_head, t->channel_tail, aux);
+}
+
+
+/**
+ * Remove a channel from a tunnel.
+ *
+ * @param t Tunnel.
+ * @param ch Channel.
+ */
+void
+GMT_remove_channel (struct MeshTunnel3 *t, struct MeshChannel *ch)
+{
+ struct MeshTChannel *aux;
+
+ for (aux = t->channel_head; aux != NULL; aux = aux->next)
+ if (aux->ch == ch)
+ {
+ GNUNET_CONTAINER_DLL_remove (t->channel_head, t->channel_tail, aux);
+ GNUNET_free (aux);
+ return;
+ }
+}
+
+
+/**
+ * Search for a channel by global ID.
+ *
+ * @param t Tunnel containing the channel.
+ * @param chid Public channel number.
+ *
+ * @return channel handler, NULL if doesn't exist
+ */
+struct MeshChannel *
+GMT_get_channel (struct MeshTunnel3 *t, MESH_ChannelNumber chid)
+{
+ struct MeshTChannel *iter;
+
+ if (NULL == t)
+ return NULL;
- for (c = t->connection_head; NULL != c; c = c->next)
+ for (iter = t->channel_head; NULL != iter; iter = iter->next)
{
- if (GNUNET_NO == c->destroy)
- GMC_send_destroy (c);
+ if (GMCH_get_id (iter->ch) == chid)
+ break;
+ }
+
+ return NULL == iter ? NULL : iter->ch;
+}
+
+
+/**
+ * Tunnel is empty: destroy it.
+ *
+ * Notifies all connections about the destruction.
+ *
+ * @param t Tunnel to destroy.
+ */
+void
+GMT_destroy_empty (struct MeshTunnel3 *t)
+{
+ struct MeshTConnection *iter;
+
+ for (iter = t->connection_head; NULL != iter; iter = iter->next)
+ {
+ GMC_send_destroy (iter->c);
}
if (0 == t->pending_messages)
* @param t Tunnel to destroy if empty.
*/
void
-GMT_destroy_if_empty (struct MeshTunnel2 *t)
+GMT_destroy_if_empty (struct MeshTunnel3 *t)
{
- if (1 <= GMCH_count (t->channel_head))
+ if (1 < GMT_count_channels (t))
return;
GMT_destroy_empty (t);
}
-
/**
* Destroy the tunnel.
*
* @param t The tunnel to destroy.
*/
void
-GMT_destroy (struct MeshTunnel2 *t)
+GMT_destroy (struct MeshTunnel3 *t)
{
- struct MeshConnection *c;
- struct MeshConnection *next;
+ struct MeshTConnection *iter;
+ struct MeshTConnection *next;
if (NULL == t)
return;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "destroying tunnel %s\n",
- peer2s (t->peer));
+ 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);
- for (c = t->connection_head; NULL != c; c = next)
+ for (iter = t->connection_head; NULL != iter; iter = next)
{
- next = c->next;
- GMC_destroy (c);
+ next = iter->next;
+ GMC_destroy (iter->c);
+ GNUNET_free (iter);
}
GNUNET_STATISTICS_update (stats, "# tunnels", -1, GNUNET_NO);
- t->peer->tunnel = NULL;
+ GMP_set_tunnel (t->peer, NULL);
GNUNET_free (t);
}
-/**
- * Demultiplex by message type and call appropriate handler for a message
- * towards a channel of a local tunnel.
- *
- * @param t Tunnel this message came on.
- * @param msgh Message header.
- * @param fwd Is this message fwd?
- */
-void
-GMT_handle_decrypted (struct MeshTunnel2 *t,
- const struct GNUNET_MessageHeader *msgh,
- int fwd)
-{
- switch (ntohs (msgh->type))
- {
- case GNUNET_MESSAGE_TYPE_MESH_DATA:
- /* Don't send hop ACK, wait for client to ACK */
- handle_data (t, (struct GNUNET_MESH_Data *) msgh, fwd);
- break;
-
- case GNUNET_MESSAGE_TYPE_MESH_DATA_ACK:
- handle_data_ack (t, (struct GNUNET_MESH_DataACK *) msgh, fwd);
- break;
-
- case GNUNET_MESSAGE_TYPE_MESH_CHANNEL_CREATE:
- handle_channel_create (t,
- (struct GNUNET_MESH_ChannelCreate *) msgh,
- fwd);
- break;
-
- case GNUNET_MESSAGE_TYPE_MESH_CHANNEL_ACK:
- handle_channel_ack (t,
- (struct GNUNET_MESH_ChannelManage *) msgh,
- fwd);
- break;
-
- case GNUNET_MESSAGE_TYPE_MESH_CHANNEL_DESTROY:
- handle_channel_destroy (t,
- (struct GNUNET_MESH_ChannelManage *) msgh,
- fwd);
- break;
-
- default:
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "end-to-end message not known (%u)\n",
- ntohs (msgh->type));
- }
-}
-
-
-/**
- * Notifies a tunnel that a connection has broken that affects at least
- * some of its peers. Sends a notification towards the root of the tree.
- * In case the peer is the owner of the tree, notifies the client that owns
- * the tunnel and tries to reconnect.
- *
- * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME
- *
- * @param t Tunnel affected.
- * @param p1 Peer that got disconnected from p2.
- * @param p2 Peer that got disconnected from p1.
- *
- * @return Short ID of the peer disconnected (either p1 or p2).
- * 0 if the tunnel remained unaffected.
- */
-GNUNET_PEER_Id
-GMT_notify_connection_broken (struct MeshTunnel2* t,
- GNUNET_PEER_Id p1, GNUNET_PEER_Id p2)
-{
-// if (myid != p1 && myid != p2) FIXME
-// {
-// return;
-// }
-//
-// if (tree_get_predecessor (t->tree) != 0)
-// {
-// /* We are the peer still connected, notify owner of the disconnection. */
-// struct GNUNET_MESH_PathBroken msg;
-// struct GNUNET_PeerIdentity neighbor;
-//
-// msg.header.size = htons (sizeof (msg));
-// msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_PATH_BROKEN);
-// GNUNET_PEER_resolve (t->id.oid, &msg.oid);
-// msg.tid = htonl (t->id.tid);
-// msg.peer1 = my_full_id;
-// GNUNET_PEER_resolve (pid, &msg.peer2);
-// GNUNET_PEER_resolve (tree_get_predecessor (t->tree), &neighbor);
-// send_prebuilt_message (&msg.header, &neighbor, t);
-// }
- return 0;
-}
/**
* @brief Use the given path for the tunnel.
* @return Connection created.
*/
struct MeshConnection *
-GMT_use_path (struct MeshTunnel2 *t, struct MeshPeerPath *p)
+GMT_use_path (struct MeshTunnel3 *t, struct MeshPeerPath *p)
{
struct MeshConnection *c;
struct GNUNET_HashCode cid;
- struct MeshPeer *peer;
unsigned int own_pos;
if (NULL == t || NULL == p)
return NULL;
}
- GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_NONCE, &cid);
-
- c = GMC_new (&cid);
- c->t = t;
- GNUNET_CONTAINER_DLL_insert (t->connection_head, t->connection_tail, c);
for (own_pos = 0; own_pos < p->length; own_pos++)
{
if (p->peers[own_pos] == myid)
if (own_pos > p->length - 1)
{
GNUNET_break (0);
- connection_destroy (c);
return NULL;
}
- c->own_pos = own_pos;
- c->path = p;
- if (0 == own_pos)
- {
- c->fwd_maintenance_task =
- GNUNET_SCHEDULER_add_delayed (refresh_connection_time,
- &connection_fwd_keepalive, c);
- }
+ GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_NONCE, &cid);
+ c = GMC_new (&cid, t, p, own_pos);
+ GMT_add_connection (t, c);
+ return c;
+}
+
- peer = connection_get_next_hop (c);
- if (NULL == peer->connections)
+/**
+ * Count established (ready) connections of a tunnel.
+ *
+ * @param t Tunnel on which to count.
+ *
+ * @return Number of connections.
+ */
+unsigned int
+GMT_count_connections (struct MeshTunnel3 *t)
+{
+ struct MeshTConnection *iter;
+ unsigned int count;
+
+ for (count = 0, iter = t->connection_head;
+ NULL != iter;
+ iter = iter->next, count++);
+
+ return count;
+}
+
+/**
+ * Count channels of a tunnel.
+ *
+ * @param t Tunnel on which to count.
+ *
+ * @return Number of channels.
+ */
+unsigned int
+GMT_count_channels (struct MeshTunnel3 *t)
+{
+ struct MeshTChannel *iter;
+ unsigned int count;
+
+ for (count = 0, iter = t->channel_head;
+ NULL != iter;
+ iter = iter->next, count++) /* skip */;
+
+ return count;
+}
+
+
+/**
+ * Get the state of a tunnel.
+ *
+ * @param t Tunnel.
+ *
+ * @return Tunnel's state.
+ */
+enum MeshTunnel3State
+GMT_get_state (struct MeshTunnel3 *t)
+{
+ if (NULL == t)
+ return (enum MeshTunnel3State) -1;
+ return t->state;
+}
+
+/**
+ * Get the total buffer space for a tunnel.
+ *
+ * If terminal, use the biggest channel buffer (or 64) if no channel exists.
+ * If not terminal, use the sum of all connection buffers.
+ *
+ * @param t Tunnel.
+ * @param fwd Is this for FWD traffic?
+ *
+ * @return Buffer space offered by all entities (c/ch) in the tunnel.
+ */
+unsigned int
+GMT_get_buffer (struct MeshTunnel3 *t, int fwd)
+{
+ struct MeshTConnection *iter;
+ unsigned int buffer;
+
+ iter = t->connection_head;
+ buffer = 0;
+
+ /* If terminal, return biggest channel buffer */
+ if (NULL == iter || GMC_is_terminal (iter->c, fwd))
{
- connection_destroy (c);
- return NULL;
+ struct MeshTChannel *iter_ch;
+ unsigned int ch_buf;
+
+ if (NULL == t->channel_head)
+ {
+ /* Probably getting buffer for a channel create/handshake. */
+ return 64;
+ }
+
+ for (iter_ch = t->channel_head; NULL != iter_ch; iter_ch = iter_ch->next)
+ {
+ ch_buf = GMCH_get_buffer (iter_ch->ch, fwd);
+ if (ch_buf > buffer)
+ buffer = ch_buf;
+ }
+ return buffer;
}
- GNUNET_CONTAINER_multihashmap_put (peer->connections, &c->id, c,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
- peer = connection_get_prev_hop (c);
- if (NULL == peer->connections)
+
+ /* If not terminal, return sum of connection buffers */
+ while (NULL != iter)
{
- connection_destroy (c);
- return NULL;
+ if (GMC_get_state (iter->c) != MESH_CONNECTION_READY)
+ {
+ iter = iter->next;
+ continue;
+ }
+
+ buffer += GMC_get_buffer (iter->c, fwd);
+ iter = iter->next;
}
- GNUNET_CONTAINER_multihashmap_put (peer->connections, &c->id, c,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
- return c;
+
+ return buffer;
}
/**
- * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME
- * Encrypt data with the tunnel key.
+ * Get the tunnel's destination.
*
- * @param t Tunnel whose key to use.
- * @param dst Destination for the encrypted data.
- * @param src Source of the plaintext.
- * @param size Size of the plaintext.
- * @param iv Initialization Vector to use.
- * @param fwd Is this a fwd message?
+ * @param t Tunnel.
+ *
+ * @return ID of the destination peer.
*/
-void
-GMT_encrypt (struct MeshTunnel2 *t,
- void *dst, const void *src,
- size_t size, uint64_t iv, int fwd)
+const struct GNUNET_PeerIdentity *
+GMT_get_destination (struct MeshTunnel3 *t)
{
- memcpy (dst, src, size);
+ return GMP_get_id (t->peer);
}
/**
- * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME
- * Decrypt data with the tunnel key.
+ * Get the tunnel's next free global channel ID.
*
- * @param t Tunnel whose key to use.
- * @param dst Destination for the plaintext.
- * @param src Source of the encrypted data.
- * @param size Size of the encrypted data.
- * @param iv Initialization Vector to use.
- * @param fwd Is this a fwd message?
+ * @param t Tunnel.
+ *
+ * @return GID of a channel free to use.
+ */
+MESH_ChannelNumber
+GMT_get_next_chid (struct MeshTunnel3 *t)
+{
+ MESH_ChannelNumber chid;
+
+ while (NULL != GMT_get_channel (t, t->next_chid))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Channel %u exists...\n", t->next_chid);
+ t->next_chid = (t->next_chid + 1) & ~GNUNET_MESH_LOCAL_CHANNEL_ID_CLI;
+ }
+ chid = t->next_chid;
+ t->next_chid = (t->next_chid + 1) & ~GNUNET_MESH_LOCAL_CHANNEL_ID_CLI;
+
+ return chid;
+}
+
+
+/**
+ * Send ACK on one or more channels due to buffer in connections..
+ *
+ * @param t Channel which has some free buffer space.
+ * @param fwd Is this for FWD traffic? (ACK goes to root)
*/
void
-GMT_decrypt (struct MeshTunnel2 *t,
- void *dst, const void *src,
- size_t size, uint64_t iv, int fwd)
+GMT_unchoke_channels (struct MeshTunnel3 *t, int fwd)
{
- memcpy (dst, src, size);
+ struct MeshTChannel *iter;
+ unsigned int buffer;
+ unsigned int channels = GMT_count_channels (t);
+ unsigned int choked_n;
+ struct MeshChannel *choked[channels];
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "GMT_unchoke_channels on %s\n", GMT_2s (t));
+
+ if (NULL == t)
+ {
+ GNUNET_break (0);
+ return;
+ }
+
+ /* Get buffer space */
+ buffer = GMT_get_buffer (t, fwd);
+ if (0 == buffer)
+ {
+ return;
+ }
+
+ /* Count and remember choked channels */
+ choked_n = 0;
+ for (iter = t->channel_head; NULL != iter; iter = iter->next)
+ {
+ if (GNUNET_NO == GMCH_get_allowed (iter->ch, fwd))
+ {
+ choked[choked_n++] = iter->ch;
+ }
+ }
+
+ /* Unchoke random channels */
+ while (0 < buffer && 0 < choked_n)
+ {
+ unsigned int r = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
+ choked_n);
+ GMCH_allow_client (choked[r], fwd);
+ choked_n--;
+ buffer--;
+ choked[r] = choked[choked_n];
+ }
}
/**
- * Count established (ready) connections of a tunnel.
+ * Send ACK on one or more connections due to buffer space to the client.
*
- * @param t Tunnel on which to send the message.
+ * Iterates all connections of the tunnel and sends ACKs appropriately.
*
- * @return Number of connections.
+ * @param t Tunnel.
+ * @param fwd Is this in for FWD traffic? (ACK goes dest->root)
*/
-unsigned int
-GMT_count_connections (struct MeshTunnel2 *t)
+void
+GMT_send_acks (struct MeshTunnel3 *t, int fwd)
{
- return GMC_count (t->connection_head);
+ struct MeshTConnection *iter;
+ uint32_t allowed;
+ uint32_t to_allow;
+ uint32_t allow_per_connection;
+ unsigned int cs;
+ unsigned int buffer;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Tunnel send %s ACKs on %s\n",
+ fwd ? "FWD" : "BCK", GMT_2s (t));
+
+ if (NULL == t)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ if (NULL == t->channel_head ||
+ GNUNET_NO == GMCH_is_origin (t->channel_head->ch, !fwd))
+ {
+ GNUNET_break (0);
+ return;
+ }
+
+ buffer = GMT_get_buffer (t, fwd);
+
+ /* Count connections, how many messages are already allowed */
+ cs = GMT_count_connections (t);
+ for (allowed = 0, iter = t->connection_head; NULL != iter; iter = iter->next)
+ {
+ allowed += GMC_get_allowed (iter->c, fwd);
+ }
+
+ /* Make sure there is no overflow */
+ if (allowed > buffer)
+ {
+ GNUNET_break (0);
+ return;
+ }
+
+ /* Authorize connections to send more data */
+ to_allow = buffer; /* - allowed; */
+
+ for (iter = t->connection_head; NULL != iter && to_allow > 0; iter = iter->next)
+ {
+ allow_per_connection = to_allow/cs;
+ to_allow -= allow_per_connection;
+ cs--;
+ if (GMC_get_allowed (iter->c, fwd) > 64 / 3)
+ {
+ continue;
+ }
+ GMC_allow (iter->c, buffer, fwd);
+ }
+
+ GNUNET_break (to_allow == 0);
}
/**
- * Sends an already built message on a tunnel, choosing the best connection.
+ * Sends an already built message on a tunnel, GMT_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 fwd Is this a fwd message?
*/
void
-GMT_send_prebuilt_message (struct GNUNET_MESH_Encrypted *msg,
- struct MeshTunnel2 *t,
+GMT_send_prebuilt_message (const struct GNUNET_MessageHeader *message,
+ struct MeshTunnel3 *t,
struct MeshChannel *ch,
int fwd)
{
struct MeshConnection *c;
+ struct GNUNET_MESH_Encrypted *msg;
+ size_t size = ntohs (message->size);
+ char *cbuf[sizeof (struct GNUNET_MESH_Encrypted) + size];
+ uint64_t iv;
uint16_t type;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Send on Tunnel %s\n",
- peer2s (t->peer));
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "GMT Send on Tunnel %s\n", GMP_2s (t->peer));
+
+ iv = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX);
+ msg = (struct GNUNET_MESH_Encrypted *) cbuf;
+ msg->header.type = htons (GNUNET_MESSAGE_TYPE_MESH_ENCRYPTED);
+ msg->header.size = htons (sizeof (struct GNUNET_MESH_Encrypted) + size);
+ msg->iv = GNUNET_htonll (iv);
+ GMT_encrypt (t, &msg[1], message, size, iv, fwd);
c = tunnel_get_connection (t, fwd);
if (NULL == c)
{
GNUNET_break (GNUNET_YES == t->destroy);
return;
}
- type = ntohs (msg->header.type);
+ type = ntohs (message->type);
switch (type)
{
- case GNUNET_MESSAGE_TYPE_MESH_FWD:
- case GNUNET_MESSAGE_TYPE_MESH_BCK:
+ case GNUNET_MESSAGE_TYPE_MESH_DATA:
case GNUNET_MESSAGE_TYPE_MESH_CHANNEL_CREATE:
case GNUNET_MESSAGE_TYPE_MESH_CHANNEL_DESTROY:
- msg->cid = c->id;
+ case GNUNET_MESSAGE_TYPE_MESH_CHANNEL_ACK:
+ msg->cid = *GMC_get_id (c);
msg->ttl = htonl (default_ttl);
break;
default:
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "unkown type %s\n",
- GNUNET_MESH_DEBUG_M2S (type));
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "unkown type %s\n",
+ GNUNET_MESH_DEBUG_M2S (type));
GNUNET_break (0);
}
msg->reserved = 0;
+ t->pending_messages++;
GMC_send_prebuilt_message (&msg->header, c, ch, fwd);
}
+
+/**
+ * Is the tunnel directed towards the local peer?
+ *
+ * @param t Tunnel.
+ *
+ * @return GNUNET_YES if it is loopback.
+ */
+int
+GMT_is_loopback (const struct MeshTunnel3 *t)
+{
+ return (myid == GMP_get_short_id(t->peer));
+}
+
+
+/**
+ * Is the tunnel using this path already?
+ *
+ * @param t Tunnel.
+ * @param p Path.
+ *
+ * @return GNUNET_YES a connection uses this path.
+ */
+int
+GMT_is_path_used (const struct MeshTunnel3 *t, const struct MeshPeerPath *p)
+{
+ struct MeshTConnection *iter;
+
+ for (iter = t->connection_head; NULL != iter; iter = iter->next)
+ if (GMC_get_path (iter->c) == p)
+ return GNUNET_YES;
+
+ return GNUNET_NO;
+}
+
+
+/**
+ * Get a cost of a path for a tunnel considering existing connections.
+ *
+ * @param t Tunnel.
+ * @param path Candidate path.
+ *
+ * @return Cost of the path (path length + number of overlapping nodes)
+ */
+unsigned int
+GMT_get_path_cost (const struct MeshTunnel3 *t,
+ const struct MeshPeerPath *path)
+{
+ struct MeshTConnection *iter;
+ unsigned int overlap;
+ unsigned int i;
+ unsigned int j;
+
+ if (NULL == path)
+ return 0;
+
+ overlap = 0;
+ GNUNET_assert (NULL != t);
+
+ for (i = 0; i < path->length; i++)
+ {
+ for (iter = t->connection_head; NULL != iter; iter = iter->next)
+ {
+ for (j = 0; j < GMC_get_path (iter->c)->length; j++)
+ {
+ if (path->peers[i] == GMC_get_path (iter->c)->peers[j])
+ {
+ overlap++;
+ break;
+ }
+ }
+ }
+ }
+ return (path->length + overlap) * (path->score * -1);
+}
+
+
+/**
+ * Get the static string for the peer this tunnel is directed.
+ *
+ * @param t Tunnel.
+ *
+ * @return Static string the destination peer's ID.
+ */
+const char *
+GMT_2s (const struct MeshTunnel3 *t)
+{
+ return GMP_2s (t->peer);
+}
\ No newline at end of file