From edb7d1cabe38109d1c320e5ad48e206801c59bac Mon Sep 17 00:00:00 2001 From: orignal Date: Sat, 14 Feb 2026 15:13:20 -0500 Subject: [PATCH] count non replied tunnel requests per hop --- libi2pd/Profiling.cpp | 14 ++++-- libi2pd/Profiling.h | 26 +++++----- libi2pd/Tunnel.cpp | 113 +++++++++++++++++++++--------------------- 3 files changed, 79 insertions(+), 74 deletions(-) diff --git a/libi2pd/Profiling.cpp b/libi2pd/Profiling.cpp index ac988cf7..e77865b5 100644 --- a/libi2pd/Profiling.cpp +++ b/libi2pd/Profiling.cpp @@ -129,7 +129,7 @@ namespace data auto participations = pt.get_child (PEER_PROFILE_SECTION_PARTICIPATION); m_NumTunnelsAgreed = participations.get (PEER_PROFILE_PARTICIPATION_AGREED, 0); m_NumTunnelsDeclined = participations.get (PEER_PROFILE_PARTICIPATION_DECLINED, 0); - m_NumTunnelsNonReplied = participations.get (PEER_PROFILE_PARTICIPATION_NON_REPLIED, 0); + m_NumTunnelsNonReplied = participations.get (PEER_PROFILE_PARTICIPATION_NON_REPLIED, 0.0f); } catch (boost::property_tree::ptree_bad_path& ex) { @@ -173,11 +173,15 @@ namespace data } } - void RouterProfile::TunnelNonReplied () + void RouterProfile::TunnelNonReplied (int tunnelLength) { - m_NumTunnelsNonReplied++; + if (!tunnelLength) return; + if (tunnelLength == 1) + m_NumTunnelsNonReplied++; + else + m_NumTunnelsNonReplied += 1.0f/tunnelLength; UpdateTime (); - if (m_NumTunnelsNonReplied > 2*m_NumTunnelsAgreed && m_NumTunnelsNonReplied > 3) + if (m_NumTunnelsNonReplied > m_NumTunnelsAgreed + m_NumTunnelsDeclined && m_NumTunnelsNonReplied > 3) { m_LastDeclineTime = i2p::util::GetSecondsSinceEpoch (); } @@ -231,7 +235,7 @@ namespace data isBad = m_ProfilesRng () % m_NumTunnelsDeclined; // only zero means not bad if (!isBad && IsLowPartcipationRate ()) { - auto failed = m_NumTunnelsDeclined + m_NumTunnelsNonReplied - m_NumTunnelsAgreed; + auto failed = m_NumTunnelsDeclined + (int)m_NumTunnelsNonReplied - m_NumTunnelsAgreed; if (failed > 0) isBad = m_ProfilesRng () % failed; // only zero means not bad } diff --git a/libi2pd/Profiling.h b/libi2pd/Profiling.h index 59995b3f..83f3cc1e 100644 --- a/libi2pd/Profiling.h +++ b/libi2pd/Profiling.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2026, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -33,7 +33,7 @@ namespace data const char PEER_PROFILE_USAGE_REJECTED[] = "rejected"; const char PEER_PROFILE_USAGE_CONNECTED[] = "connected"; const char PEER_PROFILE_USAGE_DUPLICATED[] = "duplicated"; - + const int PEER_PROFILE_EXPIRATION_TIMEOUT = 36*60*60; // in seconds (1.5 days) const int PEER_PROFILE_AUTOCLEAN_TIMEOUT = 1500; // in seconds (25 minutes) const int PEER_PROFILE_AUTOCLEAN_VARIANCE = 900; // in seconds (15 minutes) @@ -45,9 +45,9 @@ namespace data const int PEER_PROFILE_UNREACHABLE_INTERVAL = 480; // in seconds (8 minutes) const int PEER_PROFILE_USEFUL_THRESHOLD = 3; const int PEER_PROFILE_ALWAYS_DECLINING_NUM = 5; // num declines in row to consider always declined - const int PEER_PROFILE_APPLY_POSTPONED_TIMEOUT = 2100; // in milliseconds - const int PEER_PROFILE_APPLY_POSTPONED_TIMEOUT_VARIANCE = 500; // in milliseconds - + const int PEER_PROFILE_APPLY_POSTPONED_TIMEOUT = 2100; // in milliseconds + const int PEER_PROFILE_APPLY_POSTPONED_TIMEOUT_VARIANCE = 500; // in milliseconds + class RouterProfile { public: @@ -59,10 +59,10 @@ namespace data bool IsBad (); bool IsUnreachable (); - bool IsReal () const { return m_HasConnected || m_NumTunnelsAgreed > 0 || m_NumTunnelsDeclined > 0; } + bool IsReal () const { return m_HasConnected || m_NumTunnelsAgreed > 0 || m_NumTunnelsDeclined > 0; } void TunnelBuildResponse (uint8_t ret); - void TunnelNonReplied (); + void TunnelNonReplied (int tunnelLength); void Unreachable (bool unreachable); void Connected (); @@ -75,15 +75,15 @@ namespace data void SetLastAccessTime (uint64_t ts) { m_LastAccessTime = ts; }; uint64_t GetLastPersistTime () const { return m_LastPersistTime; }; void SetLastPersistTime (uint64_t ts) { m_LastPersistTime = ts; }; - + bool IsUseful() const; bool IsDuplicated () const { return m_IsDuplicated; }; const boost::asio::ip::udp::endpoint& GetLastEndpoint () const { return m_LastEndpoint; } void SetLastEndpoint (const boost::asio::ip::udp::endpoint& ep) { m_LastEndpoint = ep; } - bool HasLastEndpoint (bool v4) const { return !m_LastEndpoint.address ().is_unspecified () && m_LastEndpoint.port () && + bool HasLastEndpoint (bool v4) const { return !m_LastEndpoint.address ().is_unspecified () && m_LastEndpoint.port () && ((v4 && m_LastEndpoint.address ().is_v4 ()) || (!v4 && m_LastEndpoint.address ().is_v6 ())); } - + private: void UpdateTime (); @@ -96,16 +96,16 @@ namespace data private: bool m_IsUpdated; - uint64_t m_LastDeclineTime, m_LastUnreachableTime, m_LastUpdateTime, + uint64_t m_LastDeclineTime, m_LastUnreachableTime, m_LastUpdateTime, m_LastAccessTime, m_LastPersistTime; // in seconds // participation uint32_t m_NumTunnelsAgreed; uint32_t m_NumTunnelsDeclined; - uint32_t m_NumTunnelsNonReplied; + float m_NumTunnelsNonReplied; // per one hop tunnel, 2 hops tunnel adds 0.5 // usage uint32_t m_NumTimesTaken; uint32_t m_NumTimesRejected; - bool m_HasConnected; // successful trusted(incoming or NTCP2) connection + bool m_HasConnected; // successful trusted(incoming or NTCP2) connection bool m_IsDuplicated; // connectivity boost::asio::ip::udp::endpoint m_LastEndpoint; // SSU2 for non-published addresses diff --git a/libi2pd/Tunnel.cpp b/libi2pd/Tunnel.cpp index 3ce2b006..a47ab95a 100644 --- a/libi2pd/Tunnel.cpp +++ b/libi2pd/Tunnel.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2025, The PurpleI2P Project +* Copyright (c) 2013-2026, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -44,12 +44,12 @@ namespace tunnel void Tunnel::Build (uint32_t replyMsgID, std::shared_ptr outboundTunnel) { auto numHops = m_Config->GetNumHops (); - bool insertPhonyRecord = m_Config->IsInbound() && numHops < MAX_NUM_RECORDS; - if (insertPhonyRecord) - { + bool insertPhonyRecord = m_Config->IsInbound() && numHops < MAX_NUM_RECORDS; + if (insertPhonyRecord) + { m_Config->CreatePhonyHop (); numHops++; - } + } int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : MAX_NUM_RECORDS; auto msg = numRecords <= STANDARD_NUM_RECORDS ? NewI2NPShortMessage () : NewI2NPMessage (); *msg->GetPayload () = numRecords; @@ -58,7 +58,7 @@ namespace tunnel // shuffle records std::vector recordIndicies; for (int i = 0; i < numRecords; i++) recordIndicies.push_back(i); - std::shuffle (recordIndicies.begin(), recordIndicies.end(), tunnels.GetRng ()); + std::shuffle (recordIndicies.begin(), recordIndicies.end(), tunnels.GetRng ()); // create real records uint8_t * records = msg->GetPayload () + 1; TunnelHopConfig * hop = m_Config->GetFirstHop (); @@ -96,15 +96,15 @@ namespace tunnel } // delete phony hop after encryption if (insertPhonyRecord) m_Config->DeletePhonyHop (); - + msg->FillI2NPMessageHeader (m_Config->IsShort () ? eI2NPShortTunnelBuild : eI2NPVariableTunnelBuild); auto s = shared_from_this (); msg->onDrop = [s]() { LogPrint (eLogInfo, "I2NP: Tunnel ", s->GetTunnelID (), " request was not sent"); - s->SetState (i2p::tunnel::eTunnelStateBuildFailed); + s->SetState (i2p::tunnel::eTunnelStateBuildFailed); }; - + // send message if (outboundTunnel) { @@ -144,13 +144,13 @@ namespace tunnel { LogPrint (eLogError, "Tunnel: Too many records in TunnelBuildResponse", num); return false; - } + } if (len < num*m_Config->GetRecordSize () + 1) { LogPrint (eLogError, "Tunnel: TunnelBuildResponse of ", num, " records is too short ", len); return false; } - + TunnelHopConfig * hop = m_Config->GetLastHop (); while (hop) { @@ -263,12 +263,12 @@ namespace tunnel m_State = state; } - bool Tunnel::IsSlow () const - { + bool Tunnel::IsSlow () const + { return LatencyIsKnown() && m_Latency > HIGH_LATENCY_PER_HOP*GetNumHops () + - i2p::transport::transports.GetLocalDelay ()*1000; + i2p::transport::transports.GetLocalDelay ()*1000; } - + void Tunnel::VisitTunnelHops(TunnelHopVisitor v) { // hops are in inverted order, we must return in direct order @@ -278,18 +278,18 @@ namespace tunnel void InboundTunnel::HandleTunnelDataMsg (std::shared_ptr&& msg) { - if (!IsEstablished () && GetState () != eTunnelStateExpiring) - { + if (!IsEstablished () && GetState () != eTunnelStateExpiring) + { // incoming messages means a tunnel is alive - SetState (eTunnelStateEstablished); + SetState (eTunnelStateEstablished); auto pool = GetTunnelPool (); if (pool) { // update LeaseSet auto dest = pool->GetLocalDestination (); if (dest) dest->SetLeaseSetUpdated (true); - } - } + } + } EncryptTunnelMsg (msg, msg); msg->from = GetSharedFromThis (); m_Endpoint.HandleDecryptedTunnelDataMsg (msg); @@ -305,11 +305,11 @@ namespace tunnel SetRecreated (true); pool->RecreateInboundTunnel (std::static_pointer_cast(shared_from_this ())); return true; - } + } } return false; - } - + } + ZeroHopsInboundTunnel::ZeroHopsInboundTunnel (): InboundTunnel (std::make_shared ()), m_NumReceivedBytes (0) @@ -330,7 +330,7 @@ namespace tunnel { TunnelMessageBlock block; block.tunnelID = 0; // Initialize tunnelID to a default value - + if (gwHash) { block.hash = gwHash; @@ -349,8 +349,8 @@ namespace tunnel block.deliveryType = eDeliveryTypeLocal; } return block; - } - + } + void OutboundTunnel::SendTunnelDataMsgTo (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr msg) { auto block = CreateTunnelMessageBlock (gwHash, gwTunnel); @@ -374,10 +374,10 @@ namespace tunnel { block.data = it; m_Gateway.PutTunnelDataMsg (block); - } + } m_Gateway.SendBuffer (); - } - + } + void OutboundTunnel::HandleTunnelDataMsg (std::shared_ptr&& tunnelMsg) { LogPrint (eLogError, "Tunnel: Incoming message for outbound tunnel ", GetTunnelID ()); @@ -393,11 +393,11 @@ namespace tunnel SetRecreated (true); pool->RecreateOutboundTunnel (std::static_pointer_cast(shared_from_this ())); return true; - } + } } return false; } - + ZeroHopsOutboundTunnel::ZeroHopsOutboundTunnel (): OutboundTunnel (std::make_shared ()), m_NumSentBytes (0) @@ -436,10 +436,10 @@ namespace tunnel { block.data = it; blocks.push_back (block); - } + } SendTunnelDataMsgs (blocks); - } - + } + Tunnels tunnels; Tunnels::Tunnels (): m_IsRunning (false), m_Thread (nullptr), m_MaxNumTransitTunnels (DEFAULT_MAX_NUM_TRANSIT_TUNNELS), @@ -469,13 +469,13 @@ namespace tunnel std::lock_guard l(m_TunnelsMutex); return m_Tunnels.emplace (tunnel->GetTunnelID (), tunnel).second; } - + void Tunnels::RemoveTunnel (uint32_t tunnelID) { std::lock_guard l(m_TunnelsMutex); m_Tunnels.erase (tunnelID); - } - + } + std::shared_ptr Tunnels::GetPendingInboundTunnel (uint32_t replyMsgID) { return GetPendingTunnel (replyMsgID, m_PendingInboundTunnels); @@ -531,11 +531,11 @@ namespace tunnel return tunnel; } - std::shared_ptr Tunnels::CreateTunnelPool (int numInboundHops, - int numOutboundHops, int numInboundTunnels, int numOutboundTunnels, + std::shared_ptr Tunnels::CreateTunnelPool (int numInboundHops, + int numOutboundHops, int numInboundTunnels, int numOutboundTunnels, int inboundVariance, int outboundVariance, bool isHighBandwidth) { - auto pool = std::make_shared (numInboundHops, numOutboundHops, + auto pool = std::make_shared (numInboundHops, numOutboundHops, numInboundTunnels, numOutboundTunnels, inboundVariance, outboundVariance, isHighBandwidth); std::unique_lock l(m_PoolsMutex); m_Pools.push_back (pool); @@ -634,35 +634,35 @@ namespace tunnel } case eI2NPShortTunnelBuild: HandleShortTunnelBuildMsg (msg); - break; + break; case eI2NPVariableTunnelBuild: HandleVariableTunnelBuildMsg (msg); - break; + break; case eI2NPShortTunnelBuildReply: HandleTunnelBuildReplyMsg (msg, true); break; case eI2NPVariableTunnelBuildReply: HandleTunnelBuildReplyMsg (msg, false); - break; + break; case eI2NPTunnelBuild: case eI2NPTunnelBuildReply: LogPrint (eLogWarning, "Tunnel: TunnelBuild is too old for ECIES router"); - break; + break; default: LogPrint (eLogWarning, "Tunnel: Unexpected message type ", (int) typeID); } prevTunnelID = tunnelID; prevTunnel = tunnel; - numMsgs++; - + numMsgs++; + if (msgs.empty ()) - { + { if (numMsgs < MAX_TUNNEL_MSGS_BATCH_SIZE && !m_Queue.IsEmpty ()) m_Queue.GetWholeQueue (msgs); // try more else if (tunnel) tunnel->FlushTunnelDataMsgs (); // otherwise flush last - } + } } } @@ -743,7 +743,7 @@ namespace tunnel } else m_TransitTunnels.PostTransitTunnelBuildMsg (std::move (msg)); - } + } void Tunnels::HandleVariableTunnelBuildMsg (std::shared_ptr msg) { @@ -766,7 +766,7 @@ namespace tunnel } else m_TransitTunnels.PostTransitTunnelBuildMsg (std::move (msg)); - } + } void Tunnels::HandleTunnelBuildReplyMsg (std::shared_ptr msg, bool isShort) { @@ -790,8 +790,8 @@ namespace tunnel else LogPrint (eLogWarning, "Tunnel: Pending tunnel for message ", msg->GetMsgID(), " not found"); - } - + } + void Tunnels::ManageTunnels (uint64_t ts) { ManagePendingTunnels (ts); @@ -800,12 +800,12 @@ namespace tunnel ManageOutboundTunnels (ts, tunnelsToRecreate); // rec-create in random order if (!tunnelsToRecreate.empty ()) - { + { if (tunnelsToRecreate.size () > 1) std::shuffle (tunnelsToRecreate.begin(), tunnelsToRecreate.end(), m_Rng); for (auto& it: tunnelsToRecreate) it->Recreate (); - } + } } void Tunnels::ManagePendingTunnels (uint64_t ts) @@ -832,14 +832,15 @@ namespace tunnel auto config = tunnel->GetTunnelConfig (); if (config) { + auto numHops = config->GetNumHops (); auto hop = config->GetFirstHop (); while (hop) { if (hop->ident) i2p::data::UpdateRouterProfile (hop->ident->GetIdentHash (), - [](std::shared_ptr profile) + [numHops](std::shared_ptr profile) { - if (profile) profile->TunnelNonReplied (); + if (profile) profile->TunnelNonReplied (numHops); }); hop = hop->next; }