X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fmesh%2Fmesh_tunnel_tree.c;h=a7577e36f8b14b810aed41975cf8b7a470234594;hb=c4f0fe3ea5a5ca3ce1f7dfecef719da631e4c6ac;hp=ef4e4a5a03ecdcbe9013c8f2dd86f86d63ce7423;hpb=739aec3d1590e7c80d1322608781cf140c0e9f6a;p=oweals%2Fgnunet.git diff --git a/src/mesh/mesh_tunnel_tree.c b/src/mesh/mesh_tunnel_tree.c index ef4e4a5a0..a7577e36f 100644 --- a/src/mesh/mesh_tunnel_tree.c +++ b/src/mesh/mesh_tunnel_tree.c @@ -27,11 +27,112 @@ #include "mesh.h" #include "mesh_tunnel_tree.h" +#define MESH_TREE_DEBUG GNUNET_YES + + +/** + * 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) @@ -48,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 * @@ -58,91 +209,335 @@ 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 tree 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 MeshTunnelTree *t, GNUNET_PEER_Id peer) +static struct MeshTunnelTreeNode * +tree_node_new (struct MeshTunnelTreeNode *parent, GNUNET_PEER_Id peer) { - struct GNUNET_PeerIdentity id; + struct MeshTunnelTreeNode *node; - GNUNET_PEER_resolve (peer, &id); - return GNUNET_CONTAINER_multihashmap_get (t->first_hops, - &id.hashPubKey); + 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); + + 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; +} + + +/** + * Recusively update the info about what is the first hop to reach the node + * + * @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); } /** - * Get the cost of the path relative to the already built tunnel tree + * Destroys and frees the node and all children * - * @param t The tunnel tree to which compare - * @param path The individual path to reach a peer + * @param parent Parent node to be destroyed + */ +static void +tree_node_destroy (struct MeshTunnelTreeNode *parent) +{ + 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); +} + + + +/** + * Create a new tree. * - * @return Number of hops to reach destination, UINT_MAX in case the peer is not - * in the path + * @param peer A short peer id of the root of the tree. * - * TODO: remove dummy implementation, look into the tunnel tree + * @return A newly allocated and initialized tunnel tree. */ -unsigned int -path_get_cost (struct MeshTunnelTree *t, struct MeshPeerPath *path) +struct MeshTunnelTree * +tree_new (GNUNET_PEER_Id peer) { - return path_get_length (path); + 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 (1 == peer) + { + tree->me = tree->root; + } + + return tree; +} + + +/** + * 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; } /** - * Recursively find the given peer in the tree. + * Get the status of a node. * - * @param t Tunnel where to look for the peer. - * @param peer Peer to find + * @param tree Tree whose node's status we want to now. + * @param peer A short peer id of the node. * - * @return Pointer to the node of the peer. NULL if not found. + * @return Status of the peer. */ -struct MeshTunnelTreeNode * -tree_find_peer (struct MeshTunnelTreeNode *root, GNUNET_PEER_Id peer_id) +enum MeshPeerState +tree_get_status (struct MeshTunnelTree *tree, GNUNET_PEER_Id peer) { struct MeshTunnelTreeNode *n; - unsigned int i; - if (root->peer == peer_id) - return root; - for (i = 0; i < root->nchildren; i++) + 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) { - n = tree_find_peer (&root->children[i], peer_id); - if (NULL != n) - return n; + struct MeshTunnelTreeNode *n; + + n = tree_find_peer (t, peer); + if (NULL != t->me && NULL != n) + { + tree_node_update_first_hops (t, n, NULL); + r = GNUNET_CONTAINER_multihashmap_get (t->first_hops, &id.hashPubKey); + GNUNET_assert (NULL != r); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Tree structure inconsistent! me: %p, n: %p", t->me, n); + GNUNET_break (0); + } } - return NULL; + + return r; +} + + +/** + * Find the given peer in the tree. + * + * @param tree Tree where to look for the peer. + * @param peer_id Short ID of the peer to find. + * + * @return Pointer to the node of the peer. NULL if not found. + */ +struct MeshTunnelTreeNode * +tree_find_peer (struct MeshTunnelTree *tree, GNUNET_PEER_Id peer_id) +{ + return tree_node_find_peer (tree->root, peer_id); } @@ -152,46 +547,76 @@ tree_find_peer (struct MeshTunnelTreeNode *root, GNUNET_PEER_Id peer_id) * @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. */ -void +static void tree_mark_peers_disconnected (struct MeshTunnelTree *tree, struct MeshTunnelTreeNode *parent, - MeshNodeDisconnectCB cb) + MeshTreeCallback cb, void *cbcls) { struct GNUNET_PeerIdentity *pi; struct GNUNET_PeerIdentity id; - unsigned int i; + struct MeshTunnelTreeNode *n; - for (i = 0; i < parent->nchildren; i++) + for (n = parent->children_head; NULL != n; n = n->next) { - tree_mark_peers_disconnected (tree, &parent->children[i], cb); + tree_mark_peers_disconnected (tree, n, cb, cbcls); } if (MESH_PEER_READY == parent->status) { - cb (parent); + if (NULL != cb) + cb (cbcls, parent->peer); + parent->status = MESH_PEER_RECONNECTING; } - 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); + 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); -// struct GNUNET_MESH_PeerControl msg; -// 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) -// return; -// GNUNET_SERVER_notification_context_unicast (nc, parent->t->client->handle, -// &msg.header, GNUNET_NO); + GNUNET_free (pi); } +/** + * Iterate over all children of the local node. + * + * @param tree Tree to use. Must have "me" set. + * @param cb Callback to call over each child. + * @param cls Closure. + */ +void +tree_iterate_children (struct MeshTunnelTree *tree, MeshTreeCallback cb, + void *cls) +{ + struct MeshTunnelTreeNode *n; + + if (NULL == tree->me) + { + GNUNET_break (0); + return; + } + 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); +} /** @@ -200,44 +625,68 @@ tree_mark_peers_disconnected (struct MeshTunnelTree *tree, * a new path to it or destroy it explicitly, taking care of it's child nodes. * * @param t Tunnel tree where to delete the path from. - * @param peer Destination peer whose path we want to remove. + * @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 MeshTunnelTreeNode * tree_del_path (struct MeshTunnelTree *t, GNUNET_PEER_Id peer_id, - MeshNodeDisconnectCB cb) + MeshTreeCallback cb, void *cbcls) { struct MeshTunnelTreeNode *parent; struct MeshTunnelTreeNode *node; struct MeshTunnelTreeNode *n; - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Deleting path to %u.\n", peer_id); +#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; - n = tree_find_peer (t->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 = GNUNET_malloc(sizeof(struct MeshTunnelTreeNode)); - *node = *n; + node = n; + parent = n->parent; - parent->nchildren--; + GNUNET_CONTAINER_DLL_remove (parent->children_head, parent->children_tail, n); n->parent = NULL; - *n = parent->children[parent->nchildren]; - parent->children = GNUNET_realloc(parent->children, - parent->nchildren - * sizeof(struct MeshTunnelTreeNode)); - while (t->root != parent && MESH_PEER_RELAY == parent->status && - 0 == parent->nchildren) + + while (MESH_PEER_RELAY == parent->status && NULL == parent->children_head) { - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Deleting node %u.\n", parent->peer); +#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); + tree_node_destroy (parent); parent = n; } +#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 - tree_mark_peers_disconnected (t, node, cb); + tree_mark_peers_disconnected (t, node, cb, cbcls); return node; } @@ -247,45 +696,62 @@ tree_del_path (struct MeshTunnelTree *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 * -tree_get_path_to_peer(struct MeshTunnelTree *t, GNUNET_PEER_Id peer) +tree_get_path_to_peer (struct MeshTunnelTree *t, GNUNET_PEER_Id peer) { struct MeshTunnelTreeNode *n; struct MeshPeerPath *p; - GNUNET_PEER_Id myid = t->me->peer; - n = tree_find_peer(t->me, peer); - 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 cb Callback to use to notify about peers temporarily disconnecting + * @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. @@ -296,24 +762,24 @@ tree_get_path_to_peer(struct MeshTunnelTree *t, GNUNET_PEER_Id peer) */ int tree_add_path (struct MeshTunnelTree *t, const struct MeshPeerPath *p, - MeshNodeDisconnectCB cb) + MeshTreeCallback cb, void *cbcls) { 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->me->peer; int me; unsigned int i; - unsigned int j; - - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, - "Adding path [%u] towards peer %u to peer %u.\n", - p->length, - p->peers[p->length - 1], - t->me->peer); - GNUNET_assert(0 != p->length); + +#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]) { @@ -322,27 +788,34 @@ tree_add_path (struct MeshTunnelTree *t, const struct MeshPeerPath *p, } if (1 == p->length) return GNUNET_OK; - oldnode = tree_del_path (t, p->peers[p->length - 1], cb); + 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). */ - me = t->root->peer == myid ? 0 : -1; + me = t->root->peer == 1 ? 0 : -1; for (i = 1; i < p->length; i++) { - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, - "Looking for peer %u.\n", - p->peers[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; } } @@ -351,101 +824,219 @@ tree_add_path (struct MeshTunnelTree *t, const struct MeshPeerPath *p, if (parent == n) break; } - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, - "All childen visited.\n"); - 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) { - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, - "Adding peer %u, to %u.\n", - p->peers[i], - parent->peer); - parent->nchildren++; - parent->children = GNUNET_realloc (parent->children, - parent->nchildren * - sizeof(struct MeshTunnelTreeNode)); - 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) { - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Putting old note into place.\n"); - /* Assignation and free can be misleading, using explicit mempcy */ - memcpy (n, oldnode, sizeof (struct MeshTunnelTreeNode)); - GNUNET_free (oldnode); - for (j = 0; j < n->nchildren; j++) - { - n->children[j].parent = n; - } +#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 { - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Creating new node.\n"); - n->t = 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]; - n->nchildren = 0; - n->children = NULL; + 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_CONTAINER_multihashmap_get(t->first_hops, &id.hashPubKey); - if (NULL != hop) - GNUNET_free(hop); - hop = GNUNET_malloc(sizeof(struct GNUNET_PeerIdentity)); - GNUNET_PEER_resolve (p->peers[me + 1], hop); - GNUNET_CONTAINER_multihashmap_put (t->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, "New node added.\n"); + 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; } /** - * Destroy the node and all children - * - * @param n Parent node to be destroyed + * 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 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 -tree_node_destroy (struct MeshTunnelTreeNode *n) +GNUNET_PEER_Id +tree_notify_connection_broken (struct MeshTunnelTree *t, GNUNET_PEER_Id p1, + GNUNET_PEER_Id p2, MeshTreeCallback cb, + void *cbcls) { - struct MeshTunnelTreeNode *parent; + 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; +} + + +/** + * 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) + { + 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; - if (n->nchildren != 0) + 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 (i = 0; i < n->nchildren; i++) + for (n = p->children_head; NULL != n; n = n->next) { - tree_node_destroy(&n->children[i]); + if (path->peers[i] == n->peer) + { + break; + } } - if (n->children != NULL) - GNUNET_free(n->children); + if (NULL == n) + return l - i; + p = n; } - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Destroying node %u.\n", n->peer); - if (NULL == (parent = n->parent)) - return; - i = (n - parent->children) / sizeof(struct MeshTunnelTreeNode); - parent->children[i] = parent->children[parent->nchildren - 1]; - parent->nchildren--; - parent->children = realloc(parent->children, - parent->nchildren - * sizeof(struct MeshTunnelTreeNode)); - - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Destroyed.\n"); + return l - i; +} + + +/** + * Print the tree on stderr + * + * @param t The tree + */ +void +tree_debug (struct MeshTunnelTree *t) +{ + tree_node_debug (t->root, 0); } @@ -461,21 +1052,24 @@ tree_node_destroy (struct MeshTunnelTreeNode *n) static int iterate_free (void *cls, const GNUNET_HashCode * key, void *value) { - GNUNET_free(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) { - tree_node_destroy(t->root); - GNUNET_free(t->root); - GNUNET_CONTAINER_multihashmap_iterate(t->first_hops, &iterate_free, NULL); - GNUNET_CONTAINER_multihashmap_destroy(t->first_hops); -} \ No newline at end of file +#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); +}