1 /* Copyright (c) 2010-2019. The SimGrid Team. All rights reserved. */
3 /* This program is free software; you can redistribute it and/or modify it
4 * under the terms of the license (GNU LGPL) which comes with this package. */
7 #include "routing_table.hpp"
9 XBT_LOG_NEW_DEFAULT_CATEGORY(kademlia_node, "Messages specific for this example");
12 static void destroy(void* message)
14 Message* msg = static_cast<Message*>(message);
20 * @brief Tries to join the network
21 * @param known_id id of the node I know in the network.
23 bool Node::join(unsigned int known_id)
27 bool got_answer = false;
29 /* Add the guy we know to our routing table and ourselves. */
30 routingTableUpdate(id_);
31 routingTableUpdate(known_id);
33 /* First step: Send a "FIND_NODE" request to the node we know */
34 sendFindNode(known_id, id_);
36 simgrid::s4u::Mailbox* mailbox = simgrid::s4u::Mailbox::by_name(std::to_string(id_));
38 if (receive_comm == nullptr)
39 receive_comm = mailbox->get_async(&received_msg);
40 if (receive_comm->test()) {
41 XBT_DEBUG("Received an answer from the node I know.");
43 // retrieve the node list and ping them.
44 Message* msg = static_cast<Message*>(received_msg);
45 node_list = msg->answer_;
47 for (auto contact : node_list->nodes)
48 routingTableUpdate(contact.first);
54 receive_comm = nullptr;
56 simgrid::s4u::this_actor::sleep_for(1);
57 } while (not got_answer);
59 /* Second step: Send a FIND_NODE to a a random node in buckets */
60 unsigned int bucket_id = table.findBucket(known_id)->getId();
61 xbt_assert(bucket_id <= IDENTIFIER_SIZE);
62 for (i = 0; ((bucket_id > i) || (bucket_id + i) <= IDENTIFIER_SIZE) && i < JOIN_BUCKETS_QUERIES; i++) {
64 unsigned int id_in_bucket = get_id_in_prefix(id_, bucket_id - i);
65 findNode(id_in_bucket, false);
67 if (bucket_id + i <= IDENTIFIER_SIZE) {
68 unsigned int id_in_bucket = get_id_in_prefix(id_, bucket_id + i);
69 findNode(id_in_bucket, false);
75 /** @brief Send a "FIND_NODE" to a node
76 * @param id node we are querying
77 * @param destination node we are trying to find.
79 void Node::sendFindNode(unsigned int id, unsigned int destination)
81 /* Gets the mailbox to send to */
82 simgrid::s4u::Mailbox* mailbox = simgrid::s4u::Mailbox::by_name(std::to_string(id));
84 Message* msg = new Message(id_, destination, simgrid::s4u::Mailbox::by_name(std::to_string(id_)),
85 simgrid::s4u::Host::current()->get_cname());
88 mailbox->put_init(msg, 1)->detach(kademlia::destroy);
89 XBT_VERB("Asking %u for its closest nodes", id);
93 * Sends to the best "KADEMLIA_ALPHA" nodes in the "node_list" array a "FIND_NODE" request, to ask them for their best
96 unsigned int Node::sendFindNodeToBest(Answer* node_list)
100 unsigned int destination = node_list->getDestinationId();
101 for (auto node_to_query : node_list->nodes) {
102 /* We need to have at most "KADEMLIA_ALPHA" requests each time, according to the protocol */
103 /* Gets the node we want to send the query to */
104 if (node_to_query.first != id_) { /* No need to query ourselves */
105 sendFindNode(node_to_query.first, destination);
109 if (j == KADEMLIA_ALPHA)
115 /** @brief Updates/Puts the node id unsigned into our routing table
116 * @param id The id of the node we need to add unsigned into our routing table
118 void Node::routingTableUpdate(unsigned int id)
120 // retrieval of the bucket in which the should be
121 Bucket* bucket = table.findBucket(id);
123 // check if the id is already in the bucket.
124 auto id_pos = std::find(bucket->nodes.begin(), bucket->nodes.end(), id);
126 if (id_pos == bucket->nodes.end()) {
127 /* We check if the bucket is full or not. If it is, we evict an old element */
128 if (bucket->nodes.size() >= BUCKET_SIZE) {
129 bucket->nodes.pop_back();
131 bucket->nodes.push_front(id);
132 XBT_VERB("I'm adding to my routing table %08x", id);
134 // We push the element to the front
135 bucket->nodes.erase(id_pos);
136 bucket->nodes.push_front(id);
137 XBT_VERB("I'm updating %08x", id);
141 /** @brief Finds the closest nodes to the node given.
142 * @param node : our node
143 * @param destination_id : the id of the guy we are trying to find
145 Answer* Node::findClosest(unsigned int destination_id)
147 Answer* answer = new Answer(destination_id);
148 /* We find the corresponding bucket for the id */
149 const Bucket* bucket = table.findBucket(destination_id);
150 int bucket_id = bucket->getId();
151 xbt_assert((bucket_id <= IDENTIFIER_SIZE), "Bucket found has a wrong identifier");
152 /* So, we copy the contents of the bucket unsigned into our answer */
153 answer->addBucket(bucket);
155 /* However, if we don't have enough elements in our bucket, we NEED to include at least "BUCKET_SIZE" elements
156 * (if, of course, we know at least "BUCKET_SIZE" elements. So we're going to look unsigned into the other buckets.
158 for (int i = 1; answer->getSize() < BUCKET_SIZE && ((bucket_id - i > 0) || (bucket_id + i < IDENTIFIER_SIZE)); i++) {
159 /* We check the previous buckets */
160 if (bucket_id - i >= 0) {
161 const Bucket* bucket_p = &table.buckets[bucket_id - i];
162 answer->addBucket(bucket_p);
164 /* We check the next buckets */
165 if (bucket_id + i <= IDENTIFIER_SIZE) {
166 const Bucket* bucket_n = &table.buckets[bucket_id + i];
167 answer->addBucket(bucket_n);
170 /* We trim the array to have only BUCKET_SIZE or less elements */
171 std::sort(answer->nodes.begin(), answer->nodes.end(), sortbydistance);
177 /** @brief Send a request to find a node in the node routing table.
178 * @param id_to_find the id of the node we are trying to find
180 bool Node::findNode(unsigned int id_to_find, bool count_in_stats)
182 unsigned int queries;
183 unsigned int answers;
184 bool destination_found = false;
185 unsigned int nodes_added = 0;
186 double global_timeout = simgrid::s4u::Engine::get_clock() + FIND_NODE_GLOBAL_TIMEOUT;
187 unsigned int steps = 0;
189 /* First we build a list of who we already know */
190 Answer* node_list = findClosest(id_to_find);
191 xbt_assert((node_list != nullptr), "node_list incorrect");
192 XBT_DEBUG("Doing a FIND_NODE on %08x", id_to_find);
194 /* Ask the nodes on our list if they have information about the node we are trying to find */
197 queries = sendFindNodeToBest(node_list);
199 double timeout = simgrid::s4u::Engine::get_clock() + FIND_NODE_TIMEOUT;
201 double time_beginreceive = simgrid::s4u::Engine::get_clock();
203 simgrid::s4u::Mailbox* mailbox = simgrid::s4u::Mailbox::by_name(std::to_string(id_));
205 if (receive_comm == nullptr)
206 receive_comm = mailbox->get_async(&received_msg);
208 if (receive_comm->test()) {
209 Message* msg = static_cast<Message*>(received_msg);
210 // Check if what we have received is what we are looking for.
211 if (msg->answer_ && msg->answer_->getDestinationId() == id_to_find) {
212 routingTableUpdate(msg->sender_id_);
214 for (auto contact : node_list->nodes)
215 routingTableUpdate(contact.first);
218 nodes_added = node_list->merge(msg->answer_);
219 XBT_DEBUG("Received an answer from %s (%s) with %zu nodes on it", msg->answer_to_->get_cname(),
220 msg->issuer_host_name_.c_str(), msg->answer_->nodes.size());
223 routingTableUpdate(msg->sender_id_);
224 XBT_DEBUG("Received a wrong answer for a FIND_NODE");
228 // Update the timeout if we didn't have our answer
229 timeout += simgrid::s4u::Engine::get_clock() - time_beginreceive;
230 time_beginreceive = simgrid::s4u::Engine::get_clock();
234 receive_comm = nullptr;
236 simgrid::s4u::this_actor::sleep_for(1);
238 } while (simgrid::s4u::Engine::get_clock() < timeout && answers < queries);
239 destination_found = node_list->destinationFound();
240 } while (not destination_found && (nodes_added > 0 || answers == 0) &&
241 simgrid::s4u::Engine::get_clock() < global_timeout && steps < MAX_STEPS);
243 if (destination_found) {
247 XBT_VERB("FIND_NODE on %08x success in %u steps", id_to_find, steps);
248 routingTableUpdate(id_to_find);
250 if (count_in_stats) {
252 XBT_VERB("%08x not found in %u steps", id_to_find, steps);
256 return destination_found;
259 /** @brief Does a pseudo-random lookup for someone in the system
260 * @param node caller node data
262 void Node::randomLookup()
264 unsigned int id_to_look = RANDOM_LOOKUP_NODE; // Totally random.
265 /* TODO: Use some pseudo-random generator like RngStream. */
266 XBT_DEBUG("I'm doing a random lookup");
267 findNode(id_to_look, true);
270 /** @brief Handles the answer to an incoming "find_node" task */
271 void Node::handleFindNode(Message* msg)
273 routingTableUpdate(msg->sender_id_);
274 XBT_VERB("Received a FIND_NODE from %s (%s), he's trying to find %08x", msg->answer_to_->get_cname(),
275 msg->issuer_host_name_.c_str(), msg->destination_id_);
276 // Building the answer to the request
278 new Message(id_, msg->destination_id_, findClosest(msg->destination_id_),
279 simgrid::s4u::Mailbox::by_name(std::to_string(id_)), simgrid::s4u::Host::current()->get_cname());
280 // Sending the answer
281 msg->answer_to_->put_init(answer, 1)->detach(kademlia::destroy);
284 /**@brief Returns an identifier which is in a specific bucket of a routing table
285 * @param id id of the routing table owner
286 * @param prefix id of the bucket where we want that identifier to be
288 unsigned int get_id_in_prefix(unsigned int id, unsigned int prefix)
293 return (1U << ((unsigned int)(prefix - 1))) ^ id;
297 /** @brief Returns the prefix of an identifier.
298 * The prefix is the id of the bucket in which the remote identifier xor our identifier should be stored.
299 * @param id : big unsigned int id to test
300 * @param nb_bits : key size
302 unsigned int get_node_prefix(unsigned int id, unsigned int nb_bits)
304 unsigned int size = sizeof(unsigned int) * 8;
305 for (unsigned int j = 0; j < size; j++) {
306 if (((id >> (size - 1 - j)) & 0x1) != 0) {
307 return nb_bits - (j);