From fc94e846a6a30eccdc811c83c81ad21eef7f8c27 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 15 Nov 2016 10:20:09 -0500 Subject: [PATCH] add latency requirement option --- ClientContext.cpp | 8 +++++++- Config.cpp | 8 ++++++-- Destination.cpp | 16 ++++++++++++++++ Destination.h | 6 ++++++ Tunnel.cpp | 23 +++++++++++++++++++++++ Tunnel.h | 25 +++++++++++++++++++++++++ TunnelPool.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++-- TunnelPool.h | 15 +++++++++++++++ 8 files changed, 141 insertions(+), 5 deletions(-) diff --git a/ClientContext.cpp b/ClientContext.cpp index 4639ec05..52423a23 100644 --- a/ClientContext.cpp +++ b/ClientContext.cpp @@ -372,6 +372,8 @@ namespace client options[I2CP_PARAM_INBOUND_TUNNELS_QUANTITY] = GetI2CPOption (section, I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, DEFAULT_INBOUND_TUNNELS_QUANTITY); options[I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY] = GetI2CPOption (section, I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, DEFAULT_OUTBOUND_TUNNELS_QUANTITY); options[I2CP_PARAM_TAGS_TO_SEND] = GetI2CPOption (section, I2CP_PARAM_TAGS_TO_SEND, DEFAULT_TAGS_TO_SEND); + options[I2CP_PARAM_MIN_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MIN_TUNNEL_LATENCY, DEFAULT_MIN_TUNNEL_LATENCY); + options[I2CP_PARAM_MAX_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MAX_TUNNEL_LATENCY, DEFAULT_MAX_TUNNEL_LATENCY); } void ClientContext::ReadI2CPOptionsFromConfig (const std::string& prefix, std::map& options) const @@ -384,7 +386,11 @@ namespace client if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, value)) options[I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH] = value; if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, value)) - options[I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY] = value; + options[I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY] = value; + if (i2p::config::GetOption(prefix + I2CP_PARAM_MIN_TUNNEL_LATENCY, value)) + options[I2CP_PARAM_MIN_TUNNEL_LATENCY] = value; + if (i2p::config::GetOption(prefix + I2CP_PARAM_MAX_TUNNEL_LATENCY, value)) + options[I2CP_PARAM_MAX_TUNNEL_LATENCY] = value; } void ClientContext::ReadTunnels () diff --git a/Config.cpp b/Config.cpp index 8d932048..ed22c561 100644 --- a/Config.cpp +++ b/Config.cpp @@ -86,7 +86,9 @@ namespace config { ("httpproxy.inbound.length", value()->default_value("3"), "HTTP proxy inbound tunnel length") ("httpproxy.outbound.length", value()->default_value("3"), "HTTP proxy outbound tunnel length") ("httpproxy.inbound.quantity", value()->default_value("5"), "HTTP proxy inbound tunnels quantity") - ("httpproxy.outbound.quantity", value()->default_value("5"), "HTTP proxy outbound tunnels quantity") + ("httpproxy.outbound.quantity", value()->default_value("5"), "HTTP proxy outbound tunnels quantity") + ("httpproxy.latency.min", value()->default_value(0), "HTTP proxy min latency for tunnels") + ("httpproxy.latency.max", value()->default_value(0), "HTTP proxy max latency for tunnels") ; options_description socksproxy("SOCKS Proxy options"); @@ -98,7 +100,9 @@ namespace config { ("socksproxy.inbound.length", value()->default_value("3"), "SOCKS proxy inbound tunnel length") ("socksproxy.outbound.length", value()->default_value("3"), "SOCKS proxy outbound tunnel length") ("socksproxy.inbound.quantity", value()->default_value("5"), "SOCKS proxy inbound tunnels quantity") - ("socksproxy.outbound.quantity", value()->default_value("5"), "SOCKS proxy outbound tunnels quantity") + ("socksproxy.outbound.quantity", value()->default_value("5"), "SOCKS proxy outbound tunnels quantity") + ("socksproxy.latency.min", value()->default_value(0), "SOCKS proxy min latency for tunnels") + ("socksproxy.latency.max", value()->default_value(0), "SOCKS proxy max latency for tunnels") ("socksproxy.outproxy", value()->default_value("127.0.0.1"), "Upstream outproxy address for SOCKS Proxy") ("socksproxy.outproxyport", value()->default_value(9050), "Upstream outproxy port for SOCKS Proxy") ; diff --git a/Destination.cpp b/Destination.cpp index 9df3b438..f0bc52cb 100644 --- a/Destination.cpp +++ b/Destination.cpp @@ -63,6 +63,22 @@ namespace client m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inLen, outLen, inQty, outQty); if (explicitPeers) m_Pool->SetExplicitPeers (explicitPeers); + if(params) + { + auto itr = params->find(I2CP_PARAM_MAX_TUNNEL_LATENCY); + if (itr != params->end()) { + auto maxlatency = std::stoi(itr->second); + itr = params->find(I2CP_PARAM_MIN_TUNNEL_LATENCY); + if (itr != params->end()) { + auto minlatency = std::stoi(itr->second); + if ( minlatency > 0 && maxlatency > 0 ) { + // set tunnel pool latency + LogPrint(eLogInfo, "Destination: requiring tunnel latency [", minlatency, "ms, ", maxlatency, "ms]"); + m_Pool->RequireLatency(minlatency, maxlatency); + } + } + } + } } LeaseSetDestination::~LeaseSetDestination () diff --git a/Destination.h b/Destination.h index 121b7e16..0d3b3b4a 100644 --- a/Destination.h +++ b/Destination.h @@ -50,6 +50,12 @@ namespace client const char I2CP_PARAM_TAGS_TO_SEND[] = "crypto.tagsToSend"; const int DEFAULT_TAGS_TO_SEND = 40; + // latency + const char I2CP_PARAM_MIN_TUNNEL_LATENCY[] = "latency.min"; + const int DEFAULT_MIN_TUNNEL_LATENCY = 0; + const char I2CP_PARAM_MAX_TUNNEL_LATENCY[] = "latency.max"; + const int DEFAULT_MAX_TUNNEL_LATENCY = 0; + typedef std::function stream)> StreamRequestComplete; class LeaseSetDestination: public i2p::garlic::GarlicDestination, diff --git a/Tunnel.cpp b/Tunnel.cpp index faba99d3..2d55547c 100644 --- a/Tunnel.cpp +++ b/Tunnel.cpp @@ -21,6 +21,23 @@ namespace i2p namespace tunnel { + void TunnelLatency::AddSample(Sample s) + { + m_samples ++; + m_latency += s / m_samples; + } + + bool TunnelLatency::HasSamples() const + { + return m_samples > 0; + } + + TunnelLatency::Latency TunnelLatency::GetMeanLatency() const + { + return m_latency; + } + + Tunnel::Tunnel (std::shared_ptr config): TunnelBase (config->GetTunnelID (), config->GetNextTunnelID (), config->GetNextIdentHash ()), m_Config (config), m_Pool (nullptr), m_State (eTunnelStatePending), m_IsRecreated (false) @@ -162,6 +179,12 @@ namespace tunnel return established; } + bool Tunnel::LatencyFitsRange(uint64_t lower, uint64_t upper) const + { + auto latency = GetMeanLatency(); + return latency >= lower && latency <= upper; + } + void Tunnel::EncryptTunnelMsg (std::shared_ptr in, std::shared_ptr out) { const uint8_t * inPayload = in->GetPayload () + 4; diff --git a/Tunnel.h b/Tunnel.h index 53c93e90..8712f56d 100644 --- a/Tunnel.h +++ b/Tunnel.h @@ -79,6 +79,22 @@ namespace tunnel eTunnelStateExpiring }; + /** @brief for storing latency history */ + struct TunnelLatency + { + typedef uint64_t Sample; + typedef uint64_t Latency; + + + void AddSample(Sample s); + bool HasSamples() const; + Latency GetMeanLatency() const; + + Latency m_latency = 0; + std::size_t m_samples = 0; + + }; + class OutboundTunnel; class InboundTunnel; class Tunnel: public TunnelBase @@ -118,6 +134,14 @@ namespace tunnel void SendTunnelDataMsg (std::shared_ptr msg); void EncryptTunnelMsg (std::shared_ptr in, std::shared_ptr out); + /** @brief add latency sample */ + void AddLatencySample(const uint64_t ms) { m_Latency.AddSample(ms); } + /** @brief get this tunnel's estimated latency */ + uint64_t GetMeanLatency() const { return m_Latency.GetMeanLatency(); } + /** @breif return true if this tunnel's latency fits in range [lowerbound, upperbound] */ + bool LatencyFitsRange(uint64_t lowerbound, uint64_t upperbound) const; + + bool LatencyIsKnown() const { return m_Latency.HasSamples(); } protected: void PrintHops (std::stringstream& s) const; @@ -129,6 +153,7 @@ namespace tunnel std::shared_ptr m_Pool; // pool, tunnel belongs to, or null TunnelState m_State; bool m_IsRecreated; + TunnelLatency m_Latency; }; class OutboundTunnel: public Tunnel diff --git a/TunnelPool.cpp b/TunnelPool.cpp index 56d72bfb..625c7d11 100644 --- a/TunnelPool.cpp +++ b/TunnelPool.cpp @@ -147,12 +147,16 @@ namespace tunnel std::shared_ptr TunnelPool::GetNextOutboundTunnel (std::shared_ptr excluded) const { - std::unique_lock l(m_OutboundTunnelsMutex); + if (HasLatencyRequriement()) + return GetLowestLatencyOutboundTunnel(excluded); + std::unique_lock l(m_OutboundTunnelsMutex); return GetNextTunnel (m_OutboundTunnels, excluded); } std::shared_ptr TunnelPool::GetNextInboundTunnel (std::shared_ptr excluded) const { + if (HasLatencyRequriement()) + return GetLowestLatencyInboundTunnel(excluded); std::unique_lock l(m_InboundTunnelsMutex); return GetNextTunnel (m_InboundTunnels, excluded); } @@ -322,7 +326,12 @@ namespace tunnel test.first->SetState (eTunnelStateEstablished); if (test.second->GetState () == eTunnelStateTestFailed) test.second->SetState (eTunnelStateEstablished); - LogPrint (eLogDebug, "Tunnels: test of ", msgID, " successful. ", i2p::util::GetMillisecondsSinceEpoch () - timestamp, " milliseconds"); + uint64_t dlt = i2p::util::GetMillisecondsSinceEpoch () - timestamp; + LogPrint (eLogDebug, "Tunnels: test of ", msgID, " successful. ", dlt, " milliseconds"); + // update latency + uint64_t latency = dlt / 2; + test.first->AddLatencySample(latency); + test.second->AddLatencySample(latency); } else { @@ -523,5 +532,37 @@ namespace tunnel std::lock_guard lock(m_CustomPeerSelectorMutex); return m_CustomPeerSelector != nullptr; } + + std::shared_ptr TunnelPool::GetLowestLatencyInboundTunnel(std::shared_ptr exclude) const + { + std::shared_ptr tun = nullptr; + std::unique_lock lock(m_InboundTunnelsMutex); + uint64_t min = 1000000; + for (const auto & itr : m_InboundTunnels) { + if(!itr->LatencyIsKnown()) continue; + auto l = itr->GetMeanLatency(); + if (l >= min) continue; + tun = itr; + if(tun == exclude) continue; + min = l; + } + return tun; + } + + std::shared_ptr TunnelPool::GetLowestLatencyOutboundTunnel(std::shared_ptr exclude) const + { + std::shared_ptr tun = nullptr; + std::unique_lock lock(m_OutboundTunnelsMutex); + uint64_t min = 1000000; + for (const auto & itr : m_OutboundTunnels) { + if(!itr->LatencyIsKnown()) continue; + auto l = itr->GetMeanLatency(); + if (l >= min) continue; + tun = itr; + if(tun == exclude) continue; + min = l; + } + return tun; + } } } diff --git a/TunnelPool.h b/TunnelPool.h index d5bcf18f..ce20be6b 100644 --- a/TunnelPool.h +++ b/TunnelPool.h @@ -69,6 +69,17 @@ namespace tunnel void SetCustomPeerSelector(TunnelPeerSelector selector); void UnsetCustomPeerSelector(); bool HasCustomPeerSelector(); + + /** @brief make this tunnel pool yield tunnels that fit latency range [min, max] */ + void RequireLatency(uint64_t min, uint64_t max) { m_MinLatency = min; m_MaxLatency = max; } + + /** @brief return true if this tunnel pool has a latency requirement */ + bool HasLatencyRequriement() const { return m_MinLatency > 0 && m_MaxLatency > 0; } + + /** @brief get the lowest latency tunnel in this tunnel pool regardless of latency requirements */ + std::shared_ptr GetLowestLatencyInboundTunnel(std::shared_ptr exclude=nullptr) const; + std::shared_ptr GetLowestLatencyOutboundTunnel(std::shared_ptr exclude=nullptr) const; + private: void CreateInboundTunnel (); @@ -94,6 +105,10 @@ namespace tunnel bool m_IsActive; std::mutex m_CustomPeerSelectorMutex; TunnelPeerSelector m_CustomPeerSelector; + + uint64_t m_MinLatency=0; // if > 0 this tunnel pool will try building tunnels with minimum latency by ms + uint64_t m_MaxLatency=0; // if > 0 this tunnel pool will try building tunnels with maximum latency by ms + public: // for HTTP only