LCOV - code coverage report
Current view: top level - src/jamidht/swarm - routing_table.h (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 100.0 % 54 54
Test Date: 2026-06-13 09:18:46 Functions: 100.0 % 29 29

            Line data    Source code
       1              : /*
       2              :  *  Copyright (C) 2004-2026 Savoir-faire Linux Inc.
       3              :  *
       4              :  *  This program is free software: you can redistribute it and/or modify
       5              :  *  it under the terms of the GNU General Public License as published by
       6              :  *  the Free Software Foundation, either version 3 of the License, or
       7              :  *  (at your option) any later version.
       8              :  *
       9              :  *  This program is distributed in the hope that it will be useful,
      10              :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      11              :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      12              :  *  GNU General Public License for more details.
      13              :  *
      14              :  *  You should have received a copy of the GNU General Public License
      15              :  *  along with this program. If not, see <https://www.gnu.org/licenses/>.
      16              :  */
      17              : #pragma once
      18              : 
      19              : #include "manager.h"
      20              : 
      21              : #include <dhtnet/multiplexed_socket.h>
      22              : 
      23              : #include <opendht/infohash.h>
      24              : 
      25              : #include <asio.hpp>
      26              : #include <asio/detail/deadline_timer_service.hpp>
      27              : 
      28              : #include <utility>
      29              : #include <vector>
      30              : #include <memory>
      31              : #include <list>
      32              : #include <set>
      33              : 
      34              : using NodeId = dht::PkId;
      35              : 
      36              : namespace jami {
      37              : 
      38              : static constexpr const std::chrono::minutes FIND_PERIOD {10};
      39              : 
      40              : struct NodeInfo
      41              : {
      42              :     bool isMobile_ {false};
      43              :     std::shared_ptr<dhtnet::ChannelSocketInterface> socket {};
      44              :     asio::steady_timer refresh_timer {*Manager::instance().ioContext(), FIND_PERIOD};
      45              :     NodeInfo() = delete;
      46         1686 :     NodeInfo(NodeInfo&&) noexcept = default;
      47         1409 :     NodeInfo(std::shared_ptr<dhtnet::ChannelSocketInterface> socket_)
      48         1409 :         : socket(std::move(socket_))
      49         1410 :     {}
      50            1 :     NodeInfo(bool mobile, std::shared_ptr<dhtnet::ChannelSocketInterface> socket_)
      51            1 :         : isMobile_(mobile)
      52            1 :         , socket(std::move(socket_))
      53            1 :     {}
      54              : };
      55              : 
      56              : class Bucket
      57              : {
      58              : public:
      59              :     static constexpr int BUCKET_MAX_SIZE = 2;
      60              : 
      61              :     Bucket() = delete;
      62              :     Bucket(const Bucket&) = delete;
      63              :     Bucket(const NodeId&);
      64              : 
      65              :     /**
      66              :      * Add Node socket to bucket
      67              :      * @param socket
      68              :      * @return true if node was added, false if not
      69              :      */
      70              :     bool addNode(const std::shared_ptr<dhtnet::ChannelSocketInterface>& socket);
      71              : 
      72              :     /**
      73              :      * Add NodeInfo to bucket
      74              :      * @param nodeInfo
      75              :      * @return true if node was added, false if not
      76              :      */
      77              :     bool addNode(NodeInfo&& info);
      78              : 
      79              :     /**
      80              :      * Remove NodeId socket from bucket and insert it in known_nodes or
      81              :      * mobile_nodes depending on its type
      82              :      * @param nodeId
      83              :      * @return true if node was removed, false if not
      84              :      */
      85              :     bool removeNode(const NodeId& nodeId);
      86              : 
      87              :     /**
      88              :      * Get connected nodes from bucket
      89              :      * @return map of NodeId and NodeInfo
      90              :      */
      91         4701 :     const std::map<NodeId, NodeInfo>& getNodes() const { return nodes; }
      92          331 :     std::map<NodeId, NodeInfo>& getNodes() { return nodes; }
      93              : 
      94              :     /**
      95              :      * Get NodeIds from bucket
      96              :      * @return set of NodeIds
      97              :      */
      98              :     std::set<NodeId> getNodeIds() const;
      99              : 
     100              :     /**
     101              :      * Test if socket exists in nodes
     102              :      * @param nodeId
     103              :      * @return true if node exists, false if not
     104              :      */
     105              :     bool hasNode(const NodeId& nodeId) const;
     106              : 
     107              :     /**
     108              :      * Add NodeId to known_nodes if it doesn't exist in nodes
     109              :      * @param nodeId
     110              :      * @return true if known node was added, false if not
     111              :      */
     112              :     bool addKnownNode(const NodeId& nodeId);
     113              : 
     114              :     /**
     115              :      * Remove NodeId from known_nodes
     116              :      * @param nodeId
     117              :      */
     118           86 :     void removeKnownNode(const NodeId& nodeId) { known_nodes.erase(nodeId); }
     119              : 
     120              :     /**
     121              :      * Get NodeIds from known_nodes
     122              :      * @return set of known NodeIds
     123              :      */
     124         1938 :     const std::set<NodeId>& getKnownNodes() const { return known_nodes; }
     125              : 
     126              :     /**
     127              :      * Returns NodeId from known_nodes at index
     128              :      * @param index
     129              :      * @return NodeId
     130              :      */
     131              :     NodeId getKnownNode(unsigned index) const;
     132              : 
     133              :     /**
     134              :      * Test if NodeId exist in known_nodes
     135              :      * @param nodeId
     136              :      * @return true if known node exists, false if not
     137              :      */
     138           11 :     bool hasKnownNode(const NodeId& nodeId) const { return known_nodes.find(nodeId) != known_nodes.end(); }
     139              : 
     140              :     /**
     141              :      * Add NodeId to mobile_nodes if it doesn't exist in nodes
     142              :      * @param nodeId
     143              :      * @return true if mobile node was added, false if not
     144              :      */
     145              :     bool addMobileNode(const NodeId& nodeId);
     146              : 
     147              :     /**
     148              :      * Remove NodeId from mobile_nodes
     149              :      * @param nodeId
     150              :      */
     151           13 :     void removeMobileNode(const NodeId& nodeId) { mobile_nodes.erase(nodeId); }
     152              : 
     153              :     /**
     154              :      * Test if NodeId exist in mobile_nodes
     155              :      * @param nodeId
     156              :      * @return true if mobile node exists, false if not
     157              :      */
     158           13 :     bool hasMobileNode(const NodeId& nodeId) { return mobile_nodes.find(nodeId) != mobile_nodes.end(); }
     159              : 
     160              :     /**
     161              :      * Get NodeIds from mobile_nodes
     162              :      * @return set of mobile NodeIds
     163              :      */
     164         6332 :     const std::set<NodeId>& getMobileNodes() const { return mobile_nodes; }
     165              : 
     166              :     /**
     167              :      * Add NodeId to connecting_nodes if it doesn't exist in nodes
     168              :      * @param nodeId
     169              :      * @param nodeInfo
     170              :      * @return true if connecting node was added, false if not
     171              :      */
     172              :     bool addConnectingNode(const NodeId& nodeId);
     173              : 
     174              :     /**
     175              :      * Remove NodeId from connecting_nodes
     176              :      * @param nodeId
     177              :      */
     178          247 :     void removeConnectingNode(const NodeId& nodeId) { connecting_nodes.erase(nodeId); }
     179              : 
     180              :     /** Get NodeIds of connecting_nodes
     181              :      * @return set of connecting NodeIds
     182              :      */
     183          349 :     const std::set<NodeId>& getConnectingNodes() const { return connecting_nodes; };
     184              : 
     185              :     /**
     186              :      * Test if NodeId exist in connecting_nodes
     187              :      * @param nodeId
     188              :      * @return true if connecting node exists, false if not
     189              :      */
     190           18 :     bool hasConnectingNode(const NodeId& nodeId) const
     191              :     {
     192           18 :         return connecting_nodes.find(nodeId) != connecting_nodes.end();
     193              :     }
     194              : 
     195         4876 :     bool isEmpty() const { return nodes.empty(); }
     196              : 
     197              :     /**
     198              :      * Indicate if bucket is full
     199              :      * @return true if bucket is full, false if not
     200              :      */
     201         1710 :     bool isFull() const { return nodes.size() == BUCKET_MAX_SIZE; };
     202              : 
     203              :     /**
     204              :      * Returns random numberNodes NodeId from known_nodes
     205              :      * @param numberNodes
     206              :      * @param rd
     207              :      * @return set of numberNodes random known NodeIds
     208              :      */
     209              :     std::set<NodeId> getKnownNodesRandom(unsigned numberNodes, std::mt19937_64& rd) const;
     210              : 
     211              :     /**
     212              :      * Returns random NodeId from known_nodes
     213              :      * @param rd
     214              :      * @return random known NodeId
     215              :      */
     216            1 :     NodeId randomId(std::mt19937_64& rd) const
     217              :     {
     218            1 :         auto node = getKnownNodesRandom(1, rd);
     219            2 :         return *node.begin();
     220            1 :     }
     221              : 
     222              :     /**
     223              :      * Returns socket's timer
     224              :      * @param socket
     225              :      * @return timer
     226              :      */
     227              :     asio::steady_timer& getNodeTimer(const std::shared_ptr<dhtnet::ChannelSocketInterface>& socket);
     228              : 
     229              :     /**
     230              :      * Shutdowns socket and removes it from nodes.
     231              :      * The corresponding node is moved to known_nodes or mobile_nodes
     232              :      * @param socket
     233              :      * @return true if node was shutdown, false if not found
     234              :      */
     235              :     bool shutdownNode(const NodeId& nodeId);
     236              : 
     237              :     /**
     238              :      * Shutdowns all sockets in nodes through shutdownNode
     239              :      */
     240              :     void shutdownAllNodes();
     241              : 
     242              :     /**
     243              :      * Prints bucket and bucket's number
     244              :      */
     245              :     void printBucket(unsigned number) const;
     246              : 
     247              :     /**
     248              :      * Change mobility of specific node, mobile or not
     249              :      */
     250              :     void changeMobility(const NodeId& nodeId, bool isMobile);
     251              : 
     252              :     /**
     253              :      * Returns number of nodes in bucket
     254              :      * @return size of nodes
     255              :      */
     256         2468 :     unsigned getNodesSize() const { return nodes.size(); }
     257              : 
     258              :     /**
     259              :      * Returns number of knwon_nodes in bucket
     260              :      * @return size of knwon_nodes
     261              :      */
     262         1723 :     unsigned getKnownNodesSize() const { return known_nodes.size(); }
     263              : 
     264              :     /**
     265              :      * Returns number of mobile_nodes in bucket
     266              :      * @return size of mobile_nodes
     267              :      */
     268         3547 :     unsigned getConnectingNodesSize() const { return connecting_nodes.size(); }
     269              : 
     270              :     /**
     271              :      * Returns bucket lower limit
     272              :      * @return NodeId lower limit
     273              :      */
     274        29953 :     NodeId getLowerLimit() const { return lowerLimit_; };
     275              : 
     276              :     /**
     277              :      * Set bucket's lower limit
     278              :      * @param nodeId
     279              :      */
     280              :     void setLowerLimit(const NodeId& nodeId) { lowerLimit_ = nodeId; }
     281              : 
     282              :     // For tests
     283              : 
     284              :     /**
     285              :      * Get sockets from bucket
     286              :      * @return set of sockets
     287              :      */
     288              :     std::set<std::shared_ptr<dhtnet::ChannelSocketInterface>> getNodeSockets() const;
     289              : 
     290              : private:
     291              :     NodeId lowerLimit_;
     292              :     std::map<NodeId, NodeInfo> nodes;
     293              :     std::set<NodeId> known_nodes;
     294              :     std::set<NodeId> connecting_nodes;
     295              :     std::set<NodeId> mobile_nodes;
     296              :     mutable std::mutex mutex;
     297              : };
     298              : 
     299              : // ####################################################################################################
     300              : 
     301              : class RoutingTable
     302              : {
     303              : public:
     304              :     RoutingTable();
     305              : 
     306              :     bool isEmpty() const;
     307              : 
     308              :     /**
     309              :      * Add socket to bucket
     310              :      * @param socket
     311              :      * @return true if socket was added, false if not
     312              :      */
     313              :     bool addNode(const std::shared_ptr<dhtnet::ChannelSocketInterface>& socket);
     314              : 
     315              :     /**
     316              :      * Add socket to specific bucket
     317              :      * @param channel
     318              :      * @param bucket
     319              :      * @return true if socket was added to bucket, false if not
     320              :      */
     321              :     bool addNode(const std::shared_ptr<dhtnet::ChannelSocketInterface>& channel, std::list<Bucket>::iterator& bucket);
     322              : 
     323              :     /**
     324              :      * Removes node from routing table
     325              :      * Adds it to known_nodes or mobile_nodes depending on mobility
     326              :      * @param socket
     327              :      * @return true if node was removed, false if not
     328              :      */
     329              :     bool removeNode(const NodeId& nodeId);
     330              : 
     331              :     /**
     332              :      * Check if connected node exsits in routing table
     333              :      * @param nodeId
     334              :      * @return true if node exists, false if not
     335              :      */
     336              :     bool hasNode(const NodeId& nodeId);
     337              : 
     338              :     /**
     339              :      * Add known node to routing table
     340              :      * @param nodeId
     341              :      * @return true if known node was added, false if not
     342              :      */
     343              :     bool addKnownNode(const NodeId& nodeId);
     344              : 
     345              :     /**
     346              :      * Checks if known node exists in routing table
     347              :      * @param nodeId
     348              :      * @return true if known node exists, false if not
     349              :      */
     350            4 :     bool hasKnownNode(const NodeId& nodeId) const
     351              :     {
     352            4 :         auto bucket = findBucket(nodeId);
     353            8 :         return bucket->hasKnownNode(nodeId);
     354              :     }
     355              : 
     356              :     /**
     357              :      * Add mobile node to routing table
     358              :      * @param nodeId
     359              :      * @return true if mobile node was added, false if not
     360              :      */
     361              :     bool addMobileNode(const NodeId& nodeId);
     362              : 
     363              :     /**
     364              :      * Remove mobile node to routing table
     365              :      * @param nodeId
     366              :      * @return true if mobile node was removed, false if not
     367              :      */
     368              :     void removeMobileNode(const NodeId& nodeId);
     369              : 
     370              :     /**
     371              :      * Check if mobile node exists in routing table
     372              :      * @param nodeId
     373              :      * @return true if mobile node exists, false if not
     374              :      */
     375              :     bool hasMobileNode(const NodeId& nodeId);
     376              : 
     377              :     /**
     378              :      * Add connecting node to routing table
     379              :      * @param nodeId
     380              :      * @return true if connecting node was added, false if not
     381              :      */
     382              :     bool addConnectingNode(const NodeId& nodeId);
     383              : 
     384              :     /**
     385              :      * Remove connecting connecting node to routing table
     386              :      * @param  nodeId
     387              :      * @return true if connecting node was removed, false if not
     388              :      */
     389              :     void removeConnectingNode(const NodeId& nodeId);
     390              : 
     391              :     /**
     392              :      * Check if Connecting node exists in routing table
     393              :      * @param nodeId
     394              :      * @return true if connecting node exists, false if not
     395              :      */
     396            6 :     bool hasConnectingNode(const NodeId& nodeId) const
     397              :     {
     398            6 :         auto bucket = findBucket(nodeId);
     399           12 :         return bucket->hasConnectingNode(nodeId);
     400              :     }
     401              : 
     402              :     /**
     403              :      * Returns bucket iterator containing nodeId
     404              :      * @param nodeId
     405              :      * @return bucket iterator
     406              :      */
     407              :     std::list<Bucket>::iterator findBucket(const NodeId& nodeId);
     408              : 
     409              :     /**
     410              :      * Returns bucket iterator containing nodeId
     411              :      * @param nodeId
     412              :      * @return bucket iterator
     413              :      */
     414           10 :     inline const std::list<Bucket>::const_iterator findBucket(const NodeId& nodeId) const
     415              :     {
     416           10 :         return std::list<Bucket>::const_iterator(const_cast<RoutingTable*>(this)->findBucket(nodeId));
     417              :     }
     418              : 
     419              :     /**
     420              :      * Returns the count closest nodes to a specific nodeId
     421              :      * @param nodeId
     422              :      * @param count
     423              :      * @return vector of nodeIds
     424              :      */
     425              :     std::vector<NodeId> closestNodes(const NodeId& nodeId, unsigned count);
     426              : 
     427              :     /**
     428              :      * Returns number of buckets in routing table
     429              :      * @return size of buckets
     430              :      */
     431              :     unsigned size() const { return buckets.size(); }
     432              : 
     433              :     /**
     434              :      * Returns number of total nodes in routing table
     435              :      * @return size of nodes
     436              :      */
     437           10 :     unsigned getNodeCount() const
     438              :     {
     439           10 :         size_t count = 0;
     440           48 :         for (const auto& b : buckets)
     441           38 :             count += b.getNodesSize();
     442           10 :         return count;
     443              :     }
     444              : 
     445          452 :     unsigned getActiveNodesCount() const
     446              :     {
     447          452 :         size_t count = 0;
     448          904 :         for (const auto& b : buckets)
     449          452 :             count += b.getNodesSize() + b.getConnectingNodesSize();
     450          452 :         return count;
     451              :     }
     452              : 
     453              :     /**
     454              :      * Prints routing table
     455              :      */
     456              :     void printRoutingTable() const;
     457              : 
     458              :     /**
     459              :      * Shutdowns a node
     460              :      * @param nodeId
     461              :      */
     462              :     void shutdownNode(const NodeId& nodeId);
     463              : 
     464              :     /**
     465              :      * Shutdowns all nodes in routing table and add them to known_nodes or mobile_nodes
     466              :      */
     467          626 :     void shutdownAllNodes()
     468              :     {
     469         1583 :         for (auto& bucket : buckets)
     470          957 :             bucket.shutdownAllNodes();
     471          626 :     }
     472              : 
     473              :     /**
     474              :      * Sets id for routing table
     475              :      * @param node
     476              :      */
     477          594 :     void setId(const NodeId& node) { id_ = node; }
     478              : 
     479              :     /**
     480              :      * Returns id for routing table
     481              :      * @return Nodeid
     482              :      */
     483              :     NodeId getId() const { return id_; }
     484              : 
     485              :     /**
     486              :      * Returns buckets in routing table
     487              :      * @return list buckets
     488              :      */
     489         1066 :     std::list<Bucket>& getBuckets() { return buckets; }
     490              : 
     491              :     /**
     492              :      * Returns all routing table's connected nodes
     493              :      * @return vector of nodeIds
     494              :      */
     495              :     std::vector<NodeId> getNodes() const;
     496              : 
     497              :     /**
     498              :      * Returns all routing table's known nodes
     499              :      *@return vector of nodeIds
     500              :      */
     501              :     std::vector<NodeId> getKnownNodes() const;
     502              : 
     503              :     /**
     504              :      * Returns all routing table's mobile nodes
     505              :      * @return vector of nodeIds
     506              :      */
     507              :     std::vector<NodeId> getMobileNodes() const;
     508              : 
     509              :     /**
     510              :      * Returns all routing table's connecting nodes
     511              :      * @return vector of nodeIds
     512              :      */
     513              :     std::vector<NodeId> getConnectingNodes() const;
     514              : 
     515              :     /**
     516              :      * Returns mobile nodes corresponding to the swarm's id
     517              :      * @return vector of nodeIds
     518              :      */
     519              :     std::vector<NodeId> getBucketMobileNodes() const;
     520              : 
     521              :     std::vector<NodeId> getConnectedNodes() const;
     522              : 
     523              :     /**
     524              :      * Test if connected nodeId is in specific bucket
     525              :      * @param it
     526              :      * @param nodeId
     527              :      * @return true if nodeId is in bucket, false if not
     528              :      */
     529              :     bool contains(const std::list<Bucket>::iterator& it, const NodeId& nodeId) const;
     530              : 
     531              :     /**
     532              :      * Return every node from each bucket
     533              :      */
     534              :     std::vector<NodeId> getAllNodes() const;
     535              : 
     536              :     /**
     537              :      * Delete node from every table in bucket
     538              :      */
     539              :     void deleteNode(const NodeId& nodeId);
     540              : 
     541              :     struct NodeStats
     542              :     {
     543              :         std::string id;
     544              :         std::string status;
     545              :         std::string remoteAddress;
     546              :         std::chrono::system_clock::time_point connectionTime;
     547              :         bool isMobile;
     548              :     };
     549              :     std::vector<NodeStats> getRoutingTableStats() const;
     550              : 
     551              : private:
     552              :     RoutingTable(const RoutingTable&) = delete;
     553              :     RoutingTable& operator=(const RoutingTable&) = delete;
     554              : 
     555              :     /**
     556              :      * Returns middle of routing table
     557              :      * @param it
     558              :      * @return NodeId
     559              :      */
     560              :     NodeId middle(std::list<Bucket>::iterator& it) const;
     561              : 
     562              :     /**
     563              :      * Returns depth of routing table
     564              :      * @param bucket
     565              :      * @return depth
     566              :      */
     567              :     unsigned depth(std::list<Bucket>::iterator& bucket) const;
     568              : 
     569              :     /**
     570              :      * Splits bucket
     571              :      * @param bucket
     572              :      * @return true if bucket was split, false if not
     573              :      */
     574              :     bool split(std::list<Bucket>::iterator& bucket);
     575              : 
     576              :     NodeId id_;
     577              : 
     578              :     std::list<Bucket> buckets;
     579              : 
     580              :     mutable std::mutex mutex_;
     581              : };
     582              : }; // namespace jami
        

Generated by: LCOV version 2.0-1