diff --git a/libi2pd/Config.cpp b/libi2pd/Config.cpp index 26f09696..4997852a 100644 --- a/libi2pd/Config.cpp +++ b/libi2pd/Config.cpp @@ -71,6 +71,8 @@ namespace config { ("limits.coresize", value()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)") ("limits.openfiles", value()->default_value(0), "Maximum number of open files (0 - use system default)") ("limits.transittunnels", value()->default_value(2500), "Maximum active transit sessions (default:2500)") + ("limits.ntcpsoft", value()->default_value(0), "Threshold to start probabalistic backoff with ntcp sessions (default: use system limit)") + ("limits.ntcphard", value()->default_value(0), "Maximum number of ntcp sessions (default: use system limit)") ; options_description httpserver("HTTP Server options"); diff --git a/libi2pd/NTCPSession.cpp b/libi2pd/NTCPSession.cpp index a6329f04..cc46f9e6 100644 --- a/libi2pd/NTCPSession.cpp +++ b/libi2pd/NTCPSession.cpp @@ -791,7 +791,8 @@ namespace transport NTCPServer::NTCPServer (): m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), m_TerminationTimer (m_Service), m_NTCPAcceptor (nullptr), m_NTCPV6Acceptor (nullptr), - m_ProxyType(eNoProxy), m_Resolver(m_Service), m_ProxyEndpoint(nullptr) + m_ProxyType(eNoProxy), m_Resolver(m_Service), m_ProxyEndpoint(nullptr), + m_SoftLimit(0), m_HardLimit(0) { } @@ -965,6 +966,13 @@ namespace transport auto ep = conn->GetSocket ().remote_endpoint(ec); if (!ec) { + if(ShouldLimit()) + { + // hit limit, close premature + LogPrint(eLogWarning, "NTCP: limiting with backoff session from ", ep); + conn->GetSocket().close(); + return; + } LogPrint (eLogDebug, "NTCP: Connected from ", ep); if (conn) { @@ -993,6 +1001,14 @@ namespace transport auto ep = conn->GetSocket ().remote_endpoint(ec); if (!ec) { + if(ShouldLimit()) + { + // hit limit, close premature + LogPrint(eLogWarning, "NTCP: limiting with backoff on session from ", ep); + conn->GetSocket().close(); + return; + } + LogPrint (eLogDebug, "NTCP: Connected from ", ep); if (conn) { diff --git a/libi2pd/NTCPSession.h b/libi2pd/NTCPSession.h index a45f06f7..b64f63aa 100644 --- a/libi2pd/NTCPSession.h +++ b/libi2pd/NTCPSession.h @@ -167,8 +167,19 @@ namespace transport boost::asio::io_service& GetService () { return m_Service; }; + void SetSessionLimits(uint16_t softLimit, uint16_t hardLimit) { m_SoftLimit = softLimit; m_HardLimit = hardLimit; } + bool ShouldLimit() const { return ShouldHardLimit() || ShouldSoftLimit(); } private: + /** @brief return true for hard limit */ + bool ShouldHardLimit() const { return m_HardLimit && m_NTCPSessions.size() >= m_HardLimit; } + + /** @brief return true for probabalistic soft backoff */ + bool ShouldSoftLimit() const + { + auto sessions = m_NTCPSessions.size(); + return sessions && m_SoftLimit && m_SoftLimit < sessions && ( rand() % sessions ) <= m_SoftLimit; + } void Run (); void HandleAccept (std::shared_ptr conn, const boost::system::error_code& error); void HandleAcceptV6 (std::shared_ptr conn, const boost::system::error_code& error); @@ -198,6 +209,8 @@ namespace transport uint16_t m_ProxyPort; boost::asio::ip::tcp::resolver m_Resolver; boost::asio::ip::tcp::endpoint * m_ProxyEndpoint; + + uint16_t m_SoftLimit, m_HardLimit; public: // for HTTP/I2PControl diff --git a/libi2pd/Transports.cpp b/libi2pd/Transports.cpp index f375296f..3ae0d56a 100644 --- a/libi2pd/Transports.cpp +++ b/libi2pd/Transports.cpp @@ -147,13 +147,20 @@ namespace transport m_PeerTestTimer = new boost::asio::deadline_timer (*m_Service); } - i2p::config::GetOption("nat", m_IsNAT); - + i2p::config::GetOption("nat", m_IsNAT); m_DHKeysPairSupplier.Start (); m_IsRunning = true; m_Thread = new std::thread (std::bind (&Transports::Run, this)); std::string ntcpproxy; i2p::config::GetOption("ntcpproxy", ntcpproxy); i2p::http::URL proxyurl; + uint16_t softLimit, hardLimit; + i2p::config::GetOption("limits.ntcpsoft", softLimit); + i2p::config::GetOption("limits.ntcphard", hardLimit); + if(softLimit >= hardLimit) + { + LogPrint(eLogError, "ntcp soft limit must be less than ntcp hard limit"); + return; + } if(ntcpproxy.size() && enableNTCP) { if(proxyurl.parse(ntcpproxy)) @@ -161,12 +168,11 @@ namespace transport if(proxyurl.schema == "socks" || proxyurl.schema == "http") { m_NTCPServer = new NTCPServer(); - + m_NTCPServer->SetSessionLimits(softLimit, hardLimit); NTCPServer::ProxyType proxytype = NTCPServer::eSocksProxy; if (proxyurl.schema == "http") proxytype = NTCPServer::eHTTPProxy; - m_NTCPServer->UseProxy(proxytype, proxyurl.host, proxyurl.port) ; m_NTCPServer->Start(); if(!m_NTCPServer->NetworkIsReady()) @@ -193,6 +199,7 @@ namespace transport if (m_NTCPServer == nullptr && enableNTCP) { m_NTCPServer = new NTCPServer (); + m_NTCPServer->SetSessionLimits(softLimit, hardLimit); m_NTCPServer->Start (); if (!(m_NTCPServer->IsBoundV6() || m_NTCPServer->IsBoundV4())) { /** failed to bind to NTCP */ @@ -394,20 +401,27 @@ namespace transport { if (!peer.router->UsesIntroducer () && !peer.router->IsUnreachable ()) { - auto s = std::make_shared (*m_NTCPServer, peer.router); - if(m_NTCPServer->UsingProxy()) + if(!m_NTCPServer->ShouldLimit()) { - NTCPServer::RemoteAddressType remote = NTCPServer::eIP4Address; - std::string addr = address->host.to_string(); - - if(address->host.is_v6()) - remote = NTCPServer::eIP6Address; - - m_NTCPServer->ConnectWithProxy(addr, address->port, remote, s); + auto s = std::make_shared (*m_NTCPServer, peer.router); + if(m_NTCPServer->UsingProxy()) + { + NTCPServer::RemoteAddressType remote = NTCPServer::eIP4Address; + std::string addr = address->host.to_string(); + + if(address->host.is_v6()) + remote = NTCPServer::eIP6Address; + + m_NTCPServer->ConnectWithProxy(addr, address->port, remote, s); + } + else + m_NTCPServer->Connect (address->host, address->port, s); + return true; } else - m_NTCPServer->Connect (address->host, address->port, s); - return true; + { + LogPrint(eLogWarning, "Transports: NTCP Limit hit falling back to SSU"); + } } } else // we don't have address