diff -U2 -r /var/lib/copr-rpmbuild/results/i2pd-git/upstream-unpacked/Source0/i2pd-openssl/libi2pd_client/ClientContext.cpp /var/lib/copr-rpmbuild/results/i2pd-git/srpm-unpacked/i2pd-openssl.tar.gz-extract/i2pd-openssl/libi2pd_client/ClientContext.cpp --- /var/lib/copr-rpmbuild/results/i2pd-git/upstream-unpacked/Source0/i2pd-openssl/libi2pd_client/ClientContext.cpp 2026-08-23 00:05:54.000000000 +0000 +++ /var/lib/copr-rpmbuild/results/i2pd-git/srpm-unpacked/i2pd-openssl.tar.gz-extract/i2pd-openssl/libi2pd_client/ClientContext.cpp 2026-08-22 23:43:23.000000000 +0000 @@ -118,4 +118,11 @@ m_AddressBook.StartResolvers (); + // start UDP cleanup + if (!m_ServerForwards.empty ()) + { + m_CleanupUDPTimer.reset (new boost::asio::steady_timer(m_SharedLocalDestination->GetService ())); + ScheduleCleanupUDP(); + } + // start torrents RPC server for (auto& it: m_TorrentsRPCServers) @@ -214,4 +221,11 @@ } + LogPrint(eLogInfo, "Clients: Stopping UDP Tunnels timers"); + if (m_CleanupUDPTimer) + { + m_CleanupUDPTimer->cancel (); + m_CleanupUDPTimer = nullptr; + } + { LogPrint(eLogInfo, "Clients: Stopping Destinations"); @@ -1168,4 +1182,24 @@ } } + + void ClientContext::ScheduleCleanupUDP() + { + if (m_CleanupUDPTimer) + { + // schedule cleanup in 17 seconds + m_CleanupUDPTimer->expires_after (std::chrono::seconds (17)); + m_CleanupUDPTimer->async_wait(std::bind(&ClientContext::CleanupUDP, this, std::placeholders::_1)); + } + } + + void ClientContext::CleanupUDP(const boost::system::error_code & ecode) + { + if(!ecode) + { + std::lock_guard lock(m_ForwardsMutex); + for (auto & s : m_ServerForwards ) s.second->ExpireStale(); + ScheduleCleanupUDP(); + } + } void ClientContext::VisitTunnels (bool clean) diff -U2 -r /var/lib/copr-rpmbuild/results/i2pd-git/upstream-unpacked/Source0/i2pd-openssl/libi2pd_client/ClientContext.h /var/lib/copr-rpmbuild/results/i2pd-git/srpm-unpacked/i2pd-openssl.tar.gz-extract/i2pd-openssl/libi2pd_client/ClientContext.h --- /var/lib/copr-rpmbuild/results/i2pd-git/upstream-unpacked/Source0/i2pd-openssl/libi2pd_client/ClientContext.h 2026-08-23 00:05:54.000000000 +0000 +++ /var/lib/copr-rpmbuild/results/i2pd-git/srpm-unpacked/i2pd-openssl.tar.gz-extract/i2pd-openssl/libi2pd_client/ClientContext.h 2026-08-22 23:43:23.000000000 +0000 @@ -145,4 +145,6 @@ void ReadI2CPOptionsFromConfig (const std::string& prefix, i2p::util::Mapping& options) const; // for HTTP and SOCKS proxy + void CleanupUDP(const boost::system::error_code & ecode); + void ScheduleCleanupUDP(); void VisitTunnels (bool clean); @@ -177,4 +179,5 @@ I2CPServer * m_I2CPServer; + std::unique_ptr m_CleanupUDPTimer; // i18n diff -U2 -r /var/lib/copr-rpmbuild/results/i2pd-git/upstream-unpacked/Source0/i2pd-openssl/libi2pd_client/UDPTunnel.cpp /var/lib/copr-rpmbuild/results/i2pd-git/srpm-unpacked/i2pd-openssl.tar.gz-extract/i2pd-openssl/libi2pd_client/UDPTunnel.cpp --- /var/lib/copr-rpmbuild/results/i2pd-git/upstream-unpacked/Source0/i2pd-openssl/libi2pd_client/UDPTunnel.cpp 2026-08-23 00:05:54.000000000 +0000 +++ /var/lib/copr-rpmbuild/results/i2pd-git/srpm-unpacked/i2pd-openssl.tar.gz-extract/i2pd-openssl/libi2pd_client/UDPTunnel.cpp 2026-08-22 23:43:23.000000000 +0000 @@ -489,25 +489,4 @@ m_StatsTimer.reset (new boost::asio::steady_timer (m_LocalDest->GetService ())); ScheduleStatsTimer (); - m_CleanupTimer.reset (new boost::asio::steady_timer (m_LocalDest->GetService ())); - ScheduleCleanupTimer (); - } - - void I2PUDPServerTunnel::ScheduleCleanupTimer () - { - if (m_CleanupTimer) - { - m_CleanupTimer->expires_after (std::chrono::seconds (I2P_UDP_SESSION_CLEANUP_INTERVAL)); - m_CleanupTimer->async_wait (std::bind (&I2PUDPServerTunnel::HandleCleanupTimer, - this, std::placeholders::_1)); - } - } - - void I2PUDPServerTunnel::HandleCleanupTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - ExpireStale (); - ScheduleCleanupTimer (); - } } @@ -546,5 +525,4 @@ { if (m_StatsTimer) m_StatsTimer->cancel (); - if (m_CleanupTimer) m_CleanupTimer->cancel (); auto dgram = m_LocalDest->GetDatagramDestination (); if (dgram) { diff -U2 -r /var/lib/copr-rpmbuild/results/i2pd-git/upstream-unpacked/Source0/i2pd-openssl/libi2pd_client/UDPTunnel.h /var/lib/copr-rpmbuild/results/i2pd-git/srpm-unpacked/i2pd-openssl.tar.gz-extract/i2pd-openssl/libi2pd_client/UDPTunnel.h --- /var/lib/copr-rpmbuild/results/i2pd-git/upstream-unpacked/Source0/i2pd-openssl/libi2pd_client/UDPTunnel.h 2026-08-23 00:05:54.000000000 +0000 +++ /var/lib/copr-rpmbuild/results/i2pd-git/srpm-unpacked/i2pd-openssl.tar.gz-extract/i2pd-openssl/libi2pd_client/UDPTunnel.h 2026-08-22 23:43:23.000000000 +0000 @@ -31,5 +31,4 @@ /** 2 minute timeout for udp sessions */ const uint64_t I2P_UDP_SESSION_TIMEOUT = 1000 * 60 * 2; - const int I2P_UDP_SESSION_CLEANUP_INTERVAL = 17; // in seconds const uint64_t I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL = 100; // in milliseconds const uint64_t I2P_UDP_MAX_UNACKED_DATAGRAM_TIME = 8000; // in milliseconds @@ -178,6 +177,4 @@ void ScheduleStatsTimer (); void HandleStatsTimer (const boost::system::error_code& ecode); - void ScheduleCleanupTimer (); - void HandleCleanupTimer (const boost::system::error_code& ecode); private: @@ -196,5 +193,4 @@ size_t m_MaxWindow = I2P_UDP_MIN_MAX_NUM_UNACKED_DATAGRAMS; std::unique_ptr m_StatsTimer; - std::unique_ptr m_CleanupTimer; public: @@ -245,6 +241,4 @@ void ScheduleStatsTimer (); void HandleStatsTimer (const boost::system::error_code& ecode); - void ScheduleCleanupTimer (); - void HandleCleanupTimer (const boost::system::error_code& ecode); private: