X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fdv%2Fgnunet-service-dv.c;h=3fbd0cc030459710be5c81784a1197997342156d;hb=633c9ed2d8392f0620dee12513f65a4cc602ea60;hp=d3da3bbfd3376a0d8d16c1e7b53a6d98ea7ef2e2;hpb=bdedd80eefe17fa6d0113799ee010e4f78b78b77;p=oweals%2Fgnunet.git diff --git a/src/dv/gnunet-service-dv.c b/src/dv/gnunet-service-dv.c index d3da3bbfd..3fbd0cc03 100644 --- a/src/dv/gnunet-service-dv.c +++ b/src/dv/gnunet-service-dv.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - (C) 2013 Christian Grothoff (and other contributing authors) + Copyright (C) 2013, 2016 GNUnet e.V. GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -14,8 +14,8 @@ You should have received a copy of the GNU General Public License along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ /** @@ -26,9 +26,6 @@ * * @author Christian Grothoff * @author Nathan Evans - * - * TODO: - * - distance updates are not properly communicate to US by core/transport/ats */ #include "platform.h" #include "gnunet_util_lib.h" @@ -37,7 +34,8 @@ #include "gnunet_hello_lib.h" #include "gnunet_peerinfo_service.h" #include "gnunet_statistics_service.h" -#include "gnunet_consensus_service.h" +#include "gnunet_set_service.h" +#include "gnunet_ats_service.h" #include "dv.h" #include @@ -94,18 +92,18 @@ struct Target /** * Message exchanged between DV services (via core), requesting a - * message to be routed. + * message to be routed. */ struct RouteMessage { /** - * Type: GNUNET_MESSAGE_TYPE_DV_ROUTE + * Type: #GNUNET_MESSAGE_TYPE_DV_ROUTE */ struct GNUNET_MessageHeader header; /** * Expected (remaining) distance. Must be always smaller than - * DEFAULT_FISHEYE_DEPTH, should be zero at the target. Must + * #DEFAULT_FISHEYE_DEPTH, should be zero at the target. Must * be decremented by one at each hop. Peers must not forward * these messages further once the counter has reached zero. */ @@ -126,39 +124,6 @@ struct RouteMessage GNUNET_NETWORK_STRUCT_END -/** - * Linked list of messages to send to clients. - */ -struct PendingMessage -{ - /** - * Pointer to next item in the list - */ - struct PendingMessage *next; - - /** - * Pointer to previous item in the list - */ - struct PendingMessage *prev; - - /** - * Actual message to be sent, allocated after this struct. - */ - const struct GNUNET_MessageHeader *msg; - - /** - * Ultimate target for the message. - */ - struct GNUNET_PeerIdentity ultimate_target; - - /** - * Unique ID of the message. - */ - uint32_t uid; - -}; - - /** * Information about a direct neighbor (core-level, excluding * DV-links, only DV-enabled peers). @@ -170,50 +135,59 @@ struct DirectNeighbor * Identity of the peer. */ struct GNUNET_PeerIdentity peer; - - /** - * Head of linked list of messages to send to this peer. - */ - struct PendingMessage *pm_head; /** - * Tail of linked list of messages to send to this peer. + * Session ID we use whenever we create a set union with + * this neighbor; constructed from the XOR of our peer + * IDs and then salted with "DV-SALT" to avoid conflicts + * with other applications. */ - struct PendingMessage *pm_tail; + struct GNUNET_HashCode real_session_id; /** * Transmit handle to core service. */ - struct GNUNET_CORE_TransmitHandle *cth; + struct GNUNET_MQ_Handle *mq; /** * Routing table of the neighbor, NULL if not yet established. * Keys are peer identities, values are 'struct Target' entries. * Note that the distances in the targets are from the point-of-view * of the peer, not from us! - */ - struct GNUNET_CONTAINER_MultiHashMap *neighbor_table; + */ + struct GNUNET_CONTAINER_MultiPeerMap *neighbor_table; /** * Updated routing table of the neighbor, under construction, * NULL if we are not currently building it. * Keys are peer identities, values are 'struct Target' entries. * Note that the distances in the targets are from the point-of-view - * of the peer, not from us! - */ - struct GNUNET_CONTAINER_MultiHashMap *neighbor_table_consensus; + * of the other peer, not from us! + */ + struct GNUNET_CONTAINER_MultiPeerMap *neighbor_table_consensus; + + /** + * Our current (exposed) routing table as a set. + */ + struct GNUNET_SET_Handle *my_set; + + /** + * Handle for our current active set union operation. + */ + struct GNUNET_SET_OperationHandle *set_op; /** - * Active consensus, if we are currently synchronizing the - * routing tables. + * Handle used if we are listening for this peer, waiting for the + * other peer to initiate construction of the set union. NULL if + * we ar the initiating peer. */ - struct GNUNET_CONSENSUS_Handle *consensus; + struct GNUNET_SET_ListenHandle *listen_handle; /** * ID of the task we use to (periodically) update our consensus - * with this peer. + * with this peer. Used if we are the initiating peer. */ - GNUNET_SCHEDULER_TaskIdentifier consensus_task; + struct GNUNET_SCHEDULER_Task *initiate_task; /** * At what offset are we, with respect to inserting our own routes @@ -228,15 +202,35 @@ struct DirectNeighbor unsigned int consensus_insertion_distance; /** - * Number of messages currently in the 'pm_XXXX'-DLL. + * Elements in consensus + */ + unsigned int consensus_elements; + + /** + * Direct one hop route */ - unsigned int pm_queue_size; + struct Route *direct_route; /** * Flag set within 'check_target_removed' to trigger full global route refresh. */ int target_removed; + /** + * Our distance to this peer, 0 for unknown. + */ + uint32_t distance; + + /** + * The network this peer is in + */ + enum GNUNET_ATS_Network_Type network; + + /** + * Is this neighbor connected at the core level? + */ + int connected; + }; @@ -282,7 +276,7 @@ struct ConsensusSet struct Route **targets; /** - * Size of the 'targets' array. + * Size of the @e targets array. */ unsigned int array_length; @@ -290,22 +284,24 @@ struct ConsensusSet /** - * Hashmap of all of our direct neighbors (no DV routing). + * Peermap of all of our neighbors; processing these usually requires + * first checking to see if the peer is core-connected and if the + * distance is 1, in which case they are direct neighbors. */ -static struct GNUNET_CONTAINER_MultiHashMap *direct_neighbors; +static struct GNUNET_CONTAINER_MultiPeerMap *direct_neighbors; /** - * Hashmap with all routes that we currently support; contains + * Hashmap with all routes that we currently support; contains * routing information for all peers from distance 2 - * up to distance DEFAULT_FISHEYE_DEPTH. + * up to distance #DEFAULT_FISHEYE_DEPTH. */ -static struct GNUNET_CONTAINER_MultiHashMap *all_routes; +static struct GNUNET_CONTAINER_MultiPeerMap *all_routes; /** * Array of consensus sets we expose to the outside world. Sets * are structured by the distance to the target. */ -static struct ConsensusSet consensi[DEFAULT_FISHEYE_DEPTH - 1]; +static struct ConsensusSet consensi[DEFAULT_FISHEYE_DEPTH]; /** * Handle to the core service api. @@ -327,33 +323,59 @@ static const struct GNUNET_CONFIGURATION_Handle *cfg; * Hopefully this client will never change, although if the plugin * dies and returns for some reason it may happen. */ -static struct GNUNET_SERVER_NotificationContext *nc; +static struct GNUNET_NotificationContext *nc; /** * Handle for the statistics service. */ -struct GNUNET_STATISTICS_Handle *stats; +static struct GNUNET_STATISTICS_Handle *stats; + +/** + * Handle to ATS service. + */ +static struct GNUNET_ATS_PerformanceHandle *ats; + +/** + * Task scheduled to refresh routes based on direct neighbours. + */ +static struct GNUNET_SCHEDULER_Task *rr_task; +/** + * #GNUNET_YES if we are shutting down. + */ +static int in_shutdown; /** - * Get distance information from 'atsi'. + * Start creating a new DV set union by initiating the connection. * - * @param atsi performance data - * @param atsi_count number of entries in atsi - * @return connected transport distance + * @param cls the 'struct DirectNeighbor' of the peer we're building + * a routing consensus with */ -static uint32_t -get_atsi_distance (const struct GNUNET_ATS_Information *atsi, - unsigned int atsi_count) -{ - unsigned int i; +static void +initiate_set_union (void *cls); - for (i = 0; i < atsi_count; i++) - if (ntohl (atsi[i].type) == GNUNET_ATS_QUALITY_NET_DISTANCE) - return ntohl (atsi->value); - /* FIXME: we do not have distance data? Assume direct neighbor. */ - return DIRECT_NEIGHBOR_COST; -} + +/** + * Start creating a new DV set union construction, our neighbour has + * asked for it (callback for listening peer). + * + * @param cls the 'struct DirectNeighbor' of the peer we're building + * a routing consensus with + * @param other_peer the other peer + * @param context_msg message with application specific information from + * the other peer + * @param request request from the other peer, use GNUNET_SET_accept + * to accept it, otherwise the request will be refused + * Note that we don't use a return value here, as it is also + * necessary to specify the set we want to do the operation with, + * whith sometimes can be derived from the context message. + * Also necessary to specify the timeout. + */ +static void +listen_set_union (void *cls, + const struct GNUNET_PeerIdentity *other_peer, + const struct GNUNET_MessageHeader *context_msg, + struct GNUNET_SET_Request *request); /** @@ -364,7 +386,7 @@ get_atsi_distance (const struct GNUNET_ATS_Information *atsi, * @param distance distance to the original sender of the message */ static void -send_data_to_plugin (const struct GNUNET_MessageHeader *message, +send_data_to_plugin (const struct GNUNET_MessageHeader *message, const struct GNUNET_PeerIdentity *origin, uint32_t distance) { @@ -372,12 +394,13 @@ send_data_to_plugin (const struct GNUNET_MessageHeader *message, size_t size; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Delivering message from peer `%s'\n", - GNUNET_i2s (origin)); - size = sizeof (struct GNUNET_DV_ReceivedMessage) + + "Delivering message from peer `%s' at distance %u\n", + GNUNET_i2s (origin), + (unsigned int) distance); + size = sizeof (struct GNUNET_DV_ReceivedMessage) + ntohs (message->size); if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE) - { + { GNUNET_break (0); /* too big */ return; } @@ -386,10 +409,10 @@ send_data_to_plugin (const struct GNUNET_MessageHeader *message, received_msg->header.type = htons (GNUNET_MESSAGE_TYPE_DV_RECV); received_msg->distance = htonl (distance); received_msg->sender = *origin; - memcpy (&received_msg[1], message, ntohs (message->size)); - GNUNET_SERVER_notification_context_broadcast (nc, - &received_msg->header, - GNUNET_YES); + GNUNET_memcpy (&received_msg[1], message, ntohs (message->size)); + GNUNET_notification_context_broadcast (nc, + &received_msg->header, + GNUNET_YES); GNUNET_free (received_msg); } @@ -402,36 +425,9 @@ send_data_to_plugin (const struct GNUNET_MessageHeader *message, static void send_control_to_plugin (const struct GNUNET_MessageHeader *message) { - GNUNET_SERVER_notification_context_broadcast (nc, - message, - GNUNET_NO); -} - - -/** - * Give an (N)ACK message to the plugin, we transmitted a message for it. - * - * @param target peer that received the message - * @param uid plugin-chosen UID for the message - * @param nack GNUNET_NO to send ACK, GNUNET_YES to send NACK - */ -static void -send_ack_to_plugin (const struct GNUNET_PeerIdentity *target, - uint32_t uid, - int nack) -{ - struct GNUNET_DV_AckMessage ack_msg; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Delivering ACK for message to peer `%s'\n", - GNUNET_i2s (target)); - ack_msg.header.size = htons (sizeof (ack_msg)); - ack_msg.header.type = htons ((GNUNET_YES == nack) - ? GNUNET_MESSAGE_TYPE_DV_SEND_NACK - : GNUNET_MESSAGE_TYPE_DV_SEND_ACK); - ack_msg.uid = htonl (uid); - ack_msg.target = *target; - send_control_to_plugin (&ack_msg.header); + GNUNET_notification_context_broadcast (nc, + message, + GNUNET_NO); } @@ -440,13 +436,16 @@ send_ack_to_plugin (const struct GNUNET_PeerIdentity *target, * * @param peer peer with a changed distance * @param distance new distance to the peer + * @param network network used by the neighbor */ static void -send_distance_change_to_plugin (const struct GNUNET_PeerIdentity *peer, - uint32_t distance) +send_distance_change_to_plugin (const struct GNUNET_PeerIdentity *peer, + uint32_t distance, + enum GNUNET_ATS_Network_Type network) { struct GNUNET_DV_DistanceUpdateMessage du_msg; + GNUNET_break (GNUNET_ATS_NET_UNSPECIFIED != network); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Delivering DISTANCE_CHANGED for message about peer `%s'\n", GNUNET_i2s (peer)); @@ -454,6 +453,7 @@ send_distance_change_to_plugin (const struct GNUNET_PeerIdentity *peer, du_msg.header.type = htons (GNUNET_MESSAGE_TYPE_DV_DISTANCE_CHANGED); du_msg.distance = htonl (distance); du_msg.peer = *peer; + du_msg.network = htonl ((uint32_t) network); send_control_to_plugin (&du_msg.header); } @@ -463,19 +463,22 @@ send_distance_change_to_plugin (const struct GNUNET_PeerIdentity *peer, * * @param target peer that connected * @param distance distance to the target + * @param network the network the next hop is located in */ static void -send_connect_to_plugin (const struct GNUNET_PeerIdentity *target, - uint32_t distance) +send_connect_to_plugin (const struct GNUNET_PeerIdentity *target, + uint32_t distance, + enum GNUNET_ATS_Network_Type network) { struct GNUNET_DV_ConnectMessage cm; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Delivering CONNECT about peer `%s'\n", - GNUNET_i2s (target)); + "Delivering CONNECT about peer %s with distance %u\n", + GNUNET_i2s (target), distance); cm.header.size = htons (sizeof (cm)); cm.header.type = htons (GNUNET_MESSAGE_TYPE_DV_CONNECT); cm.distance = htonl (distance); + cm.network = htonl ((uint32_t) network); cm.peer = *target; send_control_to_plugin (&cm.header); } @@ -502,120 +505,51 @@ send_disconnect_to_plugin (const struct GNUNET_PeerIdentity *target) } -/** - * Function called to transfer a message to another peer - * via core. - * - * @param cls closure with the direct neighbor - * @param size number of bytes available in buf - * @param buf where the callee should write the message - * @return number of bytes written to buf - */ -static size_t -core_transmit_notify (void *cls, size_t size, void *buf) -{ - struct DirectNeighbor *dn = cls; - char *cbuf = buf; - struct PendingMessage *pending; - size_t off; - size_t msize; - - dn->cth = NULL; - if (NULL == buf) - { - /* peer disconnected */ - return 0; - } - off = 0; - pending = dn->pm_head; - off = 0; - while ( (NULL != (pending = dn->pm_head)) && - (size >= off + (msize = ntohs (pending->msg->size)))) - { - dn->pm_queue_size--; - GNUNET_CONTAINER_DLL_remove (dn->pm_head, - dn->pm_tail, - pending); - memcpy (&cbuf[off], pending->msg, msize); - if (0 != pending->uid) - send_ack_to_plugin (&pending->ultimate_target, - pending->uid, - GNUNET_NO); - GNUNET_free (pending); - off += msize; - } - if (NULL != dn->pm_head) - dn->cth = - GNUNET_CORE_notify_transmit_ready (core_api, - GNUNET_YES /* cork */, - 0 /* priority */, - GNUNET_TIME_UNIT_FOREVER_REL, - &dn->peer, - msize, - &core_transmit_notify, dn); - return off; -} - - /** * Forward the given payload to the given target. * * @param target where to send the message - * @param uid unique ID for the message - * @param ultimate_target ultimate recipient for the message - * @param distance expected (remaining) distance to the target + * @param distance distance to the @a sender * @param sender original sender of the message + * @param actual_target ultimate recipient for the message * @param payload payload of the message */ static void forward_payload (struct DirectNeighbor *target, uint32_t distance, - uint32_t uid, const struct GNUNET_PeerIdentity *sender, - const struct GNUNET_PeerIdentity *ultimate_target, + const struct GNUNET_PeerIdentity *actual_target, const struct GNUNET_MessageHeader *payload) { - struct PendingMessage *pm; + struct GNUNET_MQ_Envelope *env; struct RouteMessage *rm; - size_t msize; - if ( (target->pm_queue_size >= MAX_QUEUE_SIZE) && + if ( (GNUNET_MQ_get_length (target->mq) >= MAX_QUEUE_SIZE) && (0 != memcmp (sender, &my_identity, sizeof (struct GNUNET_PeerIdentity))) ) { - GNUNET_break (0 == uid); + /* not _our_ client and queue is full, drop */ + GNUNET_STATISTICS_update (stats, + "# messages dropped", + 1, + GNUNET_NO); return; } - msize = sizeof (struct RouteMessage) + ntohs (payload->size); - if (msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) + if (sizeof (struct RouteMessage) + ntohs (payload->size) + >= GNUNET_SERVER_MAX_MESSAGE_SIZE) { GNUNET_break (0); return; } - pm = GNUNET_malloc (sizeof (struct PendingMessage) + msize); - pm->ultimate_target = *ultimate_target; - pm->uid = uid; - pm->msg = (const struct GNUNET_MessageHeader *) &pm[1]; - rm = (struct RouteMessage *) &pm[1]; - rm->header.size = htons ((uint16_t) msize); - rm->header.type = htons (GNUNET_MESSAGE_TYPE_DV_ROUTE); + env = GNUNET_MQ_msg_nested_mh (rm, + GNUNET_MESSAGE_TYPE_DV_ROUTE, + payload); rm->distance = htonl (distance); - rm->target = target->peer; + rm->target = *actual_target; rm->sender = *sender; - memcpy (&rm[1], payload, ntohs (payload->size)); - GNUNET_CONTAINER_DLL_insert_tail (target->pm_head, - target->pm_tail, - pm); - target->pm_queue_size++; - if (NULL == target->cth) - target->cth = GNUNET_CORE_notify_transmit_ready (core_api, - GNUNET_YES /* cork */, - 0 /* priority */, - GNUNET_TIME_UNIT_FOREVER_REL, - &target->peer, - msize, - &core_transmit_notify, target); + GNUNET_MQ_send (target->mq, + env); } @@ -631,14 +565,17 @@ get_consensus_slot (uint32_t distance) struct ConsensusSet *cs; unsigned int i; + GNUNET_assert (distance < DEFAULT_FISHEYE_DEPTH); cs = &consensi[distance]; i = 0; while ( (i < cs->array_length) && (NULL != cs->targets[i]) ) i++; if (i == cs->array_length) + { GNUNET_array_grow (cs->targets, cs->array_length, cs->array_length * 2 + 2); + } return i; } @@ -655,6 +592,12 @@ allocate_route (struct Route *route, { unsigned int i; + if (distance >= DEFAULT_FISHEYE_DEPTH) + { + route->target.distance = htonl (distance); + route->set_offset = UINT_MAX; /* invalid slot */ + return; + } i = get_consensus_slot (distance); route->set_offset = i; consensi[distance].targets[i] = route; @@ -670,6 +613,9 @@ allocate_route (struct Route *route, static void release_route (struct Route *route) { + if (UINT_MAX == route->set_offset) + return; + GNUNET_assert (ntohl (route->target.distance) < DEFAULT_FISHEYE_DEPTH); consensi[ntohl (route->target.distance)].targets[route->set_offset] = NULL; route->set_offset = UINT_MAX; /* indicate invalid slot */ } @@ -685,26 +631,183 @@ static void move_route (struct Route *route, uint32_t new_distance) { - unsigned int i; - release_route (route); - i = get_consensus_slot (new_distance); - route->set_offset = i; - consensi[new_distance].targets[i] = route; - route->target.distance = htonl (new_distance); + allocate_route (route, new_distance); } /** - * Start creating a new consensus from scratch. + * Initialize this neighbors 'my_set' and when done give + * it to the pending set operation for execution. * - * @param cls the 'struct DirectNeighbor' of the peer we're building - * a routing consensus with - * @param tc scheduler context - */ + * Add a single element to the set per call: + * + * If we reached the last element of a consensus element: increase distance + * + * + * @param cls the neighbor for which we are building the set + */ +static void +build_set (void *cls) +{ + struct DirectNeighbor *neighbor = cls; + struct GNUNET_SET_Element element; + struct Target *target; + struct Route *route; + + target = NULL; + /* skip over NULL entries */ + while ( (DEFAULT_FISHEYE_DEPTH > neighbor->consensus_insertion_distance) && + (consensi[neighbor->consensus_insertion_distance].array_length > neighbor->consensus_insertion_offset) && + (NULL == consensi[neighbor->consensus_insertion_distance].targets[neighbor->consensus_insertion_offset]) ) + neighbor->consensus_insertion_offset++; + while ( (DEFAULT_FISHEYE_DEPTH > neighbor->consensus_insertion_distance) && + (consensi[neighbor->consensus_insertion_distance].array_length == neighbor->consensus_insertion_offset) ) + { + /* If we reached the last element of a consensus array element: increase distance and start with next array */ + neighbor->consensus_insertion_offset = 0; + neighbor->consensus_insertion_distance++; + /* skip over NULL entries */ + while ( (DEFAULT_FISHEYE_DEPTH > neighbor->consensus_insertion_distance) && + (consensi[neighbor->consensus_insertion_distance].array_length > neighbor->consensus_insertion_offset) && + (NULL == consensi[neighbor->consensus_insertion_distance].targets[neighbor->consensus_insertion_offset]) ) + neighbor->consensus_insertion_offset++; + } + if (DEFAULT_FISHEYE_DEPTH == neighbor->consensus_insertion_distance) + { + /* we have added all elements to the set, run the operation */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Finished building my SET for peer `%s' with %u elements, committing\n", + GNUNET_i2s (&neighbor->peer), + neighbor->consensus_elements); + GNUNET_SET_commit (neighbor->set_op, + neighbor->my_set); + GNUNET_SET_destroy (neighbor->my_set); + neighbor->my_set = NULL; + return; + } + + route = consensi[neighbor->consensus_insertion_distance].targets[neighbor->consensus_insertion_offset]; + GNUNET_assert (NULL != route); + target = &route->target; + GNUNET_assert (ntohl (target->distance) < DEFAULT_FISHEYE_DEPTH); + element.size = sizeof (struct Target); + element.element_type = htons (0); + element.data = target; + + /* Find next non-NULL entry */ + neighbor->consensus_insertion_offset++; + if ( (0 != memcmp (&target->peer, + &my_identity, + sizeof (my_identity))) && + (0 != memcmp (&target->peer, + &neighbor->peer, + sizeof (struct GNUNET_PeerIdentity))) ) + { + /* Add target if it is not the neighbor or this peer */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Adding peer `%s' with distance %u to SET\n", + GNUNET_i2s (&target->peer), + ntohl (target->distance) + 1); + GNUNET_SET_add_element (neighbor->my_set, + &element, + &build_set, neighbor); + neighbor->consensus_elements++; + } + else + build_set (neighbor); +} + + +/** + * A peer is now connected to us at distance 1. Initiate DV exchange. + * + * @param neighbor entry for the neighbor at distance 1 + */ static void -start_consensus (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc); +handle_direct_connect (struct DirectNeighbor *neighbor) +{ + struct Route *route; + struct GNUNET_HashCode h1; + struct GNUNET_HashCode h2; + struct GNUNET_HashCode session_id; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Direct connection to %s established, routing table exchange begins.\n", + GNUNET_i2s (&neighbor->peer)); + GNUNET_STATISTICS_update (stats, + "# peers connected (1-hop)", + 1, GNUNET_NO); + route = GNUNET_CONTAINER_multipeermap_get (all_routes, + &neighbor->peer); + if (NULL != route) + { + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multipeermap_remove (all_routes, + &neighbor->peer, + route)); + send_disconnect_to_plugin (&neighbor->peer); + release_route (route); + GNUNET_free (route); + } + + neighbor->direct_route = GNUNET_new (struct Route); + neighbor->direct_route->next_hop = neighbor; + neighbor->direct_route->target.peer = neighbor->peer; + allocate_route (neighbor->direct_route, DIRECT_NEIGHBOR_COST); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Adding direct route to %s\n", + GNUNET_i2s (&neighbor->direct_route->target.peer)); + + + /* construct session ID seed as XOR of both peer's identities */ + GNUNET_CRYPTO_hash (&my_identity, + sizeof (my_identity), + &h1); + GNUNET_CRYPTO_hash (&neighbor->peer, + sizeof (struct GNUNET_PeerIdentity), + &h2); + GNUNET_CRYPTO_hash_xor (&h1, + &h2, + &session_id); + /* make sure session ID is unique across applications by salting it with 'DV' */ + GNUNET_CRYPTO_hkdf (&neighbor->real_session_id, sizeof (struct GNUNET_HashCode), + GCRY_MD_SHA512, GCRY_MD_SHA256, + "DV-SALT", 2, + &session_id, sizeof (session_id), + NULL, 0); + if (0 < memcmp (&neighbor->peer, + &my_identity, + sizeof (struct GNUNET_PeerIdentity))) + { + if (NULL != neighbor->listen_handle) + { + GNUNET_break (0); + } + else + neighbor->initiate_task = GNUNET_SCHEDULER_add_now (&initiate_set_union, + neighbor); + } + else + { + if (NULL != neighbor->listen_handle) + { + GNUNET_break (0); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting SET listen operation with peer `%s'\n", + GNUNET_i2s (&neighbor->peer)); + neighbor->listen_handle = GNUNET_SET_listen (cfg, + GNUNET_SET_OPERATION_UNION, + &neighbor->real_session_id, + &listen_set_union, + neighbor); + } + } +} /** @@ -712,50 +815,52 @@ start_consensus (void *cls, * * @param cls closure * @param peer peer identity this notification is about - * @param atsi performance data - * @param atsi_count number of entries in atsi + * @param mq message queue for sending data to @a peer + * @return our `struct DirectNeighbour` for this peer */ -static void -handle_core_connect (void *cls, const struct GNUNET_PeerIdentity *peer) +static void * +handle_core_connect (void *cls, + const struct GNUNET_PeerIdentity *peer, + struct GNUNET_MQ_Handle *mq) { struct DirectNeighbor *neighbor; - struct Route *route; - uint32_t distance; - + /* Check for connect to self message */ - if (0 == memcmp (&my_identity, peer, sizeof (struct GNUNET_PeerIdentity))) - return; - fprintf (stderr, "FIX ATS DATA: %s:%u!\n", __FILE__, __LINE__); - distance = get_atsi_distance (NULL, 0); - neighbor = GNUNET_CONTAINER_multihashmap_get (direct_neighbors, - &peer->hashPubKey); + if (0 == memcmp (&my_identity, + peer, + sizeof (struct GNUNET_PeerIdentity))) + return NULL; + /* check if entry exists */ + neighbor = GNUNET_CONTAINER_multipeermap_get (direct_neighbors, + peer); if (NULL != neighbor) { - GNUNET_break (0); - return; + GNUNET_break (GNUNET_ATS_NET_UNSPECIFIED != neighbor->network); + GNUNET_break (GNUNET_YES != neighbor->connected); + neighbor->connected = GNUNET_YES; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core connected to %s (distance %u)\n", + GNUNET_i2s (peer), + (unsigned int) neighbor->distance); + if (DIRECT_NEIGHBOR_COST != neighbor->distance) + return NULL; + handle_direct_connect (neighbor); + return NULL; } - if (DIRECT_NEIGHBOR_COST != distance) - return; /* is a DV-neighbor */ - GNUNET_STATISTICS_update (stats, - "# peers connected (1-hop)", - 1, GNUNET_NO); - neighbor = GNUNET_malloc (sizeof (struct DirectNeighbor)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core connected to %s (distance unknown)\n", + GNUNET_i2s (peer)); + neighbor = GNUNET_new (struct DirectNeighbor); neighbor->peer = *peer; GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_put (direct_neighbors, - &peer->hashPubKey, + GNUNET_CONTAINER_multipeermap_put (direct_neighbors, + &neighbor->peer, neighbor, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - route = GNUNET_CONTAINER_multihashmap_get (all_routes, - &peer->hashPubKey); - if (NULL != route) - { - send_disconnect_to_plugin (peer); - release_route (route); - GNUNET_free (route); - } - neighbor->consensus_task = GNUNET_SCHEDULER_add_now (&start_consensus, - neighbor); + neighbor->connected = GNUNET_YES; + neighbor->distance = 0; /* unknown */ + neighbor->network = GNUNET_ATS_NET_UNSPECIFIED; + return neighbor; } @@ -765,11 +870,11 @@ handle_core_connect (void *cls, const struct GNUNET_PeerIdentity *peer) * @param cls NULL * @param key key of the value * @param value value to free - * @return GNUNET_OK to continue to iterate + * @return #GNUNET_OK to continue to iterate */ static int free_targets (void *cls, - const struct GNUNET_HashCode *key, + const struct GNUNET_PeerIdentity *key, void *value) { GNUNET_free (value); @@ -778,66 +883,99 @@ free_targets (void *cls, /** - * Multihashmap iterator for checking if a given route is + * Add a new route to the given @a target via the given @a neighbor. + * + * @param target the target of the route + * @param neighbor the next hop for communicating with the @a target + */ +static void +add_new_route (struct Target *target, + struct DirectNeighbor *neighbor) +{ + struct Route *route; + + route = GNUNET_new (struct Route); + route->next_hop = neighbor; + route->target.peer = target->peer; + allocate_route (route, ntohl (target->distance) + 1); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multipeermap_put (all_routes, + &route->target.peer, + route, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + send_connect_to_plugin (&route->target.peer, + ntohl (route->target.distance), + neighbor->network); +} + + +/** + * Multipeerhmap iterator for checking if a given route is * (now) useful to this peer. * * @param cls the direct neighbor for the given route * @param key key value stored under * @param value a 'struct Target' that may or may not be useful; not that * the distance in 'target' does not include the first hop yet - * @return GNUNET_YES to continue iteration, GNUNET_NO to stop + * @return #GNUNET_YES to continue iteration, #GNUNET_NO to stop */ static int -check_possible_route (void *cls, const struct GNUNET_HashCode * key, void *value) +check_possible_route (void *cls, + const struct GNUNET_PeerIdentity *key, + void *value) { struct DirectNeighbor *neighbor = cls; struct Target *target = value; struct Route *route; - - route = GNUNET_CONTAINER_multihashmap_get (all_routes, - key); + + if (GNUNET_YES == + GNUNET_CONTAINER_multipeermap_contains (direct_neighbors, + key)) + return GNUNET_YES; /* direct route, do not care about alternatives */ + route = GNUNET_CONTAINER_multipeermap_get (all_routes, + key); if (NULL != route) { + /* we have an existing route, check how it compares with going via 'target' */ if (ntohl (route->target.distance) > ntohl (target->distance) + 1) { - /* this 'target' is cheaper than the existing route; switch to alternative route! */ + /* via 'target' is cheaper than the existing route; switch to alternative route! */ move_route (route, ntohl (target->distance) + 1); route->next_hop = neighbor; - send_distance_change_to_plugin (&target->peer, ntohl (target->distance) + 1); + send_distance_change_to_plugin (&target->peer, + ntohl (target->distance) + 1, + neighbor->network); } return GNUNET_YES; /* got a route to this target already */ } - route = GNUNET_malloc (sizeof (struct Route)); - route->next_hop = neighbor; - route->target.distance = htonl (ntohl (target->distance) + 1); - route->target.peer = target->peer; - allocate_route (route, ntohl (route->target.distance)); - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_put (all_routes, - &route->target.peer.hashPubKey, - route, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - send_connect_to_plugin (&route->target.peer, ntohl (target->distance)); + if (ntohl (target->distance) >= DEFAULT_FISHEYE_DEPTH) + return GNUNET_YES; /* distance is too large to be interesting */ + add_new_route (target, neighbor); return GNUNET_YES; } /** - * Multihashmap iterator for finding routes that were previously + * Multipeermap iterator for finding routes that were previously * "hidden" due to a better route (called after a disconnect event). * * @param cls NULL * @param key peer identity of the given direct neighbor - * @param value a 'struct DirectNeighbor' to check for additional routes - * @return GNUNET_YES to continue iteration + * @param value a `struct DirectNeighbor` to check for additional routes + * @return #GNUNET_YES to continue iteration */ static int -refresh_routes (void *cls, const struct GNUNET_HashCode * key, void *value) +refresh_routes (void *cls, + const struct GNUNET_PeerIdentity *key, + void *value) { struct DirectNeighbor *neighbor = value; + if ( (GNUNET_YES != neighbor->connected) || + (DIRECT_NEIGHBOR_COST != neighbor->distance) ) + return GNUNET_YES; if (NULL != neighbor->neighbor_table) - GNUNET_CONTAINER_multihashmap_iterate (neighbor->neighbor_table, + GNUNET_CONTAINER_multipeermap_iterate (neighbor->neighbor_table, &check_possible_route, neighbor); return GNUNET_YES; @@ -845,82 +983,322 @@ refresh_routes (void *cls, const struct GNUNET_HashCode * key, void *value) /** - * Check if a target was removed from the set of the other peer; if so, - * if we also used it for our route, we need to remove it from our - * 'all_routes' set (and later check if an alternative path now exists). + * Task to run #refresh_routes() on all direct neighbours. * - * @param cls the 'struct DirectNeighbor' - * @param key peer identity for the target - * @param value a 'struct Target' previously reachable via the given neighbor + * @param cls NULL */ -static int -check_target_removed (void *cls, - const struct GNUNET_HashCode *key, - void *value) +static void +refresh_routes_task (void *cls) { - struct DirectNeighbor *neighbor = cls; - struct Target *new_target; - struct Route *current_route; + rr_task = NULL; + GNUNET_CONTAINER_multipeermap_iterate (direct_neighbors, + &refresh_routes, + NULL); +} - new_target = GNUNET_CONTAINER_multihashmap_get (neighbor->neighbor_table_consensus, - key); - if (NULL == new_target) - { - /* target was revoked, check if it was used */ - current_route = GNUNET_CONTAINER_multihashmap_get (all_routes, - key); - if ( (NULL == current_route) || - (current_route->next_hop != neighbor) ) - { - /* didn't matter, wasn't used */ - return GNUNET_OK; - } - /* remove existing route */ - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_remove (all_routes, key, current_route)); - send_disconnect_to_plugin (¤t_route->target.peer); - GNUNET_free (current_route); - neighbor->target_removed = GNUNET_YES; - return GNUNET_OK; - } - return GNUNET_OK; + +/** + * Asynchronously run #refresh_routes() at the next opportunity + * on all direct neighbours. + */ +static void +schedule_refresh_routes () +{ + if (NULL == rr_task) + rr_task = GNUNET_SCHEDULER_add_now (&refresh_routes_task, + NULL); } /** - * Check if a target was added to the set of the other peer; if it - * was added or impoves the existing route, do the needed updates. + * Multipeermap iterator for freeing routes that go via a particular + * neighbor that disconnected and is thus no longer available. * - * @param cls the 'struct DirectNeighbor' - * @param key peer identity for the target - * @param value a 'struct Target' now reachable via the given neighbor + * @param cls the direct neighbor that is now unavailable + * @param key key value stored under + * @param value a `struct Route` that may or may not go via neighbor + * + * @return #GNUNET_YES to continue iteration, #GNUNET_NO to stop */ static int -check_target_added (void *cls, - const struct GNUNET_HashCode *key, - void *value) +cull_routes (void *cls, + const struct GNUNET_PeerIdentity *key, + void *value) { struct DirectNeighbor *neighbor = cls; - struct Target *target = value; - struct Route *current_route; + struct Route *route = value; + + if (route->next_hop != neighbor) + return GNUNET_YES; /* not affected */ + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multipeermap_remove (all_routes, key, value)); + release_route (route); + send_disconnect_to_plugin (&route->target.peer); + GNUNET_free (route); + return GNUNET_YES; +} + + +/** + * Handle the case that a direct connection to a peer is + * disrupted. Remove all routes via that peer and + * stop the consensus with it. + * + * @param neighbor peer that was disconnected (or at least is no + * longer at distance 1) + */ +static void +handle_direct_disconnect (struct DirectNeighbor *neighbor) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Culling routes via %s due to direct disconnect\n", + GNUNET_i2s (&neighbor->peer)); + GNUNET_CONTAINER_multipeermap_iterate (all_routes, + &cull_routes, + neighbor); + if (NULL != neighbor->direct_route) + { + release_route (neighbor->direct_route); + GNUNET_free (neighbor->direct_route); + neighbor->direct_route = NULL; + } + if (NULL != neighbor->neighbor_table_consensus) + { + GNUNET_CONTAINER_multipeermap_iterate (neighbor->neighbor_table_consensus, + &free_targets, + NULL); + GNUNET_CONTAINER_multipeermap_destroy (neighbor->neighbor_table_consensus); + neighbor->neighbor_table_consensus = NULL; + } + if (NULL != neighbor->neighbor_table) + { + GNUNET_CONTAINER_multipeermap_iterate (neighbor->neighbor_table, + &free_targets, + NULL); + GNUNET_CONTAINER_multipeermap_destroy (neighbor->neighbor_table); + neighbor->neighbor_table = NULL; + } + if (NULL != neighbor->set_op) + { + GNUNET_SET_operation_cancel (neighbor->set_op); + neighbor->set_op = NULL; + } + if (NULL != neighbor->my_set) + { + GNUNET_SET_destroy (neighbor->my_set); + neighbor->my_set = NULL; + } + if (NULL != neighbor->listen_handle) + { + GNUNET_SET_listen_cancel (neighbor->listen_handle); + neighbor->listen_handle = NULL; + } + if (NULL != neighbor->initiate_task) + { + GNUNET_SCHEDULER_cancel (neighbor->initiate_task); + neighbor->initiate_task = NULL; + } +} + +/** + * Function that is called with QoS information about an address; used + * to update our current distance to another peer. + * + * @param cls closure + * @param address the address + * @param active #GNUNET_YES if this address is actively used + * to maintain a connection to a peer; + * #GNUNET_NO if the address is not actively used; + * #GNUNET_SYSERR if this address is no longer available for ATS + * @param bandwidth_out assigned outbound bandwidth for the connection + * @param bandwidth_in assigned inbound bandwidth for the connection + * @param prop performance data for the address (as far as known) + */ +static void +handle_ats_update (void *cls, + const struct GNUNET_HELLO_Address *address, + int active, + struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out, + struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in, + const struct GNUNET_ATS_Properties *prop) +{ + struct DirectNeighbor *neighbor; + uint32_t distance; + enum GNUNET_ATS_Network_Type network; + + if (NULL == address) + { + /* ATS service temporarily disconnected */ + return; + } + + if (GNUNET_YES != active) + { + // FIXME: handle disconnect/inactive case too! + return; + } + distance = prop->distance; + network = prop->scope; + GNUNET_break (GNUNET_ATS_NET_UNSPECIFIED != network); + /* check if entry exists */ + neighbor = GNUNET_CONTAINER_multipeermap_get (direct_neighbors, + &address->peer); + if (NULL != neighbor) + { + neighbor->network = network; + if (neighbor->distance == distance) + return; /* nothing new to see here, move along */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "ATS says distance to %s is now %u\n", + GNUNET_i2s (&address->peer), + (unsigned int) distance); + if ( (DIRECT_NEIGHBOR_COST == neighbor->distance) && + (DIRECT_NEIGHBOR_COST == distance) ) + return; /* no change */ + if (DIRECT_NEIGHBOR_COST == neighbor->distance) + { + neighbor->distance = distance; + GNUNET_STATISTICS_update (stats, + "# peers connected (1-hop)", + -1, GNUNET_NO); + handle_direct_disconnect (neighbor); + schedule_refresh_routes (); + return; + } + neighbor->distance = distance; + if (DIRECT_NEIGHBOR_COST != neighbor->distance) + return; + if (GNUNET_YES != neighbor->connected) + return; + handle_direct_connect (neighbor); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "ATS says distance to %s is now %u\n", + GNUNET_i2s (&address->peer), + (unsigned int) distance); + neighbor = GNUNET_new (struct DirectNeighbor); + neighbor->peer = address->peer; + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multipeermap_put (direct_neighbors, + &neighbor->peer, + neighbor, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + neighbor->connected = GNUNET_NO; /* not yet */ + neighbor->distance = distance; + neighbor->network = network; +} + + +/** + * Check if a target was removed from the set of the other peer; if so, + * if we also used it for our route, we need to remove it from our + * 'all_routes' set (and later check if an alternative path now exists). + * + * @param cls the `struct DirectNeighbor` + * @param key peer identity for the target + * @param value a `struct Target` previously reachable via the given neighbor + */ +static int +check_target_removed (void *cls, + const struct GNUNET_PeerIdentity *key, + void *value) +{ + struct DirectNeighbor *neighbor = cls; + struct Target *new_target; + struct Route *current_route; + + new_target = GNUNET_CONTAINER_multipeermap_get (neighbor->neighbor_table_consensus, + key); + current_route = GNUNET_CONTAINER_multipeermap_get (all_routes, + key); + if (NULL != new_target) + { + /* target was in old set, is in new set */ + if ( (NULL != current_route) && + (current_route->next_hop == neighbor) && + (current_route->target.distance != new_target->distance) ) + { + /* need to recalculate routes due to distance change */ + neighbor->target_removed = GNUNET_YES; + } + return GNUNET_OK; + } /* target was revoked, check if it was used */ - current_route = GNUNET_CONTAINER_multihashmap_get (all_routes, + if ( (NULL == current_route) || + (current_route->next_hop != neighbor) ) + { + /* didn't matter, wasn't used */ + return GNUNET_OK; + } + /* remove existing route */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Lost route to %s\n", + GNUNET_i2s (¤t_route->target.peer)); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multipeermap_remove (all_routes, key, current_route)); + send_disconnect_to_plugin (¤t_route->target.peer); + release_route (current_route); + GNUNET_free (current_route); + neighbor->target_removed = GNUNET_YES; + return GNUNET_OK; +} + + +/** + * Check if a target was added to the set of the other peer; if it + * was added or impoves the existing route, do the needed updates. + * + * @param cls the `struct DirectNeighbor` + * @param key peer identity for the target + * @param value a `struct Target` now reachable via the given neighbor + */ +static int +check_target_added (void *cls, + const struct GNUNET_PeerIdentity *key, + void *value) +{ + struct DirectNeighbor *neighbor = cls; + struct Target *target = value; + struct Route *current_route; + + /* target was revoked, check if it was used */ + current_route = GNUNET_CONTAINER_multipeermap_get (all_routes, key); if (NULL != current_route) { /* route exists */ if (current_route->next_hop == neighbor) { - /* we had the same route before, no change */ + /* we had the same route before, no change in target */ if (ntohl (target->distance) + 1 != ntohl (current_route->target.distance)) { - current_route->target.distance = htonl (ntohl (target->distance) + 1); - send_distance_change_to_plugin (&target->peer, ntohl (target->distance) + 1); + /* but distance changed! */ + if (ntohl (target->distance) + 1 > DEFAULT_FISHEYE_DEPTH) + { + /* distance increased beyond what is allowed, kill route */ + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multipeermap_remove (all_routes, + key, + current_route)); + send_disconnect_to_plugin (key); + release_route (current_route); + GNUNET_free (current_route); + } + else + { + /* distance decreased, update route */ + move_route (current_route, + ntohl (target->distance) + 1); + send_distance_change_to_plugin (&target->peer, + ntohl (target->distance) + 1, + neighbor->network); + } } return GNUNET_OK; } - if (ntohl (current_route->target.distance) >= ntohl (target->distance) + 1) + if (ntohl (current_route->target.distance) <= ntohl (target->distance) + 1) { /* alternative, shorter route exists, ignore */ return GNUNET_OK; @@ -930,229 +1308,297 @@ check_target_added (void *cls, very short routes to take over longer paths; as we don't check that the shorter routes actually work, a malicious direct neighbor can use this to DoS our long routes */ + + move_route (current_route, ntohl (target->distance) + 1); current_route->next_hop = neighbor; - current_route->target.distance = htonl (ntohl (target->distance) + 1); - send_distance_change_to_plugin (&target->peer, ntohl (target->distance) + 1); + send_distance_change_to_plugin (&target->peer, + ntohl (target->distance) + 1, + neighbor->network); return GNUNET_OK; } /* new route */ - current_route = GNUNET_malloc (sizeof (struct Route)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Discovered new route to %s using %u hops\n", + GNUNET_i2s (&target->peer), + (unsigned int) (ntohl (target->distance) + 1)); + current_route = GNUNET_new (struct Route); current_route->next_hop = neighbor; current_route->target.peer = target->peer; - current_route->target.distance = htonl (ntohl (target->distance) + 1); + allocate_route (current_route, ntohl (target->distance) + 1); GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_put (all_routes, - ¤t_route->target.peer.hashPubKey, + GNUNET_CONTAINER_multipeermap_put (all_routes, + ¤t_route->target.peer, current_route, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + send_connect_to_plugin (¤t_route->target.peer, - ntohl (current_route->target.distance)); + ntohl (current_route->target.distance), + neighbor->network); return GNUNET_OK; } - /** - * The consensus has concluded, clean up and schedule the next one. + * Callback for set operation results. Called for each element + * in the result set. + * We have learned a new route from the other peer. Add it to the + * route set we're building. * - * @param cls the 'struct GNUNET_DirectNeighbor' with which we created the consensus - * @param group FIXME + * @param cls the `struct DirectNeighbor` we're building the consensus with + * @param element a result element, only valid if status is #GNUNET_SET_STATUS_OK + * @param current_size current set size + * @param status see `enum GNUNET_SET_Status` */ static void -consensus_done_cb (void *cls) +handle_set_union_result (void *cls, + const struct GNUNET_SET_Element *element, + uint64_t current_size, + enum GNUNET_SET_Status status) { struct DirectNeighbor *neighbor = cls; + struct DirectNeighbor *dn; + struct Target *target; + const struct Target *ctarget; + char *status_str; - GNUNET_CONSENSUS_destroy (neighbor->consensus); - neighbor->consensus = NULL; - /* remove targets that disappeared */ - neighbor->target_removed = GNUNET_NO; - GNUNET_CONTAINER_multihashmap_iterate (neighbor->neighbor_table, - &check_target_removed, - neighbor); - if (GNUNET_YES == neighbor->target_removed) + switch (status) { - /* check if we got an alternative for the removed routes */ - GNUNET_CONTAINER_multihashmap_iterate (direct_neighbors, - &refresh_routes, - NULL); + case GNUNET_SET_STATUS_OK: + status_str = "GNUNET_SET_STATUS_OK"; + break; + case GNUNET_SET_STATUS_FAILURE: + status_str = "GNUNET_SET_STATUS_FAILURE"; + break; + case GNUNET_SET_STATUS_HALF_DONE: + status_str = "GNUNET_SET_STATUS_HALF_DONE"; + break; + case GNUNET_SET_STATUS_DONE: + status_str = "GNUNET_SET_STATUS_DONE"; + break; + default: + status_str = "UNDEFINED"; + break; } - /* add targets that appeared (and check for improved routes) */ - GNUNET_CONTAINER_multihashmap_iterate (neighbor->neighbor_table_consensus, - &check_target_added, - neighbor); - if (NULL != neighbor->neighbor_table) + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got SET union result: %s\n", + status_str); + switch (status) { - GNUNET_CONTAINER_multihashmap_iterate (neighbor->neighbor_table, - &free_targets, - NULL); - GNUNET_CONTAINER_multihashmap_destroy (neighbor->neighbor_table); - neighbor->neighbor_table = NULL; + case GNUNET_SET_STATUS_OK: + if (sizeof (struct Target) != element->size) + { + GNUNET_break_op (0); + return; + } + ctarget = element->data; + if ( (NULL != + (dn = GNUNET_CONTAINER_multipeermap_get (direct_neighbors, + &ctarget->peer))) && + (DIRECT_NEIGHBOR_COST == dn->distance) ) + { + /* this is a direct neighbor of ours, we do not care about routes + to this peer */ + return; + } + target = GNUNET_new (struct Target); + GNUNET_memcpy (target, element->data, sizeof (struct Target)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received information about peer `%s' with distance %u from SET\n", + GNUNET_i2s (&target->peer), + ntohl (target->distance) + 1); + + if (NULL == neighbor->neighbor_table_consensus) + neighbor->neighbor_table_consensus + = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO); + if (GNUNET_YES != + GNUNET_CONTAINER_multipeermap_put (neighbor->neighbor_table_consensus, + &target->peer, + target, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) + { + GNUNET_break_op (0); + GNUNET_free (target); + } + break; + case GNUNET_SET_STATUS_FAILURE: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Failed to establish DV union, will try again later\n"); + neighbor->set_op = NULL; + if (NULL != neighbor->neighbor_table_consensus) + { + GNUNET_CONTAINER_multipeermap_iterate (neighbor->neighbor_table_consensus, + &free_targets, + NULL); + GNUNET_CONTAINER_multipeermap_destroy (neighbor->neighbor_table_consensus); + neighbor->neighbor_table_consensus = NULL; + } + if (0 < memcmp (&neighbor->peer, + &my_identity, + sizeof (struct GNUNET_PeerIdentity))) + neighbor->initiate_task = GNUNET_SCHEDULER_add_delayed (GNUNET_DV_CONSENSUS_FREQUENCY, + &initiate_set_union, + neighbor); + break; + case GNUNET_SET_STATUS_HALF_DONE: + break; + case GNUNET_SET_STATUS_DONE: + /* we got all of our updates; integrate routing table! */ + neighbor->target_removed = GNUNET_NO; + if (NULL == neighbor->neighbor_table_consensus) + neighbor->neighbor_table_consensus = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO); + if (NULL != neighbor->neighbor_table) + GNUNET_CONTAINER_multipeermap_iterate (neighbor->neighbor_table, + &check_target_removed, + neighbor); + if (GNUNET_YES == neighbor->target_removed) + { + /* check if we got an alternative for the removed routes */ + schedule_refresh_routes (); + } + /* add targets that appeared (and check for improved routes) */ + GNUNET_CONTAINER_multipeermap_iterate (neighbor->neighbor_table_consensus, + &check_target_added, + neighbor); + if (NULL != neighbor->neighbor_table) + { + GNUNET_CONTAINER_multipeermap_iterate (neighbor->neighbor_table, + &free_targets, + NULL); + GNUNET_CONTAINER_multipeermap_destroy (neighbor->neighbor_table); + neighbor->neighbor_table = NULL; + } + neighbor->neighbor_table = neighbor->neighbor_table_consensus; + neighbor->neighbor_table_consensus = NULL; + + /* operation done, schedule next run! */ + neighbor->set_op = NULL; + if (0 < memcmp (&neighbor->peer, + &my_identity, + sizeof (struct GNUNET_PeerIdentity))) + neighbor->initiate_task = GNUNET_SCHEDULER_add_delayed (GNUNET_DV_CONSENSUS_FREQUENCY, + &initiate_set_union, + neighbor); + break; + default: + GNUNET_break (0); + return; } - neighbor->neighbor_table = neighbor->neighbor_table_consensus; - neighbor->neighbor_table_consensus = NULL; - neighbor->consensus_task = GNUNET_SCHEDULER_add_delayed (GNUNET_DV_CONSENSUS_FREQUENCY, - &start_consensus, - neighbor); } /** - * We inserted the last element into the consensus, get ready to - * insert the next element into the consensus or conclude if - * we're done. + * Start creating a new DV set union construction, our neighbour has + * asked for it (callback for listening peer). * * @param cls the 'struct DirectNeighbor' of the peer we're building * a routing consensus with - * @param success GNUNET_OK if the last element was added successfully, - * GNUNET_SYSERR if we failed + * @param other_peer the other peer + * @param context_msg message with application specific information from + * the other peer + * @param request request from the other peer, use GNUNET_SET_accept + * to accept it, otherwise the request will be refused + * Note that we don't use a return value here, as it is also + * necessary to specify the set we want to do the operation with, + * whith sometimes can be derived from the context message. + * Also necessary to specify the timeout. */ static void -insert_next_element (void *cls, - int success) +listen_set_union (void *cls, + const struct GNUNET_PeerIdentity *other_peer, + const struct GNUNET_MessageHeader *context_msg, + struct GNUNET_SET_Request *request) { struct DirectNeighbor *neighbor = cls; - struct GNUNET_CONSENSUS_Element element; - while ( (DEFAULT_FISHEYE_DEPTH - 1 > neighbor->consensus_insertion_distance) && - (consensi[neighbor->consensus_insertion_distance].array_length == neighbor->consensus_insertion_offset) ) + if (NULL == request) + return; /* why??? */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting to create consensus with %s\n", + GNUNET_i2s (&neighbor->peer)); + if (NULL != neighbor->set_op) { - neighbor->consensus_insertion_offset = 0; - neighbor->consensus_insertion_distance++; - /* skip over NULL entries */ - while ( (DEFAULT_FISHEYE_DEPTH - 1 > neighbor->consensus_insertion_distance) && - (consensi[neighbor->consensus_insertion_distance].array_length < neighbor->consensus_insertion_offset) && - (NULL == consensi[neighbor->consensus_insertion_distance].targets[neighbor->consensus_insertion_offset]) ) - neighbor->consensus_insertion_offset++; + GNUNET_SET_operation_cancel (neighbor->set_op); + neighbor->set_op = NULL; } - if (DEFAULT_FISHEYE_DEPTH - 1 == neighbor->consensus_insertion_distance) + if (NULL != neighbor->my_set) { - /* we're done, conclude! */ - GNUNET_CONSENSUS_conclude (neighbor->consensus, - GNUNET_DV_CONSENSUS_FREQUENCY, - &consensus_done_cb, - neighbor); - return; + GNUNET_SET_destroy (neighbor->my_set); + neighbor->my_set = NULL; } - element.size = sizeof (struct Target); - element.data = &consensi[neighbor->consensus_insertion_distance].targets[neighbor->consensus_insertion_offset++]->target; + neighbor->my_set = GNUNET_SET_create (cfg, + GNUNET_SET_OPERATION_UNION); + neighbor->set_op = GNUNET_SET_accept (request, + GNUNET_SET_RESULT_ADDED, + (struct GNUNET_SET_Option[]) {{ 0 }}, + &handle_set_union_result, + neighbor); + neighbor->consensus_insertion_offset = 0; + neighbor->consensus_insertion_distance = 0; + neighbor->consensus_elements = 0; + build_set (neighbor); +} - /* skip over NULL entries */ - while ( (DEFAULT_FISHEYE_DEPTH - 1 > neighbor->consensus_insertion_distance) && - (consensi[neighbor->consensus_insertion_distance].array_length < neighbor->consensus_insertion_offset) && - (NULL == consensi[neighbor->consensus_insertion_distance].targets[neighbor->consensus_insertion_offset]) ) - neighbor->consensus_insertion_offset++; - GNUNET_CONSENSUS_insert (neighbor->consensus, - &element, - &insert_next_element, - neighbor); + +/** + * Start creating a new DV set union by initiating the connection. + * + * @param cls the `struct DirectNeighbor *` of the peer we're building + * a routing consensus with + */ +static void +initiate_set_union (void *cls) +{ + struct DirectNeighbor *neighbor = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Initiating SET union with peer `%s'\n", + GNUNET_i2s (&neighbor->peer)); + neighbor->initiate_task = NULL; + neighbor->my_set = GNUNET_SET_create (cfg, + GNUNET_SET_OPERATION_UNION); + neighbor->set_op = GNUNET_SET_prepare (&neighbor->peer, + &neighbor->real_session_id, + NULL, + GNUNET_SET_RESULT_ADDED, + (struct GNUNET_SET_Option[]) {{ 0 }}, + &handle_set_union_result, + neighbor); + neighbor->consensus_insertion_offset = 0; + neighbor->consensus_insertion_distance = 0; + neighbor->consensus_elements = 0; + build_set (neighbor); } /** - * We have learned a new route from the other peer. Add it to the - * route set we're building. + * Check that @a rm is well-formed. * - * @param cls the 'struct DirectNeighbor' we're building the consensus with - * @param element the new element we have learned - * @return GNUNET_OK if the valid is well-formed and should be added to the consensus, - * GNUNET_SYSERR if the element should be ignored and not be propagated + * @param cls closure + * @param rm the message + * @return #GNUNET_OK if @a rm is well-formed. */ static int -learn_route_cb (void *cls, - const struct GNUNET_CONSENSUS_Element *element) +check_dv_route_message (void *cls, + const struct RouteMessage *rm) { - struct DirectNeighbor *neighbor = cls; - struct Target *target; + const struct GNUNET_MessageHeader *payload; - if (NULL == element) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to establish DV consensus, will try again later\n"); - GNUNET_CONSENSUS_destroy (neighbor->consensus); - if (NULL != neighbor->neighbor_table_consensus) - { - GNUNET_CONTAINER_multihashmap_iterate (neighbor->neighbor_table_consensus, - &free_targets, - NULL); - GNUNET_CONTAINER_multihashmap_destroy (neighbor->neighbor_table_consensus); - neighbor->neighbor_table_consensus = NULL; - } - neighbor->consensus = NULL; - neighbor->consensus_task = GNUNET_SCHEDULER_add_delayed (GNUNET_DV_CONSENSUS_FREQUENCY, - &start_consensus, - neighbor); - return GNUNET_SYSERR; - } - if (sizeof (struct Target) != element->size) + if (ntohs (rm->header.size) < sizeof (struct RouteMessage) + sizeof (struct GNUNET_MessageHeader)) { GNUNET_break_op (0); return GNUNET_SYSERR; } - target = GNUNET_malloc (sizeof (struct Target)); - memcpy (target, element->data, sizeof (struct Target)); - if (GNUNET_YES != - GNUNET_CONTAINER_multihashmap_put (neighbor->neighbor_table_consensus, - &target->peer.hashPubKey, - target, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) + payload = (const struct GNUNET_MessageHeader *) &rm[1]; + if (ntohs (rm->header.size) != sizeof (struct RouteMessage) + ntohs (payload->size)) { GNUNET_break_op (0); - GNUNET_free (target); return GNUNET_SYSERR; } return GNUNET_OK; } -/** - * Start creating a new consensus from scratch. - * - * @param cls the 'struct DirectNeighbor' of the peer we're building - * a routing consensus with - * @param tc scheduler context - */ -static void -start_consensus (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - struct DirectNeighbor *neighbor = cls; - struct GNUNET_HashCode session_id; - struct GNUNET_HashCode real_session_id; - - neighbor->consensus_task = GNUNET_SCHEDULER_NO_TASK; - neighbor->consensus_insertion_offset = 0; - neighbor->consensus_insertion_distance = 0; - GNUNET_assert (NULL == neighbor->neighbor_table_consensus); - GNUNET_assert (NULL == neighbor->consensus); - neighbor->neighbor_table_consensus = GNUNET_CONTAINER_multihashmap_create (1024, GNUNET_YES); - /* construct session ID seed as XOR of both peer's identities */ - GNUNET_CRYPTO_hash_xor (&my_identity.hashPubKey, - &neighbor->peer.hashPubKey, - &session_id); - /* make sure session ID is unique across applications by salting it with 'DV' */ - GNUNET_CRYPTO_hkdf (&real_session_id, sizeof (real_session_id), - GCRY_MD_SHA512, GCRY_MD_SHA256, - "DV-SALT", 2, - &session_id, sizeof (session_id), - NULL, 0); - neighbor->consensus = GNUNET_CONSENSUS_create (cfg, - 1, - &neighbor->peer, - &real_session_id, - &learn_route_cb, - neighbor); - if (NULL == neighbor->consensus) - { - neighbor->consensus_task = GNUNET_SCHEDULER_add_delayed (GNUNET_DV_CONSENSUS_FREQUENCY, - &start_consensus, - neighbor); - return; - } - insert_next_element (neighbor, GNUNET_OK); -} - - /** * Core handler for DV data messages. Whatever this message * contains all we really have to do is rip it out of its @@ -1160,155 +1606,227 @@ start_consensus (void *cls, * in with. * * @param cls closure - * @param peer peer which sent the message (immediate sender) - * @param message the message - * @param atsi transport ATS information (latency, distance, etc.) - * @param atsi_count number of entries in atsi - * @return GNUNET_OK on success, GNUNET_SYSERR if the other peer violated the protocol + * @param rm the message */ -static int -handle_dv_route_message (void *cls, const struct GNUNET_PeerIdentity *peer, - const struct GNUNET_MessageHeader *message) +static void +handle_dv_route_message (void *cls, + const struct RouteMessage *rm) { - const struct RouteMessage *rm; + struct DirectNeighbor *neighbor = cls; const struct GNUNET_MessageHeader *payload; struct Route *route; + struct DirectNeighbor *nneighbor; + struct DirectNeighbor *dn; + struct Target *target; + uint32_t distance; + char me[5]; + char src[5]; + char prev[5]; + char dst[5]; - if (ntohs (message->size) < sizeof (struct RouteMessage) + sizeof (struct GNUNET_MessageHeader)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - rm = (const struct RouteMessage *) message; + distance = ntohl (rm->distance); payload = (const struct GNUNET_MessageHeader *) &rm[1]; - if (ntohs (message->size) != sizeof (struct RouteMessage) + ntohs (payload->size)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } + strncpy (prev, GNUNET_i2s (&neighbor->peer), 4); + strncpy (me, GNUNET_i2s (&my_identity), 4); + strncpy (src, GNUNET_i2s (&rm->sender), 4); + strncpy (dst, GNUNET_i2s (&rm->target), 4); + prev[4] = me[4] = src[4] = dst[4] = '\0'; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Handling DV message with %u bytes payload of type %u from %s to %s routed by %s to me (%s @ hop %u)\n", + (unsigned int) (ntohs (rm->header.size) - sizeof (struct RouteMessage)), + ntohs (payload->type), + src, + dst, + prev, + me, + (unsigned int) distance + 1); + if (0 == memcmp (&rm->target, &my_identity, sizeof (struct GNUNET_PeerIdentity))) { - /* message is for me, check reverse route! */ - route = GNUNET_CONTAINER_multihashmap_get (all_routes, - &rm->sender.hashPubKey); - if (NULL == route) + if ((NULL != + (dn = GNUNET_CONTAINER_multipeermap_get (direct_neighbors, + &rm->sender))) && + (DIRECT_NEIGHBOR_COST == dn->distance)) { - /* don't have reverse route, drop */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Discarding DV message, as %s is a direct neighbor\n", + GNUNET_i2s (&rm->sender)); GNUNET_STATISTICS_update (stats, - "# message discarded (no reverse route)", - 1, GNUNET_NO); - return GNUNET_OK; + "# messages discarded (direct neighbor)", + 1, GNUNET_NO); + return; } + /* message is for me, check reverse route! */ + route = GNUNET_CONTAINER_multipeermap_get (all_routes, + &rm->sender); + if ( (NULL == route) && + (distance < DEFAULT_FISHEYE_DEPTH) ) + { + /* don't have reverse route yet, learn it! */ + target = GNUNET_new (struct Target); + target->peer = rm->sender; + target->distance = htonl (distance); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Learning sender %s at distance %u from delivery!\n", + GNUNET_i2s (&rm->sender), + (unsigned int) distance + 1); + if (NULL == neighbor->neighbor_table) + neighbor->neighbor_table = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO); + if (GNUNET_YES != + GNUNET_CONTAINER_multipeermap_put (neighbor->neighbor_table, + &target->peer, + target, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) + { + GNUNET_break_op (0); + GNUNET_free (target); + return; + } + add_new_route (target, neighbor); + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Delivering %u bytes from %s to myself!\n", + ntohs (payload->size), + GNUNET_i2s (&rm->sender)); send_data_to_plugin (payload, &rm->sender, - ntohl (route->target.distance)); - return GNUNET_OK; + 1 + distance); + return; + } + if ( (NULL == GNUNET_CONTAINER_multipeermap_get (direct_neighbors, + &rm->sender)) && + (NULL == GNUNET_CONTAINER_multipeermap_get (all_routes, + &rm->sender)) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Learning sender %s at distance %u from forwarding!\n", + GNUNET_i2s (&rm->sender), + 1 + distance); + target = GNUNET_new (struct Target); + target->peer = rm->sender; + target->distance = htonl (distance); + if (NULL == neighbor->neighbor_table) + neighbor->neighbor_table = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO); + if (GNUNET_YES != + GNUNET_CONTAINER_multipeermap_put (neighbor->neighbor_table, + &target->peer, + target, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) + { + GNUNET_break_op (0); + GNUNET_free (target); + return; + } + add_new_route (target, neighbor); } - route = GNUNET_CONTAINER_multihashmap_get (all_routes, - &rm->target.hashPubKey); + + route = GNUNET_CONTAINER_multipeermap_get (all_routes, + &rm->target); if (NULL == route) { - GNUNET_STATISTICS_update (stats, - "# messages discarded (no route)", - 1, GNUNET_NO); - return GNUNET_OK; + nneighbor = GNUNET_CONTAINER_multipeermap_get (direct_neighbors, + &rm->target); + if (NULL == nneighbor) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "No route to %s, not routing %u bytes!\n", + GNUNET_i2s (&rm->target), + ntohs (payload->size)); + GNUNET_STATISTICS_update (stats, + "# messages discarded (no route)", + 1, GNUNET_NO); + return; + } } - if (ntohl (route->target.distance) > ntohl (rm->distance) + 1) + else { - GNUNET_STATISTICS_update (stats, - "# messages discarded (target too far)", - 1, GNUNET_NO); - return GNUNET_OK; + nneighbor = route->next_hop; } - forward_payload (route->next_hop, - ntohl (route->target.distance), - 0, - &rm->target, + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Forwarding message to %s\n", + GNUNET_i2s (&nneighbor->peer)); + forward_payload (nneighbor, + distance + 1, &rm->sender, + &rm->target, payload); - return GNUNET_OK; } /** - * Service server's handler for message send requests (which come - * bubbling up to us through the DV plugin). + * Check that @a msg is well-formed * - * @param cls closure - * @param client identification of the client + * @param cls identification of the client * @param message the actual message + * @return #GNUNET_OK if @a msg is well-formed */ -static void -handle_dv_send_message (void *cls, struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *message) +static int +check_dv_send_message (void *cls, + const struct GNUNET_DV_SendMessage *msg) { - struct Route *route; - const struct GNUNET_DV_SendMessage *msg; const struct GNUNET_MessageHeader *payload; - if (ntohs (message->size) < sizeof (struct GNUNET_DV_SendMessage) + sizeof (struct GNUNET_MessageHeader)) + if (ntohs (msg->header.size) < sizeof (struct GNUNET_DV_SendMessage) + + sizeof (struct GNUNET_MessageHeader)) { GNUNET_break (0); - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; + return GNUNET_SYSERR; } - msg = (const struct GNUNET_DV_SendMessage *) message; - GNUNET_break (0 != ntohl (msg->uid)); payload = (const struct GNUNET_MessageHeader *) &msg[1]; - if (ntohs (message->size) != sizeof (struct GNUNET_DV_SendMessage) + ntohs (payload->size)) + if (ntohs (msg->header.size) != sizeof (struct GNUNET_DV_SendMessage) + ntohs (payload->size)) { GNUNET_break (0); - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; + return GNUNET_SYSERR; } - route = GNUNET_CONTAINER_multihashmap_get (all_routes, - &msg->target.hashPubKey); + return GNUNET_OK; +} + + +/** + * Service server's handler for message send requests (which come + * bubbling up to us through the DV plugin). + * + * @param cls identification of the client + * @param message the actual message + */ +static void +handle_dv_send_message (void *cls, + const struct GNUNET_DV_SendMessage *msg) +{ + struct GNUNET_SERVICE_Client *client = cls; + struct Route *route; + const struct GNUNET_MessageHeader *payload; + + payload = (const struct GNUNET_MessageHeader *) &msg[1]; + route = GNUNET_CONTAINER_multipeermap_get (all_routes, + &msg->target); if (NULL == route) { /* got disconnected */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "No route to %s, dropping local message of type %u\n", + GNUNET_i2s (&msg->target), + ntohs (payload->type)); GNUNET_STATISTICS_update (stats, "# local messages discarded (no route)", 1, GNUNET_NO); - send_ack_to_plugin (&msg->target, ntohl (msg->uid), GNUNET_YES); - GNUNET_SERVER_receive_done (client, GNUNET_OK); + GNUNET_SERVICE_client_continue (client); return; } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Forwarding %u bytes of type %u to %s\n", + ntohs (payload->size), + ntohs (payload->type), + GNUNET_i2s (&msg->target)); + forward_payload (route->next_hop, - ntohl (route->target.distance), - htonl (msg->uid), - &msg->target, + 0 /* first hop, distance is zero */, &my_identity, + &msg->target, payload); - GNUNET_SERVER_receive_done (client, GNUNET_OK); -} - - -/** - * Multihashmap iterator for freeing routes that go via a particular - * neighbor that disconnected and is thus no longer available. - * - * @param cls the direct neighbor that is now unavailable - * @param key key value stored under - * @param value a 'struct Route' that may or may not go via neighbor - * - * @return GNUNET_YES to continue iteration, GNUNET_NO to stop - */ -static int -cull_routes (void *cls, const struct GNUNET_HashCode * key, void *value) -{ - struct DirectNeighbor *neighbor = cls; - struct Route *route = value; - - if (route->next_hop != neighbor) - return GNUNET_YES; /* not affected */ - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_remove (all_routes, key, value)); - release_route (route); - send_disconnect_to_plugin (&route->target.peer); - GNUNET_free (route); - return GNUNET_YES; + GNUNET_SERVICE_client_continue (client); } @@ -1320,53 +1838,10 @@ cull_routes (void *cls, const struct GNUNET_HashCode * key, void *value) static void cleanup_neighbor (struct DirectNeighbor *neighbor) { - struct PendingMessage *pending; - - while (NULL != (pending = neighbor->pm_head)) - { - neighbor->pm_queue_size--; - GNUNET_CONTAINER_DLL_remove (neighbor->pm_head, - neighbor->pm_tail, - pending); - GNUNET_free (pending); - } - GNUNET_CONTAINER_multihashmap_iterate (all_routes, - &cull_routes, - neighbor); - if (NULL != neighbor->cth) - { - GNUNET_CORE_notify_transmit_ready_cancel (neighbor->cth); - neighbor->cth = NULL; - } - if (NULL != neighbor->neighbor_table_consensus) - { - GNUNET_CONTAINER_multihashmap_iterate (neighbor->neighbor_table_consensus, - &free_targets, - NULL); - GNUNET_CONTAINER_multihashmap_destroy (neighbor->neighbor_table_consensus); - neighbor->neighbor_table_consensus = NULL; - } - if (NULL != neighbor->neighbor_table) - { - GNUNET_CONTAINER_multihashmap_iterate (neighbor->neighbor_table, - &free_targets, - NULL); - GNUNET_CONTAINER_multihashmap_destroy (neighbor->neighbor_table); - neighbor->neighbor_table = NULL; - } - if (GNUNET_SCHEDULER_NO_TASK != neighbor->consensus_task) - { - GNUNET_SCHEDULER_cancel (neighbor->consensus_task); - neighbor->consensus_task = GNUNET_SCHEDULER_NO_TASK; - } - if (NULL != neighbor->consensus) - { - GNUNET_CONSENSUS_destroy (neighbor->consensus); - neighbor->consensus = NULL; - } + handle_direct_disconnect (neighbor); GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_remove (direct_neighbors, - &neighbor->peer.hashPubKey, + GNUNET_CONTAINER_multipeermap_remove (direct_neighbors, + &neighbor->peer, neighbor)); GNUNET_free (neighbor); } @@ -1377,52 +1852,55 @@ cleanup_neighbor (struct DirectNeighbor *neighbor) * * @param cls closure * @param peer peer identity this notification is about + * @param internal_cls the corresponding `struct DirectNeighbor` */ static void -handle_core_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer) +handle_core_disconnect (void *cls, + const struct GNUNET_PeerIdentity *peer, + void *internal_cls) { - struct DirectNeighbor *neighbor; + struct DirectNeighbor *neighbor = internal_cls; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received core peer disconnect message for peer `%s'!\n", GNUNET_i2s (peer)); /* Check for disconnect from self message */ - if (0 == memcmp (&my_identity, peer, sizeof (struct GNUNET_PeerIdentity))) - return; - neighbor = - GNUNET_CONTAINER_multihashmap_get (direct_neighbors, &peer->hashPubKey); if (NULL == neighbor) - { - /* must have been a DV-neighbor, ignore */ return; + GNUNET_break (GNUNET_YES == neighbor->connected); + neighbor->connected = GNUNET_NO; + if (DIRECT_NEIGHBOR_COST == neighbor->distance) + { + GNUNET_STATISTICS_update (stats, + "# peers connected (1-hop)", + -1, + GNUNET_NO); } - GNUNET_STATISTICS_update (stats, - "# peers connected (1-hop)", - -1, GNUNET_NO); cleanup_neighbor (neighbor); - GNUNET_CONTAINER_multihashmap_iterate (direct_neighbors, - &refresh_routes, - NULL); + if (GNUNET_YES == in_shutdown) + return; + schedule_refresh_routes (); } /** - * Multihashmap iterator for freeing routes. Should never be called. + * Multipeermap iterator for freeing routes. Should never be called. * * @param cls NULL * @param key key value stored under * @param value the route to be freed - * - * @return GNUNET_YES to continue iteration, GNUNET_NO to stop + * @return #GNUNET_YES to continue iteration, #GNUNET_NO to stop */ static int -free_route (void *cls, const struct GNUNET_HashCode * key, void *value) +free_route (void *cls, + const struct GNUNET_PeerIdentity *key, + void *value) { struct Route *route = value; GNUNET_break (0); GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_remove (all_routes, key, value)); + GNUNET_CONTAINER_multipeermap_remove (all_routes, key, value)); release_route (route); send_disconnect_to_plugin (&route->target.peer); GNUNET_free (route); @@ -1431,20 +1909,20 @@ free_route (void *cls, const struct GNUNET_HashCode * key, void *value) /** - * Multihashmap iterator for freeing direct neighbors. Should never be called. + * Multipeermap iterator for freeing direct neighbors. Should never be called. * * @param cls NULL * @param key key value stored under * @param value the direct neighbor to be freed - * - * @return GNUNET_YES to continue iteration, GNUNET_NO to stop + * @return #GNUNET_YES to continue iteration, #GNUNET_NO to stop */ static int -free_direct_neighbors (void *cls, const struct GNUNET_HashCode * key, void *value) +free_direct_neighbors (void *cls, + const struct GNUNET_PeerIdentity *key, + void *value) { struct DirectNeighbor *neighbor = value; - GNUNET_break (0); cleanup_neighbor (neighbor); return GNUNET_YES; } @@ -1454,58 +1932,68 @@ free_direct_neighbors (void *cls, const struct GNUNET_HashCode * key, void *valu * Task run during shutdown. * * @param cls unused - * @param tc unused */ static void -shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +shutdown_task (void *cls) { unsigned int i; + in_shutdown = GNUNET_YES; + GNUNET_assert (NULL != core_api); GNUNET_CORE_disconnect (core_api); core_api = NULL; - GNUNET_CONTAINER_multihashmap_iterate (direct_neighbors, - &free_direct_neighbors, NULL); - GNUNET_CONTAINER_multihashmap_iterate (all_routes, - &free_route, NULL); - GNUNET_CONTAINER_multihashmap_destroy (direct_neighbors); - GNUNET_CONTAINER_multihashmap_destroy (all_routes); + GNUNET_ATS_performance_done (ats); + ats = NULL; + GNUNET_CONTAINER_multipeermap_iterate (direct_neighbors, + &free_direct_neighbors, + NULL); + GNUNET_CONTAINER_multipeermap_iterate (all_routes, + &free_route, + NULL); + GNUNET_CONTAINER_multipeermap_destroy (direct_neighbors); + GNUNET_CONTAINER_multipeermap_destroy (all_routes); GNUNET_STATISTICS_destroy (stats, GNUNET_NO); stats = NULL; - GNUNET_SERVER_notification_context_destroy (nc); + GNUNET_notification_context_destroy (nc); nc = NULL; - for (i=0;itarget.distance); - cm.peer = route->target.peer; - - GNUNET_SERVER_notification_context_unicast (nc, - client, - &cm.header, - GNUNET_NO); + struct GNUNET_MQ_Envelope *env; + struct GNUNET_DV_ConnectMessage *cm; + + env = GNUNET_MQ_msg (cm, + GNUNET_MESSAGE_TYPE_DV_CONNECT); + cm->distance = htonl (route->target.distance); + cm->peer = route->target.peer; + GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), + env); return GNUNET_OK; } @@ -1519,13 +2007,16 @@ add_route (void *cls, * @param message the actual message */ static void -handle_start (void *cls, struct GNUNET_SERVER_Client *client, +handle_start (void *cls, const struct GNUNET_MessageHeader *message) { - GNUNET_SERVER_notification_context_add (nc, client); - GNUNET_SERVER_receive_done (client, GNUNET_OK); - GNUNET_CONTAINER_multihashmap_iterate (all_routes, - &add_route, + struct GNUNET_SERVICE_Client *client = cls; + + GNUNET_notification_context_add (nc, + GNUNET_SERVICE_client_get_mq (client)); + GNUNET_SERVICE_client_continue (client); + GNUNET_CONTAINER_multipeermap_iterate (all_routes, + ¬ify_client_about_route, client); } @@ -1534,11 +2025,10 @@ handle_start (void *cls, struct GNUNET_SERVER_Client *client, * Called on core init. * * @param cls unused - * @param server legacy * @param identity this peer's identity */ static void -core_init (void *cls, struct GNUNET_CORE_Handle *server, +core_init (void *cls, const struct GNUNET_PeerIdentity *identity) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, @@ -1552,62 +2042,105 @@ core_init (void *cls, struct GNUNET_CORE_Handle *server, * Process dv requests. * * @param cls closure - * @param server the initialized server * @param c configuration to use + * @param service the initialized service */ static void -run (void *cls, struct GNUNET_SERVER_Handle *server, - const struct GNUNET_CONFIGURATION_Handle *c) +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *c, + struct GNUNET_SERVICE_Handle *service) { - static struct GNUNET_CORE_MessageHandler core_handlers[] = { - {&handle_dv_route_message, GNUNET_MESSAGE_TYPE_DV_ROUTE, 0}, - {NULL, 0, 0} - }; - static struct GNUNET_SERVER_MessageHandler plugin_handlers[] = { - {&handle_start, NULL, - GNUNET_MESSAGE_TYPE_DV_START, - sizeof (struct GNUNET_MessageHeader) }, - { &handle_dv_send_message, NULL, - GNUNET_MESSAGE_TYPE_DV_SEND, - 0}, - {NULL, NULL, 0, 0} + struct GNUNET_MQ_MessageHandler core_handlers[] = { + GNUNET_MQ_hd_var_size (dv_route_message, + GNUNET_MESSAGE_TYPE_DV_ROUTE, + struct RouteMessage, + NULL), + GNUNET_MQ_handler_end () }; - + in_shutdown = GNUNET_NO; cfg = c; - direct_neighbors = GNUNET_CONTAINER_multihashmap_create (128, GNUNET_NO); - all_routes = GNUNET_CONTAINER_multihashmap_create (65536, GNUNET_NO); - core_api = GNUNET_CORE_connect (cfg, NULL, - &core_init, + direct_neighbors = GNUNET_CONTAINER_multipeermap_create (128, + GNUNET_NO); + all_routes = GNUNET_CONTAINER_multipeermap_create (65536, + GNUNET_NO); + core_api = GNUNET_CORE_connect (cfg, + NULL, + &core_init, &handle_core_connect, &handle_core_disconnect, - NULL, GNUNET_NO, - NULL, GNUNET_NO, core_handlers); if (NULL == core_api) return; - nc = GNUNET_SERVER_notification_context_create (server, - MAX_QUEUE_SIZE_PLUGIN); - stats = GNUNET_STATISTICS_create ("dv", cfg); - GNUNET_SERVER_add_handlers (server, plugin_handlers); - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, - &shutdown_task, NULL); + ats = GNUNET_ATS_performance_init (cfg, + &handle_ats_update, + NULL); + if (NULL == ats) + { + GNUNET_CORE_disconnect (core_api); + core_api = NULL; + return; + } + nc = GNUNET_notification_context_create (MAX_QUEUE_SIZE_PLUGIN); + stats = GNUNET_STATISTICS_create ("dv", + cfg); + GNUNET_SCHEDULER_add_shutdown (&shutdown_task, + NULL); } /** - * The main function for the dv service. + * Callback called when a client connects to the service. * - * @param argc number of arguments from the command line - * @param argv command line arguments - * @return 0 ok, 1 on error + * @param cls closure for the service + * @param c the new client that connected to the service + * @param mq the message queue used to send messages to the client + * @return @a c */ -int -main (int argc, char *const *argv) +static void * +client_connect_cb (void *cls, + struct GNUNET_SERVICE_Client *c, + struct GNUNET_MQ_Handle *mq) { - return (GNUNET_OK == - GNUNET_SERVICE_run (argc, argv, "dv", GNUNET_SERVICE_OPTION_NONE, - &run, NULL)) ? 0 : 1; + return c; } + +/** + * Callback called when a client disconnected from the service + * + * @param cls closure for the service + * @param c the client that disconnected + * @param internal_cls should be equal to @a c + */ +static void +client_disconnect_cb (void *cls, + struct GNUNET_SERVICE_Client *c, + void *internal_cls) +{ + GNUNET_assert (c == internal_cls); +} + + +/** + * Define "main" method using service macro. + */ +GNUNET_SERVICE_MAIN +("dv", + GNUNET_SERVICE_OPTION_NONE, + &run, + &client_connect_cb, + &client_disconnect_cb, + NULL, + GNUNET_MQ_hd_fixed_size (start, + GNUNET_MESSAGE_TYPE_DV_START, + struct GNUNET_MessageHeader, + NULL), + GNUNET_MQ_hd_var_size (dv_send_message, + GNUNET_MESSAGE_TYPE_DV_SEND, + struct GNUNET_DV_SendMessage, + NULL), + GNUNET_MQ_handler_end ()); + + /* end of gnunet-service-dv.c */