diff --git a/libi2pd_client/ClientContext.cpp b/libi2pd_client/ClientContext.cpp index dc6616b8..246fb803 100644 --- a/libi2pd_client/ClientContext.cpp +++ b/libi2pd_client/ClientContext.cpp @@ -680,6 +680,13 @@ namespace client auto clientTunnel = std::make_shared (name, dest, end, localDestination, destinationPort, gzip, (i2p::datagram::DatagramVersion)datagramVersion); + uint32_t keepAlive = section.second.get(I2P_CLIENT_TUNNEL_KEEP_ALIVE_INTERVAL, 0); + if (keepAlive) + { + clientTunnel->SetKeepAliveInterval (keepAlive); + LogPrint(eLogInfo, "Clients: UDP Client tunnel keep alive interval set to ", keepAlive); + } + auto ins = m_ClientForwards.insert (std::make_pair (end, clientTunnel)); if (ins.second) { diff --git a/libi2pd_client/UDPTunnel.cpp b/libi2pd_client/UDPTunnel.cpp index 279901ea..330f9be5 100644 --- a/libi2pd_client/UDPTunnel.cpp +++ b/libi2pd_client/UDPTunnel.cpp @@ -424,10 +424,15 @@ namespace client if (m_ResolveThread == nullptr) m_ResolveThread = new std::thread (std::bind (&I2PUDPClientTunnel::TryResolving, this)); RecvFromLocal (); + + if (m_KeepAliveInterval) + ScheduleKeepAliveTimer (); } void I2PUDPClientTunnel::Stop () { + if (m_KeepAliveTimer) m_KeepAliveTimer->cancel (); + auto dgram = m_LocalDest->GetDatagramDestination (); if (dgram) { @@ -670,5 +675,32 @@ namespace client else LogPrint (eLogWarning, "UDP Client: Not tracking udp session using port ", (int) toPort); } + + void I2PUDPClientTunnel::SetKeepAliveInterval (uint32_t keepAliveInterval) + { + m_KeepAliveInterval = keepAliveInterval; + if (m_KeepAliveInterval) + m_KeepAliveTimer.reset (new boost::asio::steady_timer (m_LocalDest->GetService ())); + } + + void I2PUDPClientTunnel::ScheduleKeepAliveTimer () + { + if (m_KeepAliveTimer) + { + m_KeepAliveTimer->expires_after (std::chrono::seconds (m_KeepAliveInterval)); + m_KeepAliveTimer->async_wait (std::bind (&I2PUDPClientTunnel::HandleKeepAliveTimer, + this, std::placeholders::_1)); + } + } + + void I2PUDPClientTunnel::HandleKeepAliveTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + if (i2p::util::GetMillisecondsSinceEpoch () > m_LastRepliableDatagramTime + m_KeepAliveInterval*1000) + HandleRecvFromLocal (boost::system::error_code(), 0); // send empty packet like it was received from local + ScheduleKeepAliveTimer (); + } + } } } diff --git a/libi2pd_client/UDPTunnel.h b/libi2pd_client/UDPTunnel.h index 424bd397..3d8aaf7d 100644 --- a/libi2pd_client/UDPTunnel.h +++ b/libi2pd_client/UDPTunnel.h @@ -183,6 +183,7 @@ namespace client const boost::asio::ip::udp::endpoint& GetLocalEndpoint () const { return m_LocalEndpoint; }; void ExpireStale (const uint64_t delta=I2P_UDP_SESSION_TIMEOUT); + void SetKeepAliveInterval (uint32_t keepAliveInterval); private: @@ -194,6 +195,9 @@ namespace client void HandleRecvFromI2PRaw (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len); void TryResolving (); + void ScheduleKeepAliveTimer (); + void HandleKeepAliveTimer (const boost::system::error_code& ecode); + private: const std::string m_Name; @@ -211,6 +215,8 @@ namespace client bool m_Gzip; i2p::datagram::DatagramVersion m_DatagramVersion; std::shared_ptr m_LastSession; + uint32_t m_KeepAliveInterval; + std::unique_ptr m_KeepAliveTimer; public: