diff --git a/libi2pd/Identity.cpp b/libi2pd/Identity.cpp index 4d7fd257..a82059a7 100644 --- a/libi2pd/Identity.cpp +++ b/libi2pd/Identity.cpp @@ -7,6 +7,7 @@ */ #include "Crypto.h" +#include "Siphash.h" #include "I2PEndian.h" #include "Log.h" #include "Timestamp.h" @@ -889,5 +890,52 @@ namespace data return m; } + + PeerOrdering::PeerOrdering () + { + RAND_bytes (m_PeerOrderingKey, 16); + } + + int PeerOrdering::CalculatePeerOrderingGroup (const IdentHash& routerIdent) + { + uint8_t hash[16]; +#if OPENSSL_SIPHASH + EVP_PKEY * sipKey = EVP_PKEY_new_raw_private_key (EVP_PKEY_SIPHASH, nullptr, m_PeerOrderingKey, 16); + EVP_MD_CTX * ctx = EVP_MD_CTX_create (); + EVP_DigestSignInit (ctx, nullptr, nullptr, nullptr, sipKey); + size_t l = 16; + EVP_DigestSign (ctx, hash, &l, routerIdent, 32); + EVP_MD_CTX_destroy (ctx); + EVP_PKEY_free (sipKey); +#else + i2p::crypto::Siphash<16> (hash, routerIdent, 32, m_PeerOrderingKey); +#endif + return hash[0] & 0x03; + } + + int PeerOrdering::GetPeerOrderingGroup (const IdentHash& routerIdent) + { + auto ts = i2p::util::GetSecondsSinceEpoch (); + auto it = m_OrderingGroups.find (routerIdent); + if (it != m_OrderingGroups.end ()) + { + it->second.second = ts; + return it->second.first; + } + int group = CalculatePeerOrderingGroup (routerIdent); + m_OrderingGroups.emplace (routerIdent, std::pair{group, ts}); + return group; + } + + void PeerOrdering::CleanUp (uint64_t ts) + { + for (auto it = m_OrderingGroups.begin (); it != m_OrderingGroups.end ();) + { + if (ts > it->second.second + PEER_ORDERING_INACTIVITY_TIMEOUT) + it = m_OrderingGroups.erase (it); + else + it++; + } + } } } diff --git a/libi2pd/Identity.h b/libi2pd/Identity.h index a8cb1245..87c493d7 100644 --- a/libi2pd/Identity.h +++ b/libi2pd/Identity.h @@ -15,8 +15,10 @@ #include #include #include +#include #include "Base.h" #include "Signature.h" +#include "Tag.h" namespace i2p { @@ -225,6 +227,26 @@ namespace data IdentHash CreateRoutingKey (const IdentHash& ident, bool nextDay = false); XORMetric operator^(const IdentHash& key1, const IdentHash& key2); + // peer ordering + constexpr uint64_t PEER_ORDERING_INACTIVITY_TIMEOUT = 400; // in seconds + class PeerOrdering + { + public: + + PeerOrdering (); + int GetPeerOrderingGroup (const IdentHash& routerIdent); + void CleanUp (uint64_t ts); + + private: + + int CalculatePeerOrderingGroup (const IdentHash& routerIdent); + + private: + + Tag<16> m_PeerOrderingKey; + std::unordered_map > m_OrderingGroups; // router ident hash -> (group, last request time) + }; + // destination for delivery instructions class RoutingDestination { diff --git a/libi2pd/NetDb.cpp b/libi2pd/NetDb.cpp index da1e2071..55639530 100644 --- a/libi2pd/NetDb.cpp +++ b/libi2pd/NetDb.cpp @@ -16,7 +16,6 @@ #include "I2PEndian.h" #include "Base.h" #include "Crypto.h" -#include "Siphash.h" #include "Log.h" #include "Timestamp.h" #include "I2NPProtocol.h" @@ -1407,22 +1406,5 @@ namespace data if (r->GetBuffer ()) return true; return r->LoadBuffer (m_Storage.Path (r->GetIdentHashBase64 ())); } - - int CalculatePeerOrderingGroup (i2p::data::Tag<16> key, const i2p::data::IdentHash& routerIdent) - { - uint8_t hash[16]; -#if OPENSSL_SIPHASH - EVP_PKEY * sipKey = EVP_PKEY_new_raw_private_key (EVP_PKEY_SIPHASH, nullptr, key, 16); - EVP_MD_CTX * ctx = EVP_MD_CTX_create (); - EVP_DigestSignInit (ctx, nullptr, nullptr, nullptr, sipKey); - size_t l = 16; - EVP_DigestSign (ctx, hash, &l, routerIdent, 32); - EVP_MD_CTX_destroy (ctx); - EVP_PKEY_free (sipKey); -#else - i2p::crypto::Siphash<16> (hash, routerIdent, 32, key); -#endif - return hash[0] & 0x03; - } } } diff --git a/libi2pd/NetDb.hpp b/libi2pd/NetDb.hpp index 6130a9ad..c60bcc24 100644 --- a/libi2pd/NetDb.hpp +++ b/libi2pd/NetDb.hpp @@ -205,8 +205,6 @@ namespace data }; extern NetDb netdb; - - int CalculatePeerOrderingGroup (i2p::data::Tag<16> key, const i2p::data::IdentHash& routerIdent); } } diff --git a/libi2pd/Transports.cpp b/libi2pd/Transports.cpp index dffca287..57c47bc2 100644 --- a/libi2pd/Transports.cpp +++ b/libi2pd/Transports.cpp @@ -1183,7 +1183,7 @@ namespace transport } template - std::shared_ptr Transports::GetRandomPeer (Filter filter, i2p::data::Tag<16> * peerOrderingKey) const + std::shared_ptr Transports::GetRandomPeer (Filter filter, i2p::data::PeerOrdering * peerOrdering) const { std::vector>> peers; { @@ -1205,7 +1205,7 @@ namespace transport // try random peer if (ts > peer->lastSelectionTime + PEER_SELECTION_MIN_INTERVAL) { - int peerOrderingGroup = peerOrderingKey ? i2p::data::CalculatePeerOrderingGroup (*peerOrderingKey, ident) : 0; + int peerOrderingGroup = peerOrdering ? peerOrdering->GetPeerOrderingGroup (ident) : 0; if (!peerOrderingGroup && filter (peer)) { foundIdent = ident; @@ -1239,7 +1239,7 @@ namespace transport auto& [ident, peer] = peers[i]; if (ts > peer->lastSelectionTime + PEER_SELECTION_MIN_INTERVAL) { - int peerOrderingGroup = peerOrderingKey ? i2p::data::CalculatePeerOrderingGroup (*peerOrderingKey, ident) : 0; + int peerOrderingGroup = peerOrdering ? peerOrdering->GetPeerOrderingGroup (ident) : 0; if (!peerOrderingGroup && filter (peer)) { foundIdent = ident; @@ -1258,7 +1258,7 @@ namespace transport auto& [ident, peer] = peers[i]; if (ts > peer->lastSelectionTime + PEER_SELECTION_MIN_INTERVAL) { - int peerOrderingGroup = peerOrderingKey ? i2p::data::CalculatePeerOrderingGroup (*peerOrderingKey, ident) : 0; + int peerOrderingGroup = peerOrdering ? peerOrdering->GetPeerOrderingGroup (ident) : 0; if (!peerOrderingGroup && filter (peer)) { foundIdent = ident; @@ -1277,7 +1277,7 @@ namespace transport auto& [ident, peer] = peers[i]; if (ts > peer->lastSelectionTime + PEER_SELECTION_MIN_INTERVAL) { - int peerOrderingGroup = peerOrderingKey ? i2p::data::CalculatePeerOrderingGroup (*peerOrderingKey, ident) : 0; + int peerOrderingGroup = peerOrdering ? peerOrdering->GetPeerOrderingGroup (ident) : 0; if (!peerOrderingGroup && filter (peer)) { foundIdent = ident; @@ -1293,7 +1293,7 @@ namespace transport return found ? i2p::data::netdb.FindRouter (foundIdent) : nullptr; } - std::shared_ptr Transports::GetRandomPeer (bool isHighBandwidth, i2p::data::Tag<16> * peerOrderingKey) const + std::shared_ptr Transports::GetRandomPeer (bool isHighBandwidth, i2p::data::PeerOrdering * peerOrdering) const { return GetRandomPeer ( [isHighBandwidth, this](std::shared_ptr peer)->bool @@ -1318,7 +1318,7 @@ namespace transport } return true; }, - i2p::context.IsLimitedConnectivity () ? nullptr : peerOrderingKey); + i2p::context.IsLimitedConnectivity () ? nullptr : peerOrdering); } void Transports::RestrictRoutesToFamilies(const std::vector& families) diff --git a/libi2pd/Transports.h b/libi2pd/Transports.h index 003079c4..5329d0f4 100644 --- a/libi2pd/Transports.h +++ b/libi2pd/Transports.h @@ -173,7 +173,7 @@ namespace transport uint32_t GetTransitBandwidth15s () const { return m_TransitBandwidth15s; }; int GetCongestionLevel (bool longTerm) const; size_t GetNumPeers () const { return m_Peers.size (); }; - std::shared_ptr GetRandomPeer (bool isHighBandwidth, i2p::data::Tag<16> * peerOrderingKey = nullptr) const; + std::shared_ptr GetRandomPeer (bool isHighBandwidth, i2p::data::PeerOrdering * peerOrdering = nullptr) const; /** get a trusted first hop for restricted routes */ std::shared_ptr GetRestrictedPeer(); @@ -215,7 +215,7 @@ namespace transport void DetectExternalIP (); template - std::shared_ptr GetRandomPeer (Filter filter, i2p::data::Tag<16> * peerOrderingKey) const; + std::shared_ptr GetRandomPeer (Filter filter, i2p::data::PeerOrdering * peerOrdering) const; boost::asio::ip::address GetNetworkAddress (std::shared_ptr session) const; boost::asio::ip::address GetNetworkAddress (const boost::asio::ip::address& addr) const; diff --git a/libi2pd/TunnelPool.cpp b/libi2pd/TunnelPool.cpp index ddcd8782..67e10729 100644 --- a/libi2pd/TunnelPool.cpp +++ b/libi2pd/TunnelPool.cpp @@ -61,7 +61,6 @@ namespace tunnel if (m_OutboundVariance > 0 && m_NumOutboundHops + m_OutboundVariance > STANDARD_NUM_RECORDS) m_OutboundVariance = (m_NumOutboundHops < STANDARD_NUM_RECORDS) ? STANDARD_NUM_RECORDS - m_NumOutboundHops : 0; m_NextManageTime = i2p::util::GetSecondsSinceEpoch () + m_Rng () % TUNNEL_POOL_MANAGE_INTERVAL; - RAND_bytes (m_PeerOrderingKey, 16); } TunnelPool::~TunnelPool () @@ -485,6 +484,7 @@ namespace tunnel { CreateTunnels (ts); TestTunnels (ts); + m_PeerOrdering.CleanUp (ts); } m_NextManageTime = ts + TUNNEL_POOL_MANAGE_INTERVAL + (tunnels.GetRng ()() % TUNNEL_POOL_MANAGE_INTERVAL)/2; } @@ -613,7 +613,7 @@ namespace tunnel (i2p::context.IsLimitedConnectivity () && i2p::transport::transports.GetNumPeers () > 0)))) { auto r = i2p::transport::transports.GetRandomPeer (m_IsHighBandwidth && !i2p::context.IsLimitedConnectivity (), - &m_PeerOrderingKey); + &m_PeerOrdering); if (r && r->IsECIES () && (!r->HasProfile () || !r->GetProfile ()->IsBad ()) && (numHops > 1 || (r->IsV4 () && (!inbound || r->IsPublished (true))))) // first inbound must be published ipv4 { @@ -629,7 +629,7 @@ namespace tunnel if (!hop && !i) // if no suitable peer found for first hop, try already connected { LogPrint (eLogInfo, "Tunnels: Can't select first hop for a tunnel. Trying already connected"); - hop = i2p::transport::transports.GetRandomPeer (false, &m_PeerOrderingKey); + hop = i2p::transport::transports.GetRandomPeer (false, &m_PeerOrdering); if (hop && !hop->IsECIES ()) hop = nullptr; } if (!hop) diff --git a/libi2pd/TunnelPool.h b/libi2pd/TunnelPool.h index 03f47d48..029c2fa5 100644 --- a/libi2pd/TunnelPool.h +++ b/libi2pd/TunnelPool.h @@ -153,7 +153,7 @@ namespace tunnel std::mutex m_CustomPeerSelectorMutex; ITunnelPeerSelector * m_CustomPeerSelector; std::mt19937 m_Rng; // for tunnel selection - i2p::data::Tag<16> m_PeerOrderingKey; + i2p::data::PeerOrdering m_PeerOrdering; int m_MinLatency = 0; // if > 0 this tunnel pool will try building tunnels with minimum latency by ms int m_MaxLatency = 0; // if > 0 this tunnel pool will try building tunnels with maximum latency by ms