From d55c8698825605630770157c786e064af0c20467 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 16 Jan 2017 18:13:09 +0100 Subject: [PATCH] more work on peers, paths and tunnels --- src/cadet/gnunet-service-cadet-new.c | 16 +- src/cadet/gnunet-service-cadet-new.h | 26 +- .../gnunet-service-cadet-new_connection.c | 136 ++++++++++ .../gnunet-service-cadet-new_connection.h | 58 ++++ src/cadet/gnunet-service-cadet-new_paths.c | 174 ++++++++++-- src/cadet/gnunet-service-cadet-new_paths.h | 75 +++++- src/cadet/gnunet-service-cadet-new_peer.c | 105 +++++--- src/cadet/gnunet-service-cadet-new_peer.h | 51 ++-- src/cadet/gnunet-service-cadet-new_tunnels.c | 248 +++++++++++++++++- src/cadet/gnunet-service-cadet-new_tunnels.h | 15 ++ 10 files changed, 797 insertions(+), 107 deletions(-) diff --git a/src/cadet/gnunet-service-cadet-new.c b/src/cadet/gnunet-service-cadet-new.c index f367c4c73..74296a27e 100644 --- a/src/cadet/gnunet-service-cadet-new.c +++ b/src/cadet/gnunet-service-cadet-new.c @@ -271,8 +271,7 @@ GSC_bind (struct CadetClient *c, msg->channel_id = lid; msg->port = *port; msg->opt = htonl (options); - GCP_id (dest, - &msg->peer); + msg->peer = *GCP_get_id (dest); GSC_send_to_client (c, env); return lid; @@ -732,15 +731,15 @@ handle_get_peers (void *cls, * Message contains blocks of peers, first not included. * * @param cls message queue for transmission - * @param peer Peer this path is towards. * @param path Path itself + * @param off offset of the peer on @a path * @return #GNUNET_YES if should keep iterating. * #GNUNET_NO otherwise. */ static int path_info_iterator (void *cls, - struct CadetPeer *peer, - struct CadetPeerPath *path) + struct CadetPeerPath *path, + unsigned int off) { struct GNUNET_MQ_Handle *mq = cls; struct GNUNET_MQ_Envelope *env; @@ -767,10 +766,9 @@ path_info_iterator (void *cls, /* Don't copy first peer. First peer is always the local one. Last * peer is always the destination (leave as 0, EOL). */ - for (i = 0; i < path_length - 1; i++) - GCPP_get_pid_at_offset (path, - i + 1, - &id[i]); + for (i = 0; i < off; i++) + id[i] = *GCP_get_id (GCPP_get_peer_at_offset (path, + i + 1)); GNUNET_MQ_send (mq, env); return GNUNET_YES; diff --git a/src/cadet/gnunet-service-cadet-new.h b/src/cadet/gnunet-service-cadet-new.h index 2b68862b7..9521b6363 100644 --- a/src/cadet/gnunet-service-cadet-new.h +++ b/src/cadet/gnunet-service-cadet-new.h @@ -31,27 +31,32 @@ #include "gnunet_util_lib.h" /** - * A client to the CADET service. + * A client to the CADET service. Each client gets a unique handle. */ struct CadetClient; /** - * A peer in the GNUnet network. + * A peer in the GNUnet network. Each peer we care about must have one globally + * unique such handle within this process. */ struct CadetPeer; /** - * Tunnel from us to another peer. + * Tunnel from us to another peer. There can only be at most one + * tunnel per peer. */ struct CadetTunnel; /** - * Entry in the message queue of a `struct CadetTunnel` + * Entry in the message queue of a `struct CadetTunnel`. */ struct CadetTunnelQueueEntry; /** - * A path of peer in the GNUnet network. + * A path of peer in the GNUnet network. There must only be at most + * once such path. Paths may share disjoint prefixes, but must all + * end at a unique suffix. Paths must also not be proper subsets of + * other existing paths. */ struct CadetPeerPath; @@ -80,6 +85,11 @@ struct CadetPeerPathEntry */ struct CadetPeerPath *path; + /** + * Connection using this path, or NULL for none. + */ + struct CadetConnection *cc; + /** * Path's historic score up to this point. Basically, how often did * we succeed or fail to use the path up to this entry in a @@ -93,12 +103,14 @@ struct CadetPeerPathEntry /** - * Active path through the network (used by a tunnel). + * Active path through the network (used by a tunnel). There may + * be at most one connection per path. */ struct CadetConnection; /** - * Logical end-to-end conenction between clients. + * Logical end-to-end conenction between clients. There can be + * any number of channels between clients. */ struct CadetChannel; diff --git a/src/cadet/gnunet-service-cadet-new_connection.c b/src/cadet/gnunet-service-cadet-new_connection.c index b24e4bd49..ea0b2c6e5 100644 --- a/src/cadet/gnunet-service-cadet-new_connection.c +++ b/src/cadet/gnunet-service-cadet-new_connection.c @@ -26,9 +26,145 @@ * @author Christian Grothoff */ #include "platform.h" +#include "gnunet-service-cadet-new_channel.h" +#include "gnunet-service-cadet-new_paths.h" +#include "gnunet-service-cadet-new_peer.h" #include "gnunet-service-cadet-new_connection.h" +/** + * Low-level connection to a destination. + */ +struct CadetConnection +{ + /** + * To which peer does this connection go? + */ + struct CadetPeer *destination; + + /** + * Path we are using to our destination. + */ + struct CadetPeerPath *path; + + /** + * Function to call once we are ready to transmit. + */ + GNUNET_SCHEDULER_TaskCallback ready_cb; + + /** + * Closure for @e ready_cb. + */ + void *ready_cb_cls; + + /** + * Offset of our @e destination in @e path. + */ + unsigned int off; + +}; + + +/** + * Is the given connection currently ready for transmission? + * + * @param cc connection to transmit on + * @return #GNUNET_YES if we could transmit + */ +int +GCC_is_ready (struct CadetConnection *cc) +{ + GNUNET_break (0); + return GNUNET_NO; +} + + +/** + * Destroy a connection. + * + * @param cc connection to destroy + */ +void +GCC_destroy (struct CadetConnection *cc) +{ + GCPP_del_connection (cc->path, + cc->off, + cc); + GNUNET_assert (0); // FIXME: incomplete implementation! + GNUNET_free (cc); +} + + +/** + * Create a connection to @a destination via @a path and + * notify @a cb whenever we are ready for more data. + * + * @param destination where to go + * @param path which path to take (may not be the full path) + * @param ready_cb function to call when ready to transmit + * @param ready_cb_cls closure for @a cb + */ +struct CadetConnection * +GCC_create (struct CadetPeer *destination, + struct CadetPeerPath *path, + GNUNET_SCHEDULER_TaskCallback ready_cb, + void *ready_cb_cls) +{ + struct CadetConnection *cc; + unsigned int off; + + off = GCPP_find_peer (path, + destination); + GNUNET_assert (UINT_MAX > off); + + GNUNET_assert (0); // fIXME: unfinished + + cc = GNUNET_new (struct CadetConnection); + cc->path = path; + cc->off = off; + GCPP_add_connection (path, + off, + cc); + for (unsigned int i=0;ipath; +} + + /** * Obtain unique ID for the connection. * diff --git a/src/cadet/gnunet-service-cadet-new_connection.h b/src/cadet/gnunet-service-cadet-new_connection.h index 7745e4fec..c11e34024 100644 --- a/src/cadet/gnunet-service-cadet-new_connection.h +++ b/src/cadet/gnunet-service-cadet-new_connection.h @@ -33,6 +33,64 @@ #include "gnunet-service-cadet-new_peer.h" +/** + * Is the given connection currently ready for transmission? + * + * @param cc connection to transmit on + * @return #GNUNET_YES if we could transmit + */ +int +GCC_is_ready (struct CadetConnection *cc); + + +/** + * Destroy a connection. + * + * @param cc connection to destroy + */ +void +GCC_destroy (struct CadetConnection *cc); + + +/** + * Create a connection to @a destination via @a path and + * notify @a cb whenever we are ready for more data. + * + * @param destination where to go + * @param path which path to take (may not be the full path) + * @param ready_cb function to call when ready to transmit + * @param ready_cb_cls closure for @a cb + */ +struct CadetConnection * +GCC_create (struct CadetPeer *destination, + struct CadetPeerPath *path, + GNUNET_SCHEDULER_TaskCallback ready_cb, + void *ready_cb_cls); + + +/** + * Transmit message @a msg via connection @a cc. Must only be called + * (once) after the connection has signalled that it is ready via the + * `ready_cb`. Clients can also use #GCC_is_ready() to check if the + * connection is right now ready for transmission. + * + * @param cc connection identification + * @param msg message to transmit + */ +void +GCC_transmit (struct CadetConnection *cc, + const struct GNUNET_MessageHeader *msg); + + +/** + * Obtain the path used by this connection. + * + * @param cc connection + * @return path to @a cc + */ +struct CadetPeerPath * +GCC_get_path (struct CadetConnection *cc); + /** * Obtain unique ID for the connection. diff --git a/src/cadet/gnunet-service-cadet-new_paths.c b/src/cadet/gnunet-service-cadet-new_paths.c index 663ce317f..005d8e807 100644 --- a/src/cadet/gnunet-service-cadet-new_paths.c +++ b/src/cadet/gnunet-service-cadet-new_paths.c @@ -86,7 +86,7 @@ struct CadetPeerPath GNUNET_CONTAINER_HeapCostType GCPP_get_desirability (const struct CadetPeerPath *path) { - GNUNET_assert (0); + GNUNET_break (0); return 0; } @@ -98,16 +98,100 @@ GCPP_get_desirability (const struct CadetPeerPath *path) * the path desirable). * * @param path the path that is being released - * @param cp original final destination of @a path * @param node entry in the heap of @a cp where this path is anchored * should be used for updates to the desirability of this path */ void GCPP_acquire (struct CadetPeerPath *path, - struct CadetPeer *cp, struct GNUNET_CONTAINER_HeapNode *node) { - GNUNET_assert (0); + GNUNET_assert (NULL == path->hn); + path->hn = node; +} + + +/** + * Return connection to @a destination using @a path, or return + * NULL if no such connection exists. + * + * @param path path to traverse + * @param destination destination node to get to, must be on path + * @param off offset of @a destination on @a path + * @return NULL if @a create is NO and we have no existing connection + * otherwise connection from us to @a destination via @a path + */ +struct CadetConnection * +GCPP_get_connection (struct CadetPeerPath *path, + struct CadetPeer *destination, + unsigned int off) +{ + struct CadetPeerPathEntry *entry; + + GNUNET_assert (off < path->entries_length); + entry = &path->entries[off]; + GNUNET_assert (entry->peer == destination); + return entry->cc; +} + + +/** + * Notify @a path that it is used for connection @a cc + * which ends at the path's offset @a off. + * + * @param path the path to remember the @a cc + * @param off the offset where the @a cc ends + * @param cc the connection to remember + */ +void +GCPP_add_connection (struct CadetPeerPath *path, + unsigned int off, + struct CadetConnection *cc) +{ + struct CadetPeerPathEntry *entry; + + GNUNET_assert (off < path->entries_length); + entry = &path->entries[off]; + GNUNET_assert (NULL == entry->cc); + entry->cc = cc; +} + + + +/** + * Notify @a path that it is no longer used for connection @a cc which + * ended at the path's offset @a off. + * + * @param path the path to forget the @a cc + * @param off the offset where the @a cc ended + * @param cc the connection to forget + */ +void +GCPP_del_connection (struct CadetPeerPath *path, + unsigned int off, + struct CadetConnection *cc) +{ + struct CadetPeerPathEntry *entry; + + GNUNET_assert (off < path->entries_length); + entry = &path->entries[off]; + GNUNET_assert (cc == entry->cc); + entry->cc = NULL; +} + + +/** + * This path is no longer needed, free resources. + * + * @param path path resources to free + */ +static void +path_destroy (struct CadetPeerPath *path) +{ + GNUNET_assert (0 == + GNUNET_CONTAINER_multipeermap_size (path->connections)); + GNUNET_CONTAINER_multipeermap_destroy (path->connections); + GNUNET_free (path->entries); + GNUNET_free (path); } @@ -121,7 +205,34 @@ GCPP_acquire (struct CadetPeerPath *path, void GCPP_release (struct CadetPeerPath *path) { - GNUNET_assert (0); + struct CadetPeerPathEntry *entry; + + path->hn = NULL; + entry = &path->entries[path->entries_length - 1]; + while (1) + { + /* cut 'off' end of path, verifying it is not in use */ + GNUNET_assert (NULL == + GNUNET_CONTAINER_multipeermap_get (path->connections, + GCP_get_id (entry->peer))); + GCP_path_entry_remove (entry->peer, + entry, + path->entries_length - 1); + path->entries_length--; /* We don't bother shrinking the 'entries' array, + as it's probably not worth it. */ + if (0 == path->entries_length) + break; /* the end */ + + /* see if new peer at the end likes this path any better */ + entry = &path->entries[path->entries_length - 1]; + path->hn = GCP_attach_path (entry->peer, + path); + if (NULL != path->hn) + return; /* yep, got attached, we are done. */ + } + + /* nobody wants us, discard the path */ + path_destroy (path); } @@ -165,6 +276,16 @@ GCPP_update_score (struct CadetPeerPath *path, /** * Create a peer path based on the result of a DHT lookup. + * If we already know this path, or one that is longer, + * simply return NULL. + * + * FIXME: change API completely! + * Should in here create path transiently, then call + * callback, and then do path destroy (if applicable) + * without returning in the middle. + * + * FIXME: also need to nicely handle case that this path + * extends (lengthens!) an existing path. * * @param get_path path of the get request * @param get_path_length lenght of @a get_path @@ -208,9 +329,11 @@ GCPP_path_from_dht (const struct GNUNET_PeerIdentity *get_path, * @param p path to destroy. */ void -GCPP_path_destroy (struct CadetPeerPath *p) +GCPP_path_destroy (struct CadetPeerPath *path) { - GNUNET_assert (0); + if (NULL != path->hn) + return; /* path was attached, to be kept! */ + path_destroy (path); } @@ -224,24 +347,43 @@ GCPP_path_destroy (struct CadetPeerPath *p) unsigned int GCPP_get_length (struct CadetPeerPath *path) { - GNUNET_assert (0); - return -1; + return path->entries_length; } /** - * Obtain the identity of the peer at offset @a off in @a path. + * Find peer's offset on path. + * + * @param path path to search + * @param cp peer to look for + * @return offset of @a cp on @a path, or UINT_MAX if not found + */ +unsigned int +GCPP_find_peer (struct CadetPeerPath *path, + struct CadetPeer *cp) +{ + for (unsigned int off = 0; + off < path->entries_length; + off++) + if (cp == GCPP_get_peer_at_offset (path, + off)) + return off; + return UINT_MAX; +} + + +/** + * Obtain the peer at offset @a off in @a path. * * @param path peer path to inspect * @param off offset to return, must be smaller than path length - * @param[out] pid where to write the pid, must not be NULL + * @return the peer at offset @a off */ -void -GCPP_get_pid_at_offset (struct CadetPeerPath *path, - unsigned int off, - struct GNUNET_PeerIdentity *pid) +struct CadetPeer * +GCPP_get_peer_at_offset (struct CadetPeerPath *path, + unsigned int off) { - GNUNET_assert (0); + return path->entries[off].peer; } diff --git a/src/cadet/gnunet-service-cadet-new_paths.h b/src/cadet/gnunet-service-cadet-new_paths.h index e00b05a3d..4b47784a0 100644 --- a/src/cadet/gnunet-service-cadet-new_paths.h +++ b/src/cadet/gnunet-service-cadet-new_paths.h @@ -33,6 +33,8 @@ /** * Create a peer path based on the result of a DHT lookup. + * If we already know this path, or one that is longer, + * simply return NULL. * * @param get_path path of the get request * @param get_path_length lenght of @a get_path @@ -50,10 +52,10 @@ GCPP_path_from_dht (const struct GNUNET_PeerIdentity *get_path, /** * Destroy a path, we no longer need it. * - * @param p path to destroy. + * @param path path to destroy. */ void -GCPP_path_destroy (struct CadetPeerPath *p); +GCPP_path_destroy (struct CadetPeerPath *path); /** @@ -67,6 +69,62 @@ unsigned int GCPP_get_length (struct CadetPeerPath *path); +/** + * Return connection to @a destination using @a path, or return + * NULL if no such connection exists. + * + * @param path path to traverse + * @param destination destination node to get to, must be on path + * @param off offset of @a destination on @a path + * @return NULL if @a create is NO and we have no existing connection + * otherwise connection from us to @a destination via @a path + */ +struct CadetConnection * +GCPP_get_connection (struct CadetPeerPath *path, + struct CadetPeer *destination, + unsigned int off); + + +/** + * Notify @a path that it is used for connection @a cc + * which ends at the path's offset @a off. + * + * @param path the path to remember the @a cc + * @param off the offset where the @a cc ends + * @param cc the connection to remember + */ +void +GCPP_add_connection (struct CadetPeerPath *path, + unsigned int off, + struct CadetConnection *cc); + + +/** + * Notify @a path that it is no longer used for connection @a cc which + * ended at the path's offset @a off. + * + * @param path the path to forget the @a cc + * @param off the offset where the @a cc ended + * @param cc the connection to forget + */ +void +GCPP_del_connection (struct CadetPeerPath *path, + unsigned int off, + struct CadetConnection *cc); + + +/** + * Find peer's offset on path. + * + * @param path path to search + * @param cp peer to look for + * @return offset of @a cp on @a path, or UINT_MAX if not found + */ +unsigned int +GCPP_find_peer (struct CadetPeerPath *path, + struct CadetPeer *cp); + + /** * Return how much we like keeping the path. This is an aggregate * score based on various factors, including the age of the path @@ -91,13 +149,11 @@ GCPP_get_desirability (const struct CadetPeerPath *path); * the path desirable). * * @param path the path that is being released - * @param cp original final destination of @a path * @param node entry in the heap of @a cp where this path is anchored * should be used for updates to the desirability of this path */ void GCPP_acquire (struct CadetPeerPath *path, - struct CadetPeer *cp, struct GNUNET_CONTAINER_HeapNode *node); @@ -114,16 +170,15 @@ GCPP_release (struct CadetPeerPath *path); /** - * Obtain the identity of the peer at offset @a off in @a path. + * Obtain the peer at offset @a off in @a path. * * @param path peer path to inspect * @param off offset to return, must be smaller than path length - * @param[out] pid where to write the pid, must not be NULL + * @return peer at offset @a off */ -void -GCPP_get_pid_at_offset (struct CadetPeerPath *path, - unsigned int off, - struct GNUNET_PeerIdentity *pid); +struct CadetPeer * +GCPP_get_peer_at_offset (struct CadetPeerPath *path, + unsigned int off); #endif diff --git a/src/cadet/gnunet-service-cadet-new_peer.c b/src/cadet/gnunet-service-cadet-new_peer.c index 0ef65b7b6..8c8b23820 100644 --- a/src/cadet/gnunet-service-cadet-new_peer.c +++ b/src/cadet/gnunet-service-cadet-new_peer.c @@ -350,6 +350,13 @@ GCP_path_entry_add (struct CadetPeer *cp, cp->path_tails[off], entry); cp->num_paths++; + + /* If we have a tunnel to this peer, tell the tunnel that there is a + new path available. */ + if (NULL != cp->t) + GCT_consider_path (cp->t, + entry->path, + off); } @@ -374,19 +381,22 @@ GCP_path_entry_remove (struct CadetPeer *cp, /** - * Function called when the DHT finds a @a path to the peer (@a cls). + * Try adding a @a path to this @a peer. If the peer already + * has plenty of paths, return NULL. * - * @param cls the `struct CadetPeer` - * @param path the path that was found + * @param cp peer to which the @a path leads to + * @param path a path looking for an owner + * @return NULL if this peer does not care to become a new owner, + * otherwise the node in the peer's path heap for the @a path. */ -static void -dht_result_cb (void *cls, - struct CadetPeerPath *path) +struct GNUNET_CONTAINER_HeapNode * +GCP_attach_path (struct CadetPeer *cp, + struct CadetPeerPath *path) { - struct CadetPeer *cp = cls; GNUNET_CONTAINER_HeapCostType desirability; struct CadetPeerPath *root; GNUNET_CONTAINER_HeapCostType root_desirability; + struct GNUNET_CONTAINER_HeapNode *hn; desirability = GCPP_get_desirability (path); if (GNUNET_NO == @@ -397,23 +407,60 @@ dht_result_cb (void *cls, root = NULL; root_desirability = 0; } - if ( (DESIRED_CONNECTIONS_PER_TUNNEL <= cp->num_paths) || - (desirability >= root_desirability) ) - { - /* Yes, we'd like to add this path, add to our heap */ - GCPP_acquire (path, - cp, - GNUNET_CONTAINER_heap_insert (cp->path_heap, - (void *) path, - desirability)); - } + + if ( (DESIRED_CONNECTIONS_PER_TUNNEL > cp->num_paths) && + (desirability < root_desirability) ) + return NULL; + + /* Yes, we'd like to add this path, add to our heap */ + hn = GNUNET_CONTAINER_heap_insert (cp->path_heap, + (void *) cp, + desirability); + + /* Consider maybe dropping other paths because of the new one */ if (GNUNET_CONTAINER_heap_get_size (cp->path_heap) >= 2 * DESIRED_CONNECTIONS_PER_TUNNEL) { - /* Now we have way too many, drop least desirable */ - root = GNUNET_CONTAINER_heap_remove_root (cp->path_heap); - GCPP_release (path); + /* Now we have way too many, drop least desirable UNLESS it is in use! + (Note that this intentionally keeps highly desireable, but currently + unused paths around in the hope that we might be able to switch, even + if the number of paths exceeds the threshold.) */ + root = GNUNET_CONTAINER_heap_peek (cp->path_heap); + if (NULL == + GCPP_get_connection (root, + cp, + GCPP_get_length (root) - 1)) + { + /* Got plenty of paths to this destination, and this is a low-quality + one that we don't care, allow it to die. */ + GNUNET_assert (root == + GNUNET_CONTAINER_heap_remove_root (cp->path_heap)); + GCPP_release (root); + } } + return hn; +} + + +/** + * Function called when the DHT finds a @a path to the peer (@a cls). + * + * @param cls the `struct CadetPeer` + * @param path the path that was found + */ +static void +dht_result_cb (void *cls, + struct CadetPeerPath *path) +{ + struct CadetPeer *cp = cls; + struct GNUNET_CONTAINER_HeapNode *hn; + + hn = GCP_attach_path (cp, + path); + if (NULL == hn) + return; + GCPP_acquire (path, + hn); } @@ -508,9 +555,6 @@ GCP_get (const struct GNUNET_PeerIdentity *peer_id, cp->connections = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_YES); cp->path_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN); - cp->search_h = NULL; // FIXME: start search immediately!? - cp->connectivity_suggestion = NULL; // FIXME: request with ATS!? - GNUNET_assert (GNUNET_YES == GNUNET_CONTAINER_multipeermap_put (peers, &cp->pid, @@ -524,13 +568,12 @@ GCP_get (const struct GNUNET_PeerIdentity *peer_id, * Obtain the peer identity for a `struct CadetPeer`. * * @param cp our peer handle - * @param[out] peer_id where to write the peer identity + * @return the peer identity */ -void -GCP_id (struct CadetPeer *cp, - struct GNUNET_PeerIdentity *peer_id) +const struct GNUNET_PeerIdentity * +GCP_get_id (struct CadetPeer *cp) { - *peer_id = cp->pid; + return &cp->pid; } @@ -586,8 +629,8 @@ GCP_iterate_paths (struct CadetPeer *peer, { if (GNUNET_NO == callback (callback_cls, - peer, - pe->path)) + pe->path, + i)) return ret; ret++; } @@ -629,7 +672,7 @@ void GCP_set_hello (struct CadetPeer *peer, const struct GNUNET_HELLO_Message *hello) { - /* FIXME! */ + /* FIXME: keep HELLO, possibly offer to TRANSPORT... */ consider_peer_destroy (peer); } diff --git a/src/cadet/gnunet-service-cadet-new_peer.h b/src/cadet/gnunet-service-cadet-new_peer.h index cc9a347fd..e1c8476d1 100644 --- a/src/cadet/gnunet-service-cadet-new_peer.h +++ b/src/cadet/gnunet-service-cadet-new_peer.h @@ -63,11 +63,10 @@ GCP_get (const struct GNUNET_PeerIdentity *peer_id, * Obtain the peer identity for a `struct CadetPeer`. * * @param cp our peer handle - * @param[out] peer_id where to write the peer identity + * @return the peer identity */ -void -GCP_id (struct CadetPeer *cp, - struct GNUNET_PeerIdentity *peer_id); +const struct GNUNET_PeerIdentity * +GCP_get_id (struct CadetPeer *cp); /** @@ -84,40 +83,38 @@ GCP_iterate_all (GNUNET_CONTAINER_PeerMapIterator iter, /** * Count the number of known paths toward the peer. * - * @param peer Peer to get path info. + * @param cp Peer to get path info. * @return Number of known paths. */ unsigned int -GCP_count_paths (const struct CadetPeer *peer); +GCP_count_paths (const struct CadetPeer *cp); /** * Peer path iterator. * * @param cls Closure. - * @param peer Peer this path is towards. * @param path Path itself + * @param off offset of the target peer in @a path * @return #GNUNET_YES if should keep iterating. * #GNUNET_NO otherwise. - * - * FIXME: peer argument should be redundant; remove! */ typedef int (*GCP_PathIterator) (void *cls, - struct CadetPeer *peer, - struct CadetPeerPath *path); + struct CadetPeerPath *path, + unsigned int off); /** * Iterate over the paths to a peer. * - * @param peer Peer to get path info. + * @param cp Peer to get path info. * @param callback Function to call for every path. * @param callback_cls Closure for @a callback. * @return Number of iterated paths. */ unsigned int -GCP_iterate_paths (struct CadetPeer *peer, +GCP_iterate_paths (struct CadetPeer *cp, GCP_PathIterator callback, void *callback_cls); @@ -151,12 +148,12 @@ GCP_path_entry_add (struct CadetPeer *cp, /** * Get the tunnel towards a peer. * - * @param peer Peer to get from. + * @param cp Peer to get from. * @param create #GNUNET_YES to create a tunnel if we do not have one * @return Tunnel towards peer. */ struct CadetTunnel * -GCP_get_tunnel (struct CadetPeer *peer, +GCP_get_tunnel (struct CadetPeer *cp, int create); @@ -164,23 +161,37 @@ GCP_get_tunnel (struct CadetPeer *peer, * The tunnel to the given peer no longer exists, remove it from our * data structures, and possibly clean up the peer itself. * - * @param peer the peer affected + * @param cp the peer affected * @param t the dead tunnel */ void -GCP_drop_tunnel (struct CadetPeer *peer, +GCP_drop_tunnel (struct CadetPeer *cp, struct CadetTunnel *t); /** - * We got a HELLO for a @a peer, remember it, and possibly + * Try adding a @a path to this @a cp. If the peer already + * has plenty of paths, return NULL. + * + * @param cp peer to which the @a path leads to + * @param path a path looking for an owner + * @return NULL if this peer does not care to become a new owner, + * otherwise the node in the peer's path heap for the @a path. + */ +struct GNUNET_CONTAINER_HeapNode * +GCP_attach_path (struct CadetPeer *cp, + struct CadetPeerPath *path); + + +/** + * We got a HELLO for a @a cp, remember it, and possibly * trigger adequate actions (like trying to connect). * - * @param peer the peer we got a HELLO for + * @param cp the peer we got a HELLO for * @param hello the HELLO to remember */ void -GCP_set_hello (struct CadetPeer *peer, +GCP_set_hello (struct CadetPeer *cp, const struct GNUNET_HELLO_Message *hello); diff --git a/src/cadet/gnunet-service-cadet-new_tunnels.c b/src/cadet/gnunet-service-cadet-new_tunnels.c index 4299d5bf7..1a07140f2 100644 --- a/src/cadet/gnunet-service-cadet-new_tunnels.c +++ b/src/cadet/gnunet-service-cadet-new_tunnels.c @@ -24,6 +24,11 @@ * @brief Information we track per tunnel. * @author Bartlomiej Polot * @author Christian Grothoff + * + * FIXME: + * - when managing connections, distinguish those that + * have (recently) had traffic from those that were + * never ready (or not recently) */ #include "platform.h" #include "gnunet_util_lib.h" @@ -35,6 +40,7 @@ #include "gnunet-service-cadet-new_connection.h" #include "gnunet-service-cadet-new_tunnels.h" #include "gnunet-service-cadet-new_peer.h" +#include "gnunet-service-cadet-new_paths.h" /** @@ -212,7 +218,12 @@ struct CadetTConnection /** * Connection handle. */ - struct CadetConnection *c; + struct CadetConnection *cc; + + /** + * Tunnel this connection belongs to. + */ + struct CadetTunnel *t; /** * Creation time, to keep oldest connection alive. @@ -269,9 +280,9 @@ struct CadetTunnelQueueEntry struct CadetTunnel { /** - * Endpoint of the tunnel. + * Destination of the tunnel. */ - struct CadetPeer *peer; + struct CadetPeer *destination; /** * Peer's ephemeral key, to recreate @c e_key and @c d_key when own @@ -348,7 +359,7 @@ struct CadetTunnel /** * Task to trim connections if too many are present. */ - struct GNUNET_SCHEDULER_Task *trim_connections_task; + struct GNUNET_SCHEDULER_Task *maintain_connections_task; /** * Ephemeral message in the queue (to avoid queueing more than one). @@ -390,7 +401,7 @@ GCT_2s (const struct CadetTunnel *t) GNUNET_snprintf (buf, sizeof (buf), "T(%s)", - GCP_2s (t->peer)); + GCP_2s (t->destination)); return buf; } @@ -404,7 +415,7 @@ GCT_2s (const struct CadetTunnel *t) struct CadetPeer * GCT_get_destination (struct CadetTunnel *t) { - return t->peer; + return t->destination; } @@ -503,16 +514,218 @@ static void destroy_tunnel (void *cls) { struct CadetTunnel *t = cls; + struct CadetTConnection *ct; + struct CadetTunnelQueueEntry *tqe; t->destroy_task = NULL; - - // FIXME: implement! - GCP_drop_tunnel (t->peer, + GNUNET_assert (0 == GNUNET_CONTAINER_multihashmap32_size (t->channels)); + while (NULL != (ct = t->connection_head)) + { + GNUNET_assert (ct->t == t); + GNUNET_CONTAINER_DLL_remove (t->connection_head, + t->connection_tail, + ct); + GCC_destroy (ct->cc); + GNUNET_free (ct); + } + while (NULL != (tqe = t->tq_head)) + { + GNUNET_CONTAINER_DLL_remove (t->tq_head, + t->tq_tail, + tqe); + // FIXME: implement! + GNUNET_free (tqe); + } + GCP_drop_tunnel (t->destination, t); + GNUNET_CONTAINER_multihashmap32_destroy (t->channels); + if (NULL != t->maintain_connections_task) + { + GNUNET_SCHEDULER_cancel (t->maintain_connections_task); + t->maintain_connections_task = NULL; + } 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. + * + * @param cls the `struct CadetTConnection` that is ready + */ +static void +connection_ready_cb (void *cls) +{ + struct CadetTConnection *ct = cls; + struct CadetTunnel *t = ct->t; + struct CadetTunnelQueueEntry *tq = t->tq_head; + + if (NULL == tq) + return; /* no messages pending right now */ + + /* ready to send message 'tq' on tunnel 'ct' */ + GNUNET_assert (t == tq->t); + GNUNET_CONTAINER_DLL_remove (t->tq_head, + t->tq_tail, + tq); + GCC_transmit (ct->cc, + (const struct GNUNET_MessageHeader *) &tq[1]); + tq->cont (tq->cont_cls); + GNUNET_free (tq); +} + + +/** + * 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) +{ + struct CadetTConnection *ct; + + 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; + if (NULL == ct) + return; /* no connections ready */ + connection_ready_cb (ct); +} + + +/** + * Function called to maintain the connections underlying our tunnel. + * Tries to maintain (incl. tear down) connections for the tunnel, and + * if there is a significant change, may trigger transmissions. + * + * Basically, needs to check if there are connections that perform + * badly, and if so eventually kill them and trigger a replacement. + * The strategy is to open one more connection than + * #DESIRED_CONNECTIONS_PER_TUNNEL, and then periodically kick out the + * least-performing one, and then inquire for new ones. + * + * @param cls the `struct CadetTunnel` + */ +static void +maintain_connections_cb (void *cls) +{ + struct CadetTunnel *t = cls; + + GNUNET_break (0); // FIXME: implement! +} + + +/** + * Consider using the path @a p for the tunnel @a t. + * The tunnel destination is at offset @a off in path @a p. + * + * @param cls our tunnel + * @param path a path to our destination + * @param off offset of the destination on path @a path + * @return #GNUNET_YES (should keep iterating) + */ +static int +consider_path_cb (void *cls, + struct CadetPeerPath *path, + unsigned int off) +{ + struct CadetTunnel *t = cls; + unsigned int min_length = UINT_MAX; + GNUNET_CONTAINER_HeapCostType max_desire = 0; + struct CadetTConnection *ct; + + /* Check if we care about the new path. */ + for (ct = t->connection_head; + NULL != ct; + ct = ct->next) + { + struct CadetPeerPath *ps; + + ps = GCC_get_path (ct->cc); + if (ps == path) + return GNUNET_YES; /* duplicate */ + min_length = GNUNET_MIN (min_length, + GCPP_get_length (ps)); + max_desire = GNUNET_MAX (max_desire, + GCPP_get_desirability (ps)); + } + + /* FIXME: not sure we should really just count + 'num_connections' here, as they may all have + consistently failed to connect. */ + + /* We iterate by increasing path length; if we have enough paths and + this one is more than twice as long than what we are currently + using, then ignore all of these super-long ones! */ + if ( (t->num_connections > DESIRED_CONNECTIONS_PER_TUNNEL) && + (min_length * 2 < off) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Ignoring paths of length %u, they are way too long.\n", + min_length * 2); + return GNUNET_NO; + } + /* If we have enough paths and this one looks no better, ignore it. */ + if ( (t->num_connections >= DESIRED_CONNECTIONS_PER_TUNNEL) && + (min_length < GCPP_get_length (path)) && + (max_desire > GCPP_get_desirability (path)) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Ignoring path (%u/%llu) to %s, got something better already.\n", + GCPP_get_length (path), + (unsigned long long) GCPP_get_desirability (path), + GCP_2s (t->destination)); + return GNUNET_YES; + } + + /* Path is interesting (better by some metric, or we don't have + enough paths yet). */ + ct = GNUNET_new (struct CadetTConnection); + ct->created = GNUNET_TIME_absolute_get (); + ct->t = t; + ct->cc = GCC_create (t->destination, + path, + &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!) */ + GNUNET_CONTAINER_DLL_insert (t->connection_head, + t->connection_tail, + ct); + t->num_connections++; + return GNUNET_YES; +} + + +/** + * Consider using the path @a p for the tunnel @a t. + * The tunnel destination is at offset @a off in path @a p. + * + * @param cls our tunnel + * @param path a path to our destination + * @param off offset of the destination on path @a path + */ +void +GCT_consider_path (struct CadetTunnel *t, + struct CadetPeerPath *p, + unsigned int off) +{ + (void) consider_path_cb (t, + p, + off); +} + + /** * Create a tunnel to @a destionation. Must only be called * from within #GCP_get_tunnel(). @@ -526,9 +739,14 @@ GCT_create_tunnel (struct CadetPeer *destination) struct CadetTunnel *t; t = GNUNET_new (struct CadetTunnel); - t->peer = destination; + t->destination = destination; t->channels = GNUNET_CONTAINER_multihashmap32_create (8); - + (void) GCP_iterate_paths (destination, + &consider_path_cb, + t); + t->maintain_connections_task + = GNUNET_SCHEDULER_add_now (&maintain_connections_cb, + t); return t; } @@ -589,7 +807,8 @@ GCT_send (struct CadetTunnel *t, GNUNET_CONTAINER_DLL_insert_tail (t->tq_head, t->tq_tail, q); - /* FIXME: initiate transmission process! */ + /* FIXME: what about KX being ready? */ + trigger_transmissions (t); return q; } @@ -631,7 +850,7 @@ GCT_iterate_connections (struct CadetTunnel *t, NULL != ct; ct = ct->next) iter (iter_cls, - ct->c); + ct->cc); } @@ -820,7 +1039,8 @@ GCT_debug (const struct CadetTunnel *t, LOG2 (level, "TTT connections:\n"); for (iter_c = t->connection_head; NULL != iter_c; iter_c = iter_c->next) - GCC_debug (iter_c->c, level); + GCC_debug (iter_c->cc, + level); LOG2 (level, "TTT TUNNEL END\n"); diff --git a/src/cadet/gnunet-service-cadet-new_tunnels.h b/src/cadet/gnunet-service-cadet-new_tunnels.h index 5e06559d5..0abe99f70 100644 --- a/src/cadet/gnunet-service-cadet-new_tunnels.h +++ b/src/cadet/gnunet-service-cadet-new_tunnels.h @@ -28,6 +28,7 @@ #ifndef GNUNET_SERVICE_CADET_TUNNELS_H #define GNUNET_SERVICE_CADET_TUNNELS_H +#include "gnunet-service-cadet-new.h" /** * How many connections would we like to have per tunnel? @@ -155,6 +156,20 @@ struct CadetPeer * GCT_get_destination (struct CadetTunnel *t); +/** + * Consider using the path @a p for the tunnel @a t. + * The tunnel destination is at offset @a off in path @a p. + * + * @param cls our tunnel + * @param path a path to our destination + * @param off offset of the destination on path @a path + */ +void +GCT_consider_path (struct CadetTunnel *t, + struct CadetPeerPath *p, + unsigned int off); + + /** * Add a channel to a tunnel. * -- 2.25.1