diff --git a/libi2pd/NetDb.cpp b/libi2pd/NetDb.cpp index c6c68237..da1e2071 100644 --- a/libi2pd/NetDb.cpp +++ b/libi2pd/NetDb.cpp @@ -16,6 +16,7 @@ #include "I2PEndian.h" #include "Base.h" #include "Crypto.h" +#include "Siphash.h" #include "Log.h" #include "Timestamp.h" #include "I2NPProtocol.h" @@ -26,8 +27,8 @@ #include "Garlic.h" #include "ECIESX25519AEADRatchetSession.h" #include "Config.h" -#include "NetDb.hpp" #include "util.h" +#include "NetDb.hpp" using namespace i2p::transport; @@ -1406,5 +1407,22 @@ 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 c60bcc24..6130a9ad 100644 --- a/libi2pd/NetDb.hpp +++ b/libi2pd/NetDb.hpp @@ -205,6 +205,8 @@ 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 b15cf40b..dffca287 100644 --- a/libi2pd/Transports.cpp +++ b/libi2pd/Transports.cpp @@ -1183,7 +1183,7 @@ namespace transport } template - std::shared_ptr Transports::GetRandomPeer (Filter filter) const + std::shared_ptr Transports::GetRandomPeer (Filter filter, i2p::data::Tag<16> * peerOrderingKey) const { std::vector>> peers; { @@ -1203,13 +1203,17 @@ namespace transport inds[0] %= count; auto& [ident, peer] = peers[inds[0]]; // try random peer - if (filter (peer)) + if (ts > peer->lastSelectionTime + PEER_SELECTION_MIN_INTERVAL) { - foundIdent = ident; - peer->lastSelectionTime = ts; - found = true; + int peerOrderingGroup = peerOrderingKey ? i2p::data::CalculatePeerOrderingGroup (*peerOrderingKey, ident) : 0; + if (!peerOrderingGroup && filter (peer)) + { + foundIdent = ident; + peer->lastSelectionTime = ts; + found = true; + } } - else + if (!found) { // try some peers around if (inds[0]) @@ -1233,13 +1237,16 @@ namespace transport for (auto i = inds[1]; i < inds[2]; i++) { auto& [ident, peer] = peers[i]; - if (ts > peer->lastSelectionTime + PEER_SELECTION_MIN_INTERVAL && - filter (peer)) + if (ts > peer->lastSelectionTime + PEER_SELECTION_MIN_INTERVAL) { - foundIdent = ident; - peer->lastSelectionTime = ts; - found = true; - break; + int peerOrderingGroup = peerOrderingKey ? i2p::data::CalculatePeerOrderingGroup (*peerOrderingKey, ident) : 0; + if (!peerOrderingGroup && filter (peer)) + { + foundIdent = ident; + peer->lastSelectionTime = ts; + found = true; + break; + } } } @@ -1249,13 +1256,16 @@ namespace transport for (auto i = 0; i < inds[1]; i++) { auto& [ident, peer] = peers[i]; - if (ts > peer->lastSelectionTime + PEER_SELECTION_MIN_INTERVAL && - filter (peer)) + if (ts > peer->lastSelectionTime + PEER_SELECTION_MIN_INTERVAL) { - foundIdent = ident; - peer->lastSelectionTime = ts; - found = true; - break; + int peerOrderingGroup = peerOrderingKey ? i2p::data::CalculatePeerOrderingGroup (*peerOrderingKey, ident) : 0; + if (!peerOrderingGroup && filter (peer)) + { + foundIdent = ident; + peer->lastSelectionTime = ts; + found = true; + break; + } } } @@ -1265,13 +1275,16 @@ namespace transport for (auto i = inds[2]; i < peers.size (); i++) { auto& [ident, peer] = peers[i]; - if (ts > peer->lastSelectionTime + PEER_SELECTION_MIN_INTERVAL && - filter (peer)) + if (ts > peer->lastSelectionTime + PEER_SELECTION_MIN_INTERVAL) { - foundIdent = ident; - peer->lastSelectionTime = ts; - found = true; - break; + int peerOrderingGroup = peerOrderingKey ? i2p::data::CalculatePeerOrderingGroup (*peerOrderingKey, ident) : 0; + if (!peerOrderingGroup && filter (peer)) + { + foundIdent = ident; + peer->lastSelectionTime = ts; + found = true; + break; + } } } } @@ -1280,7 +1293,7 @@ namespace transport return found ? i2p::data::netdb.FindRouter (foundIdent) : nullptr; } - std::shared_ptr Transports::GetRandomPeer (bool isHighBandwidth) const + std::shared_ptr Transports::GetRandomPeer (bool isHighBandwidth, i2p::data::Tag<16> * peerOrderingKey) const { return GetRandomPeer ( [isHighBandwidth, this](std::shared_ptr peer)->bool @@ -1304,7 +1317,8 @@ namespace transport } } return true; - }); + }, + i2p::context.IsLimitedConnectivity () ? nullptr : peerOrderingKey); } void Transports::RestrictRoutesToFamilies(const std::vector& families) diff --git a/libi2pd/Transports.h b/libi2pd/Transports.h index 3643f105..003079c4 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) const; + std::shared_ptr GetRandomPeer (bool isHighBandwidth, i2p::data::Tag<16> * peerOrderingKey = 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) const; + std::shared_ptr GetRandomPeer (Filter filter, i2p::data::Tag<16> * peerOrderingKey) 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 07c682bc..ddcd8782 100644 --- a/libi2pd/TunnelPool.cpp +++ b/libi2pd/TunnelPool.cpp @@ -61,6 +61,7 @@ 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 () @@ -611,7 +612,8 @@ namespace tunnel (inbound && (i2p::transport::transports.GetNumPeers () > 25 || (i2p::context.IsLimitedConnectivity () && i2p::transport::transports.GetNumPeers () > 0)))) { - auto r = i2p::transport::transports.GetRandomPeer (m_IsHighBandwidth && !i2p::context.IsLimitedConnectivity ()); + auto r = i2p::transport::transports.GetRandomPeer (m_IsHighBandwidth && !i2p::context.IsLimitedConnectivity (), + &m_PeerOrderingKey); if (r && r->IsECIES () && (!r->HasProfile () || !r->GetProfile ()->IsBad ()) && (numHops > 1 || (r->IsV4 () && (!inbound || r->IsPublished (true))))) // first inbound must be published ipv4 { @@ -627,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); + hop = i2p::transport::transports.GetRandomPeer (false, &m_PeerOrderingKey); if (hop && !hop->IsECIES ()) hop = nullptr; } if (!hop) diff --git a/libi2pd/TunnelPool.h b/libi2pd/TunnelPool.h index 91a5f612..03f47d48 100644 --- a/libi2pd/TunnelPool.h +++ b/libi2pd/TunnelPool.h @@ -153,6 +153,7 @@ namespace tunnel std::mutex m_CustomPeerSelectorMutex; ITunnelPeerSelector * m_CustomPeerSelector; std::mt19937 m_Rng; // for tunnel selection + i2p::data::Tag<16> m_PeerOrderingKey; 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