From 8510dce429dd7fd8d5bc3fa3fea7f9c8fe875366 Mon Sep 17 00:00:00 2001 From: orignal Date: Sun, 27 Sep 2026 21:09:16 -0400 Subject: [PATCH] remove expired nodes from routing table --- libi2pd_client/TorrentsDHT.cpp | 64 +++++++++++++++++++++++++++++++--- libi2pd_client/TorrentsDHT.h | 10 +++++- 2 files changed, 69 insertions(+), 5 deletions(-) diff --git a/libi2pd_client/TorrentsDHT.cpp b/libi2pd_client/TorrentsDHT.cpp index b6b50c33..e17ead3d 100644 --- a/libi2pd_client/TorrentsDHT.cpp +++ b/libi2pd_client/TorrentsDHT.cpp @@ -153,6 +153,34 @@ namespace torrents } } + std::list RoutingTable::DeleteExpiredNodes () + { + auto ts = i2p::util::GetMonotonicSeconds (); + auto bucket = m_Buckets; + std::list deleted; + while (bucket) + { + if (ts > bucket->lastUpdateTime + DHT_BUCKET_EXPIRATION_THRESHOLD) + { + auto it = bucket->nodes.begin (); + while (it != bucket->nodes.end ()) + { + if (ts > it->second + DHT_NODE_EXPIRATION_TIME) + { + deleted.push_back (it->first); + it = bucket->nodes.erase (it); + } + else + it++; + } + } + bucket = bucket->next; + } + if (!deleted.empty ()) + RemoveEmptyBuckets (); + return deleted; + } + bool RoutingTable::AddNode (const NodeID& id) { if (id == m_OurNode) return false; @@ -174,7 +202,11 @@ namespace torrents while (bucket->IsFull ()); } if (bucket) - bucket->nodes.emplace (id, i2p::util::GetMonotonicSeconds ()); + { + auto ts = i2p::util::GetMonotonicSeconds (); + bucket->nodes.emplace (id, ts); + bucket->lastUpdateTime = ts; + } RemoveEmptyBuckets (); return true; } @@ -268,7 +300,8 @@ namespace torrents } TorrentsDHT::TorrentsDHT (TorrentsTunnel& tunnel, uint16_t port): - m_Tunnel (tunnel), m_DHTUpdateCheckTimer (tunnel.GetService ()), m_Port (port), + m_Tunnel (tunnel), m_DHTUpdateCheckTimer (tunnel.GetService ()), + m_DHTExpirationCheckTimer (tunnel.GetService ()), m_Port (port), m_NextDHTExploratoryTime (i2p::util::GetMonotonicSeconds () + DHT_INITIAL_EXPLORATORY_INTERVAL) { auto dest = tunnel.GetLocalDestination (); @@ -297,11 +330,13 @@ namespace torrents } Load (GetDHTFilePath (filename)); ScheduleDHTUpdateCheck (); + ScheduleDHTExpirationCheck (); } void TorrentsDHT::Stop () { m_DHTUpdateCheckTimer.cancel (); + m_DHTExpirationCheckTimer.cancel (); std::string filename ("nodest"); auto dest = m_Tunnel.GetLocalDestination (); if (dest) @@ -698,8 +733,8 @@ namespace torrents { auto node = std::make_shared(nodeInfo); LogPrint (eLogDebug, "TorrentsDHT: find_node response received ", node->peer.ToBase64 ()); - if (m_Nodes.emplace (node->id, node).second && m_RoutingTable) - m_RoutingTable->AddNode (node->id); + m_Nodes.emplace (node->id, node); + if (m_RoutingTable) m_RoutingTable->AddNode (node->id); } else LogPrint (eLogInfo, "TorrentsDHT: Response to unknown query ", query); @@ -868,6 +903,27 @@ namespace torrents ScheduleDHTUpdateCheck (); } } + + void TorrentsDHT::ScheduleDHTExpirationCheck () + { + m_DHTExpirationCheckTimer.cancel (); + m_DHTExpirationCheckTimer.expires_after (std::chrono::seconds (DHT_EXPIRATION_CHECK_INTERVAL)); + m_DHTExpirationCheckTimer.async_wait (std::bind_front(&TorrentsDHT::HandleDHTExpirationCheckTimer, this)); + } + + void TorrentsDHT::HandleDHTExpirationCheckTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + if (m_RoutingTable) + { + auto deleted = m_RoutingTable->DeleteExpiredNodes (); + for (auto it: deleted) + m_Nodes.erase (it); + } + ScheduleDHTExpirationCheck (); + } + } } } diff --git a/libi2pd_client/TorrentsDHT.h b/libi2pd_client/TorrentsDHT.h index 57b7b818..8b481316 100644 --- a/libi2pd_client/TorrentsDHT.h +++ b/libi2pd_client/TorrentsDHT.h @@ -36,9 +36,12 @@ namespace i2p namespace torrents { constexpr int DHT_UPDATE_CHECK_INTERVAL = 24; // in seconds + constexpr int DHT_EXPIRATION_CHECK_INTERVAL = 73; // in seconds constexpr int DHT_EXPLORATORY_INTERVAL = 4*60; // in seconds constexpr int DHT_EXPLORATORY_INTERVAL_VARIANCE = 40; // in seconds constexpr int DHT_INITIAL_EXPLORATORY_INTERVAL = 90; // in seconds + constexpr int DHT_NODE_EXPIRATION_TIME = 855; // in seconds + constexpr int DHT_BUCKET_EXPIRATION_THRESHOLD = 290; // in seconds using Distance = Torrent::InfoHash; struct NodeID: public Torrent::InfoHash @@ -87,6 +90,7 @@ namespace torrents Bucket * next; std::map nodes; // nodeID->update time in monotonic seconds NodeID start; + uint64_t lastUpdateTime = 0; // monotonic seconds Bucket (): next (nullptr), start{} {} Bucket (const NodeID& start1): next (nullptr), start (start1) {} @@ -112,6 +116,7 @@ namespace torrents std::optional FindClosestNode (const Torrent::InfoHash& infoHash) const; std::list > GetExploratoryTargets () const; // (target, node to send find_node to) NodeID FindClosestNodeInBucket (const NodeID& target) const; + std::list DeleteExpiredNodes (); private: @@ -197,10 +202,13 @@ namespace torrents void ScheduleDHTUpdateCheck (); void HandleDHTUpdateCheckTimer (const boost::system::error_code& ecode); + void ScheduleDHTExpirationCheck (); + void HandleDHTExpirationCheckTimer (const boost::system::error_code& ecode); + private: TorrentsTunnel& m_Tunnel; - boost::asio::steady_timer m_DHTUpdateCheckTimer; + boost::asio::steady_timer m_DHTUpdateCheckTimer, m_DHTExpirationCheckTimer; uint16_t m_Port; NodeID m_NodeID; std::unique_ptr m_RoutingTable;