123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621 |
- #include <algorithm>
- #include "I2PEndian.h"
- #include "Crypto.h"
- #include "Tunnel.h"
- #include "NetDb.hpp"
- #include "Timestamp.h"
- #include "Garlic.h"
- #include "Transports.h"
- #include "Log.h"
- #include "Tunnel.h"
- #include "TunnelPool.h"
- #include "Destination.h"
- #ifdef WITH_EVENTS
- #include "Event.h"
- #endif
- namespace i2p
- {
- namespace tunnel
- {
- TunnelPool::TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels):
- m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops),
- m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels), m_IsActive (true),
- m_CustomPeerSelector(nullptr)
- {
- }
- TunnelPool::~TunnelPool ()
- {
- DetachTunnels ();
- }
- void TunnelPool::SetExplicitPeers (std::shared_ptr<std::vector<i2p::data::IdentHash> > explicitPeers)
- {
- m_ExplicitPeers = explicitPeers;
- if (m_ExplicitPeers)
- {
- int size = m_ExplicitPeers->size ();
- if (m_NumInboundHops > size)
- {
- m_NumInboundHops = size;
- LogPrint (eLogInfo, "Tunnels: Inbound tunnel length has beed adjusted to ", size, " for explicit peers");
- }
- if (m_NumOutboundHops > size)
- {
- m_NumOutboundHops = size;
- LogPrint (eLogInfo, "Tunnels: Outbound tunnel length has beed adjusted to ", size, " for explicit peers");
- }
- m_NumInboundTunnels = 1;
- m_NumOutboundTunnels = 1;
- }
- }
- void TunnelPool::DetachTunnels ()
- {
- {
- std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
- for (auto& it: m_InboundTunnels)
- it->SetTunnelPool (nullptr);
- m_InboundTunnels.clear ();
- }
- {
- std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
- for (auto& it: m_OutboundTunnels)
- it->SetTunnelPool (nullptr);
- m_OutboundTunnels.clear ();
- }
- m_Tests.clear ();
- }
- bool TunnelPool::Reconfigure(int inHops, int outHops, int inQuant, int outQuant) {
- if( inHops >= 0 && outHops >= 0 && inQuant > 0 && outQuant > 0)
- {
- m_NumInboundHops = inHops;
- m_NumOutboundHops = outHops;
- m_NumInboundTunnels = inQuant;
- m_NumOutboundTunnels = outQuant;
- return true;
- }
- return false;
- }
-
- void TunnelPool::TunnelCreated (std::shared_ptr<InboundTunnel> createdTunnel)
- {
- if (!m_IsActive) return;
- {
- #ifdef WITH_EVENTS
- EmitTunnelEvent("tunnels.created", createdTunnel);
- #endif
- std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
- m_InboundTunnels.insert (createdTunnel);
- }
- if (m_LocalDestination)
- m_LocalDestination->SetLeaseSetUpdated ();
- OnTunnelBuildResult(createdTunnel, eBuildResultOkay);
- }
- void TunnelPool::TunnelExpired (std::shared_ptr<InboundTunnel> expiredTunnel)
- {
- if (expiredTunnel)
- {
- #ifdef WITH_EVENTS
- EmitTunnelEvent("tunnels.expired", expiredTunnel);
- #endif
- expiredTunnel->SetTunnelPool (nullptr);
- for (auto& it: m_Tests)
- if (it.second.second == expiredTunnel) it.second.second = nullptr;
- std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
- m_InboundTunnels.erase (expiredTunnel);
- }
- }
- void TunnelPool::TunnelCreated (std::shared_ptr<OutboundTunnel> createdTunnel)
- {
- if (!m_IsActive) return;
- {
- #ifdef WITH_EVENTS
- EmitTunnelEvent("tunnels.created", createdTunnel);
- #endif
- std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
- m_OutboundTunnels.insert (createdTunnel);
- }
- OnTunnelBuildResult(createdTunnel, eBuildResultOkay);
- //CreatePairedInboundTunnel (createdTunnel);
- }
- void TunnelPool::TunnelExpired (std::shared_ptr<OutboundTunnel> expiredTunnel)
- {
- if (expiredTunnel)
- {
- #ifdef WITH_EVENTS
- EmitTunnelEvent("tunnels.expired", expiredTunnel);
- #endif
- expiredTunnel->SetTunnelPool (nullptr);
- for (auto& it: m_Tests)
- if (it.second.first == expiredTunnel) it.second.first = nullptr;
- std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
- m_OutboundTunnels.erase (expiredTunnel);
- }
- }
- std::vector<std::shared_ptr<InboundTunnel> > TunnelPool::GetInboundTunnels (int num) const
- {
- std::vector<std::shared_ptr<InboundTunnel> > v;
- int i = 0;
- std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
- for (const auto& it : m_InboundTunnels)
- {
- if (i >= num) break;
- if (it->IsEstablished ())
- {
- v.push_back (it);
- i++;
- }
- }
- return v;
- }
- std::shared_ptr<OutboundTunnel> TunnelPool::GetNextOutboundTunnel (std::shared_ptr<OutboundTunnel> excluded) const
- {
- std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
- return GetNextTunnel (m_OutboundTunnels, excluded);
- }
- std::shared_ptr<InboundTunnel> TunnelPool::GetNextInboundTunnel (std::shared_ptr<InboundTunnel> excluded) const
- {
- std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
- return GetNextTunnel (m_InboundTunnels, excluded);
- }
- template<class TTunnels>
- typename TTunnels::value_type TunnelPool::GetNextTunnel (TTunnels& tunnels, typename TTunnels::value_type excluded) const
- {
- if (tunnels.empty ()) return nullptr;
- uint32_t ind = rand () % (tunnels.size ()/2 + 1), i = 0;
- typename TTunnels::value_type tunnel = nullptr;
- for (const auto& it: tunnels)
- {
- if (it->IsEstablished () && it != excluded)
- {
- if(HasLatencyRequirement() && it->LatencyIsKnown() && !it->LatencyFitsRange(m_MinLatency, m_MaxLatency)) {
- i ++;
- continue;
- }
- tunnel = it;
- i++;
- }
- if (i > ind && tunnel) break;
- }
- if(HasLatencyRequirement() && !tunnel) {
- ind = rand () % (tunnels.size ()/2 + 1), i = 0;
- for (const auto& it: tunnels)
- {
- if (it->IsEstablished () && it != excluded)
- {
- tunnel = it;
- i++;
- }
- if (i > ind && tunnel) break;
- }
- }
- if (!tunnel && excluded && excluded->IsEstablished ()) tunnel = excluded;
- return tunnel;
- }
- std::shared_ptr<OutboundTunnel> TunnelPool::GetNewOutboundTunnel (std::shared_ptr<OutboundTunnel> old) const
- {
- if (old && old->IsEstablished ()) return old;
- std::shared_ptr<OutboundTunnel> tunnel;
- if (old)
- {
- std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
- for (const auto& it: m_OutboundTunnels)
- if (it->IsEstablished () && old->GetEndpointIdentHash () == it->GetEndpointIdentHash ())
- {
- tunnel = it;
- break;
- }
- }
- if (!tunnel)
- tunnel = GetNextOutboundTunnel ();
- return tunnel;
- }
- void TunnelPool::CreateTunnels ()
- {
- int num = 0;
- {
- std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
- for (const auto& it : m_OutboundTunnels)
- if (it->IsEstablished ()) num++;
- }
- for (int i = num; i < m_NumOutboundTunnels; i++)
- CreateOutboundTunnel ();
- num = 0;
- {
- std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
- for (const auto& it : m_InboundTunnels)
- if (it->IsEstablished ()) num++;
- }
- for (int i = num; i < m_NumInboundTunnels; i++)
- CreateInboundTunnel ();
- if (num < m_NumInboundTunnels && m_NumInboundHops <= 0 && m_LocalDestination) // zero hops IB
- m_LocalDestination->SetLeaseSetUpdated (); // update LeaseSet immediately
- }
- void TunnelPool::TestTunnels ()
- {
- decltype(m_Tests) tests;
- {
- std::unique_lock<std::mutex> l(m_TestsMutex);
- tests.swap(m_Tests);
- }
- for (auto& it: tests)
- {
- LogPrint (eLogWarning, "Tunnels: test of tunnel ", it.first, " failed");
- // if test failed again with another tunnel we consider it failed
- if (it.second.first)
- {
- if (it.second.first->GetState () == eTunnelStateTestFailed)
- {
- it.second.first->SetState (eTunnelStateFailed);
- std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
- m_OutboundTunnels.erase (it.second.first);
- }
- else
- it.second.first->SetState (eTunnelStateTestFailed);
- }
- if (it.second.second)
- {
- if (it.second.second->GetState () == eTunnelStateTestFailed)
- {
- it.second.second->SetState (eTunnelStateFailed);
- {
- std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
- m_InboundTunnels.erase (it.second.second);
- }
- if (m_LocalDestination)
- m_LocalDestination->SetLeaseSetUpdated ();
- }
- else
- it.second.second->SetState (eTunnelStateTestFailed);
- }
- }
- // new tests
- auto it1 = m_OutboundTunnels.begin ();
- auto it2 = m_InboundTunnels.begin ();
- while (it1 != m_OutboundTunnels.end () && it2 != m_InboundTunnels.end ())
- {
- bool failed = false;
- if ((*it1)->IsFailed ())
- {
- failed = true;
- ++it1;
- }
- if ((*it2)->IsFailed ())
- {
- failed = true;
- ++it2;
- }
- if (!failed)
- {
- uint32_t msgID;
- RAND_bytes ((uint8_t *)&msgID, 4);
- {
- std::unique_lock<std::mutex> l(m_TestsMutex);
- m_Tests[msgID] = std::make_pair (*it1, *it2);
- }
- (*it1)->SendTunnelDataMsg ((*it2)->GetNextIdentHash (), (*it2)->GetNextTunnelID (),
- CreateDeliveryStatusMsg (msgID));
- ++it1; ++it2;
- }
- }
- }
- void TunnelPool::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
- {
- if (m_LocalDestination)
- m_LocalDestination->ProcessGarlicMessage (msg);
- else
- LogPrint (eLogWarning, "Tunnels: local destination doesn't exist, dropped");
- }
- void TunnelPool::ProcessDeliveryStatus (std::shared_ptr<I2NPMessage> msg)
- {
- const uint8_t * buf = msg->GetPayload ();
- uint32_t msgID = bufbe32toh (buf);
- buf += 4;
- uint64_t timestamp = bufbe64toh (buf);
- decltype(m_Tests)::mapped_type test;
- bool found = false;
- {
- std::unique_lock<std::mutex> l(m_TestsMutex);
- auto it = m_Tests.find (msgID);
- if (it != m_Tests.end ())
- {
- found = true;
- test = it->second;
- m_Tests.erase (it);
- }
- }
- if (found)
- {
- // restore from test failed state if any
- if (test.first->GetState () == eTunnelStateTestFailed)
- test.first->SetState (eTunnelStateEstablished);
- if (test.second->GetState () == eTunnelStateTestFailed)
- test.second->SetState (eTunnelStateEstablished);
- 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
- {
- if (m_LocalDestination)
- m_LocalDestination->ProcessDeliveryStatusMessage (msg);
- else
- LogPrint (eLogWarning, "Tunnels: Local destination doesn't exist, dropped");
- }
- }
- std::shared_ptr<const i2p::data::RouterInfo> TunnelPool::SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop) const
- {
- bool isExploratory = (i2p::tunnel::tunnels.GetExploratoryPool () == shared_from_this ());
- auto hop = isExploratory ? i2p::data::netdb.GetRandomRouter (prevHop):
- i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop);
- if (!hop || hop->GetProfile ()->IsBad ())
- hop = i2p::data::netdb.GetRandomRouter (prevHop);
- return hop;
- }
- bool StandardSelectPeers(Path & peers, int numHops, bool inbound, SelectHopFunc nextHop)
- {
- auto prevHop = i2p::context.GetSharedRouterInfo ();
- if(i2p::transport::transports.RoutesRestricted())
- {
- /** if routes are restricted prepend trusted first hop */
- auto hop = i2p::transport::transports.GetRestrictedPeer();
- if(!hop) return false;
- peers.push_back(hop->GetRouterIdentity());
- prevHop = hop;
- }
- else if (i2p::transport::transports.GetNumPeers () > 25)
- {
- auto r = i2p::transport::transports.GetRandomPeer ();
- if (r && !r->GetProfile ()->IsBad ())
- {
- prevHop = r;
- peers.push_back (r->GetRouterIdentity ());
- numHops--;
- }
- }
- for(int i = 0; i < numHops; i++ )
- {
- auto hop = nextHop (prevHop);
- if (!hop)
- {
- LogPrint (eLogError, "Tunnels: Can't select next hop for ", prevHop->GetIdentHashBase64 ());
- return false;
- }
- prevHop = hop;
- peers.push_back (hop->GetRouterIdentity ());
- }
- return true;
- }
- bool TunnelPool::SelectPeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers, bool isInbound)
- {
- int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops;
- // peers is empty
- if (numHops <= 0) return true;
- // custom peer selector in use ?
- {
- std::lock_guard<std::mutex> lock(m_CustomPeerSelectorMutex);
- if (m_CustomPeerSelector)
- return m_CustomPeerSelector->SelectPeers(peers, numHops, isInbound);
- }
- // explicit peers in use
- if (m_ExplicitPeers) return SelectExplicitPeers (peers, isInbound);
- return StandardSelectPeers(peers, numHops, isInbound, std::bind(&TunnelPool::SelectNextHop, this, std::placeholders::_1));
- }
- bool TunnelPool::SelectExplicitPeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers, bool isInbound)
- {
- int size = m_ExplicitPeers->size ();
- std::vector<int> peerIndicies;
- for (int i = 0; i < size; i++) peerIndicies.push_back(i);
- std::random_shuffle (peerIndicies.begin(), peerIndicies.end());
- int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops;
- for (int i = 0; i < numHops; i++)
- {
- auto& ident = (*m_ExplicitPeers)[peerIndicies[i]];
- auto r = i2p::data::netdb.FindRouter (ident);
- if (r)
- peers.push_back (r->GetRouterIdentity ());
- else
- {
- LogPrint (eLogInfo, "Tunnels: Can't find router for ", ident.ToBase64 ());
- i2p::data::netdb.RequestDestination (ident);
- return false;
- }
- }
- return true;
- }
- void TunnelPool::CreateInboundTunnel ()
- {
- auto outboundTunnel = GetNextOutboundTunnel ();
- if (!outboundTunnel)
- outboundTunnel = tunnels.GetNextOutboundTunnel ();
- LogPrint (eLogDebug, "Tunnels: Creating destination inbound tunnel...");
- std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers;
- if (SelectPeers (peers, true))
- {
- std::shared_ptr<TunnelConfig> config;
- if (m_NumInboundHops > 0)
- {
- std::reverse (peers.begin (), peers.end ());
- config = std::make_shared<TunnelConfig> (peers);
- }
- auto tunnel = tunnels.CreateInboundTunnel (config, outboundTunnel);
- tunnel->SetTunnelPool (shared_from_this ());
- if (tunnel->IsEstablished ()) // zero hops
- TunnelCreated (tunnel);
- }
- else
- LogPrint (eLogError, "Tunnels: Can't create inbound tunnel, no peers available");
- }
- void TunnelPool::RecreateInboundTunnel (std::shared_ptr<InboundTunnel> tunnel)
- {
- auto outboundTunnel = GetNextOutboundTunnel ();
- if (!outboundTunnel)
- outboundTunnel = tunnels.GetNextOutboundTunnel ();
- LogPrint (eLogDebug, "Tunnels: Re-creating destination inbound tunnel...");
- std::shared_ptr<TunnelConfig> config;
- if (m_NumInboundHops > 0 && tunnel->GetPeers().size())
- {
- config = std::make_shared<TunnelConfig>(tunnel->GetPeers ());
- }
- if (m_NumInboundHops == 0 || config)
- {
- auto newTunnel = tunnels.CreateInboundTunnel (config, outboundTunnel);
- newTunnel->SetTunnelPool (shared_from_this());
- if (newTunnel->IsEstablished ()) // zero hops
- TunnelCreated (newTunnel);
- }
- }
- void TunnelPool::CreateOutboundTunnel ()
- {
- auto inboundTunnel = GetNextInboundTunnel ();
- if (!inboundTunnel)
- inboundTunnel = tunnels.GetNextInboundTunnel ();
- if (inboundTunnel)
- {
- LogPrint (eLogDebug, "Tunnels: Creating destination outbound tunnel...");
- std::vector<std::shared_ptr<const i2p::data::IdentityEx> > peers;
- if (SelectPeers (peers, false))
- {
- std::shared_ptr<TunnelConfig> config;
- if (m_NumOutboundHops > 0)
- config = std::make_shared<TunnelConfig>(peers, inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ());
- auto tunnel = tunnels.CreateOutboundTunnel (config);
- tunnel->SetTunnelPool (shared_from_this ());
- if (tunnel->IsEstablished ()) // zero hops
- TunnelCreated (tunnel);
- }
- else
- LogPrint (eLogError, "Tunnels: Can't create outbound tunnel, no peers available");
- }
- else
- LogPrint (eLogError, "Tunnels: Can't create outbound tunnel, no inbound tunnels found");
- }
- void TunnelPool::RecreateOutboundTunnel (std::shared_ptr<OutboundTunnel> tunnel)
- {
- auto inboundTunnel = GetNextInboundTunnel ();
- if (!inboundTunnel)
- inboundTunnel = tunnels.GetNextInboundTunnel ();
- if (inboundTunnel)
- {
- LogPrint (eLogDebug, "Tunnels: Re-creating destination outbound tunnel...");
- std::shared_ptr<TunnelConfig> config;
- if (m_NumOutboundHops > 0 && tunnel->GetPeers().size())
- {
- config = std::make_shared<TunnelConfig>(tunnel->GetPeers (), inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ());
- }
- if(m_NumOutboundHops == 0 || config)
- {
- auto newTunnel = tunnels.CreateOutboundTunnel (config);
- newTunnel->SetTunnelPool (shared_from_this ());
- if (newTunnel->IsEstablished ()) // zero hops
- TunnelCreated (newTunnel);
- }
- }
- else
- LogPrint (eLogDebug, "Tunnels: Can't re-create outbound tunnel, no inbound tunnels found");
- }
- void TunnelPool::CreatePairedInboundTunnel (std::shared_ptr<OutboundTunnel> outboundTunnel)
- {
- LogPrint (eLogDebug, "Tunnels: Creating paired inbound tunnel...");
- auto tunnel = tunnels.CreateInboundTunnel (std::make_shared<TunnelConfig>(outboundTunnel->GetInvertedPeers ()), outboundTunnel);
- tunnel->SetTunnelPool (shared_from_this ());
- }
- void TunnelPool::SetCustomPeerSelector(ITunnelPeerSelector * selector)
- {
- std::lock_guard<std::mutex> lock(m_CustomPeerSelectorMutex);
- m_CustomPeerSelector = selector;
- }
- void TunnelPool::UnsetCustomPeerSelector()
- {
- SetCustomPeerSelector(nullptr);
- }
- bool TunnelPool::HasCustomPeerSelector()
- {
- std::lock_guard<std::mutex> lock(m_CustomPeerSelectorMutex);
- return m_CustomPeerSelector != nullptr;
- }
- std::shared_ptr<InboundTunnel> TunnelPool::GetLowestLatencyInboundTunnel(std::shared_ptr<InboundTunnel> exclude) const
- {
- std::shared_ptr<InboundTunnel> tun = nullptr;
- std::unique_lock<std::mutex> 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<OutboundTunnel> TunnelPool::GetLowestLatencyOutboundTunnel(std::shared_ptr<OutboundTunnel> exclude) const
- {
- std::shared_ptr<OutboundTunnel> tun = nullptr;
- std::unique_lock<std::mutex> 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;
- }
- void TunnelPool::OnTunnelBuildResult(std::shared_ptr<Tunnel> tunnel, TunnelBuildResult result)
- {
- auto peers = tunnel->GetPeers();
- if(m_CustomPeerSelector) m_CustomPeerSelector->OnBuildResult(peers, tunnel->IsInbound(), result);
- }
- }
- }
|