From a683b98e73948748b6fb529fc21fdf36d831a020 Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 2 Apr 2026 13:37:35 -0400 Subject: [PATCH] check for idle time. don't create and test tunnel if idling --- libi2pd/Garlic.cpp | 20 +++++++++---------- libi2pd/Garlic.h | 3 +++ libi2pd/TunnelPool.cpp | 7 +++++-- libi2pd_client/I2PService.cpp | 37 +++++++++++++++++++++++++++++++++++ libi2pd_client/I2PService.h | 6 +++++- 5 files changed, 60 insertions(+), 13 deletions(-) diff --git a/libi2pd/Garlic.cpp b/libi2pd/Garlic.cpp index 8221134c..1d19fc71 100644 --- a/libi2pd/Garlic.cpp +++ b/libi2pd/Garlic.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 * @@ -105,8 +105,8 @@ namespace garlic for (auto& msg: msgs) ret.push_back (WrapSingleMessage(msg)); return ret; - } - + } + ElGamalAESSession::ElGamalAESSession (GarlicDestination * owner, std::shared_ptr destination, int numTags, bool attachLeaseSet): GarlicRoutingSession (owner, attachLeaseSet), @@ -435,8 +435,8 @@ namespace garlic GarlicDestination::GarlicDestination (): m_Rng(i2p::util::GetMonotonicMicroseconds () % 1000000LL), - m_NumTags (32), // 32 tags by default - m_PayloadBuffer (nullptr), m_LastIncomingSessionTimestamp (0), + m_IsIdling (false), m_NumTags (32), // 32 tags by default + m_PayloadBuffer (nullptr), m_LastIncomingSessionTimestamp (0), m_NumRatchetInboundTags (0) // 0 means standard { } @@ -550,8 +550,8 @@ namespace garlic { // otherwise ECIESx25519 auto ts = i2p::util::GetMillisecondsSinceEpoch (); - if (ts > m_LastIncomingSessionTimestamp + INCOMING_SESSIONS_MINIMAL_INTERVAL) - { + if (ts > m_LastIncomingSessionTimestamp + INCOMING_SESSIONS_MINIMAL_INTERVAL) + { auto session = std::make_shared (this, false); // incoming if (session->HandleNextMessage (buf, length, nullptr, 0)) m_LastIncomingSessionTimestamp = ts; @@ -761,7 +761,7 @@ namespace garlic if (destination->GetEncryptionType () >= i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) { if (SupportsEncryptionType (destination->GetEncryptionType ())) - { + { ECIESX25519AEADRatchetSessionPtr session; uint8_t staticKey[32]; destination->Encrypt (nullptr, staticKey); // we are supposed to get static key @@ -1125,11 +1125,11 @@ namespace garlic { return m_Encryptor.Encrypt (msg, msgLen, ad, adLen, key, nonce, buf, len); } - + bool GarlicDestination::AEADChaCha20Poly1305Decrypt (const uint8_t * msg, size_t msgLen, const uint8_t * ad, size_t adLen, const uint8_t * key, const uint8_t * nonce, uint8_t * buf, size_t len) { return m_Decryptor.Decrypt (msg, msgLen, ad, adLen, key, nonce, buf, len); - } + } } } diff --git a/libi2pd/Garlic.h b/libi2pd/Garlic.h index fe74b264..ef395678 100644 --- a/libi2pd/Garlic.h +++ b/libi2pd/Garlic.h @@ -245,6 +245,8 @@ namespace garlic void CleanUp (); std::mt19937& GetRng () { return m_Rng; }; + bool IsIdling () const { return m_IsIdling; }; + void SetIsIdling (bool isIdling) { m_IsIdling = isIdling; }; void SetNumTags (int numTags) { m_NumTags = numTags; }; int GetNumTags () const { return m_NumTags; }; void SetNumRatchetInboundTags (int numTags) { m_NumRatchetInboundTags = numTags; }; @@ -308,6 +310,7 @@ namespace garlic private: std::mt19937 m_Rng; + bool m_IsIdling; // outgoing sessions int m_NumTags; std::mutex m_SessionsMutex; diff --git a/libi2pd/TunnelPool.cpp b/libi2pd/TunnelPool.cpp index e9e71ca4..07c682bc 100644 --- a/libi2pd/TunnelPool.cpp +++ b/libi2pd/TunnelPool.cpp @@ -480,8 +480,11 @@ namespace tunnel { if (ts > m_NextManageTime || ts + 2*TUNNEL_POOL_MANAGE_INTERVAL < m_NextManageTime) // in case if clock was adjusted { - CreateTunnels (ts); - TestTunnels (ts); + if (!m_LocalDestination || !m_LocalDestination->IsIdling ()) + { + CreateTunnels (ts); + TestTunnels (ts); + } m_NextManageTime = ts + TUNNEL_POOL_MANAGE_INTERVAL + (tunnels.GetRng ()() % TUNNEL_POOL_MANAGE_INTERVAL)/2; } } diff --git a/libi2pd_client/I2PService.cpp b/libi2pd_client/I2PService.cpp index 11b200f7..186368f4 100644 --- a/libi2pd_client/I2PService.cpp +++ b/libi2pd_client/I2PService.cpp @@ -54,10 +54,28 @@ namespace client m_ConnectTimeout = timeout; } + void I2PService::SetCloseIdleTime (uint64_t idleTime) + { + if (idleTime > 0 && idleTime < I2P_SERVICE_MIN_CLOSE_IDLE_TIME) idleTime = I2P_SERVICE_MIN_CLOSE_IDLE_TIME; + m_CloseIdleTime = idleTime; + if (m_CloseIdleTime) + { + if (!m_IdleCheckTimer) m_IdleCheckTimer.reset (new boost::asio::steady_timer(m_LocalDestination->GetService ())); + ScheduleIdleCheckTimer (); + } + } + void I2PService::UpdateLastActivityTime () { if (m_CloseIdleTime) + { m_LastActivityTime = i2p::util::GetMonotonicMilliseconds (); + if (m_LocalDestination->IsIdling ()) + { + m_LocalDestination->SetIsIdling (false); + ScheduleIdleCheckTimer (); + } + } } void I2PService::AddReadyCallback(ReadyCallback cb) @@ -108,6 +126,25 @@ namespace client m_ReadyTimerTriggered = false; } + void I2PService::ScheduleIdleCheckTimer () + { + if (!m_IdleCheckTimer || !m_CloseIdleTime) return; + m_IdleCheckTimer->expires_after(std::chrono::milliseconds (m_CloseIdleTime/2)); + m_IdleCheckTimer->async_wait(std::bind(&I2PService::HandleIdleCheckTimer, shared_from_this (), std::placeholders::_1)); + } + + void I2PService::HandleIdleCheckTimer(const boost::system::error_code & ec) + { + if (ec != boost::asio::error::operation_aborted) + { + auto ts = i2p::util::GetMonotonicMilliseconds (); + if (ts > m_LastActivityTime + m_CloseIdleTime) + m_LocalDestination->SetIsIdling (true); + else + ScheduleIdleCheckTimer (); + } + } + void I2PService::CreateStream (StreamRequestComplete streamRequestComplete, std::string_view dest, uint16_t port) { assert(streamRequestComplete); auto address = i2p::client::context.GetAddressBook ().GetAddress (dest); diff --git a/libi2pd_client/I2PService.h b/libi2pd_client/I2PService.h index 20fecc7d..ff4d98fe 100644 --- a/libi2pd_client/I2PService.h +++ b/libi2pd_client/I2PService.h @@ -23,6 +23,7 @@ namespace i2p namespace client { constexpr int I2P_SERVICE_READINESS_CHECK_INTERVAL = 1; // in seconds + constexpr uint64_t I2P_SERVICE_MIN_CLOSE_IDLE_TIME = 30000; // in milliseconds class I2PServiceHandler; class I2PService : public std::enable_shared_from_this @@ -50,7 +51,7 @@ namespace client void ClearHandlers (); void SetConnectTimeout(uint64_t timeout); - void SetCloseIdleTime (uint64_t idleTime) { m_CloseIdleTime = idleTime; }; + void SetCloseIdleTime (uint64_t idleTime); void UpdateLastActivityTime (); void AddReadyCallback(ReadyCallback cb); @@ -76,6 +77,8 @@ namespace client void TriggerReadyCheckTimer(); void HandleReadyCheckTimer(const boost::system::error_code & ec); + void ScheduleIdleCheckTimer (); + void HandleIdleCheckTimer(const boost::system::error_code & ec); private: @@ -88,6 +91,7 @@ namespace client uint64_t m_ConnectTimeout; // in seconds uint64_t m_CloseIdleTime; // in milliseconds uint64_t m_LastActivityTime; // monotonic milliseconds + std::unique_ptr m_IdleCheckTimer; static constexpr size_t NEVER_TIMES_OUT = 0;