X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fmesh%2Fmesh_tunnel_tree.c;h=a7577e36f8b14b810aed41975cf8b7a470234594;hb=c4f0fe3ea5a5ca3ce1f7dfecef719da631e4c6ac;hp=10298ca5aa04ab08acae4eccff3e88c22aedbf76;hpb=dd6f8f750c62c0680528ae4a0744403aa80074be;p=oweals%2Fgnunet.git diff --git a/src/mesh/mesh_tunnel_tree.c b/src/mesh/mesh_tunnel_tree.c index 10298ca5a..a7577e36f 100644 --- a/src/mesh/mesh_tunnel_tree.c +++ b/src/mesh/mesh_tunnel_tree.c @@ -27,15 +27,112 @@ #include "mesh.h" #include "mesh_tunnel_tree.h" +#define MESH_TREE_DEBUG GNUNET_YES -extern GNUNET_PEER_Id myid; -extern struct GNUNET_PeerIdentity my_full_id; + +/** + * Node of path tree for a tunnel + */ +struct MeshTunnelTreeNode +{ + /** + * Peer this node describes + */ + GNUNET_PEER_Id peer; + + /** + * Parent node in the tree + */ + struct MeshTunnelTreeNode *parent; + + /** + * DLL of siblings + */ + struct MeshTunnelTreeNode *next; + + /** + * DLL of siblings + */ + struct MeshTunnelTreeNode *prev; + + /** + * DLL of children + */ + struct MeshTunnelTreeNode *children_head; + + /** + * DLL of children + */ + struct MeshTunnelTreeNode *children_tail; + + /** + * Status of the peer in the tunnel + */ + enum MeshPeerState status; +}; + + +/** + * Tree to reach all peers in the tunnel + */ +struct MeshTunnelTree +{ + /** + * Root node of peer tree + */ + struct MeshTunnelTreeNode *root; + + /** + * Node that represents our position in the tree (for non local tunnels) + */ + struct MeshTunnelTreeNode *me; + + /** + * DLL of disconneted nodes + */ + struct MeshTunnelTreeNode *disconnected_head; + + /** + * DLL of disconneted nodes + */ + struct MeshTunnelTreeNode *disconnected_tail; + + /** + * Cache of all peers and the first hop to them. + * Indexed by PeerIdentity, contains a pointer to the PeerIdentity + * of 1st hop. + */ + struct GNUNET_CONTAINER_MultiHashMap *first_hops; + +}; + + +/** + * Create a new path + * + * @param length How many hops will the path have. + * + * @return A newly allocated path with a peer array of the specified length. + */ +struct MeshPeerPath * +path_new (unsigned int length) +{ + struct MeshPeerPath *p; + + p = GNUNET_malloc (sizeof (struct MeshPeerPath)); + if (length > 0) + { + p->length = length; + p->peers = GNUNET_malloc (length * sizeof (GNUNET_PEER_Id)); + } + return p; +} /** * Invert the path * - * @param p the path to invert + * @param path the path to invert */ void path_invert (struct MeshPeerPath *path) @@ -52,6 +149,56 @@ path_invert (struct MeshPeerPath *path) } +/** + * Duplicate a path, incrementing short peer's rc. + * + * @param path The path to duplicate. + */ +struct MeshPeerPath * +path_duplicate (struct MeshPeerPath *path) +{ + struct MeshPeerPath *aux; + unsigned int i; + + aux = path_new (path->length); + memcpy (aux->peers, path->peers, path->length * sizeof (GNUNET_PEER_Id)); + for (i = 0; i < path->length; i++) + GNUNET_PEER_change_rc (path->peers[i], 1); + return aux; +} + + +/** + * Recusively update the info about what is the first hop to reach the node + * + * @param tree Tree this nodes belongs to. + * @param parent The node form which to start updating. + * @param hop If known, ID of the first hop. + * If not known, NULL to find out and pass on children. + */ +static void +tree_node_update_first_hops (struct MeshTunnelTree *tree, + struct MeshTunnelTreeNode *parent, + struct GNUNET_PeerIdentity *hop); + + +/** + * Get the length of a path. + * + * @param path The path to measure, with the local peer at any point of it. + * + * @return Number of hops to reach destination. + * UINT_MAX in case the peer is not in the path. + */ +unsigned int +path_get_length (struct MeshPeerPath *path) +{ + if (NULL == path) + return UINT_MAX; + return path->length; +} + + /** * Destroy the path and free any allocated resources linked to it * @@ -62,209 +209,414 @@ path_invert (struct MeshPeerPath *path) int path_destroy (struct MeshPeerPath *p) { + if (NULL == p) + return GNUNET_OK; GNUNET_PEER_decrement_rcs (p->peers, p->length); - GNUNET_free (p->peers); + GNUNET_free_non_null (p->peers); GNUNET_free (p); return GNUNET_OK; } + /** - * Find the first peer whom to send a packet to go down this path + * Allocates and initializes a new node. + * Sets ID and parent of the new node and inserts it in the DLL of the parent * - * @param t The tunnel to use - * @param peer The peerinfo of the peer we are trying to reach + * @param parent Node that will be the parent from the new node, NULL for root + * @param peer Short Id of the new node * - * @return peerinfo of the peer who is the first hop in the tunnel - * NULL on error + * @return Newly allocated node */ -struct GNUNET_PeerIdentity * -path_get_first_hop (struct MeshTunnel *t, struct MeshPeerInfo *peer) +static struct MeshTunnelTreeNode * +tree_node_new (struct MeshTunnelTreeNode *parent, GNUNET_PEER_Id peer) { - struct GNUNET_PeerIdentity id; + struct MeshTunnelTreeNode *node; + + node = GNUNET_malloc (sizeof (struct MeshTunnelTreeNode)); + node->peer = peer; + GNUNET_PEER_change_rc (peer, 1); + node->parent = parent; + if (NULL != parent) + GNUNET_CONTAINER_DLL_insert (parent->children_head, parent->children_tail, + node); - GNUNET_PEER_resolve (peer->id, &id); - return GNUNET_CONTAINER_multihashmap_get (t->tree->first_hops, - &id.hashPubKey); + return node; } /** - * Get the length of a path + * Recursively find the given peer. * - * @param path The path to measure, with the local peer at any point of it + * @param parent Node where to start looking. + * @param peer_id Short ID of the peer to find. * - * @return Number of hops to reach destination - * UINT_MAX in case the peer is not in the path + * @return Pointer to the node of the peer. NULL if not found. */ -unsigned int -path_get_length (struct MeshPeerPath *path) +static struct MeshTunnelTreeNode * +tree_node_find_peer (struct MeshTunnelTreeNode *parent, GNUNET_PEER_Id peer_id) { - if (NULL == path) - return UINT_MAX; - return path->length; + struct MeshTunnelTreeNode *n; + struct MeshTunnelTreeNode *r; + + if (parent->peer == peer_id) + return parent; + for (n = parent->children_head; NULL != n; n = n->next) + { + r = tree_node_find_peer (n, peer_id); + if (NULL != r) + return r; + } + return NULL; } /** - * Get the cost of the path relative to the already built tunnel tree - * - * @param t The tunnel to which compare - * @param path The individual path to reach a peer + * Recusively update the info about what is the first hop to reach the node * - * @return Number of hops to reach destination, UINT_MAX in case the peer is not - * in the path + * @param tree Tree this nodes belongs to. + * @param parent ID from node form which to start updating. + * @param hop If known, ID of the first hop. + * If not known, NULL to find out and pass on children. + */ +static void +tree_node_update_first_hops (struct MeshTunnelTree *tree, + struct MeshTunnelTreeNode *parent, + struct GNUNET_PeerIdentity *hop) +{ + struct GNUNET_PeerIdentity pi; + struct GNUNET_PeerIdentity *copy; + struct GNUNET_PeerIdentity id; + struct MeshTunnelTreeNode *n; + +#if MESH_TREE_DEBUG + GNUNET_PEER_resolve (parent->peer, &id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "tree: Finding first hop for %s.\n", + GNUNET_i2s (&id)); +#endif + if (NULL == hop) + { + struct MeshTunnelTreeNode *aux; + struct MeshTunnelTreeNode *old; + + aux = old = parent; + while (aux != tree->me) + { +#if MESH_TREE_DEBUG + GNUNET_PEER_resolve (aux->peer, &id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "tree: ... checking %s.\n", + GNUNET_i2s (&id)); +#endif + old = aux; + aux = aux->parent; + GNUNET_assert (NULL != aux); + } +#if MESH_TREE_DEBUG + GNUNET_PEER_resolve (old->peer, &id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "tree: It's %s!\n", + GNUNET_i2s (&id)); +#endif + hop = π + GNUNET_PEER_resolve (old->peer, hop); + } + GNUNET_PEER_resolve (parent->peer, &id); + copy = GNUNET_CONTAINER_multihashmap_get (tree->first_hops, &id.hashPubKey); + if (NULL == copy) + copy = GNUNET_malloc (sizeof (struct GNUNET_PeerIdentity)); + *copy = *hop; + + (void) GNUNET_CONTAINER_multihashmap_put (tree->first_hops, &id.hashPubKey, + copy, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE); + + for (n = parent->children_head; NULL != n; n = n->next) + { + tree_node_update_first_hops (tree, n, hop); + } +} + + +static void +tree_node_debug (struct MeshTunnelTreeNode *n, uint16_t level) +{ + struct MeshTunnelTreeNode *c; + struct GNUNET_PeerIdentity id;; + uint16_t i; + + for (i = 0; i < level; i++) + fprintf (stderr, " "); + if (n->status == MESH_PEER_READY) + fprintf (stderr, "#"); + if (n->status == MESH_PEER_SEARCHING) + fprintf (stderr, "+"); + if (n->status == MESH_PEER_RELAY) + fprintf (stderr, "-"); + if (n->status == MESH_PEER_RECONNECTING) + fprintf (stderr, "*"); + + GNUNET_PEER_resolve (n->peer, &id); + fprintf (stderr, "%s, [%u, %p] ", GNUNET_i2s (&id), n->peer, n); + if (NULL != n->parent) + { + GNUNET_PEER_resolve (n->parent->peer, &id); + fprintf (stderr, "(-> %s [%u])\n", GNUNET_i2s (&id), n->parent->peer); + } + else + fprintf (stderr, "(root)\n"); + for (c = n->children_head; NULL != c; c = c->next) + tree_node_debug (c, level + 1); +} + + +/** + * Destroys and frees the node and all children * - * TODO: remove dummy implementation, look into the tunnel tree + * @param parent Parent node to be destroyed */ -unsigned int -path_get_cost (struct MeshTunnel *t, struct MeshPeerPath *path) +static void +tree_node_destroy (struct MeshTunnelTreeNode *parent) { - return path_get_length (path); + struct MeshTunnelTreeNode *n; + struct MeshTunnelTreeNode *next; + +#if MESH_TREE_DEBUG + struct GNUNET_PeerIdentity id; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "tree: Destroying node %u\n", + parent->peer); + GNUNET_PEER_resolve (parent->peer, &id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "tree: (%s)\n", GNUNET_i2s (&id)); +#endif + n = parent->children_head; + while (NULL != n) + { + next = n->next; + tree_node_destroy (n); + n = next; + } + GNUNET_PEER_change_rc (parent->peer, -1); + if (NULL != parent->parent) + GNUNET_CONTAINER_DLL_remove (parent->parent->children_head, + parent->parent->children_tail, parent); + GNUNET_free (parent); } + /** - * Add the path to the peer and update the path used to reach it in case this - * is the shortest. + * Create a new tree. * - * @param peer_info Destination peer to add the path to. - * @param path New path to add. Last peer must be the peer in arg 1. - * Path will be either used of freed if already known. + * @param peer A short peer id of the root of the tree. * - * TODO: trim the part from origin to us? Add it as path to origin? + * @return A newly allocated and initialized tunnel tree. */ -void -path_add_to_peer (struct MeshPeerInfo *peer_info, struct MeshPeerPath *path) +struct MeshTunnelTree * +tree_new (GNUNET_PEER_Id peer) { - struct MeshPeerPath *aux; - unsigned int l; - unsigned int l2; + struct MeshTunnelTree *tree; + + tree = GNUNET_malloc (sizeof (struct MeshTunnelTree)); + tree->first_hops = GNUNET_CONTAINER_multihashmap_create (32); + tree->root = tree_node_new (NULL, peer); + tree->root->status = MESH_PEER_ROOT; - if (NULL == peer_info || NULL == path) + if (1 == peer) { - GNUNET_break (0); - return; + tree->me = tree->root; } - l = path_get_length (path); + return tree; +} + - for (aux = peer_info->path_head; aux != NULL; aux = aux->next) +/** + * Set the status of a node. + * + * @param tree Tree. + * @param peer A short peer id of the node. + * @param status New status to set. + */ +void +tree_set_status (struct MeshTunnelTree *tree, GNUNET_PEER_Id peer, + enum MeshPeerState status) +{ + struct MeshTunnelTreeNode *n; + + n = tree_find_peer (tree, peer); + if (NULL == n) + return; + n->status = status; +} + + +/** + * Get the status of a node. + * + * @param tree Tree whose node's status we want to now. + * @param peer A short peer id of the node. + * + * @return Status of the peer. + */ +enum MeshPeerState +tree_get_status (struct MeshTunnelTree *tree, GNUNET_PEER_Id peer) +{ + struct MeshTunnelTreeNode *n; + + n = tree_find_peer (tree, peer); + if (NULL == n) + return MESH_PEER_INVALID; + return n->status; +} + + +/** + * Get the id of the predecessor of the local node. + * + * @param tree Tree whose local id we want to now. + * + * @return Short peer id of local peer. + */ +GNUNET_PEER_Id +tree_get_predecessor (struct MeshTunnelTree *tree) +{ + if (NULL != tree->me && NULL != tree->me->parent) + return tree->me->parent->peer; + else + return (GNUNET_PEER_Id) 0; +} + + +/** + * Find the first peer whom to send a packet to go down this path + * + * @param t The tunnel tree to use + * @param peer The peerinfo of the peer we are trying to reach + * + * @return peerinfo of the peer who is the first hop in the tunnel + * NULL on error + */ +struct GNUNET_PeerIdentity * +tree_get_first_hop (struct MeshTunnelTree *t, GNUNET_PEER_Id peer) +{ + struct GNUNET_PeerIdentity id; + struct GNUNET_PeerIdentity *r; + + GNUNET_PEER_resolve (peer, &id); + r = GNUNET_CONTAINER_multihashmap_get (t->first_hops, &id.hashPubKey); + if (NULL == r) { - l2 = path_get_length (aux); - if (l2 > l) + struct MeshTunnelTreeNode *n; + + n = tree_find_peer (t, peer); + if (NULL != t->me && NULL != n) { - GNUNET_CONTAINER_DLL_insert_before (peer_info->path_head, - peer_info->path_tail, aux, path); + tree_node_update_first_hops (t, n, NULL); + r = GNUNET_CONTAINER_multihashmap_get (t->first_hops, &id.hashPubKey); + GNUNET_assert (NULL != r); } else { - if (l2 == l && memcmp(path->peers, aux->peers, l) == 0) - { - path_destroy(path); - return; - } + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Tree structure inconsistent! me: %p, n: %p", t->me, n); + GNUNET_break (0); } } - GNUNET_CONTAINER_DLL_insert_tail (peer_info->path_head, peer_info->path_tail, - path); - return; + + return r; } /** - * Send keepalive packets for a peer + * Find the given peer in the tree. * - * @param cls unused - * @param tc unused + * @param tree Tree where to look for the peer. + * @param peer_id Short ID of the peer to find. * - * FIXME path + * @return Pointer to the node of the peer. NULL if not found. */ -void -path_refresh (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +struct MeshTunnelTreeNode * +tree_find_peer (struct MeshTunnelTree *tree, GNUNET_PEER_Id peer_id) { - struct MeshTunnel *t = cls; - -// struct GNUNET_PeerIdentity id; - - if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN) - { - return; - } - /* FIXME implement multicast keepalive. Just an empty multicast packet? */ -// GNUNET_PEER_resolve (path_get_first_hop (path->t, path->peer)->id, &id); -// GNUNET_CORE_notify_transmit_ready (core_handle, 0, 0, -// GNUNET_TIME_UNIT_FOREVER_REL, &id, -// sizeof (struct GNUNET_MESH_ManipulatePath) -// + -// (path->path->length * -// sizeof (struct GNUNET_PeerIdentity)), -// &send_core_create_path, -// t); - t->path_refresh_task = - GNUNET_SCHEDULER_add_delayed (t->tree->refresh, &path_refresh, t); - return; + return tree_node_find_peer (tree->root, peer_id); } - /** - * Recursively find the given peer in the tree. - * - * @param t Tunnel where to look for the peer. - * @param peer Peer to find + * Recusively mark peer and children as disconnected, notify client * - * @return Pointer to the node of the peer. NULL if not found. + * @param tree Tree this node belongs to + * @param parent Node to be clean, potentially with children + * @param cb Callback to use to notify about disconnected peers. + * @param cbcls Closure for cb. */ -struct MeshTunnelPathNode * -tunnel_find_peer (struct MeshTunnelPathNode *root, GNUNET_PEER_Id peer_id) +static void +tree_mark_peers_disconnected (struct MeshTunnelTree *tree, + struct MeshTunnelTreeNode *parent, + MeshTreeCallback cb, void *cbcls) { - struct MeshTunnelPathNode *n; - unsigned int i; + struct GNUNET_PeerIdentity *pi; + struct GNUNET_PeerIdentity id; + struct MeshTunnelTreeNode *n; - if (root->peer == peer_id) - return root; - for (i = 0; i < root->nchildren; i++) + for (n = parent->children_head; NULL != n; n = n->next) { - n = tunnel_find_peer (&root->children[i], peer_id); - if (NULL != n) - return n; + tree_mark_peers_disconnected (tree, n, cb, cbcls); } - return NULL; + if (MESH_PEER_READY == parent->status) + { + if (NULL != cb) + cb (cbcls, parent->peer); + parent->status = MESH_PEER_RECONNECTING; + } + + /* Remove and free info about first hop */ + GNUNET_PEER_resolve (parent->peer, &id); + pi = GNUNET_CONTAINER_multihashmap_get (tree->first_hops, &id.hashPubKey); + GNUNET_CONTAINER_multihashmap_remove_all (tree->first_hops, &id.hashPubKey); + if (NULL != pi) + GNUNET_free (pi); } /** - * Recusively mark peer and children as disconnected, notify client + * Iterate over all children of the local node. * - * @param parent Node to be clean, potentially with children - * @param nc Notification context to use to alert the client + * @param tree Tree to use. Must have "me" set. + * @param cb Callback to call over each child. + * @param cls Closure. */ void -tunnel_mark_peers_disconnected (struct MeshTunnelPathNode *parent, - struct GNUNET_SERVER_NotificationContext *nc) +tree_iterate_children (struct MeshTunnelTree *tree, MeshTreeCallback cb, + void *cls) { - struct GNUNET_MESH_PeerControl msg; - unsigned int i; + struct MeshTunnelTreeNode *n; - parent->status = MESH_PEER_RECONNECTING; - for (i = 0; i < parent->nchildren; i++) + if (NULL == tree->me) { - tunnel_mark_peers_disconnected (&parent->children[i], nc); - } - if (NULL == parent->t->client) - return; - msg.header.size = htons (sizeof (msg)); - msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_DEL); - msg.tunnel_id = htonl (parent->t->local_tid); - GNUNET_PEER_resolve (parent->peer, &msg.peer); - if (NULL == nc) + GNUNET_break (0); return; - GNUNET_SERVER_notification_context_unicast (nc, parent->t->client->handle, - &msg.header, GNUNET_NO); + } + for (n = tree->me->children_head; NULL != n; n = n->next) + { + cb (cls, n->peer); + } } +/** + * Recusively update the info about what is the first hop to reach the node + * + * @param tree Tree this nodes belongs to. + * @param parent_id Short ID from node form which to start updating. + * @param hop If known, ID of the first hop. + * If not known, NULL to find out and pass on children. + */ +void +tree_update_first_hops (struct MeshTunnelTree *tree, GNUNET_PEER_Id parent_id, + struct GNUNET_PeerIdentity *hop) +{ + tree_node_update_first_hops (tree, tree_find_peer (tree, parent_id), hop); +} /** @@ -272,41 +624,69 @@ tunnel_mark_peers_disconnected (struct MeshTunnelPathNode *parent, * The destination peer is NOT destroyed, it is returned in order to either set * a new path to it or destroy it explicitly, taking care of it's child nodes. * - * @param t Tunnel where to delete the path from. - * @param peer Destination peer whose path we want to remove. - * @param nc Notification context to alert the client of disconnected peers. + * @param t Tunnel tree where to delete the path from. + * @param peer_id Short ID of the destination peer whose path we want to remove. + * @param cb Callback to use to notify about disconnected peers. + * @param cbcls Closure for cb. * - * @return pointer to the pathless node, NULL on error + * @return pointer to the pathless node. + * NULL when not found */ -struct MeshTunnelPathNode * -tunnel_del_path (struct MeshTunnel *t, GNUNET_PEER_Id peer_id, - struct GNUNET_SERVER_NotificationContext *nc) +struct MeshTunnelTreeNode * +tree_del_path (struct MeshTunnelTree *t, GNUNET_PEER_Id peer_id, + MeshTreeCallback cb, void *cbcls) { - struct MeshTunnelPathNode *parent; - struct MeshTunnelPathNode *node; - struct MeshTunnelPathNode *n; + struct MeshTunnelTreeNode *parent; + struct MeshTunnelTreeNode *node; + struct MeshTunnelTreeNode *n; - if (peer_id == t->tree->root->peer) +#if MESH_TREE_DEBUG + struct GNUNET_PeerIdentity id; + + GNUNET_PEER_resolve (peer_id, &id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "tree: Deleting path to %s.\n", + GNUNET_i2s (&id)); +#endif + if (peer_id == t->root->peer) return NULL; - node = n = tunnel_find_peer (t->tree->me, peer_id); + + for (n = t->disconnected_head; NULL != n; n = n->next) + { + if (n->peer == peer_id) + { + /* Was already pathless, waiting for reconnection */ + GNUNET_CONTAINER_DLL_remove (t->disconnected_head, t->disconnected_tail, + n); + return n; + } + } + n = tree_find_peer (t, peer_id); if (NULL == n) return NULL; + node = n; + parent = n->parent; + GNUNET_CONTAINER_DLL_remove (parent->children_head, parent->children_tail, n); n->parent = NULL; - while (NULL != parent && MESH_PEER_RELAY == parent->status && - 1 == parent->nchildren) + + while (MESH_PEER_RELAY == parent->status && NULL == parent->children_head) { - n = parent; - GNUNET_free (parent->children); - parent = parent->parent; +#if MESH_TREE_DEBUG + GNUNET_PEER_resolve (parent->peer, &id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "tree: Deleting node %s.\n", + GNUNET_i2s (&id)); +#endif + n = parent->parent; + tree_node_destroy (parent); + parent = n; } - if (NULL == parent) - return node; - *n = parent->children[parent->nchildren - 1]; - parent->nchildren--; - parent->children = GNUNET_realloc (parent->children, parent->nchildren); +#if MESH_TREE_DEBUG + GNUNET_PEER_resolve (parent->peer, &id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "tree: Not deleted peer %s.\n", + GNUNET_i2s (&id)); +#endif - tunnel_mark_peers_disconnected (node, nc); + tree_mark_peers_disconnected (t, node, cb, cbcls); return node; } @@ -316,46 +696,62 @@ tunnel_del_path (struct MeshTunnel *t, GNUNET_PEER_Id peer_id, * Return a newly allocated individual path to reach a peer from the local peer, * according to the path tree of some tunnel. * - * @param t Tunnel from which to read the path tree - * @param peer_info Destination peer to whom we want a path + * @param t Tunnel from which to read the path tree. + * @param peer Short ID of the destination peer to whom we want a path. * * @return A newly allocated individual path to reach the destination peer. * Path must be destroyed afterwards. */ struct MeshPeerPath * -tunnel_get_path_to_peer(struct MeshTunnel *t, struct MeshPeerInfo *peer_info) +tree_get_path_to_peer (struct MeshTunnelTree *t, GNUNET_PEER_Id peer) { - struct MeshTunnelPathNode *n; + struct MeshTunnelTreeNode *n; struct MeshPeerPath *p; - GNUNET_PEER_Id myid = t->tree->me->peer; - n = tunnel_find_peer(t->tree->me, peer_info->id); - p = GNUNET_malloc(sizeof(struct MeshPeerPath)); + n = tree_find_peer (t, peer); + if (NULL == n) + { + GNUNET_break (0); + return NULL; + } + p = path_new (0); /* Building the path (inverted!) */ - while (n->peer != myid) + while (n->peer != 1) { - GNUNET_array_append(p->peers, p->length, n->peer); - GNUNET_PEER_change_rc(n->peer, 1); + GNUNET_array_append (p->peers, p->length, n->peer); + GNUNET_PEER_change_rc (n->peer, 1); n = n->parent; - GNUNET_assert(NULL != n); + if (NULL == n) + { + GNUNET_break (0); + path_destroy (p); + return NULL; + } } - GNUNET_array_append(p->peers, p->length, myid); - GNUNET_PEER_change_rc(myid, 1); + GNUNET_array_append (p->peers, p->length, 1); + GNUNET_PEER_change_rc (1, 1); - path_invert(p); + path_invert (p); return p; } + /** * Integrate a stand alone path into the tunnel tree. + * If the peer toward which the new path is already in the tree, the peer + * and its children will be maked as disconnected and the callback + * will be called on each one of them. They will be maked as online only after + * receiving a PATH ACK for the new path for each one of them, so the caller + * should take care of sending a new CREATE PATH message for each disconnected + * peer. * * @param t Tunnel where to add the new path. * @param p Path to be integrated. - * @param nc Notification context to alert clients of peers - * temporarily disconnected + * @param cb Callback to use to notify about peers temporarily disconnecting. + * @param cbcls Closure for cb. * * @return GNUNET_OK in case of success. * GNUNET_SYSERR in case of error. @@ -365,20 +761,26 @@ tunnel_get_path_to_peer(struct MeshTunnel *t, struct MeshPeerInfo *peer_info) * - do not disconnect peers until new path is created & connected */ int -tunnel_add_path (struct MeshTunnel *t, const struct MeshPeerPath *p) +tree_add_path (struct MeshTunnelTree *t, const struct MeshPeerPath *p, + MeshTreeCallback cb, void *cbcls) { - struct MeshTunnelPathNode *parent; - struct MeshTunnelPathNode *oldnode; - struct MeshTunnelPathNode *n; + struct MeshTunnelTreeNode *parent; + struct MeshTunnelTreeNode *oldnode; + struct MeshTunnelTreeNode *n; + struct MeshTunnelTreeNode *c; struct GNUNET_PeerIdentity id; - struct GNUNET_PeerIdentity *hop; - GNUNET_PEER_Id myid = t->tree->me->peer; int me; unsigned int i; - unsigned int j; - GNUNET_assert(0 != p->length); - n = t->tree->root; +#if MESH_TREE_DEBUG + GNUNET_PEER_resolve (p->peers[p->length - 1], &id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "tree: Adding path [%u] towards peer %s.\n", p->length, + GNUNET_i2s (&id)); +#endif + + GNUNET_assert (0 != p->length); + parent = n = t->root; if (n->peer != p->peers[0]) { GNUNET_break (0); @@ -386,23 +788,34 @@ tunnel_add_path (struct MeshTunnel *t, const struct MeshPeerPath *p) } if (1 == p->length) return GNUNET_OK; - oldnode = tunnel_del_path (t, p->peers[p->length - 1], NULL); + oldnode = tree_del_path (t, p->peers[p->length - 1], cb, cbcls); /* Look for the first node that is not already present in the tree * * Assuming that the tree is somewhat balanced, O(log n * log N). * - Length of the path is expected to be log N (size of whole network). * - Each level of the tree is expected to have log n children (size of tree). */ - for (i = 0, me = -1; i < p->length; i++) + me = t->root->peer == 1 ? 0 : -1; + for (i = 1; i < p->length; i++) { +#if MESH_TREE_DEBUG + GNUNET_PEER_resolve (p->peers[i], &id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "tree: Looking for peer %s.\n", + GNUNET_i2s (&id)); +#endif parent = n; - if (p->peers[i] == myid) + if (p->peers[i] == 1) me = i; - for (j = 0; j < n->nchildren; j++) + for (c = n->children_head; NULL != c; c = c->next) { - if (n->children[j].peer == p->peers[i]) + if (c->peer == p->peers[i]) { - n = &n->children[j]; +#if MESH_TREE_DEBUG + GNUNET_PEER_resolve (parent->peer, &id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "tree: Found in children of %s.\n", GNUNET_i2s (&id)); +#endif + n = c; break; } } @@ -411,84 +824,252 @@ tunnel_add_path (struct MeshTunnel *t, const struct MeshPeerPath *p) if (parent == n) break; } - if (-1 == me) - { - /* New path deviates from tree before reaching us. What happened? */ - GNUNET_break (0); - return GNUNET_SYSERR; - } +#if MESH_TREE_DEBUG + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "tree: All childen visited.\n"); +#endif /* Add the rest of the path as a branch from parent. */ while (i < p->length) { - parent->nchildren++; - parent->children = GNUNET_realloc (parent->children, - parent->nchildren * - sizeof(struct MeshTunnelPathNode)); - n = &parent->children[parent->nchildren - 1]; +#if MESH_TREE_DEBUG + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "tree: Adding peer %u to %u.\n", + p->peers[i], parent->peer); + GNUNET_PEER_resolve (p->peers[i], &id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "tree: Adding peer %s.\n", + GNUNET_i2s (&id)); + GNUNET_PEER_resolve (parent->peer, &id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "tree: to %s.\n", + GNUNET_i2s (&id)); +#endif + if (i == p->length - 1 && NULL != oldnode) { - /* Assignation and free can be misleading, using explicit mempcy */ - memcpy (n, oldnode, sizeof (struct MeshTunnelPathNode)); - GNUNET_free (oldnode); +#if MESH_TREE_DEBUG + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "tree: Putting old node into place.\n"); +#endif + oldnode->parent = parent; + GNUNET_CONTAINER_DLL_insert (parent->children_head, parent->children_tail, + oldnode); + tree_node_update_first_hops (t, oldnode, NULL); + n = oldnode; } else { - n->t = t; +#if MESH_TREE_DEBUG + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "tree: Creating new node.\n"); +#endif + n = tree_node_new (parent, p->peers[i]); n->status = MESH_PEER_RELAY; - n->peer = p->peers[i]; + if (n->peer == 1) + { + t->me = n; + me = i; + } } - n->parent = parent; i++; parent = n; } + n->status = MESH_PEER_SEARCHING; + + GNUNET_break (-1 != me); /* Add info about first hop into hashmap. */ - if (me < p->length - 1) + if (-1 != me && me < p->length - 1) + { +#if MESH_TREE_DEBUG + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "MESH: finding first hop (own pos %d/%u)\n", me, + p->length - 1); +#endif + GNUNET_PEER_resolve (p->peers[me + 1], &id); + tree_update_first_hops (t, p->peers[me + 1], &id); + } +#if MESH_TREE_DEBUG + else { - GNUNET_PEER_resolve (p->peers[p->length - 1], &id); - hop = GNUNET_malloc(sizeof(struct GNUNET_PeerIdentity)); - GNUNET_PEER_resolve (p->peers[me + 1], hop); - GNUNET_CONTAINER_multihashmap_put (t->tree->first_hops, &id.hashPubKey, - hop, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "MESH: was last in path, not updating first hops (%d/%u)\n", + me, p->length - 1); } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "tree: New node added.\n"); +#endif + if (NULL == t->me) + t->me = tree_find_peer (t, 1); return GNUNET_OK; } /** - * Add a peer to a tunnel, accomodating paths accordingly and initializing all - * needed rescources. + * Notifies a tree that a connection it might be using is broken. + * Marks all peers down the paths as disconnected and notifies the client. * - * @param t Tunnel we want to add a new peer to - * @param peer PeerInfo of the peer being added + * @param t Tree to use. + * @param p1 Short id of one of the peers (order unimportant) + * @param p2 Short id of one of the peers (order unimportant) + * @param cb Function to call for every peer that is marked as disconnected. + * @param cbcls Closure for cb. * + * @return Short ID of the first disconnected peer in the tree. */ -void -tunnel_add_peer (struct MeshTunnel *t, struct MeshPeerInfo *peer) +GNUNET_PEER_Id +tree_notify_connection_broken (struct MeshTunnelTree *t, GNUNET_PEER_Id p1, + GNUNET_PEER_Id p2, MeshTreeCallback cb, + void *cbcls) { - struct MeshPeerPath *p; - struct MeshPeerPath *best_p; - unsigned int best_cost; - unsigned int cost; + struct MeshTunnelTreeNode *n; + struct MeshTunnelTreeNode *c; + + n = tree_find_peer (t, p1); + if (NULL == n) + return 0; + if (NULL != n->parent && n->parent->peer == p2) + { + tree_mark_peers_disconnected (t, n, cb, cbcls); + GNUNET_CONTAINER_DLL_remove (n->parent->children_head, + n->parent->children_tail, n); + GNUNET_CONTAINER_DLL_insert (t->disconnected_head, t->disconnected_tail, n); + return p1; + } + for (c = n->children_head; NULL != c; c = c->next) + { + if (c->peer == p2) + { + tree_mark_peers_disconnected (t, c, cb, cbcls); + GNUNET_CONTAINER_DLL_remove (n->children_head, n->children_tail, c); + GNUNET_CONTAINER_DLL_insert (t->disconnected_head, t->disconnected_tail, + c); + return p2; + } + } + return 0; +} - GNUNET_array_append (peer->tunnels, peer->ntunnels, t); - if (NULL == (p = peer->path_head)) - return; - best_p = p; - best_cost = UINT_MAX; - while (NULL != p) +/** + * Deletes a peer from a tunnel, liberating all unused resources on the path to + * it. It shouldn't have children, if it has they will be destroyed as well. + * If the tree is not local and no longer has any paths, the root node will be + * destroyed and marked as NULL. + * + * @param t Tunnel tree to use. + * @param peer Short ID of the peer to remove from the tunnel tree. + * @param cb Callback to notify client of disconnected peers. + * @param cbcls Closure for cb. + * + * @return GNUNET_OK or GNUNET_SYSERR + */ +int +tree_del_peer (struct MeshTunnelTree *t, GNUNET_PEER_Id peer, + MeshTreeCallback cb, void *cbcls) +{ + struct MeshTunnelTreeNode *n; + + n = tree_del_path (t, peer, cb, cbcls); + if (NULL == n) + { + GNUNET_break (0); + return GNUNET_YES; + } + GNUNET_break_op (NULL == n->children_head); + tree_node_destroy (n); + if (NULL == t->root->children_head && t->me != t->root) { - if ((cost = path_get_cost (t, p)) < best_cost) + tree_node_destroy (t->root); + t->root = NULL; + return GNUNET_NO; + } + return GNUNET_YES; +} + + + +/** + * Get the cost of the path relative to the already built tunnel tree. + * + * @param t The tunnel tree to which compare. + * @param path The individual path to reach a peer. It has to start at the + * root of the tree to be comparable. + * + * @return Number of hops to reach destination, UINT_MAX in case the peer is not + * in the path. + * + * TODO: adapt to allow any start / root combination + * TODO: take in account state of the nodes + */ +unsigned int +tree_get_path_cost (struct MeshTunnelTree *t, struct MeshPeerPath *path) +{ + struct MeshTunnelTreeNode *n; + struct MeshTunnelTreeNode *p; + unsigned int i; + unsigned int l; + + l = path_get_length (path); + p = t->root; + if (t->root->peer != path->peers[0]) + { + GNUNET_break (0); + return UINT_MAX; + } + for (i = 1; i < l; i++) + { + for (n = p->children_head; NULL != n; n = n->next) { - best_cost = cost; - best_p = p; + if (path->peers[i] == n->peer) + { + break; + } } - p = p->next; + if (NULL == n) + return l - i; + p = n; } - tunnel_add_path (t, best_p); - if (GNUNET_SCHEDULER_NO_TASK == t->path_refresh_task) - t->path_refresh_task = - GNUNET_SCHEDULER_add_delayed (t->tree->refresh, &path_refresh, t); + return l - i; +} + + +/** + * Print the tree on stderr + * + * @param t The tree + */ +void +tree_debug (struct MeshTunnelTree *t) +{ + tree_node_debug (t->root, 0); +} + + +/** + * Iterator over hash map peer entries and frees all data in it. + * Used prior to destroying a hashmap. Makes you miss anonymous functions in C. + * + * @param cls closure + * @param key current key code (will no longer contain valid data!!) + * @param value value in the hash map (treated as void *) + * @return GNUNET_YES if we should continue to iterate, GNUNET_NO if not. + */ +static int +iterate_free (void *cls, const GNUNET_HashCode * key, void *value) +{ + GNUNET_free (value); + return GNUNET_YES; +} + + +/** + * Destroy the whole tree and free all used memory and Peer_Ids + * + * @param t Tree to be destroyed + */ +void +tree_destroy (struct MeshTunnelTree *t) +{ +#if MESH_TREE_DEBUG + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "tree: Destroying tree\n"); +#endif + tree_node_destroy (t->root); + GNUNET_CONTAINER_multihashmap_iterate (t->first_hops, &iterate_free, NULL); + GNUNET_CONTAINER_multihashmap_destroy (t->first_hops); + GNUNET_free (t); }