mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-01-22 13:27:17 +01:00
Compare commits
24 commits
2f85710f77
...
3a8e30320d
Author | SHA1 | Date | |
---|---|---|---|
3a8e30320d | |||
43939cedf4 | |||
4c66608caf | |||
ec4fe9a1e6 | |||
79e8ccbb5b | |||
608056dcd2 | |||
7461b640e3 | |||
743126b2ad | |||
f611136ea7 | |||
87ae9c4b74 | |||
d3630fb2b2 | |||
500afe745f | |||
26901e2945 | |||
64bde69967 | |||
ddf30784ec | |||
ea14b00d63 | |||
a24e0eb2dc | |||
0cb677a2c0 | |||
e6cbc842bf | |||
f087654f25 | |||
10335b90c5 | |||
8a234f70e6 | |||
f98a310235 | |||
17399da399 |
13
contrib/docker/docker-compose.yml
Normal file
13
contrib/docker/docker-compose.yml
Normal file
|
@ -0,0 +1,13 @@
|
|||
services:
|
||||
i2pd:
|
||||
container_name: i2pd2
|
||||
image: purplei2p/i2pd
|
||||
#optional
|
||||
entrypoint: ["./entrypoint.sh", "--loglevel error"]
|
||||
ports:
|
||||
- 127.0.0.1:7656:7656
|
||||
- 127.0.0.1:7070:7070
|
||||
- 127.0.0.1:4444:4444
|
||||
volumes:
|
||||
- /path/to/i2pd/data:/home/i2pd/data # make sure data directory and it's contents are owned by 100:65533
|
||||
- /path/to/i2pd/i2pd_certificates:/i2pd_certificates # make sure i2pd_certificates is owned by root:root and 755 permissions on the directory
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2024, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
|
@ -25,6 +25,7 @@
|
|||
#include "RouterContext.h"
|
||||
#include "ClientContext.h"
|
||||
#include "Transports.h"
|
||||
#include "util.h"
|
||||
|
||||
void handle_signal(int sig)
|
||||
{
|
||||
|
@ -220,6 +221,7 @@ namespace i2p
|
|||
|
||||
void DaemonLinux::run ()
|
||||
{
|
||||
i2p::util::SetThreadName ("i2pd-daemon");
|
||||
while (running)
|
||||
{
|
||||
std::this_thread::sleep_for (std::chrono::seconds(1));
|
||||
|
|
|
@ -997,7 +997,7 @@ namespace crypto
|
|||
}
|
||||
else
|
||||
{
|
||||
#if defined(LIBRESSL_VERSION_NUMBER)
|
||||
#if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x4000000fL
|
||||
std::vector<uint8_t> m(msgLen + 16);
|
||||
if (msg == buf)
|
||||
{
|
||||
|
|
|
@ -142,14 +142,14 @@ namespace client
|
|||
void CancelDestinationRequestWithEncryptedLeaseSet (std::shared_ptr<const i2p::data::BlindedPublicKey> dest, bool notify = true);
|
||||
|
||||
// implements GarlicDestination
|
||||
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet ();
|
||||
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const { return m_Pool; }
|
||||
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () override;
|
||||
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const override { return m_Pool; }
|
||||
|
||||
// override GarlicDestination
|
||||
bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag);
|
||||
void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag);
|
||||
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag) override;
|
||||
void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag) override;
|
||||
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg) override;
|
||||
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg) override;
|
||||
void SetLeaseSetUpdated (bool post) override;
|
||||
|
||||
bool IsPublic () const { return m_IsPublic; };
|
||||
|
@ -158,8 +158,8 @@ namespace client
|
|||
protected:
|
||||
|
||||
// implements GarlicDestination
|
||||
void HandleI2NPMessage (const uint8_t * buf, size_t len);
|
||||
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID);
|
||||
void HandleI2NPMessage (const uint8_t * buf, size_t len) override;
|
||||
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID) override;
|
||||
|
||||
void SetLeaseSet (std::shared_ptr<const i2p::data::LocalLeaseSet> newLeaseSet);
|
||||
int GetLeaseSetType () const { return m_LeaseSetType; };
|
||||
|
|
|
@ -396,8 +396,26 @@ namespace i2p
|
|||
return false;
|
||||
}
|
||||
uint8_t retCode = 0;
|
||||
// decide if we should accept tunnel
|
||||
bool accept = i2p::context.AcceptsTunnels ();
|
||||
if (accept)
|
||||
{
|
||||
auto congestionLevel = i2p::context.GetCongestionLevel (false);
|
||||
if (congestionLevel >= CONGESTION_LEVEL_MEDIUM)
|
||||
{
|
||||
if (congestionLevel < CONGESTION_LEVEL_FULL)
|
||||
{
|
||||
// random reject depending on congestion level
|
||||
int level = i2p::tunnel::tunnels.GetRng ()() % (CONGESTION_LEVEL_FULL - CONGESTION_LEVEL_MEDIUM) + CONGESTION_LEVEL_MEDIUM;
|
||||
if (congestionLevel > level)
|
||||
accept = false;
|
||||
}
|
||||
else
|
||||
accept = false;
|
||||
}
|
||||
}
|
||||
// replace record to reply
|
||||
if (i2p::context.AcceptsTunnels () && i2p::context.GetCongestionLevel (false) < CONGESTION_LEVEL_FULL)
|
||||
if (accept)
|
||||
{
|
||||
auto transitTunnel = i2p::tunnel::CreateTransitTunnel (
|
||||
bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
|
||||
|
|
|
@ -638,7 +638,8 @@ namespace data
|
|||
if (checkForExpiration && uptime > i2p::transport::SSU2_TO_INTRODUCER_SESSION_DURATION) // 1 hour
|
||||
expirationTimeout = i2p::context.IsFloodfill () ? NETDB_FLOODFILL_EXPIRATION_TIMEOUT*1000LL :
|
||||
NETDB_MIN_EXPIRATION_TIMEOUT*1000LL + (NETDB_MAX_EXPIRATION_TIMEOUT - NETDB_MIN_EXPIRATION_TIMEOUT)*1000LL*NETDB_MIN_ROUTERS/total;
|
||||
|
||||
bool isOffline = checkForExpiration && i2p::transport::transports.GetNumPeers () < NETDB_MIN_TRANSPORTS; // enough routers and uptime, but no tranports
|
||||
|
||||
std::list<std::pair<std::string, std::shared_ptr<RouterInfo::Buffer> > > saveToDisk;
|
||||
std::list<std::string> removeFromDisk;
|
||||
|
||||
|
@ -672,7 +673,7 @@ namespace data
|
|||
if (r->GetProfile ()->IsUnreachable ())
|
||||
r->SetUnreachable (true);
|
||||
// make router reachable back if too few routers or floodfills
|
||||
if (r->IsUnreachable () && (total - deletedCount < NETDB_MIN_ROUTERS || isLowRate ||
|
||||
if (r->IsUnreachable () && (total - deletedCount < NETDB_MIN_ROUTERS || isLowRate || isOffline ||
|
||||
(r->IsFloodfill () && totalFloodfills - deletedFloodfillsCount < NETDB_MIN_FLOODFILLS)))
|
||||
r->SetUnreachable (false);
|
||||
if (!r->IsUnreachable ())
|
||||
|
|
|
@ -39,6 +39,7 @@ namespace data
|
|||
{
|
||||
const int NETDB_MIN_ROUTERS = 90;
|
||||
const int NETDB_MIN_FLOODFILLS = 5;
|
||||
const int NETDB_MIN_TRANSPORTS = 10 ; // otherwise assume offline
|
||||
const int NETDB_NUM_FLOODFILLS_THRESHOLD = 1200;
|
||||
const int NETDB_NUM_ROUTERS_THRESHOLD = 4*NETDB_NUM_FLOODFILLS_THRESHOLD;
|
||||
const int NETDB_TUNNEL_CREATION_RATE_THRESHOLD = 10; // in %
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include <memory>
|
||||
#include <future>
|
||||
#include <boost/asio.hpp>
|
||||
#include "Identity.h"
|
||||
|
||||
namespace i2p
|
||||
|
@ -67,6 +68,11 @@ namespace data
|
|||
|
||||
bool IsUseful() const;
|
||||
bool IsDuplicated () const { return m_IsDuplicated; };
|
||||
|
||||
const boost::asio::ip::udp::endpoint& GetLastEndpoint () const { return m_LastEndpoint; }
|
||||
void SetLastEndpoint (const boost::asio::ip::udp::endpoint& ep) { m_LastEndpoint = ep; }
|
||||
bool HasLastEndpoint (bool v4) const { return !m_LastEndpoint.address ().is_unspecified () && m_LastEndpoint.port () &&
|
||||
((v4 && m_LastEndpoint.address ().is_v4 ()) || (!v4 && m_LastEndpoint.address ().is_v6 ())); }
|
||||
|
||||
private:
|
||||
|
||||
|
@ -90,6 +96,8 @@ namespace data
|
|||
uint32_t m_NumTimesRejected;
|
||||
bool m_HasConnected; // successful trusted(incoming or NTCP2) connection
|
||||
bool m_IsDuplicated;
|
||||
// connectivity
|
||||
boost::asio::ip::udp::endpoint m_LastEndpoint; // SSU2 for non-published addresses
|
||||
};
|
||||
|
||||
std::shared_ptr<RouterProfile> GetRouterProfile (const IdentHash& identHash);
|
||||
|
|
|
@ -1489,7 +1489,7 @@ namespace i2p
|
|||
void RouterContext::UpdateCongestion ()
|
||||
{
|
||||
auto c = i2p::data::RouterInfo::eLowCongestion;
|
||||
if (!AcceptsTunnels () || !m_ShareRatio)
|
||||
if (!AcceptsTunnels () || !m_ShareRatio || (m_Error == eRouterErrorSymmetricNAT && !SupportsV6 () && !SupportsMesh ()))
|
||||
c = i2p::data::RouterInfo::eRejectAll;
|
||||
else
|
||||
{
|
||||
|
|
|
@ -146,7 +146,6 @@ namespace garlic
|
|||
void SetNetID (int netID) { m_NetID = netID; };
|
||||
bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data);
|
||||
bool DecryptTunnelShortRequestRecord (const uint8_t * encrypted, uint8_t * data);
|
||||
void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag);
|
||||
|
||||
void UpdatePort (int port); // called from Daemon
|
||||
void UpdateAddress (const boost::asio::ip::address& host); // called from SSU2 or Daemon
|
||||
|
@ -186,24 +185,24 @@ namespace garlic
|
|||
void UpdateTimestamp (uint64_t ts); // in seconds, called from NetDb before publishing
|
||||
|
||||
// implements LocalDestination
|
||||
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const { return m_Keys.GetPublic (); };
|
||||
bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const;
|
||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
|
||||
void SetLeaseSetUpdated () {};
|
||||
std::shared_ptr<const i2p::data::IdentityEx> GetIdentity () const override{ return m_Keys.GetPublic (); };
|
||||
bool Decrypt (const uint8_t * encrypted, uint8_t * data, i2p::data::CryptoKeyType preferredCrypto) const override;
|
||||
void SetLeaseSetUpdated (bool post) override {};
|
||||
|
||||
// implements GarlicDestination
|
||||
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () { return nullptr; };
|
||||
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const;
|
||||
std::shared_ptr<const i2p::data::LocalLeaseSet> GetLeaseSet () override { return nullptr; };
|
||||
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const override;
|
||||
|
||||
// override GarlicDestination
|
||||
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg) override;
|
||||
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg) override;
|
||||
void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag) override;
|
||||
|
||||
protected:
|
||||
|
||||
// implements GarlicDestination
|
||||
void HandleI2NPMessage (const uint8_t * buf, size_t len);
|
||||
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID);
|
||||
void HandleI2NPMessage (const uint8_t * buf, size_t len) override;
|
||||
bool HandleCloveI2NPMessage (I2NPMessageType typeID, const uint8_t * payload, size_t len, uint32_t msgID) override;
|
||||
|
||||
private:
|
||||
|
||||
|
@ -216,6 +215,7 @@ namespace garlic
|
|||
void UpdateSSU2Keys ();
|
||||
bool Load ();
|
||||
void SaveKeys ();
|
||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
|
||||
uint16_t SelectRandomPort () const;
|
||||
void PublishNTCP2Address (std::shared_ptr<i2p::data::RouterInfo::Address> address, int port, bool publish) const;
|
||||
|
||||
|
|
|
@ -216,15 +216,16 @@ namespace transport
|
|||
return ep.port ();
|
||||
}
|
||||
|
||||
bool SSU2Server::IsConnectedRecently (const boost::asio::ip::udp::endpoint& ep)
|
||||
bool SSU2Server::IsConnectedRecently (const boost::asio::ip::udp::endpoint& ep, bool max)
|
||||
{
|
||||
if (!ep.port () || ep.address ().is_unspecified ()) return false;
|
||||
std::lock_guard<std::mutex> l(m_ConnectedRecentlyMutex);
|
||||
auto it = m_ConnectedRecently.find (ep);
|
||||
if (it != m_ConnectedRecently.end ())
|
||||
{
|
||||
if (i2p::util::GetSecondsSinceEpoch () <= it->second + SSU2_HOLE_PUNCH_EXPIRATION)
|
||||
if (i2p::util::GetSecondsSinceEpoch () <= it->second + (max ? SSU2_MAX_HOLE_PUNCH_EXPIRATION : SSU2_MIN_HOLE_PUNCH_EXPIRATION))
|
||||
return true;
|
||||
else
|
||||
else if (max)
|
||||
m_ConnectedRecently.erase (it);
|
||||
}
|
||||
return false;
|
||||
|
@ -233,7 +234,8 @@ namespace transport
|
|||
void SSU2Server::AddConnectedRecently (const boost::asio::ip::udp::endpoint& ep, uint64_t ts)
|
||||
{
|
||||
if (!ep.port () || ep.address ().is_unspecified () ||
|
||||
i2p::util::GetSecondsSinceEpoch () > ts + SSU2_HOLE_PUNCH_EXPIRATION) return;
|
||||
i2p::util::GetSecondsSinceEpoch () > ts + SSU2_MAX_HOLE_PUNCH_EXPIRATION) return;
|
||||
std::lock_guard<std::mutex> l(m_ConnectedRecentlyMutex);
|
||||
auto [it, added] = m_ConnectedRecently.try_emplace (ep, ts);
|
||||
if (!added && ts > it->second)
|
||||
it->second = ts; // renew timestamp of existing endpoint
|
||||
|
@ -475,7 +477,7 @@ namespace transport
|
|||
HandleReceivedPackets (std::move (receivedPackets));
|
||||
}
|
||||
|
||||
void SSU2Server::AddSession (std::shared_ptr<SSU2Session> session)
|
||||
bool SSU2Server::AddSession (std::shared_ptr<SSU2Session> session)
|
||||
{
|
||||
if (session)
|
||||
{
|
||||
|
@ -483,8 +485,10 @@ namespace transport
|
|||
{
|
||||
if (session->GetState () != eSSU2SessionStatePeerTest)
|
||||
AddSessionByRouterHash (session);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SSU2Server::RemoveSession (uint64_t connID)
|
||||
|
@ -833,6 +837,29 @@ namespace transport
|
|||
}
|
||||
}
|
||||
|
||||
bool SSU2Server::CheckPendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep, bool peerTest)
|
||||
{
|
||||
auto s = FindPendingOutgoingSession (ep);
|
||||
if (s)
|
||||
{
|
||||
if (peerTest)
|
||||
{
|
||||
// if peer test requested add it to the list for pending session
|
||||
auto onEstablished = s->GetOnEstablished ();
|
||||
if (onEstablished)
|
||||
s->SetOnEstablished ([s, onEstablished]()
|
||||
{
|
||||
onEstablished ();
|
||||
s->SendPeerTest ();
|
||||
});
|
||||
else
|
||||
s->SetOnEstablished ([s]() { s->SendPeerTest (); });
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SSU2Server::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||
std::shared_ptr<const i2p::data::RouterInfo::Address> address, bool peerTest)
|
||||
{
|
||||
|
@ -852,34 +879,28 @@ namespace transport
|
|||
if (isValidEndpoint)
|
||||
{
|
||||
if (i2p::transport::transports.IsInReservedRange(address->host)) return false;
|
||||
auto s = FindPendingOutgoingSession (boost::asio::ip::udp::endpoint (address->host, address->port));
|
||||
if (s)
|
||||
{
|
||||
if (peerTest)
|
||||
{
|
||||
// if peer test requested add it to the list for pending session
|
||||
auto onEstablished = s->GetOnEstablished ();
|
||||
if (onEstablished)
|
||||
s->SetOnEstablished ([s, onEstablished]()
|
||||
{
|
||||
onEstablished ();
|
||||
s->SendPeerTest ();
|
||||
});
|
||||
else
|
||||
s->SetOnEstablished ([s]() { s->SendPeerTest (); });
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (CheckPendingOutgoingSession (boost::asio::ip::udp::endpoint (address->host, address->port), peerTest)) return false;
|
||||
}
|
||||
|
||||
auto session = std::make_shared<SSU2Session> (*this, router, address);
|
||||
if (!isValidEndpoint && router->GetProfile ()->HasLastEndpoint (address->IsV4 ()))
|
||||
{
|
||||
// router doesn't publish endpoint, but we connected before and hole punch might be alive
|
||||
auto ep = router->GetProfile ()->GetLastEndpoint ();
|
||||
if (IsConnectedRecently (ep, false))
|
||||
{
|
||||
if (CheckPendingOutgoingSession (ep, peerTest)) return false;
|
||||
session->SetRemoteEndpoint (ep);
|
||||
isValidEndpoint = true;
|
||||
}
|
||||
}
|
||||
if (peerTest)
|
||||
session->SetOnEstablished ([session]() {session->SendPeerTest (); });
|
||||
|
||||
if (address->UsesIntroducer ())
|
||||
GetService ().post (std::bind (&SSU2Server::ConnectThroughIntroducer, this, session));
|
||||
else if (isValidEndpoint) // we can't connect without endpoint
|
||||
if (isValidEndpoint) // we know endpoint
|
||||
GetService ().post ([session]() { session->Connect (); });
|
||||
else if (address->UsesIntroducer ()) // we don't know endpoint yet
|
||||
GetService ().post (std::bind (&SSU2Server::ConnectThroughIntroducer, this, session));
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
@ -1129,7 +1150,7 @@ namespace transport
|
|||
|
||||
for (auto it = m_ConnectedRecently.begin (); it != m_ConnectedRecently.end (); )
|
||||
{
|
||||
if (ts > it->second + SSU2_HOLE_PUNCH_EXPIRATION)
|
||||
if (ts > it->second + SSU2_MAX_HOLE_PUNCH_EXPIRATION)
|
||||
it = m_ConnectedRecently.erase (it);
|
||||
else
|
||||
it++;
|
||||
|
|
|
@ -42,7 +42,8 @@ namespace transport
|
|||
const int SSU2_KEEP_ALIVE_INTERVAL = 15; // in seconds
|
||||
const int SSU2_KEEP_ALIVE_INTERVAL_VARIANCE = 4; // in seconds
|
||||
const int SSU2_PROXY_CONNECT_RETRY_TIMEOUT = 30; // in seconds
|
||||
const int SSU2_HOLE_PUNCH_EXPIRATION = 150; // in seconds
|
||||
const int SSU2_MIN_HOLE_PUNCH_EXPIRATION = 30; // in seconds
|
||||
const int SSU2_MAX_HOLE_PUNCH_EXPIRATION = 160; // in seconds
|
||||
const size_t SSU2_MAX_NUM_PACKETS_PER_BATCH = 64;
|
||||
|
||||
class SSU2Server: private i2p::util::RunnableServiceWithWork
|
||||
|
@ -77,14 +78,14 @@ namespace transport
|
|||
bool UsesProxy () const { return m_IsThroughProxy; };
|
||||
bool IsSupported (const boost::asio::ip::address& addr) const;
|
||||
uint16_t GetPort (bool v4) const;
|
||||
bool IsConnectedRecently (const boost::asio::ip::udp::endpoint& ep);
|
||||
bool IsConnectedRecently (const boost::asio::ip::udp::endpoint& ep, bool max = true);
|
||||
void AddConnectedRecently (const boost::asio::ip::udp::endpoint& ep, uint64_t ts);
|
||||
std::mt19937& GetRng () { return m_Rng; }
|
||||
bool IsMaxNumIntroducers (bool v4) const { return (v4 ? m_Introducers.size () : m_IntroducersV6.size ()) >= SSU2_MAX_NUM_INTRODUCERS; }
|
||||
bool IsSyncClockFromPeers () const { return m_IsSyncClockFromPeers; };
|
||||
void AdjustTimeOffset (int64_t offset, std::shared_ptr<const i2p::data::IdentityEx> from);
|
||||
|
||||
void AddSession (std::shared_ptr<SSU2Session> session);
|
||||
bool AddSession (std::shared_ptr<SSU2Session> session);
|
||||
void RemoveSession (uint64_t connID);
|
||||
void RequestRemoveSession (uint64_t connID);
|
||||
void AddSessionByRouterHash (std::shared_ptr<SSU2Session> session);
|
||||
|
@ -147,6 +148,7 @@ namespace transport
|
|||
void ScheduleResend (bool more);
|
||||
void HandleResendTimer (const boost::system::error_code& ecode);
|
||||
|
||||
bool CheckPendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep, bool peerTest);
|
||||
void ConnectThroughIntroducer (std::shared_ptr<SSU2Session> session);
|
||||
std::vector<std::shared_ptr<SSU2Session> > FindIntroducers (int maxNumIntroducers,
|
||||
bool v4, const std::unordered_set<i2p::data::IdentHash>& excluded);
|
||||
|
@ -193,6 +195,7 @@ namespace transport
|
|||
std::shared_ptr<const i2p::data::IdentityEx> m_PendingTimeOffsetFrom;
|
||||
std::mt19937 m_Rng;
|
||||
std::map<boost::asio::ip::udp::endpoint, uint64_t> m_ConnectedRecently; // endpoint -> last activity time in seconds
|
||||
mutable std::mutex m_ConnectedRecentlyMutex;
|
||||
std::unordered_map<uint32_t, std::pair <std::weak_ptr<SSU2PeerTestSession>, uint64_t > > m_RequestedPeerTests; // nonce->(Alice, timestamp)
|
||||
std::list<Packet *> m_ReceivedPacketsQueue;
|
||||
mutable std::mutex m_ReceivedPacketsQueueMutex;
|
||||
|
|
|
@ -83,7 +83,7 @@ namespace transport
|
|||
std::shared_ptr<const i2p::data::RouterInfo::Address> addr, bool noise):
|
||||
TransportSession (in_RemoteRouter, SSU2_CONNECT_TIMEOUT),
|
||||
m_Server (server), m_Address (addr), m_RemoteTransports (0), m_RemotePeerTestTransports (0),
|
||||
m_DestConnID (0), m_SourceConnID (0), m_State (eSSU2SessionStateUnknown),
|
||||
m_RemoteVersion (0), m_DestConnID (0), m_SourceConnID (0), m_State (eSSU2SessionStateUnknown),
|
||||
m_SendPacketNum (0), m_ReceivePacketNum (0), m_LastDatetimeSentPacketNum (0),
|
||||
m_IsDataReceived (false), m_RTT (SSU2_UNKNOWN_RTT),
|
||||
m_MsgLocalExpirationTimeout (I2NP_MESSAGE_LOCAL_EXPIRATION_TIMEOUT_MAX),
|
||||
|
@ -103,6 +103,7 @@ namespace transport
|
|||
InitNoiseXKState1 (*m_NoiseState, m_Address->s);
|
||||
m_RemoteEndpoint = boost::asio::ip::udp::endpoint (m_Address->host, m_Address->port);
|
||||
m_RemoteTransports = in_RemoteRouter->GetCompatibleTransports (false);
|
||||
m_RemoteVersion = in_RemoteRouter->GetVersion ();
|
||||
if (in_RemoteRouter->IsSSU2PeerTesting (true)) m_RemotePeerTestTransports |= i2p::data::RouterInfo::eSSU2V4;
|
||||
if (in_RemoteRouter->IsSSU2PeerTesting (false)) m_RemotePeerTestTransports |= i2p::data::RouterInfo::eSSU2V6;
|
||||
RAND_bytes ((uint8_t *)&m_DestConnID, 8);
|
||||
|
@ -226,6 +227,13 @@ namespace transport
|
|||
if (m_Server.AddPendingOutgoingSession (shared_from_this ()))
|
||||
{
|
||||
m_Server.RemoveSession (GetConnID ());
|
||||
// update endpoint in profile because we know it now
|
||||
auto identity = GetRemoteIdentity ();
|
||||
if (identity)
|
||||
{
|
||||
auto profile = i2p::data::GetRouterProfile (identity->GetIdentHash ());
|
||||
if (profile) profile->SetLastEndpoint (m_RemoteEndpoint);
|
||||
}
|
||||
// connect
|
||||
LogPrint (eLogDebug, "SSU2: Connecting after introduction to ", GetIdentHashBase64());
|
||||
Connect ();
|
||||
|
@ -1169,6 +1177,8 @@ namespace transport
|
|||
" and actual endpoint ", m_RemoteEndpoint.address (), " from ", i2p::data::GetIdentHashAbbreviation (ri->GetIdentHash ()));
|
||||
return false;
|
||||
}
|
||||
if (!m_Address->published)
|
||||
ri->GetProfile ()->SetLastEndpoint (m_RemoteEndpoint);
|
||||
SetRemoteIdentity (ri->GetRouterIdentity ());
|
||||
AdjustMaxPayloadSize ();
|
||||
m_Server.AddSessionByRouterHash (shared_from_this ()); // we know remote router now
|
||||
|
@ -1176,7 +1186,8 @@ namespace transport
|
|||
m_RemotePeerTestTransports = 0;
|
||||
if (ri->IsSSU2PeerTesting (true)) m_RemotePeerTestTransports |= i2p::data::RouterInfo::eSSU2V4;
|
||||
if (ri->IsSSU2PeerTesting (false)) m_RemotePeerTestTransports |= i2p::data::RouterInfo::eSSU2V6;
|
||||
|
||||
m_RemoteVersion = ri->GetVersion ();
|
||||
|
||||
// handle other blocks
|
||||
HandlePayload (decryptedPayload.data () + riSize + 3, decryptedPayload.size () - riSize - 3);
|
||||
Established ();
|
||||
|
@ -1920,25 +1931,28 @@ namespace transport
|
|||
return;
|
||||
}
|
||||
auto mts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
session->m_RelaySessions.emplace (bufbe32toh (buf + 1), // nonce
|
||||
std::make_pair (shared_from_this (), mts/1000) );
|
||||
uint32_t nonce = bufbe32toh (buf + 1);
|
||||
if (session->m_RelaySessions.emplace (nonce, std::make_pair (shared_from_this (), mts/1000)).second)
|
||||
{
|
||||
// send relay intro to Charlie
|
||||
auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ()); // Alice's RI
|
||||
if (r && (r->IsUnreachable () || !i2p::data::netdb.PopulateRouterInfoBuffer (r))) r = nullptr;
|
||||
if (!r) LogPrint (eLogWarning, "SSU2: RelayRequest Alice's router info not found");
|
||||
|
||||
// send relay intro to Charlie
|
||||
auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ()); // Alice's RI
|
||||
if (r && (r->IsUnreachable () || !i2p::data::netdb.PopulateRouterInfoBuffer (r))) r = nullptr;
|
||||
if (!r) LogPrint (eLogWarning, "SSU2: RelayRequest Alice's router info not found");
|
||||
|
||||
auto packet = m_Server.GetSentPacketsPool ().AcquireShared ();
|
||||
packet->payloadSize = r ? CreateRouterInfoBlock (packet->payload, m_MaxPayloadSize - len - 32, r) : 0;
|
||||
if (!packet->payloadSize && r)
|
||||
session->SendFragmentedMessage (CreateDatabaseStoreMsg (r));
|
||||
packet->payloadSize += CreateRelayIntroBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize, buf + 1, len -1);
|
||||
if (packet->payloadSize < m_MaxPayloadSize)
|
||||
packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
|
||||
uint32_t packetNum = session->SendData (packet->payload, packet->payloadSize);
|
||||
packet->sendTime = mts;
|
||||
// Charlie always responds with RelayResponse
|
||||
session->m_SentPackets.emplace (packetNum, packet);
|
||||
auto packet = m_Server.GetSentPacketsPool ().AcquireShared ();
|
||||
packet->payloadSize = r ? CreateRouterInfoBlock (packet->payload, m_MaxPayloadSize - len - 32, r) : 0;
|
||||
if (!packet->payloadSize && r)
|
||||
session->SendFragmentedMessage (CreateDatabaseStoreMsg (r));
|
||||
packet->payloadSize += CreateRelayIntroBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize, buf + 1, len -1);
|
||||
if (packet->payloadSize < m_MaxPayloadSize)
|
||||
packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
|
||||
uint32_t packetNum = session->SendData (packet->payload, packet->payloadSize);
|
||||
packet->sendTime = mts;
|
||||
// Charlie always responds with RelayResponse
|
||||
session->m_SentPackets.emplace (packetNum, packet);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogInfo, "SSU2: Relay request nonce ", nonce, " already exists. Ignore");
|
||||
}
|
||||
|
||||
void SSU2Session::HandleRelayIntro (const uint8_t * buf, size_t len, int attempts)
|
||||
|
@ -2024,15 +2038,22 @@ namespace transport
|
|||
{
|
||||
// send HolePunch
|
||||
auto holePunchSession = std::make_shared<SSU2HolePunchSession>(m_Server, nonce, ep, addr);
|
||||
m_Server.AddSession (holePunchSession);
|
||||
holePunchSession->SendHolePunch (packet->payload, packet->payloadSize); // relay response block
|
||||
if (m_Server.AddSession (holePunchSession))
|
||||
holePunchSession->SendHolePunch (packet->payload, packet->payloadSize); // relay response block
|
||||
else
|
||||
{
|
||||
LogPrint (eLogInfo, "SSU2: Relay intro nonce ", nonce, " already exists. Ignore");
|
||||
return;
|
||||
}
|
||||
}
|
||||
packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
|
||||
/*uint32_t packetNum = */SendData (packet->payload, packet->payloadSize);
|
||||
// sometimes Bob doesn't ack this RelayResponse
|
||||
// TODO: uncomment line below once the problem is resolved
|
||||
//packet->sendTime = mts;
|
||||
//m_SentPackets.emplace (packetNum, packet);
|
||||
uint32_t packetNum = SendData (packet->payload, packet->payloadSize);
|
||||
if (m_RemoteVersion >= SSU2_MIN_RELAY_RESPONSE_RESEND_VERSION)
|
||||
{
|
||||
// sometimes Bob doesn't ack this RelayResponse in older versions
|
||||
packet->sendTime = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
m_SentPackets.emplace (packetNum, packet);
|
||||
}
|
||||
}
|
||||
|
||||
void SSU2Session::HandleRelayResponse (const uint8_t * buf, size_t len)
|
||||
|
@ -2067,11 +2088,13 @@ namespace transport
|
|||
memcpy (payload + 3, buf, len); // forward to Alice as is
|
||||
packet->payloadSize = len + 3;
|
||||
packet->payloadSize += CreatePaddingBlock (payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
|
||||
/*uint32_t packetNum = */it->second.first->SendData (packet->payload, packet->payloadSize);
|
||||
// sometimes Alice doesn't ack this RelayResponse
|
||||
// TODO: uncomment line below once the problem is resolved
|
||||
//packet->sendTime = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
//it->second.first->m_SentPackets.emplace (packetNum, packet);
|
||||
uint32_t packetNum = it->second.first->SendData (packet->payload, packet->payloadSize);
|
||||
if (m_RemoteVersion >= SSU2_MIN_RELAY_RESPONSE_RESEND_VERSION)
|
||||
{
|
||||
// sometimes Alice doesn't ack this RelayResponse in older versions
|
||||
packet->sendTime = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
it->second.first->m_SentPackets.emplace (packetNum, packet);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2139,29 +2162,33 @@ namespace transport
|
|||
GetRemoteIdentity ()->GetIdentHash ());
|
||||
if (session) // session with Charlie
|
||||
{
|
||||
m_Server.AddPeerTest (nonce, shared_from_this (), ts/1000);
|
||||
auto packet = m_Server.GetSentPacketsPool ().AcquireShared ();
|
||||
// Alice's RouterInfo
|
||||
auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ());
|
||||
if (r && (r->IsUnreachable () || !i2p::data::netdb.PopulateRouterInfoBuffer (r))) r = nullptr;
|
||||
packet->payloadSize = r ? CreateRouterInfoBlock (packet->payload, m_MaxPayloadSize - len - 32, r) : 0;
|
||||
if (!packet->payloadSize && r)
|
||||
session->SendFragmentedMessage (CreateDatabaseStoreMsg (r));
|
||||
if (packet->payloadSize + len + 48 > m_MaxPayloadSize)
|
||||
{
|
||||
// doesn't fit one message, send RouterInfo in separate message
|
||||
if (m_Server.AddPeerTest (nonce, shared_from_this (), ts/1000))
|
||||
{
|
||||
auto packet = m_Server.GetSentPacketsPool ().AcquireShared ();
|
||||
// Alice's RouterInfo
|
||||
auto r = i2p::data::netdb.FindRouter (GetRemoteIdentity ()->GetIdentHash ());
|
||||
if (r && (r->IsUnreachable () || !i2p::data::netdb.PopulateRouterInfoBuffer (r))) r = nullptr;
|
||||
packet->payloadSize = r ? CreateRouterInfoBlock (packet->payload, m_MaxPayloadSize - len - 32, r) : 0;
|
||||
if (!packet->payloadSize && r)
|
||||
session->SendFragmentedMessage (CreateDatabaseStoreMsg (r));
|
||||
if (packet->payloadSize + len + 48 > m_MaxPayloadSize)
|
||||
{
|
||||
// doesn't fit one message, send RouterInfo in separate message
|
||||
uint32_t packetNum = session->SendData (packet->payload, packet->payloadSize, SSU2_FLAG_IMMEDIATE_ACK_REQUESTED);
|
||||
packet->sendTime = ts;
|
||||
session->m_SentPackets.emplace (packetNum, packet);
|
||||
packet = m_Server.GetSentPacketsPool ().AcquireShared (); // new packet
|
||||
}
|
||||
// PeerTest to Charlie
|
||||
packet->payloadSize += CreatePeerTestBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize, 2,
|
||||
eSSU2PeerTestCodeAccept, GetRemoteIdentity ()->GetIdentHash (), buf + offset, len - offset);
|
||||
packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
|
||||
uint32_t packetNum = session->SendData (packet->payload, packet->payloadSize, SSU2_FLAG_IMMEDIATE_ACK_REQUESTED);
|
||||
packet->sendTime = ts;
|
||||
session->m_SentPackets.emplace (packetNum, packet);
|
||||
packet = m_Server.GetSentPacketsPool ().AcquireShared (); // new packet
|
||||
}
|
||||
// PeerTest to Charlie
|
||||
packet->payloadSize += CreatePeerTestBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize, 2,
|
||||
eSSU2PeerTestCodeAccept, GetRemoteIdentity ()->GetIdentHash (), buf + offset, len - offset);
|
||||
packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
|
||||
uint32_t packetNum = session->SendData (packet->payload, packet->payloadSize, SSU2_FLAG_IMMEDIATE_ACK_REQUESTED);
|
||||
packet->sendTime = ts;
|
||||
session->m_SentPackets.emplace (packetNum, packet);
|
||||
else
|
||||
LogPrint (eLogInfo, "SSU2: Peer test 1 nonce ", nonce, " already exists. Ignored");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2303,11 +2330,11 @@ namespace transport
|
|||
{
|
||||
session->SetRemoteIdentity (r->GetIdentity ());
|
||||
auto addr = r->GetSSU2Address (m_Address->IsV4 ());
|
||||
if (addr)
|
||||
if (addr && addr->IsPeerTesting ())
|
||||
{
|
||||
if (session->GetMsgNumReceived () >= 5)
|
||||
{
|
||||
// msg 5 already received
|
||||
// msg 5 already received and we know remote endpoint
|
||||
if (session->GetMsgNumReceived () == 5)
|
||||
{
|
||||
if (!session->IsConnectedRecently ())
|
||||
|
@ -2324,7 +2351,11 @@ namespace transport
|
|||
if (GetTestingState ())
|
||||
{
|
||||
// schedule msg 6 with delay
|
||||
session->SendPeerTest (6, buf + offset, len - offset, addr, true);
|
||||
if (!addr->host.is_unspecified () && addr->port)
|
||||
{
|
||||
session->SetRemoteEndpoint (boost::asio::ip::udp::endpoint (addr->host, addr->port));
|
||||
session->SendPeerTest (6, buf + offset, len - offset, addr, true);
|
||||
}
|
||||
SetTestingState (false);
|
||||
if (GetRouterStatus () != eRouterStatusFirewalled && addr->IsPeerTesting ())
|
||||
{
|
||||
|
@ -2342,7 +2373,7 @@ namespace transport
|
|||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU2: Peer test 4 address not found");
|
||||
LogPrint (eLogWarning, "SSU2: Peer test 4 address not found or not supported");
|
||||
session->Done ();
|
||||
}
|
||||
}
|
||||
|
@ -3024,7 +3055,7 @@ namespace transport
|
|||
{
|
||||
if (ts > it->second.second + SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU2: Relay nonce ", it->first, " was not responded in ", SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT, " seconds, deleted");
|
||||
LogPrint (eLogInfo, "SSU2: Relay nonce ", it->first, " was not responded in ", SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT, " seconds, deleted");
|
||||
it = m_RelaySessions.erase (it);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <set>
|
||||
#include <list>
|
||||
#include <boost/asio.hpp>
|
||||
#include "version.h"
|
||||
#include "Crypto.h"
|
||||
#include "RouterInfo.h"
|
||||
#include "RouterContext.h"
|
||||
|
@ -55,6 +56,7 @@ namespace transport
|
|||
const int SSU2_MAX_NUM_ACK_RANGES = 32; // to send
|
||||
const uint8_t SSU2_MAX_NUM_FRAGMENTS = 64;
|
||||
const int SSU2_SEND_DATETIME_NUM_PACKETS = 256;
|
||||
const int SSU2_MIN_RELAY_RESPONSE_RESEND_VERSION = MAKE_VERSION_NUMBER(0, 9, 64); // 0.9.64
|
||||
|
||||
// flags
|
||||
const uint8_t SSU2_FLAG_IMMEDIATE_ACK_REQUESTED = 0x01;
|
||||
|
@ -368,6 +370,7 @@ namespace transport
|
|||
std::shared_ptr<const i2p::data::RouterInfo::Address> m_Address;
|
||||
boost::asio::ip::udp::endpoint m_RemoteEndpoint;
|
||||
i2p::data::RouterInfo::CompatibleTransports m_RemoteTransports, m_RemotePeerTestTransports;
|
||||
int m_RemoteVersion;
|
||||
uint64_t m_DestConnID, m_SourceConnID;
|
||||
SSU2SessionState m_State;
|
||||
uint8_t m_KeyDataSend[64], m_KeyDataReceive[64];
|
||||
|
|
|
@ -73,14 +73,14 @@ namespace stream
|
|||
m_LastConfirmedReceivedSequenceNumber (0), // for limit inbound speed
|
||||
m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_IsNAcked (false), m_IsFirstACK (false),
|
||||
m_IsResendNeeded (false), m_IsFirstRttSample (false), m_IsSendTime (true), m_IsWinDropped (false),
|
||||
m_IsTimeOutResend (false), m_IsImmediateAckRequested (false), m_LocalDestination (local),
|
||||
m_IsTimeOutResend (false), m_IsImmediateAckRequested (false), m_IsRemoteLeaseChangeInProgress (false), m_LocalDestination (local),
|
||||
m_RemoteLeaseSet (remote), m_ReceiveTimer (m_Service), m_SendTimer (m_Service), m_ResendTimer (m_Service),
|
||||
m_AckSendTimer (m_Service), m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (port),
|
||||
m_RTT (INITIAL_RTT), m_SlowRTT (INITIAL_RTT), m_SlowRTT2 (INITIAL_RTT), m_WindowSize (INITIAL_WINDOW_SIZE), m_LastWindowDropSize (0),
|
||||
m_WindowDropTargetSize (0), m_WindowIncCounter (0), m_RTO (INITIAL_RTO),
|
||||
m_AckDelay (local.GetOwner ()->GetStreamingAckDelay ()), m_PrevRTTSample (INITIAL_RTT),
|
||||
m_Jitter (0), m_MinPacingTime (0),
|
||||
m_PacingTime (INITIAL_PACING_TIME), m_PacingTimeRem (0), m_LastSendTime (0),
|
||||
m_PacingTime (INITIAL_PACING_TIME), m_PacingTimeRem (0), m_LastSendTime (0), m_RemoteLeaseChangeTime (0),
|
||||
m_LastACKSendTime (0), m_PacketACKInterval (1), m_PacketACKIntervalRem (0), // for limit inbound speed
|
||||
m_NumResendAttempts (0), m_NumPacketsToSend (0), m_MTU (STREAMING_MTU)
|
||||
{
|
||||
|
@ -101,13 +101,13 @@ namespace stream
|
|||
m_LastConfirmedReceivedSequenceNumber (0), // for limit inbound speed
|
||||
m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_IsNAcked (false), m_IsFirstACK (false),
|
||||
m_IsResendNeeded (false), m_IsFirstRttSample (false), m_IsSendTime (true), m_IsWinDropped (false),
|
||||
m_IsTimeOutResend (false), m_IsImmediateAckRequested (false), m_LocalDestination (local),
|
||||
m_IsTimeOutResend (false), m_IsImmediateAckRequested (false), m_IsRemoteLeaseChangeInProgress (false), m_LocalDestination (local),
|
||||
m_ReceiveTimer (m_Service), m_SendTimer (m_Service), m_ResendTimer (m_Service), m_AckSendTimer (m_Service),
|
||||
m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (0), m_RTT (INITIAL_RTT), m_SlowRTT (INITIAL_RTT), m_SlowRTT2 (INITIAL_RTT),
|
||||
m_WindowSize (INITIAL_WINDOW_SIZE), m_LastWindowDropSize (0), m_WindowDropTargetSize (0), m_WindowIncCounter (0),
|
||||
m_RTO (INITIAL_RTO), m_AckDelay (local.GetOwner ()->GetStreamingAckDelay ()),
|
||||
m_PrevRTTSample (INITIAL_RTT), m_Jitter (0), m_MinPacingTime (0),
|
||||
m_PacingTime (INITIAL_PACING_TIME), m_PacingTimeRem (0), m_LastSendTime (0),
|
||||
m_PacingTime (INITIAL_PACING_TIME), m_PacingTimeRem (0), m_LastSendTime (0), m_RemoteLeaseChangeTime (0),
|
||||
m_LastACKSendTime (0), m_PacketACKInterval (1), m_PacketACKIntervalRem (0), // for limit inbound speed
|
||||
m_NumResendAttempts (0), m_NumPacketsToSend (0), m_MTU (STREAMING_MTU)
|
||||
{
|
||||
|
@ -256,6 +256,7 @@ namespace stream
|
|||
if (receivedSeqn <= m_PreviousReceivedSequenceNumber || receivedSeqn == m_LastReceivedSequenceNumber)
|
||||
{
|
||||
m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNextOutboundTunnel (m_CurrentOutboundTunnel);
|
||||
CancelRemoteLeaseChange ();
|
||||
UpdateCurrentRemoteLease ();
|
||||
}
|
||||
m_PreviousReceivedSequenceNumber = receivedSeqn;
|
||||
|
@ -1104,6 +1105,7 @@ namespace stream
|
|||
{
|
||||
if (!m_RemoteLeaseSet)
|
||||
{
|
||||
CancelRemoteLeaseChange ();
|
||||
UpdateCurrentRemoteLease ();
|
||||
if (!m_RemoteLeaseSet)
|
||||
{
|
||||
|
@ -1127,9 +1129,30 @@ namespace stream
|
|||
}
|
||||
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
if (!m_CurrentRemoteLease || !m_CurrentRemoteLease->endDate || // excluded from LeaseSet
|
||||
ts >= m_CurrentRemoteLease->endDate - i2p::data::LEASE_ENDDATE_THRESHOLD)
|
||||
if (!m_CurrentRemoteLease || !m_CurrentRemoteLease->endDate) // excluded from LeaseSet
|
||||
{
|
||||
CancelRemoteLeaseChange ();
|
||||
UpdateCurrentRemoteLease (true);
|
||||
}
|
||||
if (m_RemoteLeaseChangeTime && m_IsRemoteLeaseChangeInProgress && ts > m_RemoteLeaseChangeTime + INITIAL_RTT)
|
||||
{
|
||||
CancelRemoteLeaseChange ();
|
||||
m_CurrentRemoteLease = m_NextRemoteLease;
|
||||
HalveWindowSize ();
|
||||
}
|
||||
auto currentRemoteLease = m_CurrentRemoteLease;
|
||||
if (!m_IsRemoteLeaseChangeInProgress && m_RemoteLeaseSet && m_CurrentRemoteLease && ts >= m_CurrentRemoteLease->endDate - i2p::data::LEASE_ENDDATE_THRESHOLD)
|
||||
{
|
||||
auto leases = m_RemoteLeaseSet->GetNonExpiredLeases (false);
|
||||
if (leases.size ())
|
||||
{
|
||||
m_IsRemoteLeaseChangeInProgress = true;
|
||||
UpdateCurrentRemoteLease (true);
|
||||
m_NextRemoteLease = m_CurrentRemoteLease;
|
||||
}
|
||||
else
|
||||
UpdateCurrentRemoteLease (true);
|
||||
}
|
||||
if (m_CurrentRemoteLease && ts < m_CurrentRemoteLease->endDate + i2p::data::LEASE_ENDDATE_THRESHOLD)
|
||||
{
|
||||
bool freshTunnel = false;
|
||||
|
@ -1166,6 +1189,11 @@ namespace stream
|
|||
msg
|
||||
});
|
||||
m_NumSentBytes += it->GetLength ();
|
||||
if (m_IsRemoteLeaseChangeInProgress && !m_RemoteLeaseChangeTime)
|
||||
{
|
||||
m_RemoteLeaseChangeTime = ts;
|
||||
m_CurrentRemoteLease = currentRemoteLease; // change it back before new lease is confirmed
|
||||
}
|
||||
}
|
||||
m_CurrentOutboundTunnel->SendTunnelDataMsgs (msgs);
|
||||
}
|
||||
|
@ -1209,7 +1237,8 @@ namespace stream
|
|||
if (m_Status != eStreamStatusTerminated)
|
||||
{
|
||||
m_SendTimer.cancel ();
|
||||
m_SendTimer.expires_from_now (boost::posix_time::microseconds(SEND_INTERVAL));
|
||||
m_SendTimer.expires_from_now (boost::posix_time::microseconds(
|
||||
SEND_INTERVAL + m_LocalDestination.GetRandom () % SEND_INTERVAL_VARIANCE));
|
||||
m_SendTimer.async_wait (std::bind (&Stream::HandleSendTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
|
@ -1222,8 +1251,17 @@ namespace stream
|
|||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
if (m_LastSendTime && ts*1000 > m_LastSendTime*1000 + m_PacingTime)
|
||||
{
|
||||
m_NumPacketsToSend = ((ts*1000 - m_LastSendTime*1000) + m_PacingTimeRem) / m_PacingTime;
|
||||
m_PacingTimeRem = ((ts*1000 - m_LastSendTime*1000) + m_PacingTimeRem) - (m_NumPacketsToSend * m_PacingTime);
|
||||
if (m_PacingTime)
|
||||
{
|
||||
auto numPackets = std::lldiv (m_PacingTimeRem + ts*1000 - m_LastSendTime*1000, m_PacingTime);
|
||||
m_NumPacketsToSend = numPackets.quot;
|
||||
m_PacingTimeRem = numPackets.rem;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "Streaming: pacing time is zero");
|
||||
m_NumPacketsToSend = 1; m_PacingTimeRem = 0;
|
||||
}
|
||||
m_IsSendTime = true;
|
||||
if (m_WindowIncCounter && m_WindowSize < MAX_WINDOW_SIZE && !m_SendBuffer.IsEmpty () && m_PacingTime > m_MinPacingTime)
|
||||
{
|
||||
|
@ -1366,6 +1404,7 @@ namespace stream
|
|||
}
|
||||
else
|
||||
{
|
||||
CancelRemoteLeaseChange ();
|
||||
UpdateCurrentRemoteLease (); // pick another lease
|
||||
LogPrint (eLogWarning, "Streaming: Resend #", m_NumResendAttempts,
|
||||
", another remote lease has been selected for stream with rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID);
|
||||
|
@ -1506,22 +1545,9 @@ namespace stream
|
|||
LogPrint (eLogWarning, "Streaming: Remote LeaseSet not found");
|
||||
m_CurrentRemoteLease = nullptr;
|
||||
}
|
||||
if (isLeaseChanged)
|
||||
if (isLeaseChanged && !m_IsRemoteLeaseChangeInProgress)
|
||||
{
|
||||
// drop window to initial upon RemoteLease change
|
||||
m_RTO = INITIAL_RTO;
|
||||
if (m_WindowSize > INITIAL_WINDOW_SIZE)
|
||||
{
|
||||
m_WindowDropTargetSize = std::max (m_WindowSize/2, (float)INITIAL_WINDOW_SIZE);
|
||||
m_IsWinDropped = true;
|
||||
}
|
||||
else
|
||||
m_WindowSize = INITIAL_WINDOW_SIZE;
|
||||
m_LastWindowDropSize = 0;
|
||||
m_WindowIncCounter = 0;
|
||||
m_IsFirstRttSample = true;
|
||||
m_IsFirstACK = true;
|
||||
UpdatePacingTime ();
|
||||
HalveWindowSize ();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1559,7 +1585,30 @@ namespace stream
|
|||
m_IsWinDropped = true; // don't drop window twice
|
||||
UpdatePacingTime ();
|
||||
}
|
||||
|
||||
|
||||
void Stream::HalveWindowSize ()
|
||||
{
|
||||
m_RTO = INITIAL_RTO;
|
||||
if (m_WindowSize > INITIAL_WINDOW_SIZE)
|
||||
{
|
||||
m_WindowDropTargetSize = std::max (m_WindowSize/2, (float)INITIAL_WINDOW_SIZE);
|
||||
m_IsWinDropped = true;
|
||||
}
|
||||
else
|
||||
m_WindowSize = INITIAL_WINDOW_SIZE;
|
||||
m_LastWindowDropSize = 0;
|
||||
m_WindowIncCounter = 0;
|
||||
m_IsFirstRttSample = true;
|
||||
m_IsFirstACK = true;
|
||||
UpdatePacingTime ();
|
||||
}
|
||||
|
||||
void Stream::CancelRemoteLeaseChange ()
|
||||
{
|
||||
m_RemoteLeaseChangeTime = 0;
|
||||
m_IsRemoteLeaseChangeInProgress = false;
|
||||
}
|
||||
|
||||
StreamingDestination::StreamingDestination (std::shared_ptr<i2p::client::ClientDestination> owner, uint16_t localPort, bool gzip):
|
||||
m_Owner (owner), m_LocalPort (localPort), m_Gzip (gzip),
|
||||
m_PendingIncomingTimer (m_Owner->GetService ()),
|
||||
|
|
|
@ -69,7 +69,8 @@ namespace stream
|
|||
const int PENDING_INCOMING_TIMEOUT = 10; // in seconds
|
||||
const int MAX_RECEIVE_TIMEOUT = 20; // in seconds
|
||||
const uint16_t DELAY_CHOKING = 60000; // in milliseconds
|
||||
const uint64_t SEND_INTERVAL = 1000; // in microseconds
|
||||
const uint64_t SEND_INTERVAL = 10000; // in microseconds
|
||||
const uint64_t SEND_INTERVAL_VARIANCE = 2000; // in microseconds
|
||||
const uint64_t REQUEST_IMMEDIATE_ACK_INTERVAL = 7500; // in milliseconds
|
||||
const uint64_t REQUEST_IMMEDIATE_ACK_INTERVAL_VARIANCE = 3200; // in milliseconds
|
||||
const bool LOSS_BASED_CONTROL_ENABLED = 1; // 0/1
|
||||
|
@ -248,6 +249,8 @@ namespace stream
|
|||
|
||||
void UpdatePacingTime ();
|
||||
void ProcessWindowDrop ();
|
||||
void HalveWindowSize ();
|
||||
void CancelRemoteLeaseChange ();
|
||||
|
||||
private:
|
||||
|
||||
|
@ -268,12 +271,14 @@ namespace stream
|
|||
bool m_IsWinDropped;
|
||||
bool m_IsTimeOutResend;
|
||||
bool m_IsImmediateAckRequested;
|
||||
bool m_IsRemoteLeaseChangeInProgress;
|
||||
StreamingDestination& m_LocalDestination;
|
||||
std::shared_ptr<const i2p::data::IdentityEx> m_RemoteIdentity;
|
||||
std::shared_ptr<const i2p::crypto::Verifier> m_TransientVerifier; // in case of offline key
|
||||
std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet;
|
||||
std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
|
||||
std::shared_ptr<const i2p::data::Lease> m_CurrentRemoteLease;
|
||||
std::shared_ptr<const i2p::data::Lease> m_NextRemoteLease;
|
||||
std::shared_ptr<i2p::tunnel::OutboundTunnel> m_CurrentOutboundTunnel;
|
||||
std::queue<Packet *> m_ReceiveQueue;
|
||||
std::set<Packet *, PacketCmp> m_SavedPackets;
|
||||
|
@ -289,7 +294,7 @@ namespace stream
|
|||
int m_WindowIncCounter, m_RTO, m_AckDelay, m_PrevRTTSample;
|
||||
double m_Jitter;
|
||||
uint64_t m_MinPacingTime, m_PacingTime, m_PacingTimeRem, // microseconds
|
||||
m_LastSendTime; // miliseconds
|
||||
m_LastSendTime, m_RemoteLeaseChangeTime; // miliseconds
|
||||
uint64_t m_LastACKSendTime, m_PacketACKInterval, m_PacketACKIntervalRem; // for limit inbound speed
|
||||
int m_NumResendAttempts, m_NumPacketsToSend;
|
||||
size_t m_MTU;
|
||||
|
|
|
@ -672,6 +672,31 @@ namespace transport
|
|||
if (transport & compatibleTransports)
|
||||
peer->priority.push_back (transport);
|
||||
}
|
||||
if (peer->priority.empty ())
|
||||
{
|
||||
// try recently connected SSU2 if any
|
||||
auto supportedTransports = context.GetRouterInfo ().GetCompatibleTransports (false) &
|
||||
peer->router->GetCompatibleTransports (false);
|
||||
if (supportedTransports & (i2p::data::RouterInfo::eSSU2V4 | i2p::data::RouterInfo::eSSU2V6))
|
||||
{
|
||||
auto ep = peer->router->GetProfile ()->GetLastEndpoint ();
|
||||
if (!ep.address ().is_unspecified () && ep.port ())
|
||||
{
|
||||
if (ep.address ().is_v4 ())
|
||||
{
|
||||
if ((supportedTransports & i2p::data::RouterInfo::eSSU2V4) &&
|
||||
m_SSU2Server->IsConnectedRecently (ep, false))
|
||||
peer->priority.push_back (i2p::data::RouterInfo::eSSU2V4);
|
||||
}
|
||||
else if (ep.address ().is_v6 ())
|
||||
{
|
||||
if ((supportedTransports & i2p::data::RouterInfo::eSSU2V6) &&
|
||||
m_SSU2Server->IsConnectedRecently (ep))
|
||||
peer->priority.push_back (i2p::data::RouterInfo::eSSU2V6);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Transports::RequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident)
|
||||
|
|
|
@ -350,7 +350,8 @@ namespace tunnel
|
|||
|
||||
Tunnels::Tunnels (): m_IsRunning (false), m_Thread (nullptr), m_MaxNumTransitTunnels (DEFAULT_MAX_NUM_TRANSIT_TUNNELS),
|
||||
m_TotalNumSuccesiveTunnelCreations (0), m_TotalNumFailedTunnelCreations (0), // for normal average
|
||||
m_TunnelCreationSuccessRate (TCSR_START_VALUE), m_TunnelCreationAttemptsNum(0)
|
||||
m_TunnelCreationSuccessRate (TCSR_START_VALUE), m_TunnelCreationAttemptsNum(0),
|
||||
m_Rng(i2p::util::GetMonotonicMicroseconds ()%1000000LL)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include "util.h"
|
||||
#include "Queue.h"
|
||||
#include "Crypto.h"
|
||||
|
@ -244,6 +245,8 @@ namespace tunnel
|
|||
uint32_t GetMaxNumTransitTunnels () const { return m_MaxNumTransitTunnels; };
|
||||
int GetCongestionLevel() const { return m_MaxNumTransitTunnels ? CONGESTION_LEVEL_FULL * m_TransitTunnels.size() / m_MaxNumTransitTunnels : CONGESTION_LEVEL_FULL; }
|
||||
|
||||
std::mt19937& GetRng () { return m_Rng; };
|
||||
|
||||
private:
|
||||
|
||||
template<class TTunnel>
|
||||
|
@ -307,6 +310,7 @@ namespace tunnel
|
|||
int m_TotalNumSuccesiveTunnelCreations, m_TotalNumFailedTunnelCreations;
|
||||
double m_TunnelCreationSuccessRate;
|
||||
int m_TunnelCreationAttemptsNum;
|
||||
std::mt19937 m_Rng;
|
||||
|
||||
public:
|
||||
|
||||
|
|
Loading…
Reference in a new issue