From d6d440ba8a14d44d9191d5e3d91ed8d563162fda Mon Sep 17 00:00:00 2001 From: Vort Date: Sun, 25 Feb 2024 22:57:57 +0200 Subject: [PATCH 01/32] allow 0ms latency for tunnel --- libi2pd/Tunnel.cpp | 6 +++--- libi2pd/Tunnel.h | 13 +++++++------ libi2pd/TunnelPool.cpp | 8 +++++--- libi2pd/TunnelPool.h | 6 +++--- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/libi2pd/Tunnel.cpp b/libi2pd/Tunnel.cpp index 246f68e4..57feff45 100644 --- a/libi2pd/Tunnel.cpp +++ b/libi2pd/Tunnel.cpp @@ -33,7 +33,7 @@ namespace tunnel TunnelBase (config->GetTunnelID (), config->GetNextTunnelID (), config->GetNextIdentHash ()), m_Config (config), m_IsShortBuildMessage (false), m_Pool (nullptr), m_State (eTunnelStatePending), m_FarEndTransports (i2p::data::RouterInfo::eAllTransports), - m_IsRecreated (false), m_Latency (0) + m_IsRecreated (false), m_Latency (UNKNOWN_LATENCY) { } @@ -198,10 +198,10 @@ namespace tunnel return established; } - bool Tunnel::LatencyFitsRange(uint64_t lower, uint64_t upper) const + bool Tunnel::LatencyFitsRange(int lowerbound, int upperbound) const { auto latency = GetMeanLatency(); - return latency >= lower && latency <= upper; + return latency >= lowerbound && latency <= upperbound; } void Tunnel::EncryptTunnelMsg (std::shared_ptr in, std::shared_ptr out) diff --git a/libi2pd/Tunnel.h b/libi2pd/Tunnel.h index 4d9ea830..6311b4f2 100644 --- a/libi2pd/Tunnel.h +++ b/libi2pd/Tunnel.h @@ -39,6 +39,7 @@ namespace tunnel const int TUNNEL_CREATION_TIMEOUT = 30; // 30 seconds const int STANDARD_NUM_RECORDS = 4; // in VariableTunnelBuild message const int MAX_NUM_RECORDS = 8; + const int UNKNOWN_LATENCY = -1; const int HIGH_LATENCY_PER_HOP = 250; // in milliseconds const int MAX_TUNNEL_MSGS_BATCH_SIZE = 100; // handle messages without interrupt const uint16_t DEFAULT_MAX_NUM_TRANSIT_TUNNELS = 5000; @@ -108,14 +109,14 @@ namespace tunnel void EncryptTunnelMsg (std::shared_ptr in, std::shared_ptr out) override; /** @brief add latency sample */ - void AddLatencySample(const uint64_t ms) { m_Latency = (m_Latency + ms) >> 1; } + void AddLatencySample(const int ms) { m_Latency = LatencyIsKnown() ? (m_Latency + ms) >> 1 : ms; } /** @brief get this tunnel's estimated latency */ - uint64_t GetMeanLatency() const { return m_Latency; } + int GetMeanLatency() const { return m_Latency; } /** @brief return true if this tunnel's latency fits in range [lowerbound, upperbound] */ - bool LatencyFitsRange(uint64_t lowerbound, uint64_t upperbound) const; + bool LatencyFitsRange(int lowerbound, int upperbound) const; - bool LatencyIsKnown() const { return m_Latency > 0; } - bool IsSlow () const { return LatencyIsKnown() && (int)m_Latency > HIGH_LATENCY_PER_HOP*GetNumHops (); } + bool LatencyIsKnown() const { return m_Latency != UNKNOWN_LATENCY; } + bool IsSlow () const { return LatencyIsKnown() && m_Latency > HIGH_LATENCY_PER_HOP*GetNumHops (); } /** visit all hops we currently store */ void VisitTunnelHops(TunnelHopVisitor v); @@ -129,7 +130,7 @@ namespace tunnel TunnelState m_State; i2p::data::RouterInfo::CompatibleTransports m_FarEndTransports; bool m_IsRecreated; // if tunnel is replaced by new, or new tunnel requested to replace - uint64_t m_Latency; // in milliseconds + int m_Latency; // in milliseconds }; class OutboundTunnel: public Tunnel diff --git a/libi2pd/TunnelPool.cpp b/libi2pd/TunnelPool.cpp index 2a5e9dd1..f576b62d 100644 --- a/libi2pd/TunnelPool.cpp +++ b/libi2pd/TunnelPool.cpp @@ -477,8 +477,10 @@ namespace tunnel } if (found) { - uint64_t dlt = i2p::util::GetMillisecondsSinceEpoch () - timestamp; + int dlt = (int)((int64_t)i2p::util::GetMillisecondsSinceEpoch () - (int64_t)timestamp); LogPrint (eLogDebug, "Tunnels: Test of ", msgID, " successful. ", dlt, " milliseconds"); + if (dlt < 0) + dlt = 0; int numHops = 0; if (test.first) numHops += test.first->GetNumHops (); if (test.second) numHops += test.second->GetNumHops (); @@ -488,7 +490,7 @@ namespace tunnel if (test.first->GetState () != eTunnelStateExpiring) test.first->SetState (eTunnelStateEstablished); // update latency - uint64_t latency = 0; + int latency = 0; if (numHops) latency = dlt*test.first->GetNumHops ()/numHops; if (!latency) latency = dlt/2; test.first->AddLatencySample(latency); @@ -498,7 +500,7 @@ namespace tunnel if (test.second->GetState () != eTunnelStateExpiring) test.second->SetState (eTunnelStateEstablished); // update latency - uint64_t latency = 0; + int latency = 0; if (numHops) latency = dlt*test.second->GetNumHops ()/numHops; if (!latency) latency = dlt/2; test.second->AddLatencySample(latency); diff --git a/libi2pd/TunnelPool.h b/libi2pd/TunnelPool.h index 3e845c0e..da48378c 100644 --- a/libi2pd/TunnelPool.h +++ b/libi2pd/TunnelPool.h @@ -105,7 +105,7 @@ namespace tunnel bool HasCustomPeerSelector(); /** @brief make this tunnel pool yield tunnels that fit latency range [min, max] */ - void RequireLatency(uint64_t min, uint64_t max) { m_MinLatency = min; m_MaxLatency = max; } + void RequireLatency(int min, int max) { m_MinLatency = min; m_MaxLatency = max; } /** @brief return true if this tunnel pool has a latency requirement */ bool HasLatencyRequirement() const { return m_MinLatency > 0 && m_MaxLatency > 0; } @@ -150,8 +150,8 @@ namespace tunnel std::mutex m_CustomPeerSelectorMutex; ITunnelPeerSelector * m_CustomPeerSelector; - uint64_t m_MinLatency = 0; // if > 0 this tunnel pool will try building tunnels with minimum latency by ms - uint64_t m_MaxLatency = 0; // if > 0 this tunnel pool will try building tunnels with maximum latency by ms + 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 std::random_device m_Rd; std::mt19937 m_Rng; From 3d037325551832e5c3c1f037c528722dd104f702 Mon Sep 17 00:00:00 2001 From: Vort Date: Tue, 27 Feb 2024 10:15:15 +0200 Subject: [PATCH 02/32] add tunnel test message --- libi2pd/Destination.cpp | 8 +++++--- libi2pd/I2NPProtocol.cpp | 15 +++++++++++++++ libi2pd/I2NPProtocol.h | 9 ++++++++- libi2pd/RouterContext.cpp | 6 +++--- libi2pd/Timestamp.cpp | 6 ++++++ libi2pd/Timestamp.h | 2 ++ libi2pd/Tunnel.h | 6 +++--- libi2pd/TunnelPool.cpp | 35 +++++++++++++++++++++-------------- libi2pd/TunnelPool.h | 3 ++- 9 files changed, 65 insertions(+), 25 deletions(-) diff --git a/libi2pd/Destination.cpp b/libi2pd/Destination.cpp index 64e5d085..35bad171 100644 --- a/libi2pd/Destination.cpp +++ b/libi2pd/Destination.cpp @@ -367,9 +367,11 @@ namespace client HandleDataMessage (payload, len); break; case eI2NPDeliveryStatus: - // try tunnel test first - if (!m_Pool || !m_Pool->ProcessDeliveryStatus (bufbe32toh (payload + DELIVERY_STATUS_MSGID_OFFSET), bufbe64toh (payload + DELIVERY_STATUS_TIMESTAMP_OFFSET))) - HandleDeliveryStatusMessage (bufbe32toh (payload + DELIVERY_STATUS_MSGID_OFFSET)); + HandleDeliveryStatusMessage (bufbe32toh (payload + DELIVERY_STATUS_MSGID_OFFSET)); + break; + case eI2NPTunnelTest: + if (m_Pool) + m_Pool->ProcessTunnelTest (bufbe32toh (payload + TUNNEL_TEST_MSGID_OFFSET), bufbe64toh (payload + TUNNEL_TEST_TIMESTAMP_OFFSET)); break; case eI2NPDatabaseStore: HandleDatabaseStoreMessage (payload, len); diff --git a/libi2pd/I2NPProtocol.cpp b/libi2pd/I2NPProtocol.cpp index da6da638..e687f39c 100644 --- a/libi2pd/I2NPProtocol.cpp +++ b/libi2pd/I2NPProtocol.cpp @@ -115,6 +115,17 @@ namespace i2p return newMsg; } + std::shared_ptr CreateTunnelTestMsg (uint32_t msgID) + { + auto m = NewI2NPShortMessage (); + uint8_t * buf = m->GetPayload (); + htobe32buf (buf + TUNNEL_TEST_MSGID_OFFSET, msgID); + htobe64buf (buf + TUNNEL_TEST_TIMESTAMP_OFFSET, i2p::util::GetSteadyMicroseconds ()); + m->len += TUNNEL_TEST_SIZE; + m->FillI2NPMessageHeader (eI2NPTunnelTest); + return m; + } + std::shared_ptr CreateDeliveryStatusMsg (uint32_t msgID) { auto m = NewI2NPShortMessage (); @@ -870,6 +881,10 @@ namespace i2p i2p::context.ProcessDeliveryStatusMessage (msg); break; } + case eI2NPTunnelTest: + if (msg->from && msg->from->GetTunnelPool ()) + msg->from->GetTunnelPool ()->ProcessTunnelTest (msg); + break; case eI2NPVariableTunnelBuild: case eI2NPTunnelBuild: case eI2NPShortTunnelBuild: diff --git a/libi2pd/I2NPProtocol.h b/libi2pd/I2NPProtocol.h index 36facbe3..b1e2d170 100644 --- a/libi2pd/I2NPProtocol.h +++ b/libi2pd/I2NPProtocol.h @@ -48,6 +48,11 @@ namespace i2p const size_t DELIVERY_STATUS_TIMESTAMP_OFFSET = DELIVERY_STATUS_MSGID_OFFSET + 4; const size_t DELIVERY_STATUS_SIZE = DELIVERY_STATUS_TIMESTAMP_OFFSET + 8; + // TunnelTest + const size_t TUNNEL_TEST_MSGID_OFFSET = 0; + const size_t TUNNEL_TEST_TIMESTAMP_OFFSET = TUNNEL_TEST_MSGID_OFFSET + 4; + const size_t TUNNEL_TEST_SIZE = TUNNEL_TEST_TIMESTAMP_OFFSET + 8; + // DatabaseStore const size_t DATABASE_STORE_KEY_OFFSET = 0; const size_t DATABASE_STORE_TYPE_OFFSET = DATABASE_STORE_KEY_OFFSET + 32; @@ -116,7 +121,8 @@ namespace i2p eI2NPVariableTunnelBuild = 23, eI2NPVariableTunnelBuildReply = 24, eI2NPShortTunnelBuild = 25, - eI2NPShortTunnelBuildReply = 26 + eI2NPShortTunnelBuildReply = 26, + eI2NPTunnelTest = 231 }; const uint8_t TUNNEL_BUILD_RECORD_GATEWAY_FLAG = 0x80; @@ -279,6 +285,7 @@ namespace tunnel std::shared_ptr CreateI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr from = nullptr); std::shared_ptr CopyI2NPMessage (std::shared_ptr msg); + std::shared_ptr CreateTunnelTestMsg (uint32_t msgID); std::shared_ptr CreateDeliveryStatusMsg (uint32_t msgID); std::shared_ptr CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from, uint32_t replyTunnelID, bool exploratory = false, std::set * excludedPeers = nullptr); diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp index 2c1dc979..bf683b8e 100644 --- a/libi2pd/RouterContext.cpp +++ b/libi2pd/RouterContext.cpp @@ -1152,13 +1152,13 @@ namespace i2p bool RouterContext::HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID) { - if (typeID == eI2NPDeliveryStatus) + if (typeID == eI2NPTunnelTest) { // try tunnel test auto pool = GetTunnelPool (); - if (pool && pool->ProcessDeliveryStatus (bufbe32toh (payload + DELIVERY_STATUS_MSGID_OFFSET), bufbe64toh (payload + DELIVERY_STATUS_TIMESTAMP_OFFSET))) + if (pool && pool->ProcessTunnelTest (bufbe32toh (payload + TUNNEL_TEST_MSGID_OFFSET), bufbe64toh (payload + TUNNEL_TEST_TIMESTAMP_OFFSET))) return true; - } + } auto msg = CreateI2NPMessage (typeID, payload, len, msgID); if (!msg) return false; i2p::HandleI2NPMessage (msg); diff --git a/libi2pd/Timestamp.cpp b/libi2pd/Timestamp.cpp index 99507398..27e3a619 100644 --- a/libi2pd/Timestamp.cpp +++ b/libi2pd/Timestamp.cpp @@ -232,6 +232,12 @@ namespace util return GetLocalHoursSinceEpoch () + g_TimeOffset/3600; } + uint64_t GetSteadyMicroseconds() + { + return std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()).count(); + } + void GetCurrentDate (char * date) { GetDateString (GetSecondsSinceEpoch (), date); diff --git a/libi2pd/Timestamp.h b/libi2pd/Timestamp.h index ff777257..6a925079 100644 --- a/libi2pd/Timestamp.h +++ b/libi2pd/Timestamp.h @@ -24,6 +24,8 @@ namespace util uint32_t GetMinutesSinceEpoch (); uint32_t GetHoursSinceEpoch (); + uint64_t GetSteadyMicroseconds(); + void GetCurrentDate (char * date); // returns date as YYYYMMDD string, 9 bytes void GetDateString (uint64_t timestamp, char * date); // timestamp is seconds since epoch, returns date as YYYYMMDD string, 9 bytes void AdjustTimeOffset (int64_t offset); // in seconds from current diff --git a/libi2pd/Tunnel.h b/libi2pd/Tunnel.h index 6311b4f2..f9369672 100644 --- a/libi2pd/Tunnel.h +++ b/libi2pd/Tunnel.h @@ -109,9 +109,9 @@ namespace tunnel void EncryptTunnelMsg (std::shared_ptr in, std::shared_ptr out) override; /** @brief add latency sample */ - void AddLatencySample(const int ms) { m_Latency = LatencyIsKnown() ? (m_Latency + ms) >> 1 : ms; } + void AddLatencySample(const int us) { m_Latency = LatencyIsKnown() ? (m_Latency + us) >> 1 : us; } /** @brief get this tunnel's estimated latency */ - int GetMeanLatency() const { return m_Latency; } + int GetMeanLatency() const { return (m_Latency + 500) / 1000; } /** @brief return true if this tunnel's latency fits in range [lowerbound, upperbound] */ bool LatencyFitsRange(int lowerbound, int upperbound) const; @@ -130,7 +130,7 @@ namespace tunnel TunnelState m_State; i2p::data::RouterInfo::CompatibleTransports m_FarEndTransports; bool m_IsRecreated; // if tunnel is replaced by new, or new tunnel requested to replace - int m_Latency; // in milliseconds + int m_Latency; // in microseconds }; class OutboundTunnel: public Tunnel diff --git a/libi2pd/TunnelPool.cpp b/libi2pd/TunnelPool.cpp index f576b62d..4f93ce52 100644 --- a/libi2pd/TunnelPool.cpp +++ b/libi2pd/TunnelPool.cpp @@ -399,7 +399,7 @@ namespace tunnel std::unique_lock l(m_TestsMutex); m_Tests[msgID] = it; } - auto msg = CreateDeliveryStatusMsg (msgID); + auto msg = CreateTunnelTestMsg (msgID); auto outbound = it.first; auto s = shared_from_this (); msg->onDrop = [msgID, outbound, s]() @@ -452,16 +452,23 @@ namespace tunnel buf += 4; uint64_t timestamp = bufbe64toh (buf); - if (!ProcessDeliveryStatus (msgID, timestamp)) - { - if (m_LocalDestination) - m_LocalDestination->ProcessDeliveryStatusMessage (msg); - else - LogPrint (eLogWarning, "Tunnels: Local destination doesn't exist, dropped"); - } + if (m_LocalDestination) + m_LocalDestination->ProcessDeliveryStatusMessage (msg); + else + LogPrint (eLogWarning, "Tunnels: Local destination doesn't exist, dropped"); + } + + void TunnelPool::ProcessTunnelTest (std::shared_ptr msg) + { + const uint8_t * buf = msg->GetPayload (); + uint32_t msgID = bufbe32toh (buf); + buf += 4; + uint64_t timestamp = bufbe64toh (buf); + + ProcessTunnelTest (msgID, timestamp); } - bool TunnelPool::ProcessDeliveryStatus (uint32_t msgID, uint64_t timestamp) + bool TunnelPool::ProcessTunnelTest (uint32_t msgID, uint64_t timestamp) { decltype(m_Tests)::mapped_type test; bool found = false; @@ -477,9 +484,9 @@ namespace tunnel } if (found) { - int dlt = (int)((int64_t)i2p::util::GetMillisecondsSinceEpoch () - (int64_t)timestamp); - LogPrint (eLogDebug, "Tunnels: Test of ", msgID, " successful. ", dlt, " milliseconds"); - if (dlt < 0) + int dlt = (int)((int64_t)i2p::util::GetSteadyMicroseconds () - (int64_t)timestamp); + LogPrint (eLogDebug, "Tunnels: Test of ", msgID, " successful. ", dlt, " microseconds"); + if (dlt < 0) // should not happen dlt = 0; int numHops = 0; if (test.first) numHops += test.first->GetNumHops (); @@ -493,7 +500,7 @@ namespace tunnel int latency = 0; if (numHops) latency = dlt*test.first->GetNumHops ()/numHops; if (!latency) latency = dlt/2; - test.first->AddLatencySample(latency); + test.first->AddLatencySample (latency); } if (test.second) { @@ -503,7 +510,7 @@ namespace tunnel int latency = 0; if (numHops) latency = dlt*test.second->GetNumHops ()/numHops; if (!latency) latency = dlt/2; - test.second->AddLatencySample(latency); + test.second->AddLatencySample (latency); } } return found; diff --git a/libi2pd/TunnelPool.h b/libi2pd/TunnelPool.h index da48378c..e76453be 100644 --- a/libi2pd/TunnelPool.h +++ b/libi2pd/TunnelPool.h @@ -85,7 +85,8 @@ namespace tunnel void ManageTunnels (uint64_t ts); void ProcessGarlicMessage (std::shared_ptr msg); void ProcessDeliveryStatus (std::shared_ptr msg); - bool ProcessDeliveryStatus (uint32_t msgID, uint64_t timestamp); + void ProcessTunnelTest (std::shared_ptr msg); + bool ProcessTunnelTest (uint32_t msgID, uint64_t timestamp); bool IsExploratory () const; bool IsActive () const { return m_IsActive; }; From 6898d04a1dee34c1cbc9b6e8e0d584024ebb5984 Mon Sep 17 00:00:00 2001 From: orignal Date: Tue, 27 Feb 2024 08:30:31 -0500 Subject: [PATCH 03/32] send tunnel test mesaage only if encrypted --- libi2pd/TunnelPool.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/libi2pd/TunnelPool.cpp b/libi2pd/TunnelPool.cpp index 4f93ce52..6e779387 100644 --- a/libi2pd/TunnelPool.cpp +++ b/libi2pd/TunnelPool.cpp @@ -399,7 +399,7 @@ namespace tunnel std::unique_lock l(m_TestsMutex); m_Tests[msgID] = it; } - auto msg = CreateTunnelTestMsg (msgID); + auto msg = encrypt ? CreateTunnelTestMsg (msgID) : CreateDeliveryStatusMsg (msgID); auto outbound = it.first; auto s = shared_from_this (); msg->onDrop = [msgID, outbound, s]() @@ -452,10 +452,13 @@ namespace tunnel buf += 4; uint64_t timestamp = bufbe64toh (buf); - if (m_LocalDestination) - m_LocalDestination->ProcessDeliveryStatusMessage (msg); - else - LogPrint (eLogWarning, "Tunnels: Local destination doesn't exist, dropped"); + if (!ProcessTunnelTest (msgID, timestamp)) // if non encrypted + { + if (m_LocalDestination) + m_LocalDestination->ProcessDeliveryStatusMessage (msg); + else + LogPrint (eLogWarning, "Tunnels: Local destination doesn't exist, dropped"); + } } void TunnelPool::ProcessTunnelTest (std::shared_ptr msg) @@ -832,7 +835,7 @@ namespace tunnel { std::shared_ptr tun = nullptr; std::unique_lock lock(m_InboundTunnelsMutex); - uint64_t min = 1000000; + int min = 1000000; for (const auto & itr : m_InboundTunnels) { if(!itr->LatencyIsKnown()) continue; auto l = itr->GetMeanLatency(); @@ -848,7 +851,7 @@ namespace tunnel { std::shared_ptr tun = nullptr; std::unique_lock lock(m_OutboundTunnelsMutex); - uint64_t min = 1000000; + int min = 1000000; for (const auto & itr : m_OutboundTunnels) { if(!itr->LatencyIsKnown()) continue; auto l = itr->GetMeanLatency(); From 6656ef3c8dad317eaed4c029fe27a67c3a4984b2 Mon Sep 17 00:00:00 2001 From: orignal Date: Tue, 27 Feb 2024 09:02:26 -0500 Subject: [PATCH 04/32] correct clock for non-encrypted tunnel tests --- libi2pd/TunnelPool.cpp | 13 +++++++------ libi2pd/TunnelPool.h | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/libi2pd/TunnelPool.cpp b/libi2pd/TunnelPool.cpp index 6e779387..ab31a37a 100644 --- a/libi2pd/TunnelPool.cpp +++ b/libi2pd/TunnelPool.cpp @@ -450,9 +450,9 @@ namespace tunnel const uint8_t * buf = msg->GetPayload (); uint32_t msgID = bufbe32toh (buf); buf += 4; - uint64_t timestamp = bufbe64toh (buf); + uint64_t timestamp = bufbe64toh (buf); // milliseconds since epoch - if (!ProcessTunnelTest (msgID, timestamp)) // if non encrypted + if (!ProcessTunnelTest (msgID, timestamp, false)) // if non encrypted test { if (m_LocalDestination) m_LocalDestination->ProcessDeliveryStatusMessage (msg); @@ -471,7 +471,7 @@ namespace tunnel ProcessTunnelTest (msgID, timestamp); } - bool TunnelPool::ProcessTunnelTest (uint32_t msgID, uint64_t timestamp) + bool TunnelPool::ProcessTunnelTest (uint32_t msgID, uint64_t timestamp, bool monotonic) { decltype(m_Tests)::mapped_type test; bool found = false; @@ -487,10 +487,11 @@ namespace tunnel } if (found) { - int dlt = (int)((int64_t)i2p::util::GetSteadyMicroseconds () - (int64_t)timestamp); + int64_t ts = monotonic ? i2p::util::GetSteadyMicroseconds () : i2p::util::GetMillisecondsSinceEpoch (); + int dlt = ts - timestamp; + if (!monotonic) dlt *= 1000; // to microseconds LogPrint (eLogDebug, "Tunnels: Test of ", msgID, " successful. ", dlt, " microseconds"); - if (dlt < 0) // should not happen - dlt = 0; + if (dlt < 0) dlt = 0; // should not happen int numHops = 0; if (test.first) numHops += test.first->GetNumHops (); if (test.second) numHops += test.second->GetNumHops (); diff --git a/libi2pd/TunnelPool.h b/libi2pd/TunnelPool.h index e76453be..f78b8692 100644 --- a/libi2pd/TunnelPool.h +++ b/libi2pd/TunnelPool.h @@ -86,7 +86,7 @@ namespace tunnel void ProcessGarlicMessage (std::shared_ptr msg); void ProcessDeliveryStatus (std::shared_ptr msg); void ProcessTunnelTest (std::shared_ptr msg); - bool ProcessTunnelTest (uint32_t msgID, uint64_t timestamp); + bool ProcessTunnelTest (uint32_t msgID, uint64_t timestamp, bool monotonic = true); bool IsExploratory () const; bool IsActive () const { return m_IsActive; }; From b86c83a06845b3ed1c44e01ca7344882d14911fd Mon Sep 17 00:00:00 2001 From: orignal Date: Tue, 27 Feb 2024 11:47:32 -0500 Subject: [PATCH 05/32] encrypt tunnel tests for ElGamal-only destinations --- libi2pd/TunnelPool.cpp | 40 +++++++++++++++++++++------------------- libi2pd/TunnelPool.h | 2 +- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/libi2pd/TunnelPool.cpp b/libi2pd/TunnelPool.cpp index ab31a37a..e7cec270 100644 --- a/libi2pd/TunnelPool.cpp +++ b/libi2pd/TunnelPool.cpp @@ -366,6 +366,7 @@ namespace tunnel } // new tests + if (!m_LocalDestination) return; std::vector, std::shared_ptr > > newTests; std::vector > outboundTunnels; { @@ -390,7 +391,7 @@ namespace tunnel newTests.push_back(std::make_pair (*it1, *it2)); ++it1; ++it2; } - bool encrypt = m_LocalDestination ? m_LocalDestination->SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) : false; + bool isECIES = m_LocalDestination->SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); for (auto& it: newTests) { uint32_t msgID; @@ -399,7 +400,7 @@ namespace tunnel std::unique_lock l(m_TestsMutex); m_Tests[msgID] = it; } - auto msg = encrypt ? CreateTunnelTestMsg (msgID) : CreateDeliveryStatusMsg (msgID); + auto msg = CreateTunnelTestMsg (msgID); auto outbound = it.first; auto s = shared_from_this (); msg->onDrop = [msgID, outbound, s]() @@ -414,14 +415,22 @@ namespace tunnel std::unique_lock l(s->m_OutboundTunnelsMutex); s->m_OutboundTunnels.erase (outbound); } - }; - if (encrypt) + }; + // encrypt + if (isECIES) { - // encrypt uint8_t key[32]; RAND_bytes (key, 32); uint64_t tag; RAND_bytes ((uint8_t *)&tag, 8); m_LocalDestination->SubmitECIESx25519Key (key, tag); msg = i2p::garlic::WrapECIESX25519Message (msg, key, tag); + } + else + { + uint8_t key[32], tag[32]; + RAND_bytes (key, 32); RAND_bytes (tag, 32); + m_LocalDestination->SubmitSessionKey (key, tag); + i2p::garlic::ElGamalAESSession garlic (key, tag); + msg = garlic.WrapSingleMessage (msg); } outbound->SendTunnelDataMsgTo (it.second->GetNextIdentHash (), it.second->GetNextTunnelID (), msg); } @@ -449,16 +458,11 @@ namespace tunnel { const uint8_t * buf = msg->GetPayload (); uint32_t msgID = bufbe32toh (buf); - buf += 4; - uint64_t timestamp = bufbe64toh (buf); // milliseconds since epoch - - if (!ProcessTunnelTest (msgID, timestamp, false)) // if non encrypted test - { - if (m_LocalDestination) - m_LocalDestination->ProcessDeliveryStatusMessage (msg); - else - LogPrint (eLogWarning, "Tunnels: Local destination doesn't exist, dropped"); - } + + if (m_LocalDestination) + m_LocalDestination->ProcessDeliveryStatusMessage (msg); + else + LogPrint (eLogWarning, "Tunnels: Local destination doesn't exist, dropped"); } void TunnelPool::ProcessTunnelTest (std::shared_ptr msg) @@ -471,7 +475,7 @@ namespace tunnel ProcessTunnelTest (msgID, timestamp); } - bool TunnelPool::ProcessTunnelTest (uint32_t msgID, uint64_t timestamp, bool monotonic) + bool TunnelPool::ProcessTunnelTest (uint32_t msgID, uint64_t timestamp) { decltype(m_Tests)::mapped_type test; bool found = false; @@ -487,9 +491,7 @@ namespace tunnel } if (found) { - int64_t ts = monotonic ? i2p::util::GetSteadyMicroseconds () : i2p::util::GetMillisecondsSinceEpoch (); - int dlt = ts - timestamp; - if (!monotonic) dlt *= 1000; // to microseconds + int dlt = (uint64_t)i2p::util::GetSteadyMicroseconds () - (int64_t)timestamp; LogPrint (eLogDebug, "Tunnels: Test of ", msgID, " successful. ", dlt, " microseconds"); if (dlt < 0) dlt = 0; // should not happen int numHops = 0; diff --git a/libi2pd/TunnelPool.h b/libi2pd/TunnelPool.h index f78b8692..e76453be 100644 --- a/libi2pd/TunnelPool.h +++ b/libi2pd/TunnelPool.h @@ -86,7 +86,7 @@ namespace tunnel void ProcessGarlicMessage (std::shared_ptr msg); void ProcessDeliveryStatus (std::shared_ptr msg); void ProcessTunnelTest (std::shared_ptr msg); - bool ProcessTunnelTest (uint32_t msgID, uint64_t timestamp, bool monotonic = true); + bool ProcessTunnelTest (uint32_t msgID, uint64_t timestamp); bool IsExploratory () const; bool IsActive () const { return m_IsActive; }; From a8af683643673f548b016692464b1f835004f28f Mon Sep 17 00:00:00 2001 From: orignal Date: Tue, 27 Feb 2024 12:33:07 -0500 Subject: [PATCH 06/32] renamed steady to monotonic --- libi2pd/I2NPProtocol.cpp | 2 +- libi2pd/RouterContext.cpp | 4 ++-- libi2pd/RouterContext.h | 3 +-- libi2pd/Timestamp.cpp | 16 ++++++++++++++-- libi2pd/Timestamp.h | 8 +++++--- libi2pd/TunnelPool.cpp | 5 +---- 6 files changed, 24 insertions(+), 14 deletions(-) diff --git a/libi2pd/I2NPProtocol.cpp b/libi2pd/I2NPProtocol.cpp index e687f39c..bea3f93f 100644 --- a/libi2pd/I2NPProtocol.cpp +++ b/libi2pd/I2NPProtocol.cpp @@ -120,7 +120,7 @@ namespace i2p auto m = NewI2NPShortMessage (); uint8_t * buf = m->GetPayload (); htobe32buf (buf + TUNNEL_TEST_MSGID_OFFSET, msgID); - htobe64buf (buf + TUNNEL_TEST_TIMESTAMP_OFFSET, i2p::util::GetSteadyMicroseconds ()); + htobe64buf (buf + TUNNEL_TEST_TIMESTAMP_OFFSET, i2p::util::GetMonotonicMicroseconds ()); m->len += TUNNEL_TEST_SIZE; m->FillI2NPMessageHeader (eI2NPTunnelTest); return m; diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp index bf683b8e..3eb6ffbf 100644 --- a/libi2pd/RouterContext.cpp +++ b/libi2pd/RouterContext.cpp @@ -40,7 +40,7 @@ namespace i2p void RouterContext::Init () { srand (i2p::util::GetMillisecondsSinceEpoch () % 1000); - m_StartupTime = std::chrono::steady_clock::now(); + m_StartupTime = i2p::util::GetMonotonicSeconds (); if (!Load ()) CreateNewRouter (); @@ -1236,7 +1236,7 @@ namespace i2p uint32_t RouterContext::GetUptime () const { - return std::chrono::duration_cast (std::chrono::steady_clock::now() - m_StartupTime).count (); + return i2p::util::GetMonotonicSeconds () - m_StartupTime; } bool RouterContext::Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const diff --git a/libi2pd/RouterContext.h b/libi2pd/RouterContext.h index 062c187d..941974b2 100644 --- a/libi2pd/RouterContext.h +++ b/libi2pd/RouterContext.h @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include "Identity.h" @@ -241,7 +240,7 @@ namespace garlic std::shared_ptr m_ECIESSession; uint64_t m_LastUpdateTime; // in seconds bool m_AcceptsTunnels, m_IsFloodfill; - std::chrono::time_point m_StartupTime; + uint64_t m_StartupTime; // monotonic seconds uint64_t m_BandwidthLimit; // allowed bandwidth int m_ShareRatio; RouterStatus m_Status, m_StatusV6; diff --git a/libi2pd/Timestamp.cpp b/libi2pd/Timestamp.cpp index 27e3a619..f13efe68 100644 --- a/libi2pd/Timestamp.cpp +++ b/libi2pd/Timestamp.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2022, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -232,12 +232,24 @@ namespace util return GetLocalHoursSinceEpoch () + g_TimeOffset/3600; } - uint64_t GetSteadyMicroseconds() + uint64_t GetMonotonicMicroseconds() { return std::chrono::duration_cast( std::chrono::steady_clock::now().time_since_epoch()).count(); } + uint64_t GetMonotonicMilliseconds() + { + return std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()).count(); + } + + uint64_t GetMonotonicSeconds () + { + return std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()).count(); + } + void GetCurrentDate (char * date) { GetDateString (GetSecondsSinceEpoch (), date); diff --git a/libi2pd/Timestamp.h b/libi2pd/Timestamp.h index 6a925079..025908af 100644 --- a/libi2pd/Timestamp.h +++ b/libi2pd/Timestamp.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2022, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -24,8 +24,10 @@ namespace util uint32_t GetMinutesSinceEpoch (); uint32_t GetHoursSinceEpoch (); - uint64_t GetSteadyMicroseconds(); - + uint64_t GetMonotonicMicroseconds (); + uint64_t GetMonotonicMilliseconds (); + uint64_t GetMonotonicSeconds (); + void GetCurrentDate (char * date); // returns date as YYYYMMDD string, 9 bytes void GetDateString (uint64_t timestamp, char * date); // timestamp is seconds since epoch, returns date as YYYYMMDD string, 9 bytes void AdjustTimeOffset (int64_t offset); // in seconds from current diff --git a/libi2pd/TunnelPool.cpp b/libi2pd/TunnelPool.cpp index e7cec270..c5dcf89e 100644 --- a/libi2pd/TunnelPool.cpp +++ b/libi2pd/TunnelPool.cpp @@ -456,9 +456,6 @@ namespace tunnel void TunnelPool::ProcessDeliveryStatus (std::shared_ptr msg) { - const uint8_t * buf = msg->GetPayload (); - uint32_t msgID = bufbe32toh (buf); - if (m_LocalDestination) m_LocalDestination->ProcessDeliveryStatusMessage (msg); else @@ -491,7 +488,7 @@ namespace tunnel } if (found) { - int dlt = (uint64_t)i2p::util::GetSteadyMicroseconds () - (int64_t)timestamp; + int dlt = (uint64_t)i2p::util::GetMonotonicMicroseconds () - (int64_t)timestamp; LogPrint (eLogDebug, "Tunnels: Test of ", msgID, " successful. ", dlt, " microseconds"); if (dlt < 0) dlt = 0; // should not happen int numHops = 0; From ca45fe73e90000a92d6d5302a57f8f0b8d5376b4 Mon Sep 17 00:00:00 2001 From: orignal Date: Tue, 27 Feb 2024 16:10:17 -0500 Subject: [PATCH 07/32] never delete conneted router from netdb --- libi2pd/NetDb.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libi2pd/NetDb.cpp b/libi2pd/NetDb.cpp index bfd07df3..74a619d0 100644 --- a/libi2pd/NetDb.cpp +++ b/libi2pd/NetDb.cpp @@ -672,10 +672,11 @@ namespace data (CreateRoutingKey (it.second->GetIdentHash ()) ^ i2p::context.GetIdentHash ()).metric[0] >= 0x02)) // different first 7 bits it.second->SetUnreachable (true); } - if (it.second->IsUnreachable () && i2p::transport::transports.IsConnected (it.second->GetIdentHash ())) - it.second->SetUnreachable (false); // don't expire connected router } - + // make router reachable back if connected now + if (it.second->IsUnreachable () && i2p::transport::transports.IsConnected (it.second->GetIdentHash ())) + it.second->SetUnreachable (false); + if (it.second->IsUnreachable ()) { if (it.second->IsFloodfill ()) deletedFloodfillsCount++; From aa1de7fe94acd8656cbf4eaf32e121b776ebf853 Mon Sep 17 00:00:00 2001 From: Vort Date: Wed, 28 Feb 2024 18:55:28 +0200 Subject: [PATCH 08/32] enable non-blocking mode for UDP sockets --- libi2pd/SSU2.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libi2pd/SSU2.cpp b/libi2pd/SSU2.cpp index 3e0583ea..3d5eb7e2 100644 --- a/libi2pd/SSU2.cpp +++ b/libi2pd/SSU2.cpp @@ -258,6 +258,7 @@ namespace transport socket.set_option (boost::asio::ip::v6_only (true)); socket.set_option (boost::asio::socket_base::receive_buffer_size (SSU2_SOCKET_RECEIVE_BUFFER_SIZE)); socket.set_option (boost::asio::socket_base::send_buffer_size (SSU2_SOCKET_SEND_BUFFER_SIZE)); + socket.non_blocking (true); } catch (std::exception& ex ) { From f3c052ed0ccd40fcda7ffe7fd62af107184a8a13 Mon Sep 17 00:00:00 2001 From: Vort Date: Thu, 29 Feb 2024 10:27:52 +0200 Subject: [PATCH 09/32] write SSU2 socket buffer sizes to log --- libi2pd/SSU2.cpp | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/libi2pd/SSU2.cpp b/libi2pd/SSU2.cpp index 3d5eb7e2..b2452fda 100644 --- a/libi2pd/SSU2.cpp +++ b/libi2pd/SSU2.cpp @@ -256,8 +256,27 @@ namespace transport socket.open (localEndpoint.protocol ()); if (localEndpoint.address ().is_v6 ()) socket.set_option (boost::asio::ip::v6_only (true)); - socket.set_option (boost::asio::socket_base::receive_buffer_size (SSU2_SOCKET_RECEIVE_BUFFER_SIZE)); - socket.set_option (boost::asio::socket_base::send_buffer_size (SSU2_SOCKET_SEND_BUFFER_SIZE)); + boost::asio::socket_base::receive_buffer_size receive_buffer_size_set (SSU2_SOCKET_RECEIVE_BUFFER_SIZE); + boost::asio::socket_base::send_buffer_size send_buffer_size_set (SSU2_SOCKET_SEND_BUFFER_SIZE); + socket.set_option (receive_buffer_size_set); + socket.set_option (send_buffer_size_set); + boost::asio::socket_base::receive_buffer_size receive_buffer_size_get; + boost::asio::socket_base::send_buffer_size send_buffer_size_get; + socket.get_option (receive_buffer_size_get); + socket.get_option (send_buffer_size_get); + if (receive_buffer_size_get.value () != receive_buffer_size_set.value () || + send_buffer_size_get.value () != send_buffer_size_set.value ()) + { + LogPrint (eLogWarning, "SSU2: Socket receive buffer size: requested = ", + receive_buffer_size_set.value (), ", got = ", receive_buffer_size_get.value ()); + LogPrint (eLogWarning, "SSU2: Socket send buffer size: requested = ", + send_buffer_size_set.value (), ", got = ", send_buffer_size_get.value ()); + } + else + { + LogPrint (eLogInfo, "SSU2: Socket receive buffer size: ", receive_buffer_size_get.value ()); + LogPrint (eLogInfo, "SSU2: Socket send buffer size: ", send_buffer_size_get.value ()); + } socket.non_blocking (true); } catch (std::exception& ex ) From 89f9bec49abb0e7016e0c2c9f46d7d758a0bd91c Mon Sep 17 00:00:00 2001 From: Vort Date: Thu, 29 Feb 2024 16:02:43 +0200 Subject: [PATCH 10/32] derive SSU2 socket buffer size from bandwidth limit --- libi2pd/SSU2.cpp | 33 +++++++++++++++++++-------------- libi2pd/SSU2.h | 4 ++-- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/libi2pd/SSU2.cpp b/libi2pd/SSU2.cpp index b2452fda..d7a82ee8 100644 --- a/libi2pd/SSU2.cpp +++ b/libi2pd/SSU2.cpp @@ -256,27 +256,32 @@ namespace transport socket.open (localEndpoint.protocol ()); if (localEndpoint.address ().is_v6 ()) socket.set_option (boost::asio::ip::v6_only (true)); - boost::asio::socket_base::receive_buffer_size receive_buffer_size_set (SSU2_SOCKET_RECEIVE_BUFFER_SIZE); - boost::asio::socket_base::send_buffer_size send_buffer_size_set (SSU2_SOCKET_SEND_BUFFER_SIZE); - socket.set_option (receive_buffer_size_set); - socket.set_option (send_buffer_size_set); - boost::asio::socket_base::receive_buffer_size receive_buffer_size_get; - boost::asio::socket_base::send_buffer_size send_buffer_size_get; - socket.get_option (receive_buffer_size_get); - socket.get_option (send_buffer_size_get); - if (receive_buffer_size_get.value () != receive_buffer_size_set.value () || - send_buffer_size_get.value () != send_buffer_size_set.value ()) + + uint64_t bufferSize = i2p::context.GetBandwidthLimit() * 1024 / 5; // max lag = 200ms + bufferSize = std::max(SSU2_SOCKET_MIN_BUFFER_SIZE, std::min(bufferSize, SSU2_SOCKET_MAX_BUFFER_SIZE)); + + boost::asio::socket_base::receive_buffer_size receiveBufferSizeSet (bufferSize); + boost::asio::socket_base::send_buffer_size sendBufferSizeSet (bufferSize); + socket.set_option (receiveBufferSizeSet); + socket.set_option (sendBufferSizeSet); + boost::asio::socket_base::receive_buffer_size receiveBufferSizeGet; + boost::asio::socket_base::send_buffer_size sendBufferSizeGet; + socket.get_option (receiveBufferSizeGet); + socket.get_option (sendBufferSizeGet); + if (receiveBufferSizeGet.value () != receiveBufferSizeSet.value () || + sendBufferSizeGet.value () != sendBufferSizeSet.value ()) { LogPrint (eLogWarning, "SSU2: Socket receive buffer size: requested = ", - receive_buffer_size_set.value (), ", got = ", receive_buffer_size_get.value ()); + receiveBufferSizeSet.value (), ", got = ", receiveBufferSizeGet.value ()); LogPrint (eLogWarning, "SSU2: Socket send buffer size: requested = ", - send_buffer_size_set.value (), ", got = ", send_buffer_size_get.value ()); + sendBufferSizeSet.value (), ", got = ", sendBufferSizeGet.value ()); } else { - LogPrint (eLogInfo, "SSU2: Socket receive buffer size: ", receive_buffer_size_get.value ()); - LogPrint (eLogInfo, "SSU2: Socket send buffer size: ", send_buffer_size_get.value ()); + LogPrint (eLogInfo, "SSU2: Socket receive buffer size: ", receiveBufferSizeGet.value ()); + LogPrint (eLogInfo, "SSU2: Socket send buffer size: ", sendBufferSizeGet.value ()); } + socket.non_blocking (true); } catch (std::exception& ex ) diff --git a/libi2pd/SSU2.h b/libi2pd/SSU2.h index da5ca317..b1aa073e 100644 --- a/libi2pd/SSU2.h +++ b/libi2pd/SSU2.h @@ -25,8 +25,8 @@ namespace transport const int SSU2_RESEND_CHECK_TIMEOUT_VARIANCE = 100; // in milliseconds const int SSU2_RESEND_CHECK_MORE_TIMEOUT = 10; // in milliseconds const size_t SSU2_MAX_RESEND_PACKETS = 128; // packets to resend at the time - const size_t SSU2_SOCKET_RECEIVE_BUFFER_SIZE = 0x1FFFF; // 128K - const size_t SSU2_SOCKET_SEND_BUFFER_SIZE = 0x1FFFF; // 128K + const uint64_t SSU2_SOCKET_MIN_BUFFER_SIZE = 128 * 1024; + const uint64_t SSU2_SOCKET_MAX_BUFFER_SIZE = 4 * 1024 * 1024; const size_t SSU2_MAX_NUM_INTRODUCERS = 3; const size_t SSU2_MIN_RECEIVED_PACKET_SIZE = 40; // 16 byte short header + 8 byte minimum payload + 16 byte MAC const int SSU2_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour From e5f75eb61ca87453e5d090eba28a6112ccb1eae3 Mon Sep 17 00:00:00 2001 From: Vort Date: Thu, 29 Feb 2024 16:12:51 +0200 Subject: [PATCH 11/32] log would_block error at info level --- libi2pd/SSU2.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/libi2pd/SSU2.cpp b/libi2pd/SSU2.cpp index d7a82ee8..28ffb8a0 100644 --- a/libi2pd/SSU2.cpp +++ b/libi2pd/SSU2.cpp @@ -662,7 +662,10 @@ namespace transport if (!ec) i2p::transport::transports.UpdateSentBytes (headerLen + payloadLen); else - LogPrint (eLogError, "SSU2: Send exception: ", ec.message (), " to ", to); + { + LogPrint (ec == boost::asio::error::would_block ? eLogInfo : eLogError, + "SSU2: Send exception: ", ec.message (), " to ", to); + } } void SSU2Server::Send (const uint8_t * header, size_t headerLen, const uint8_t * headerX, size_t headerXLen, @@ -696,7 +699,10 @@ namespace transport if (!ec) i2p::transport::transports.UpdateSentBytes (headerLen + headerXLen + payloadLen); else - LogPrint (eLogError, "SSU2: Send exception: ", ec.message (), " to ", to); + { + LogPrint (ec == boost::asio::error::would_block ? eLogInfo : eLogError, + "SSU2: Send exception: ", ec.message (), " to ", to); + } } bool SSU2Server::CreateSession (std::shared_ptr router, From 3311fe62bbb2cfc60e4274a14dc3ad8fb7ec6f2e Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 1 Mar 2024 08:03:40 -0500 Subject: [PATCH 12/32] fixed potential race condition with tunnel tests --- libi2pd/TunnelPool.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/libi2pd/TunnelPool.cpp b/libi2pd/TunnelPool.cpp index c5dcf89e..30ae9dfd 100644 --- a/libi2pd/TunnelPool.cpp +++ b/libi2pd/TunnelPool.cpp @@ -102,7 +102,10 @@ namespace tunnel it->SetTunnelPool (nullptr); m_OutboundTunnels.clear (); } - m_Tests.clear (); + { + std::unique_lock l(m_TestsMutex); + m_Tests.clear (); + } } bool TunnelPool::Reconfigure(int inHops, int outHops, int inQuant, int outQuant) @@ -145,8 +148,11 @@ namespace tunnel if (expiredTunnel) { expiredTunnel->SetTunnelPool (nullptr); - for (auto& it: m_Tests) - if (it.second.second == expiredTunnel) it.second.second = nullptr; + { + std::unique_lock l(m_TestsMutex); + for (auto& it: m_Tests) + if (it.second.second == expiredTunnel) it.second.second = nullptr; + } std::unique_lock l(m_InboundTunnelsMutex); m_InboundTunnels.erase (expiredTunnel); @@ -167,8 +173,11 @@ namespace tunnel if (expiredTunnel) { expiredTunnel->SetTunnelPool (nullptr); - for (auto& it: m_Tests) - if (it.second.first == expiredTunnel) it.second.first = nullptr; + { + std::unique_lock l(m_TestsMutex); + for (auto& it: m_Tests) + if (it.second.first == expiredTunnel) it.second.first = nullptr; + } std::unique_lock l(m_OutboundTunnelsMutex); m_OutboundTunnels.erase (expiredTunnel); From b9773c88e4ae8a889fe7ab7c4cdb274fc722da50 Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 1 Mar 2024 12:45:21 -0500 Subject: [PATCH 13/32] don't set test failed state to expiring tunnels --- libi2pd/TunnelPool.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libi2pd/TunnelPool.cpp b/libi2pd/TunnelPool.cpp index 30ae9dfd..5fdf963c 100644 --- a/libi2pd/TunnelPool.cpp +++ b/libi2pd/TunnelPool.cpp @@ -351,7 +351,7 @@ namespace tunnel else it.second.first->SetState (eTunnelStateTestFailed); } - else + else if (it.second.first->GetState () != eTunnelStateExpiring) it.second.first->SetState (eTunnelStateTestFailed); } if (it.second.second) @@ -369,7 +369,7 @@ namespace tunnel if (m_LocalDestination) m_LocalDestination->SetLeaseSetUpdated (); } - else + else if (it.second.second->GetState () != eTunnelStateExpiring) it.second.second->SetState (eTunnelStateTestFailed); } } @@ -381,7 +381,7 @@ namespace tunnel { std::unique_lock l(m_OutboundTunnelsMutex); for (auto& it: m_OutboundTunnels) - if (it->IsEstablished () || it->GetState () == eTunnelStateTestFailed) + if (it->IsEstablished ()) outboundTunnels.push_back (it); } std::shuffle (outboundTunnels.begin(), outboundTunnels.end(), m_Rng); @@ -389,7 +389,7 @@ namespace tunnel { std::unique_lock l(m_InboundTunnelsMutex); for (auto& it: m_InboundTunnels) - if (it->IsEstablished () || it->GetState () == eTunnelStateTestFailed) + if (it->IsEstablished ()) inboundTunnels.push_back (it); } std::shuffle (inboundTunnels.begin(), inboundTunnels.end(), m_Rng); From 6ca266ff3bb4bbe36eef17385b38e74708b8eb75 Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 1 Mar 2024 14:03:13 -0500 Subject: [PATCH 14/32] reject peer test msg 2 if peer testing is not supported --- libi2pd/SSU2Session.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libi2pd/SSU2Session.cpp b/libi2pd/SSU2Session.cpp index 4015dcac..2b9dfb6b 100644 --- a/libi2pd/SSU2Session.cpp +++ b/libi2pd/SSU2Session.cpp @@ -2180,7 +2180,7 @@ namespace transport std::shared_ptr addr; if (ExtractEndpoint (buf + offset + 10, asz, ep)) addr = r->GetSSU2Address (ep.address ().is_v4 ()); - if (addr && m_Server.IsSupported (ep.address ())) + if (addr && m_Server.IsSupported (ep.address ()) && addr->IsPeerTesting ()) { // send msg 5 to Alice auto session = std::make_shared (m_Server, r, addr); From 38cc01e13d300ac9d9d1e67ae8170160098a04f2 Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 1 Mar 2024 14:33:11 -0500 Subject: [PATCH 15/32] check own peer test cap for peer test msg 2 --- libi2pd/SSU2Session.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libi2pd/SSU2Session.cpp b/libi2pd/SSU2Session.cpp index 2b9dfb6b..eb80c957 100644 --- a/libi2pd/SSU2Session.cpp +++ b/libi2pd/SSU2Session.cpp @@ -2180,7 +2180,8 @@ namespace transport std::shared_ptr addr; if (ExtractEndpoint (buf + offset + 10, asz, ep)) addr = r->GetSSU2Address (ep.address ().is_v4 ()); - if (addr && m_Server.IsSupported (ep.address ()) && addr->IsPeerTesting ()) + if (addr && m_Server.IsSupported (ep.address ()) && + i2p::context.GetRouterInfo ().IsSSU2PeerTesting (ep.address ().is_v4 ())) { // send msg 5 to Alice auto session = std::make_shared (m_Server, r, addr); From f8722f17c608dda31bb5b5d288edfef757d21697 Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 1 Mar 2024 21:59:52 -0500 Subject: [PATCH 16/32] pick peer test session only if Charlie's address supports peer testing --- libi2pd/SSU2.cpp | 6 +++--- libi2pd/SSU2.h | 2 +- libi2pd/SSU2Session.cpp | 10 ++++++++-- libi2pd/SSU2Session.h | 3 ++- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/libi2pd/SSU2.cpp b/libi2pd/SSU2.cpp index 28ffb8a0..ab152e8f 100644 --- a/libi2pd/SSU2.cpp +++ b/libi2pd/SSU2.cpp @@ -494,7 +494,7 @@ namespace transport m_PendingOutgoingSessions.erase (ep); } - std::shared_ptr SSU2Server::GetRandomSession ( + std::shared_ptr SSU2Server::GetRandomPeerTestSession ( i2p::data::RouterInfo::CompatibleTransports remoteTransports, const i2p::data::IdentHash& excluded) const { if (m_Sessions.empty ()) return nullptr; @@ -505,7 +505,7 @@ namespace transport std::advance (it, ind); while (it != m_Sessions.end ()) { - if ((it->second->GetRemoteTransports () & remoteTransports) && + if ((it->second->GetRemotePeerTestTransports () & remoteTransports) && it->second->GetRemoteIdentity ()->GetIdentHash () != excluded) return it->second; it++; @@ -514,7 +514,7 @@ namespace transport it = m_Sessions.begin (); while (it != m_Sessions.end () && ind) { - if ((it->second->GetRemoteTransports () & remoteTransports) && + if ((it->second->GetRemotePeerTestTransports () & remoteTransports) && it->second->GetRemoteIdentity ()->GetIdentHash () != excluded) return it->second; it++; ind--; diff --git a/libi2pd/SSU2.h b/libi2pd/SSU2.h index b1aa073e..60e70e7a 100644 --- a/libi2pd/SSU2.h +++ b/libi2pd/SSU2.h @@ -77,7 +77,7 @@ namespace transport void RemovePendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep); std::shared_ptr FindSession (const i2p::data::IdentHash& ident) const; std::shared_ptr FindPendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep) const; - std::shared_ptr GetRandomSession (i2p::data::RouterInfo::CompatibleTransports remoteTransports, + std::shared_ptr GetRandomPeerTestSession (i2p::data::RouterInfo::CompatibleTransports remoteTransports, const i2p::data::IdentHash& excluded) const; void AddRelay (uint32_t tag, std::shared_ptr relay); diff --git a/libi2pd/SSU2Session.cpp b/libi2pd/SSU2Session.cpp index eb80c957..d5f1a40f 100644 --- a/libi2pd/SSU2Session.cpp +++ b/libi2pd/SSU2Session.cpp @@ -81,7 +81,7 @@ namespace transport SSU2Session::SSU2Session (SSU2Server& server, std::shared_ptr in_RemoteRouter, std::shared_ptr addr): TransportSession (in_RemoteRouter, SSU2_CONNECT_TIMEOUT), - m_Server (server), m_Address (addr), m_RemoteTransports (0), + m_Server (server), m_Address (addr), m_RemoteTransports (0), m_RemotePeerTestTransports (0), m_DestConnID (0), m_SourceConnID (0), m_State (eSSU2SessionStateUnknown), m_SendPacketNum (0), m_ReceivePacketNum (0), m_LastDatetimeSentPacketNum (0), m_IsDataReceived (false), m_WindowSize (SSU2_MIN_WINDOW_SIZE), @@ -96,6 +96,8 @@ namespace transport InitNoiseXKState1 (*m_NoiseState, m_Address->s); m_RemoteEndpoint = boost::asio::ip::udp::endpoint (m_Address->host, m_Address->port); m_RemoteTransports = in_RemoteRouter->GetCompatibleTransports (false); + if (in_RemoteRouter->IsSSU2PeerTesting (true)) m_RemotePeerTestTransports |= i2p::data::RouterInfo::eSSU2V4; + if (in_RemoteRouter->IsSSU2PeerTesting (false)) m_RemotePeerTestTransports |= i2p::data::RouterInfo::eSSU2V6; RAND_bytes ((uint8_t *)&m_DestConnID, 8); RAND_bytes ((uint8_t *)&m_SourceConnID, 8); } @@ -1110,6 +1112,10 @@ namespace transport AdjustMaxPayloadSize (); m_Server.AddSessionByRouterHash (shared_from_this ()); // we know remote router now m_RemoteTransports = ri->GetCompatibleTransports (false); + m_RemotePeerTestTransports = 0; + if (ri->IsSSU2PeerTesting (true)) m_RemotePeerTestTransports |= i2p::data::RouterInfo::eSSU2V4; + if (ri->IsSSU2PeerTesting (false)) m_RemotePeerTestTransports |= i2p::data::RouterInfo::eSSU2V6; + // handle other blocks HandlePayload (decryptedPayload.data () + riSize + 3, decryptedPayload.size () - riSize - 3); Established (); @@ -2109,7 +2115,7 @@ namespace transport { case 1: // Bob from Alice { - auto session = m_Server.GetRandomSession ((buf[12] == 6) ? i2p::data::RouterInfo::eSSU2V4 : i2p::data::RouterInfo::eSSU2V6, + auto session = m_Server.GetRandomPeerTestSession ((buf[12] == 6) ? i2p::data::RouterInfo::eSSU2V4 : i2p::data::RouterInfo::eSSU2V6, GetRemoteIdentity ()->GetIdentHash ()); if (session) // session with Charlie { diff --git a/libi2pd/SSU2Session.h b/libi2pd/SSU2Session.h index 5dd45fd4..ed17a6f4 100644 --- a/libi2pd/SSU2Session.h +++ b/libi2pd/SSU2Session.h @@ -238,6 +238,7 @@ namespace transport void SetRemoteEndpoint (const boost::asio::ip::udp::endpoint& ep) { m_RemoteEndpoint = ep; }; const boost::asio::ip::udp::endpoint& GetRemoteEndpoint () const { return m_RemoteEndpoint; }; i2p::data::RouterInfo::CompatibleTransports GetRemoteTransports () const { return m_RemoteTransports; }; + i2p::data::RouterInfo::CompatibleTransports GetRemotePeerTestTransports () const { return m_RemotePeerTestTransports; }; std::shared_ptr GetAddress () const { return m_Address; }; void SetOnEstablished (OnEstablished e) { m_OnEstablished = e; }; OnEstablished GetOnEstablished () const { return m_OnEstablished; }; @@ -343,7 +344,7 @@ namespace transport std::unique_ptr m_SentHandshakePacket; // SessionRequest, SessionCreated or SessionConfirmed std::shared_ptr m_Address; boost::asio::ip::udp::endpoint m_RemoteEndpoint; - i2p::data::RouterInfo::CompatibleTransports m_RemoteTransports; // for peer tests + i2p::data::RouterInfo::CompatibleTransports m_RemoteTransports, m_RemotePeerTestTransports; uint64_t m_DestConnID, m_SourceConnID; SSU2SessionState m_State; uint8_t m_KeyDataSend[64], m_KeyDataReceive[64]; From edd9dd2c39b12d1b85d75efaae0cf3eed0159e66 Mon Sep 17 00:00:00 2001 From: orignal Date: Sat, 2 Mar 2024 10:57:01 -0500 Subject: [PATCH 17/32] try to publish again after 5 seconds if destination is not ready --- libi2pd/Destination.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libi2pd/Destination.cpp b/libi2pd/Destination.cpp index 35bad171..d28221d1 100644 --- a/libi2pd/Destination.cpp +++ b/libi2pd/Destination.cpp @@ -621,7 +621,14 @@ namespace client LogPrint (eLogError, "Destination: Can't publish LeaseSet, no more floodfills found"); if (!floodfill || !outbound || !inbound) { + // we can't publish now m_ExcludedFloodfills.clear (); + m_PublishReplyToken = 1; // dummy non-zero value + // try again after a while + LogPrint (eLogInfo, "Destination: Try publishing again after ", PUBLISH_CONFIRMATION_TIMEOUT, " seconds"); + m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT)); + m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer, + shared_from_this (), std::placeholders::_1)); return; } } From 2f2f14e3a7f0fe460d5c350340d1386e6dfb6980 Mon Sep 17 00:00:00 2001 From: orignal Date: Sat, 2 Mar 2024 11:26:41 -0500 Subject: [PATCH 18/32] try publishing again after 5 seconds if no tunnels in the pool --- libi2pd/Destination.cpp | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/libi2pd/Destination.cpp b/libi2pd/Destination.cpp index d28221d1..603da18f 100644 --- a/libi2pd/Destination.cpp +++ b/libi2pd/Destination.cpp @@ -586,11 +586,6 @@ namespace client shared_from_this (), std::placeholders::_1)); return; } - if (!m_Pool->GetInboundTunnels ().size () || !m_Pool->GetOutboundTunnels ().size ()) - { - LogPrint (eLogError, "Destination: Can't publish LeaseSet. Destination is not ready"); - return; - } auto floodfill = i2p::data::netdb.GetClosestFloodfill (leaseSet->GetIdentHash (), m_ExcludedFloodfills); if (!floodfill) { @@ -602,30 +597,36 @@ namespace client auto inbound = m_Pool->GetNextInboundTunnel (nullptr, floodfill->GetCompatibleTransports (true)); if (!outbound || !inbound) { - LogPrint (eLogInfo, "Destination: No compatible tunnels with ", floodfill->GetIdentHash ().ToBase64 (), ". Trying another floodfill"); - m_ExcludedFloodfills.insert (floodfill->GetIdentHash ()); - floodfill = i2p::data::netdb.GetClosestFloodfill (leaseSet->GetIdentHash (), m_ExcludedFloodfills); - if (floodfill) - { - outbound = m_Pool->GetNextOutboundTunnel (nullptr, floodfill->GetCompatibleTransports (false)); - if (outbound) + if (!m_Pool->GetInboundTunnels ().empty () && !m_Pool->GetOutboundTunnels ().empty ()) + { + LogPrint (eLogInfo, "Destination: No compatible tunnels with ", floodfill->GetIdentHash ().ToBase64 (), ". Trying another floodfill"); + m_ExcludedFloodfills.insert (floodfill->GetIdentHash ()); + floodfill = i2p::data::netdb.GetClosestFloodfill (leaseSet->GetIdentHash (), m_ExcludedFloodfills); + if (floodfill) { - inbound = m_Pool->GetNextInboundTunnel (nullptr, floodfill->GetCompatibleTransports (true)); - if (!inbound) - LogPrint (eLogError, "Destination: Can't publish LeaseSet. No inbound tunnels"); + outbound = m_Pool->GetNextOutboundTunnel (nullptr, floodfill->GetCompatibleTransports (false)); + if (outbound) + { + inbound = m_Pool->GetNextInboundTunnel (nullptr, floodfill->GetCompatibleTransports (true)); + if (!inbound) + LogPrint (eLogError, "Destination: Can't publish LeaseSet. No inbound tunnels"); + } + else + LogPrint (eLogError, "Destination: Can't publish LeaseSet. No outbound tunnels"); } else - LogPrint (eLogError, "Destination: Can't publish LeaseSet. No outbound tunnels"); - } + LogPrint (eLogError, "Destination: Can't publish LeaseSet, no more floodfills found"); + } else - LogPrint (eLogError, "Destination: Can't publish LeaseSet, no more floodfills found"); + LogPrint (eLogDebug, "Destination: No tunnels in pool"); + if (!floodfill || !outbound || !inbound) { // we can't publish now m_ExcludedFloodfills.clear (); m_PublishReplyToken = 1; // dummy non-zero value // try again after a while - LogPrint (eLogInfo, "Destination: Try publishing again after ", PUBLISH_CONFIRMATION_TIMEOUT, " seconds"); + LogPrint (eLogInfo, "Destination: Can't publish LeasetSet because destination is not ready. Try publishing again after ", PUBLISH_CONFIRMATION_TIMEOUT, " seconds"); m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT)); m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer, shared_from_this (), std::placeholders::_1)); From 1292ec67c013355d8fc0508e947f088ebc9aad2e Mon Sep 17 00:00:00 2001 From: orignal Date: Sat, 2 Mar 2024 18:02:55 -0500 Subject: [PATCH 19/32] check if remote router supports peer test --- libi2pd/SSU2.cpp | 7 +++++-- libi2pd/SSU2Session.cpp | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/libi2pd/SSU2.cpp b/libi2pd/SSU2.cpp index ab152e8f..976bffbf 100644 --- a/libi2pd/SSU2.cpp +++ b/libi2pd/SSU2.cpp @@ -856,8 +856,11 @@ namespace transport auto it = m_SessionsByRouterHash.find (router->GetIdentHash ()); if (it != m_SessionsByRouterHash.end ()) { - auto s = it->second; - if (it->second->IsEstablished ()) + auto remoteAddr = it->second->GetAddress (); + if (!remoteAddr || !remoteAddr->IsPeerTesting () || + (v4 && !addr->IsV4 ()) || (!v4 && !addr->IsV6 ())) return false; + auto s = it->second; + if (s->IsEstablished ()) GetService ().post ([s]() { s->SendPeerTest (); }); else s->SetOnEstablished ([s]() { s->SendPeerTest (); }); diff --git a/libi2pd/SSU2Session.cpp b/libi2pd/SSU2Session.cpp index d5f1a40f..2c499753 100644 --- a/libi2pd/SSU2Session.cpp +++ b/libi2pd/SSU2Session.cpp @@ -2287,7 +2287,7 @@ namespace transport if (GetTestingState ()) { SetTestingState (false); - if (GetRouterStatus () != eRouterStatusFirewalled) + if (GetRouterStatus () != eRouterStatusFirewalled && addr->IsPeerTesting ()) { SetRouterStatus (eRouterStatusFirewalled); if (m_Address->IsV4 ()) From 6ba42a0912e3b3ce6eb459ffd6c5d8f6eb0238bf Mon Sep 17 00:00:00 2001 From: orignal Date: Sat, 2 Mar 2024 18:17:56 -0500 Subject: [PATCH 20/32] check if established peer test session has the same address type --- libi2pd/SSU2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libi2pd/SSU2.cpp b/libi2pd/SSU2.cpp index 976bffbf..812f49d3 100644 --- a/libi2pd/SSU2.cpp +++ b/libi2pd/SSU2.cpp @@ -858,7 +858,7 @@ namespace transport { auto remoteAddr = it->second->GetAddress (); if (!remoteAddr || !remoteAddr->IsPeerTesting () || - (v4 && !addr->IsV4 ()) || (!v4 && !addr->IsV6 ())) return false; + (v4 && !remoteAddr->IsV4 ()) || (!v4 && !remoteAddr->IsV6 ())) return false; auto s = it->second; if (s->IsEstablished ()) GetService ().post ([s]() { s->SendPeerTest (); }); From f1058410fbc33fcace6f0548bdd9165bd474fcc0 Mon Sep 17 00:00:00 2001 From: orignal Date: Sun, 3 Mar 2024 07:42:39 -0500 Subject: [PATCH 21/32] don't request banned router --- libi2pd/NetDb.cpp | 4 +++- libi2pd/Profiling.cpp | 11 ++++++++++- libi2pd/Profiling.h | 1 + 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/libi2pd/NetDb.cpp b/libi2pd/NetDb.cpp index 74a619d0..ecfe2158 100644 --- a/libi2pd/NetDb.cpp +++ b/libi2pd/NetDb.cpp @@ -957,8 +957,10 @@ namespace data LogPrint (eLogDebug, "NetDb: Found new/outdated router. Requesting RouterInfo..."); if(m_FloodfillBootstrap) RequestDestinationFrom(router, m_FloodfillBootstrap->GetIdentHash(), true); - else + else if (!IsRouterBanned (router)) RequestDestination (router); + else + LogPrint (eLogDebug, "NetDb: Router ", peerHash, " is banned. Skipped"); } else LogPrint (eLogDebug, "NetDb: [:|||:]"); diff --git a/libi2pd/Profiling.cpp b/libi2pd/Profiling.cpp index 2031fa39..3a2db9a5 100644 --- a/libi2pd/Profiling.cpp +++ b/libi2pd/Profiling.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -245,6 +245,15 @@ namespace data return profile; } + bool IsRouterBanned (const IdentHash& identHash) + { + std::unique_lock l(g_ProfilesMutex); + auto it = g_Profiles.find (identHash); + if (it != g_Profiles.end ()) + return it->second->IsUnreachable (); + return false; + } + void InitProfilesStorage () { g_ProfilesStorage.SetPlace(i2p::fs::GetDataDir()); diff --git a/libi2pd/Profiling.h b/libi2pd/Profiling.h index ed23fb12..c4aa6a9a 100644 --- a/libi2pd/Profiling.h +++ b/libi2pd/Profiling.h @@ -87,6 +87,7 @@ namespace data }; std::shared_ptr GetRouterProfile (const IdentHash& identHash); + bool IsRouterBanned (const IdentHash& identHash); // check only existing profiles void InitProfilesStorage (); void DeleteObsoleteProfiles (); void SaveProfiles (); From 20a5e19ea16ed307376e2faf4f17f41f0e9f2b0c Mon Sep 17 00:00:00 2001 From: orignal Date: Sun, 3 Mar 2024 09:56:11 -0500 Subject: [PATCH 22/32] don't request banned router --- libi2pd/Transports.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/libi2pd/Transports.cpp b/libi2pd/Transports.cpp index 04c58cf3..0f218eab 100644 --- a/libi2pd/Transports.cpp +++ b/libi2pd/Transports.cpp @@ -492,9 +492,9 @@ namespace transport if (sz < CHECK_PROFILE_NUM_DELAYED_MESSAGES && sz + msgs.size () >= CHECK_PROFILE_NUM_DELAYED_MESSAGES) { auto profile = i2p::data::GetRouterProfile (ident); - if (profile && profile->IsUnreachable ()) + if (i2p::data::IsRouterBanned (ident)) { - LogPrint (eLogWarning, "Transports: Peer profile for ", ident.ToBase64 (), " reports unreachable. Dropped"); + LogPrint (eLogWarning, "Transports: Router ", ident.ToBase64 (), " is banned. Peer dropped"); std::unique_lock l(m_PeersMutex); m_Peers.erase (it); return; @@ -589,6 +589,14 @@ namespace transport m_Peers.erase (ident); return false; } + else if (i2p::data::IsRouterBanned (ident)) + { + LogPrint (eLogWarning, "Transports: Router ", ident.ToBase64 (), " is banned. Peer dropped"); + peer.Done (); + std::unique_lock l(m_PeersMutex); + m_Peers.erase (ident); + return false; + } else // otherwise request RI { LogPrint (eLogInfo, "Transports: RouterInfo for ", ident.ToBase64 (), " not found, requested"); From af0d853ccdaed99ac442b57cf3c06b47da80aeb1 Mon Sep 17 00:00:00 2001 From: orignal Date: Sun, 3 Mar 2024 10:28:25 -0500 Subject: [PATCH 23/32] some cleanup --- libi2pd/Transports.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libi2pd/Transports.cpp b/libi2pd/Transports.cpp index 0f218eab..8531e820 100644 --- a/libi2pd/Transports.cpp +++ b/libi2pd/Transports.cpp @@ -460,9 +460,8 @@ namespace transport auto it = m_Peers.find (ident); if (it == m_Peers.end ()) { - // check if not known as unreachable - auto profile = i2p::data::GetRouterProfile (ident); - if (profile && profile->IsUnreachable ()) return; // don't create peer to unreachable router + // check if not banned + if (i2p::data::IsRouterBanned (ident)) return; // don't create peer to unreachable router // try to connect bool connected = false; try @@ -491,7 +490,6 @@ namespace transport { if (sz < CHECK_PROFILE_NUM_DELAYED_MESSAGES && sz + msgs.size () >= CHECK_PROFILE_NUM_DELAYED_MESSAGES) { - auto profile = i2p::data::GetRouterProfile (ident); if (i2p::data::IsRouterBanned (ident)) { LogPrint (eLogWarning, "Transports: Router ", ident.ToBase64 (), " is banned. Peer dropped"); From 98543af92bf2c341ff929f0872987b6cb790bb76 Mon Sep 17 00:00:00 2001 From: Vort Date: Mon, 4 Mar 2024 13:41:50 +0200 Subject: [PATCH 24/32] fix high latency threshold --- libi2pd/Tunnel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libi2pd/Tunnel.h b/libi2pd/Tunnel.h index f9369672..d3de272d 100644 --- a/libi2pd/Tunnel.h +++ b/libi2pd/Tunnel.h @@ -40,7 +40,7 @@ namespace tunnel const int STANDARD_NUM_RECORDS = 4; // in VariableTunnelBuild message const int MAX_NUM_RECORDS = 8; const int UNKNOWN_LATENCY = -1; - const int HIGH_LATENCY_PER_HOP = 250; // in milliseconds + const int HIGH_LATENCY_PER_HOP = 250000; // in microseconds const int MAX_TUNNEL_MSGS_BATCH_SIZE = 100; // handle messages without interrupt const uint16_t DEFAULT_MAX_NUM_TRANSIT_TUNNELS = 5000; const int TUNNEL_MANAGE_INTERVAL = 15; // in seconds From 2dbf0944331594824c47359a0369c78fb438f90e Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 4 Mar 2024 13:34:57 -0500 Subject: [PATCH 25/32] try to send lookup reply directly to IBGW --- libi2pd/NetDb.cpp | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/libi2pd/NetDb.cpp b/libi2pd/NetDb.cpp index ecfe2158..7406e4dd 100644 --- a/libi2pd/NetDb.cpp +++ b/libi2pd/NetDb.cpp @@ -1114,12 +1114,24 @@ namespace data else LogPrint(eLogWarning, "NetDb: Encrypted reply requested but no tags provided"); } - auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool (); - auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : nullptr; - if (outbound) - outbound->SendTunnelDataMsgTo (replyIdent, replyTunnelID, replyMsg); - else + bool direct = true; + if (!i2p::transport::transports.IsConnected (ident)) + { + auto r = FindRouter (replyIdent); + if (r && !r->IsReachableFrom (i2p::context.GetRouterInfo ())) + direct = false; + } + if (direct) transports.SendMessage (replyIdent, i2p::CreateTunnelGatewayMsg (replyTunnelID, replyMsg)); + else + { + auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool (); + auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : nullptr; + if (outbound) + outbound->SendTunnelDataMsgTo (replyIdent, replyTunnelID, replyMsg); + else + LogPrint (eLogWarning, "NetDb: Can't send lookup reply to ", replyIdent.ToBase64 (), ". Non reachable and no outbound tunnels"); + } } else transports.SendMessage (replyIdent, replyMsg); From 3873e60cbb48cc2d12eebc02eaacd8a70e6bfcb9 Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 4 Mar 2024 14:30:49 -0500 Subject: [PATCH 26/32] try to send database store reply directly to IBGW --- libi2pd/NetDb.cpp | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/libi2pd/NetDb.cpp b/libi2pd/NetDb.cpp index 7406e4dd..5922c740 100644 --- a/libi2pd/NetDb.cpp +++ b/libi2pd/NetDb.cpp @@ -824,17 +824,31 @@ namespace data offset += 4; if (replyToken != 0xFFFFFFFFU) // if not caught on OBEP or IBGW { + IdentHash replyIdent(buf + offset); auto deliveryStatus = CreateDeliveryStatusMsg (replyToken); if (!tunnelID) // send response directly - transports.SendMessage (buf + offset, deliveryStatus); + transports.SendMessage (replyIdent, deliveryStatus); else { - auto pool = i2p::tunnel::tunnels.GetExploratoryPool (); - auto outbound = pool ? pool->GetNextOutboundTunnel () : nullptr; - if (outbound) - outbound->SendTunnelDataMsgTo (buf + offset, tunnelID, deliveryStatus); + bool direct = true; + if (!i2p::transport::transports.IsConnected (replyIdent)) + { + auto r = FindRouter (replyIdent); + if (r && !r->IsReachableFrom (i2p::context.GetRouterInfo ())) + direct = false; + } + if (direct) // send response directly to IBGW + transports.SendMessage (replyIdent, i2p::CreateTunnelGatewayMsg (tunnelID, deliveryStatus)); else - LogPrint (eLogWarning, "NetDb: No outbound tunnels for DatabaseStore reply found"); + { + // send response through exploratory tunnel + auto pool = i2p::tunnel::tunnels.GetExploratoryPool (); + auto outbound = pool ? pool->GetNextOutboundTunnel () : nullptr; + if (outbound) + outbound->SendTunnelDataMsgTo (replyIdent, tunnelID, deliveryStatus); + else + LogPrint (eLogWarning, "NetDb: No outbound tunnels for DatabaseStore reply found"); + } } } offset += 32; @@ -1115,7 +1129,7 @@ namespace data LogPrint(eLogWarning, "NetDb: Encrypted reply requested but no tags provided"); } bool direct = true; - if (!i2p::transport::transports.IsConnected (ident)) + if (!i2p::transport::transports.IsConnected (replyIdent)) { auto r = FindRouter (replyIdent); if (r && !r->IsReachableFrom (i2p::context.GetRouterInfo ())) From ff3fec9a00f359374f3746200c0e20fc1d22222a Mon Sep 17 00:00:00 2001 From: orignal Date: Tue, 5 Mar 2024 10:30:01 -0500 Subject: [PATCH 27/32] remove tag immediately after use --- libi2pd/Garlic.cpp | 60 +++++++++++----------------------------------- libi2pd/Garlic.h | 3 +-- 2 files changed, 15 insertions(+), 48 deletions(-) diff --git a/libi2pd/Garlic.cpp b/libi2pd/Garlic.cpp index ba35995f..5cb4377a 100644 --- a/libi2pd/Garlic.cpp +++ b/libi2pd/Garlic.cpp @@ -431,8 +431,7 @@ namespace garlic } GarlicDestination::GarlicDestination (): m_NumTags (32), // 32 tags by default - m_PayloadBuffer (nullptr), m_NumRatchetInboundTags (0), // 0 means standard - m_NumUsedECIESx25519Tags (0) + m_PayloadBuffer (nullptr), m_NumRatchetInboundTags (0) // 0 means standard { } @@ -589,18 +588,11 @@ namespace garlic auto it = m_ECIESx25519Tags.find (tag); if (it != m_ECIESx25519Tags.end ()) { - if (!it->second.tagset) return true; // duplicate - if (it->second.tagset->HandleNextMessage (buf, len, it->second.index)) - { + if (it->second.tagset && it->second.tagset->HandleNextMessage (buf, len, it->second.index)) m_LastTagset = it->second.tagset; - it->second.tagset = nullptr; // mark as used - } else - { LogPrint (eLogError, "Garlic: Can't handle ECIES-X25519-AEAD-Ratchet message"); - m_ECIESx25519Tags.erase (it); - } - m_NumUsedECIESx25519Tags++; + m_ECIESx25519Tags.erase (it); return true; } return false; @@ -885,41 +877,18 @@ namespace garlic } numExpiredTags = 0; - if (m_NumUsedECIESx25519Tags > ECIESX25519_TAGSET_MAX_NUM_TAGS) // too many used tags + for (auto it = m_ECIESx25519Tags.begin (); it != m_ECIESx25519Tags.end ();) { - std::unordered_map oldTags; - std::swap (m_ECIESx25519Tags, oldTags); // re-create - for (auto& it: oldTags) - if (it.second.tagset) - { - if (it.second.tagset->IsExpired (ts) || it.second.tagset->IsIndexExpired (it.second.index)) - { - it.second.tagset->DeleteSymmKey (it.second.index); - numExpiredTags++; - } - else if (it.second.tagset->IsSessionTerminated()) - numExpiredTags++; - else - m_ECIESx25519Tags.emplace (it); - } - } - else - { - for (auto it = m_ECIESx25519Tags.begin (); it != m_ECIESx25519Tags.end ();) + if (it->second.tagset->IsExpired (ts) || it->second.tagset->IsIndexExpired (it->second.index)) { - if (!it->second.tagset) - { - // delete used tag - it = m_ECIESx25519Tags.erase (it); - continue; - } - if (it->second.tagset->IsExpired (ts) || it->second.tagset->IsIndexExpired (it->second.index)) - { - it->second.tagset->DeleteSymmKey (it->second.index); - it = m_ECIESx25519Tags.erase (it); - numExpiredTags++; - } - else if (it->second.tagset->IsSessionTerminated()) + it->second.tagset->DeleteSymmKey (it->second.index); + it = m_ECIESx25519Tags.erase (it); + numExpiredTags++; + } + else + { + auto session = it->second.tagset->GetSession (); + if (!session || session->IsTerminated()) { it = m_ECIESx25519Tags.erase (it); numExpiredTags++; @@ -927,8 +896,7 @@ namespace garlic else ++it; } - } - m_NumUsedECIESx25519Tags = 0; + } if (numExpiredTags > 0) LogPrint (eLogDebug, "Garlic: ", numExpiredTags, " ECIESx25519 tags expired for ", GetIdentHash().ToBase64 ()); if (m_LastTagset && m_LastTagset->IsExpired (ts)) diff --git a/libi2pd/Garlic.h b/libi2pd/Garlic.h index 0386752b..83e3b050 100644 --- a/libi2pd/Garlic.h +++ b/libi2pd/Garlic.h @@ -291,7 +291,6 @@ namespace garlic std::unordered_map, std::hash > > m_Tags; std::unordered_map m_ECIESx25519Tags; // session tag -> session ReceiveRatchetTagSetPtr m_LastTagset; // tagset last message came for - int m_NumUsedECIESx25519Tags; // DeliveryStatus std::mutex m_DeliveryStatusSessionsMutex; std::unordered_map m_DeliveryStatusSessions; // msgID -> session @@ -300,7 +299,7 @@ namespace garlic // for HTTP only size_t GetNumIncomingTags () const { return m_Tags.size (); } - size_t GetNumIncomingECIESx25519Tags () const { return m_ECIESx25519Tags.size () - m_NumUsedECIESx25519Tags; } + size_t GetNumIncomingECIESx25519Tags () const { return m_ECIESx25519Tags.size (); } const decltype(m_Sessions)& GetSessions () const { return m_Sessions; }; const decltype(m_ECIESx25519Sessions)& GetECIESx25519Sessions () const { return m_ECIESx25519Sessions; } }; From bb702700f7ba3069340eacb03b22344adabd414b Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 6 Mar 2024 07:17:59 -0500 Subject: [PATCH 28/32] don't check session for single tag --- libi2pd/Garlic.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libi2pd/Garlic.cpp b/libi2pd/Garlic.cpp index 5cb4377a..dd66af60 100644 --- a/libi2pd/Garlic.cpp +++ b/libi2pd/Garlic.cpp @@ -887,8 +887,7 @@ namespace garlic } else { - auto session = it->second.tagset->GetSession (); - if (!session || session->IsTerminated()) + if (it->second.tagset->IsSessionTerminated ()) { it = m_ECIESx25519Tags.erase (it); numExpiredTags++; From 3ceb64db2e34c71358fec9601252f401dc71c88e Mon Sep 17 00:00:00 2001 From: Vort Date: Wed, 6 Mar 2024 14:54:02 +0200 Subject: [PATCH 29/32] 1. Use EWMA for stream RTT estimation; 2. Drop window size by 10% instead of 50% in case of resend. Change is based on code by onon. --- libi2pd/Streaming.cpp | 9 ++++++--- libi2pd/Streaming.h | 2 ++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/libi2pd/Streaming.cpp b/libi2pd/Streaming.cpp index fa809c19..f6518163 100644 --- a/libi2pd/Streaming.cpp +++ b/libi2pd/Streaming.cpp @@ -433,7 +433,10 @@ namespace stream LogPrint(eLogError, "Streaming: Packet ", seqn, "sent from the future, sendTime=", sentPacket->sendTime); rtt = 1; } - m_RTT = std::round ((m_RTT*seqn + rtt)/(seqn + 1.0)); + if (seqn) + m_RTT = std::round (RTT_EWMA_ALPHA * m_RTT + (1.0 - RTT_EWMA_ALPHA) * rtt); + else + m_RTT = rtt; m_RTO = m_RTT*1.5; // TODO: implement it better LogPrint (eLogDebug, "Streaming: Packet ", seqn, " acknowledged rtt=", rtt, " sentTime=", sentPacket->sendTime); m_SentPackets.erase (it++); @@ -998,8 +1001,8 @@ namespace stream m_RTO *= 2; switch (m_NumResendAttempts) { - case 1: // congesion avoidance - m_WindowSize >>= 1; // /2 + case 1: // congestion avoidance + m_WindowSize -= (m_WindowSize + WINDOW_SIZE_DROP_FRACTION) / WINDOW_SIZE_DROP_FRACTION; // adjustment >= 1 if (m_WindowSize < MIN_WINDOW_SIZE) m_WindowSize = MIN_WINDOW_SIZE; break; case 2: diff --git a/libi2pd/Streaming.h b/libi2pd/Streaming.h index ed79edc9..1980b6fd 100644 --- a/libi2pd/Streaming.h +++ b/libi2pd/Streaming.h @@ -56,6 +56,8 @@ namespace stream const int WINDOW_SIZE = 6; // in messages const int MIN_WINDOW_SIZE = 1; const int MAX_WINDOW_SIZE = 128; + const int WINDOW_SIZE_DROP_FRACTION = 10; // 1/10 + const double RTT_EWMA_ALPHA = 0.5; const int INITIAL_RTT = 8000; // in milliseconds const int INITIAL_RTO = 9000; // in milliseconds const int MIN_SEND_ACK_TIMEOUT = 2; // in milliseconds From 17dd5c12857212e088581eebcd2cd9b68de2a411 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 6 Mar 2024 18:36:01 -0500 Subject: [PATCH 30/32] publish encrypted leaset on floodfill closest to store hash --- libi2pd/Destination.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libi2pd/Destination.cpp b/libi2pd/Destination.cpp index 603da18f..bd5fd23f 100644 --- a/libi2pd/Destination.cpp +++ b/libi2pd/Destination.cpp @@ -586,7 +586,7 @@ namespace client shared_from_this (), std::placeholders::_1)); return; } - auto floodfill = i2p::data::netdb.GetClosestFloodfill (leaseSet->GetIdentHash (), m_ExcludedFloodfills); + auto floodfill = i2p::data::netdb.GetClosestFloodfill (leaseSet->GetStoreHash (), m_ExcludedFloodfills); if (!floodfill) { LogPrint (eLogError, "Destination: Can't publish LeaseSet, no more floodfills found"); @@ -601,7 +601,7 @@ namespace client { LogPrint (eLogInfo, "Destination: No compatible tunnels with ", floodfill->GetIdentHash ().ToBase64 (), ". Trying another floodfill"); m_ExcludedFloodfills.insert (floodfill->GetIdentHash ()); - floodfill = i2p::data::netdb.GetClosestFloodfill (leaseSet->GetIdentHash (), m_ExcludedFloodfills); + floodfill = i2p::data::netdb.GetClosestFloodfill (leaseSet->GetStoreHash (), m_ExcludedFloodfills); if (floodfill) { outbound = m_Pool->GetNextOutboundTunnel (nullptr, floodfill->GetCompatibleTransports (false)); From 92b49fb9690798e19ff6bb5b2423327bdf6d4d8a Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 6 Mar 2024 21:01:17 -0500 Subject: [PATCH 31/32] clear excluded floodfills after successive publishing --- libi2pd/Destination.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libi2pd/Destination.cpp b/libi2pd/Destination.cpp index bd5fd23f..91477775 100644 --- a/libi2pd/Destination.cpp +++ b/libi2pd/Destination.cpp @@ -699,6 +699,7 @@ namespace client { // we got latest LeasetSet LogPrint (eLogDebug, "Destination: Published LeaseSet verified for ", s->GetIdentHash().ToBase32()); + s->m_ExcludedFloodfills.clear (); s->m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_REGULAR_VERIFICATION_INTERNAL)); s->m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, s, std::placeholders::_1)); return; From 66d0b7aec4f7af23c48c45441eaff86ef801434b Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 7 Mar 2024 10:25:10 -0500 Subject: [PATCH 32/32] correct publication verification for encrypted LeaseSet --- libi2pd/Destination.cpp | 59 +++++++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/libi2pd/Destination.cpp b/libi2pd/Destination.cpp index 91477775..3d5893c7 100644 --- a/libi2pd/Destination.cpp +++ b/libi2pd/Destination.cpp @@ -410,6 +410,7 @@ namespace client } i2p::data::IdentHash key (buf + DATABASE_STORE_KEY_OFFSET); std::shared_ptr leaseSet; + std::shared_ptr request; switch (buf[DATABASE_STORE_TYPE_OFFSET]) { case i2p::data::NETDB_STORE_TYPE_LEASESET: // 1 @@ -465,34 +466,59 @@ namespace client case i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2: // 5 { auto it2 = m_LeaseSetRequests.find (key); - if (it2 != m_LeaseSetRequests.end () && it2->second->requestedBlindedKey) - { - auto ls2 = std::make_shared (buf + offset, len - offset, - it2->second->requestedBlindedKey, m_LeaseSetPrivKey ? ((const uint8_t *)*m_LeaseSetPrivKey) : nullptr , GetPreferredCryptoType ()); - if (ls2->IsValid () && !ls2->IsExpired ()) + if (it2 != m_LeaseSetRequests.end ()) + { + request = it2->second; + m_LeaseSetRequests.erase (it2); + if (request->requestedBlindedKey) { - leaseSet = ls2; - std::lock_guard lock(m_RemoteLeaseSetsMutex); - m_RemoteLeaseSets[ls2->GetIdentHash ()] = ls2; // ident is not key - m_RemoteLeaseSets[key] = ls2; // also store as key for next lookup + auto ls2 = std::make_shared (buf + offset, len - offset, + request->requestedBlindedKey, m_LeaseSetPrivKey ? ((const uint8_t *)*m_LeaseSetPrivKey) : nullptr , GetPreferredCryptoType ()); + if (ls2->IsValid () && !ls2->IsExpired ()) + { + leaseSet = ls2; + std::lock_guard lock(m_RemoteLeaseSetsMutex); + m_RemoteLeaseSets[ls2->GetIdentHash ()] = ls2; // ident is not key + m_RemoteLeaseSets[key] = ls2; // also store as key for next lookup + } + else + LogPrint (eLogError, "Destination: New remote encrypted LeaseSet2 failed"); } else - LogPrint (eLogError, "Destination: New remote encrypted LeaseSet2 failed"); + { + // publishing verification doesn't have requestedBlindedKey + auto localLeaseSet = GetLeaseSetMt (); + if (localLeaseSet->GetStoreHash () == key) + { + auto ls = std::make_shared (i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2, + localLeaseSet->GetBuffer (), localLeaseSet->GetBufferLen (), false); + leaseSet = ls; + } + else + LogPrint (eLogWarning, "Destination: Encrypted LeaseSet2 received for request without blinded key"); + } } else - LogPrint (eLogInfo, "Destination: Couldn't find request for encrypted LeaseSet2"); + LogPrint (eLogWarning, "Destination: Couldn't find request for encrypted LeaseSet2"); break; } default: LogPrint (eLogError, "Destination: Unexpected client's DatabaseStore type ", buf[DATABASE_STORE_TYPE_OFFSET], ", dropped"); } - auto it1 = m_LeaseSetRequests.find (key); - if (it1 != m_LeaseSetRequests.end ()) + if (!request) + { + auto it1 = m_LeaseSetRequests.find (key); + if (it1 != m_LeaseSetRequests.end ()) + { + request = it1->second; + m_LeaseSetRequests.erase (it1); + } + } + if (request) { - it1->second->requestTimeoutTimer.cancel (); - if (it1->second) it1->second->Complete (leaseSet); - m_LeaseSetRequests.erase (it1); + request->requestTimeoutTimer.cancel (); + request->Complete (leaseSet); } } @@ -699,7 +725,6 @@ namespace client { // we got latest LeasetSet LogPrint (eLogDebug, "Destination: Published LeaseSet verified for ", s->GetIdentHash().ToBase32()); - s->m_ExcludedFloodfills.clear (); s->m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_REGULAR_VERIFICATION_INTERNAL)); s->m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, s, std::placeholders::_1)); return;