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;
* 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;
/* 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;
#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;
*/
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
/**
- * 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;
* @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;i<off;i++)
+ {
+ // FIXME: remember existence of this connection with
+ // ALL peers on the path!
+ // (and remove on destruction of connection!)
+ }
+ return cc;
+}
+
+
+/**
+ * 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)
+{
+ GNUNET_assert (0); // FIXME
+}
+
+
+/**
+ * Obtain the path used by this connection.
+ *
+ * @param cc connection
+ * @return path to @a cc
+ */
+struct CadetPeerPath *
+GCC_get_path (struct CadetConnection *cc)
+{
+ return cc->path;
+}
+
+
/**
* Obtain unique ID for the connection.
*
#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.
GNUNET_CONTAINER_HeapCostType
GCPP_get_desirability (const struct CadetPeerPath *path)
{
- GNUNET_assert (0);
+ GNUNET_break (0);
return 0;
}
* 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);
}
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);
}
/**
* 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
* @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);
}
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;
}
/**
* 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
/**
* 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);
/**
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
* 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);
/**
- * 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
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);
}
/**
- * 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 ==
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);
}
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,
* 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;
}
{
if (GNUNET_NO ==
callback (callback_cls,
- peer,
- pe->path))
+ pe->path,
+ i))
return ret;
ret++;
}
GCP_set_hello (struct CadetPeer *peer,
const struct GNUNET_HELLO_Message *hello)
{
- /* FIXME! */
+ /* FIXME: keep HELLO, possibly offer to TRANSPORT... */
consider_peer_destroy (peer);
}
* 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);
/**
/**
* 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);
/**
* 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);
* 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);
* @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"
#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"
/**
/**
* Connection handle.
*/
- struct CadetConnection *c;
+ struct CadetConnection *cc;
+
+ /**
+ * Tunnel this connection belongs to.
+ */
+ struct CadetTunnel *t;
/**
* Creation time, to keep oldest connection alive.
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
/**
* 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).
GNUNET_snprintf (buf,
sizeof (buf),
"T(%s)",
- GCP_2s (t->peer));
+ GCP_2s (t->destination));
return buf;
}
struct CadetPeer *
GCT_get_destination (struct CadetTunnel *t)
{
- return t->peer;
+ return t->destination;
}
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().
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;
}
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;
}
NULL != ct;
ct = ct->next)
iter (iter_cls,
- ct->c);
+ ct->cc);
}
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");
#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?
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.
*