From f98a310235d7e96dd39a9423593e2fd3cbb546ed Mon Sep 17 00:00:00 2001 From: SystemFailure Date: Fri, 18 Oct 2024 13:17:47 +0000 Subject: [PATCH 01/21] Revert LibreSSL workaround when LibreSSL version >= 4.0.0 --- libi2pd/Crypto.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libi2pd/Crypto.cpp b/libi2pd/Crypto.cpp index 2f9677c1..8ce290f1 100644 --- a/libi2pd/Crypto.cpp +++ b/libi2pd/Crypto.cpp @@ -997,7 +997,7 @@ namespace crypto } else { -#if defined(LIBRESSL_VERSION_NUMBER) +#if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x4000000fL std::vector m(msgLen + 16); if (msg == buf) { From 8a234f70e6e109340d64c161097c5d651dca6d53 Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 18 Oct 2024 15:59:37 -0400 Subject: [PATCH 02/21] send a packet to new remote lease in advance if current is about to expire --- libi2pd/Streaming.cpp | 81 +++++++++++++++++++++++++++++++------------ libi2pd/Streaming.h | 6 +++- 2 files changed, 64 insertions(+), 23 deletions(-) diff --git a/libi2pd/Streaming.cpp b/libi2pd/Streaming.cpp index 6a91e33f..c81a12eb 100644 --- a/libi2pd/Streaming.cpp +++ b/libi2pd/Streaming.cpp @@ -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,28 @@ 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 () > 1) + { + m_IsRemoteLeaseChangeInProgress = true; + UpdateCurrentRemoteLease (true); + m_NextRemoteLease = m_CurrentRemoteLease; + } + } if (m_CurrentRemoteLease && ts < m_CurrentRemoteLease->endDate + i2p::data::LEASE_ENDDATE_THRESHOLD) { bool freshTunnel = false; @@ -1166,6 +1187,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); } @@ -1366,6 +1392,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 +1533,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 +1573,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 owner, uint16_t localPort, bool gzip): m_Owner (owner), m_LocalPort (localPort), m_Gzip (gzip), m_PendingIncomingTimer (m_Owner->GetService ()), diff --git a/libi2pd/Streaming.h b/libi2pd/Streaming.h index 9ac84990..8183bdad 100644 --- a/libi2pd/Streaming.h +++ b/libi2pd/Streaming.h @@ -248,6 +248,8 @@ namespace stream void UpdatePacingTime (); void ProcessWindowDrop (); + void HalveWindowSize (); + void CancelRemoteLeaseChange (); private: @@ -268,12 +270,14 @@ namespace stream bool m_IsWinDropped; bool m_IsTimeOutResend; bool m_IsImmediateAckRequested; + bool m_IsRemoteLeaseChangeInProgress; StreamingDestination& m_LocalDestination; std::shared_ptr m_RemoteIdentity; std::shared_ptr m_TransientVerifier; // in case of offline key std::shared_ptr m_RemoteLeaseSet; std::shared_ptr m_RoutingSession; std::shared_ptr m_CurrentRemoteLease; + std::shared_ptr m_NextRemoteLease; std::shared_ptr m_CurrentOutboundTunnel; std::queue m_ReceiveQueue; std::set m_SavedPackets; @@ -289,7 +293,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; From 10335b90c58bc4137b8eface9c517e3d2a053606 Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 18 Oct 2024 19:57:35 -0400 Subject: [PATCH 03/21] fixed warnings --- libi2pd/Destination.h | 16 ++++++++-------- libi2pd/RouterContext.h | 20 ++++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/libi2pd/Destination.h b/libi2pd/Destination.h index 293cd75d..c6a8cab7 100644 --- a/libi2pd/Destination.h +++ b/libi2pd/Destination.h @@ -142,14 +142,14 @@ namespace client void CancelDestinationRequestWithEncryptedLeaseSet (std::shared_ptr dest, bool notify = true); // implements GarlicDestination - std::shared_ptr GetLeaseSet (); - std::shared_ptr GetTunnelPool () const { return m_Pool; } + std::shared_ptr GetLeaseSet () override; + std::shared_ptr 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 msg); - void ProcessDeliveryStatusMessage (std::shared_ptr 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 msg) override; + void ProcessDeliveryStatusMessage (std::shared_ptr 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 newLeaseSet); int GetLeaseSetType () const { return m_LeaseSetType; }; diff --git a/libi2pd/RouterContext.h b/libi2pd/RouterContext.h index c620f8b1..269c2125 100644 --- a/libi2pd/RouterContext.h +++ b/libi2pd/RouterContext.h @@ -186,24 +186,23 @@ namespace garlic void UpdateTimestamp (uint64_t ts); // in seconds, called from NetDb before publishing // implements LocalDestination - std::shared_ptr 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 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 GetLeaseSet () { return nullptr; }; - std::shared_ptr GetTunnelPool () const; + std::shared_ptr GetLeaseSet () override { return nullptr; }; + std::shared_ptr GetTunnelPool () const override; // override GarlicDestination - void ProcessGarlicMessage (std::shared_ptr msg); - void ProcessDeliveryStatusMessage (std::shared_ptr msg); + void ProcessGarlicMessage (std::shared_ptr msg) override; + void ProcessDeliveryStatusMessage (std::shared_ptr msg) 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 address, int port, bool publish) const; From f087654f258eb691488d3bd39c45c250bc8da84d Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 18 Oct 2024 20:02:41 -0400 Subject: [PATCH 04/21] fixed warnings --- libi2pd/RouterContext.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libi2pd/RouterContext.h b/libi2pd/RouterContext.h index 269c2125..f828a182 100644 --- a/libi2pd/RouterContext.h +++ b/libi2pd/RouterContext.h @@ -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 @@ -197,6 +196,7 @@ namespace garlic // override GarlicDestination void ProcessGarlicMessage (std::shared_ptr msg) override; void ProcessDeliveryStatusMessage (std::shared_ptr msg) override; + void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag) override; protected: From e6cbc842bfb1de2f0bc23f9e897b369e226cb9a7 Mon Sep 17 00:00:00 2001 From: orignal Date: Sat, 19 Oct 2024 08:45:25 -0400 Subject: [PATCH 05/21] request new leaseset if all leases are about to expire --- libi2pd/Streaming.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libi2pd/Streaming.cpp b/libi2pd/Streaming.cpp index c81a12eb..f22e724d 100644 --- a/libi2pd/Streaming.cpp +++ b/libi2pd/Streaming.cpp @@ -1144,12 +1144,14 @@ namespace stream 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 () > 1) + 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) { From 0cb677a2c0771a6d61df43cacfd8ad407b1e63b7 Mon Sep 17 00:00:00 2001 From: orignal Date: Sat, 19 Oct 2024 09:18:31 -0400 Subject: [PATCH 06/21] don't send peer test msg 6 if remote endpoint is unknown --- libi2pd/SSU2Session.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/libi2pd/SSU2Session.cpp b/libi2pd/SSU2Session.cpp index 454c38d5..17d2c1da 100644 --- a/libi2pd/SSU2Session.cpp +++ b/libi2pd/SSU2Session.cpp @@ -2303,11 +2303,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 +2324,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 +2346,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 (); } } From a24e0eb2dc6b71af2c6e09c50b8a4870937e9d09 Mon Sep 17 00:00:00 2001 From: orignal Date: Sun, 20 Oct 2024 16:12:35 -0400 Subject: [PATCH 07/21] don't delete unreachable routers if no transports --- libi2pd/NetDb.cpp | 5 +++-- libi2pd/NetDb.hpp | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/libi2pd/NetDb.cpp b/libi2pd/NetDb.cpp index 7266f44f..341d617e 100644 --- a/libi2pd/NetDb.cpp +++ b/libi2pd/NetDb.cpp @@ -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 > > saveToDisk; std::list 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 ()) diff --git a/libi2pd/NetDb.hpp b/libi2pd/NetDb.hpp index b84387de..9d8b875a 100644 --- a/libi2pd/NetDb.hpp +++ b/libi2pd/NetDb.hpp @@ -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 % From ea14b00d634f1ac547dbaf0bc40b4366c7741a3d Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 21 Oct 2024 20:58:09 -0400 Subject: [PATCH 08/21] save router's endpoint to profile and try to use it next time without requesting introducers --- libi2pd/Profiling.h | 8 ++++++ libi2pd/SSU2.cpp | 59 ++++++++++++++++++++++++++--------------- libi2pd/SSU2.h | 1 + libi2pd/SSU2Session.cpp | 9 +++++++ 4 files changed, 56 insertions(+), 21 deletions(-) diff --git a/libi2pd/Profiling.h b/libi2pd/Profiling.h index 1846f08e..be674d95 100644 --- a/libi2pd/Profiling.h +++ b/libi2pd/Profiling.h @@ -11,6 +11,7 @@ #include #include +#include #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 GetRouterProfile (const IdentHash& identHash); diff --git a/libi2pd/SSU2.cpp b/libi2pd/SSU2.cpp index e96a7233..871b5b05 100644 --- a/libi2pd/SSU2.cpp +++ b/libi2pd/SSU2.cpp @@ -833,6 +833,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 router, std::shared_ptr address, bool peerTest) { @@ -852,34 +875,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 (*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 + const auto& ep = router->GetProfile ()->GetLastEndpoint (); + if (IsConnectedRecently (ep)) + { + 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; } diff --git a/libi2pd/SSU2.h b/libi2pd/SSU2.h index 426c6a10..31783d58 100644 --- a/libi2pd/SSU2.h +++ b/libi2pd/SSU2.h @@ -147,6 +147,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 session); std::vector > FindIntroducers (int maxNumIntroducers, bool v4, const std::unordered_set& excluded); diff --git a/libi2pd/SSU2Session.cpp b/libi2pd/SSU2Session.cpp index 17d2c1da..20f21ce5 100644 --- a/libi2pd/SSU2Session.cpp +++ b/libi2pd/SSU2Session.cpp @@ -226,6 +226,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 +1176,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 From ddf30784eca8375cc0c6a8445400e3e566101a0e Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 21 Oct 2024 21:22:16 -0400 Subject: [PATCH 09/21] connected recently mutex --- libi2pd/SSU2.cpp | 2 ++ libi2pd/SSU2.h | 1 + 2 files changed, 3 insertions(+) diff --git a/libi2pd/SSU2.cpp b/libi2pd/SSU2.cpp index 871b5b05..296b3817 100644 --- a/libi2pd/SSU2.cpp +++ b/libi2pd/SSU2.cpp @@ -219,6 +219,7 @@ namespace transport bool SSU2Server::IsConnectedRecently (const boost::asio::ip::udp::endpoint& ep) { if (!ep.port () || ep.address ().is_unspecified ()) return false; + std::lock_guard l(m_ConnectedRecentlyMutex); auto it = m_ConnectedRecently.find (ep); if (it != m_ConnectedRecently.end ()) { @@ -234,6 +235,7 @@ namespace transport { if (!ep.port () || ep.address ().is_unspecified () || i2p::util::GetSecondsSinceEpoch () > ts + SSU2_HOLE_PUNCH_EXPIRATION) return; + std::lock_guard 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 diff --git a/libi2pd/SSU2.h b/libi2pd/SSU2.h index 31783d58..aac1d02f 100644 --- a/libi2pd/SSU2.h +++ b/libi2pd/SSU2.h @@ -194,6 +194,7 @@ namespace transport std::shared_ptr m_PendingTimeOffsetFrom; std::mt19937 m_Rng; std::map m_ConnectedRecently; // endpoint -> last activity time in seconds + mutable std::mutex m_ConnectedRecentlyMutex; std::unordered_map, uint64_t > > m_RequestedPeerTests; // nonce->(Alice, timestamp) std::list m_ReceivedPacketsQueue; mutable std::mutex m_ReceivedPacketsQueueMutex; From 26901e2945f898c4c3c5161f382374f6840a6dd7 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 23 Oct 2024 20:39:00 -0400 Subject: [PATCH 10/21] try recently connected SSU2 address if no other transports found --- libi2pd/SSU2.cpp | 2 +- libi2pd/Transports.cpp | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/libi2pd/SSU2.cpp b/libi2pd/SSU2.cpp index 296b3817..305cf1a4 100644 --- a/libi2pd/SSU2.cpp +++ b/libi2pd/SSU2.cpp @@ -884,7 +884,7 @@ namespace transport if (!isValidEndpoint && router->GetProfile ()->HasLastEndpoint (address->IsV4 ())) { // router doesn't publish endpoint, but we connected before and hole punch might be alive - const auto& ep = router->GetProfile ()->GetLastEndpoint (); + auto ep = router->GetProfile ()->GetLastEndpoint (); if (IsConnectedRecently (ep)) { if (CheckPendingOutgoingSession (ep, peerTest)) return false; diff --git a/libi2pd/Transports.cpp b/libi2pd/Transports.cpp index 549efb63..fc5c42e8 100644 --- a/libi2pd/Transports.cpp +++ b/libi2pd/Transports.cpp @@ -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)) + 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 r, const i2p::data::IdentHash& ident) From 500afe745f364fad292c275c0a022e838c04ec62 Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 24 Oct 2024 18:49:11 -0400 Subject: [PATCH 11/21] use min hole punch interval for connection attempts --- libi2pd/SSU2.cpp | 12 ++++++------ libi2pd/SSU2.h | 5 +++-- libi2pd/Transports.cpp | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/libi2pd/SSU2.cpp b/libi2pd/SSU2.cpp index 305cf1a4..01a3bb6c 100644 --- a/libi2pd/SSU2.cpp +++ b/libi2pd/SSU2.cpp @@ -216,16 +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 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; @@ -234,7 +234,7 @@ 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 l(m_ConnectedRecentlyMutex); auto [it, added] = m_ConnectedRecently.try_emplace (ep, ts); if (!added && ts > it->second) @@ -885,7 +885,7 @@ namespace transport { // router doesn't publish endpoint, but we connected before and hole punch might be alive auto ep = router->GetProfile ()->GetLastEndpoint (); - if (IsConnectedRecently (ep)) + if (IsConnectedRecently (ep, false)) { if (CheckPendingOutgoingSession (ep, peerTest)) return false; session->SetRemoteEndpoint (ep); @@ -1148,7 +1148,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++; diff --git a/libi2pd/SSU2.h b/libi2pd/SSU2.h index aac1d02f..0433033d 100644 --- a/libi2pd/SSU2.h +++ b/libi2pd/SSU2.h @@ -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 = 45; // in seconds + const int SSU2_MAX_HOLE_PUNCH_EXPIRATION = 181; // in seconds const size_t SSU2_MAX_NUM_PACKETS_PER_BATCH = 64; class SSU2Server: private i2p::util::RunnableServiceWithWork @@ -77,7 +78,7 @@ 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; } diff --git a/libi2pd/Transports.cpp b/libi2pd/Transports.cpp index fc5c42e8..34bc6142 100644 --- a/libi2pd/Transports.cpp +++ b/libi2pd/Transports.cpp @@ -685,7 +685,7 @@ namespace transport if (ep.address ().is_v4 ()) { if ((supportedTransports & i2p::data::RouterInfo::eSSU2V4) && - m_SSU2Server->IsConnectedRecently (ep)) + m_SSU2Server->IsConnectedRecently (ep, false)) peer->priority.push_back (i2p::data::RouterInfo::eSSU2V4); } else if (ep.address ().is_v6 ()) From d3630fb2b2fa09a7ae33fe794815d4c85db72091 Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 25 Oct 2024 13:25:33 -0400 Subject: [PATCH 12/21] assign name to main thread --- daemon/UnixDaemon.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/daemon/UnixDaemon.cpp b/daemon/UnixDaemon.cpp index d1eb1c39..9414962d 100644 --- a/daemon/UnixDaemon.cpp +++ b/daemon/UnixDaemon.cpp @@ -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 ("Daemon"); while (running) { std::this_thread::sleep_for (std::chrono::seconds(1)); From 87ae9c4b742c2112c95343d132a87b8e775c96f8 Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 25 Oct 2024 18:40:51 -0400 Subject: [PATCH 13/21] call main thread as i2pd-daemon --- daemon/UnixDaemon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon/UnixDaemon.cpp b/daemon/UnixDaemon.cpp index 9414962d..66661e0f 100644 --- a/daemon/UnixDaemon.cpp +++ b/daemon/UnixDaemon.cpp @@ -221,7 +221,7 @@ namespace i2p void DaemonLinux::run () { - i2p::util::SetThreadName ("Daemon"); + i2p::util::SetThreadName ("i2pd-daemon"); while (running) { std::this_thread::sleep_for (std::chrono::seconds(1)); From f611136ea795868e43a357e60a239c75c6ec8210 Mon Sep 17 00:00:00 2001 From: orignal Date: Sat, 26 Oct 2024 15:30:48 -0400 Subject: [PATCH 14/21] resend relay reponnse if remote router >= 0.9.64 --- libi2pd/SSU2Session.cpp | 30 ++++++++++++++++++------------ libi2pd/SSU2Session.h | 3 +++ 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/libi2pd/SSU2Session.cpp b/libi2pd/SSU2Session.cpp index 20f21ce5..8a1a44df 100644 --- a/libi2pd/SSU2Session.cpp +++ b/libi2pd/SSU2Session.cpp @@ -83,7 +83,7 @@ namespace transport std::shared_ptr 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); @@ -1185,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 (); @@ -2037,11 +2039,13 @@ namespace transport holePunchSession->SendHolePunch (packet->payload, packet->payloadSize); // relay response block } 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) @@ -2076,11 +2080,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 { diff --git a/libi2pd/SSU2Session.h b/libi2pd/SSU2Session.h index e27671f8..d54731dc 100644 --- a/libi2pd/SSU2Session.h +++ b/libi2pd/SSU2Session.h @@ -15,6 +15,7 @@ #include #include #include +#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 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]; From 743126b2ad11f774363fd41e280cbeb2806aa675 Mon Sep 17 00:00:00 2001 From: orignal Date: Sat, 26 Oct 2024 19:05:08 -0400 Subject: [PATCH 15/21] better hole punch expiration intervals --- libi2pd/SSU2.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libi2pd/SSU2.h b/libi2pd/SSU2.h index 0433033d..6f51f955 100644 --- a/libi2pd/SSU2.h +++ b/libi2pd/SSU2.h @@ -42,8 +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_MIN_HOLE_PUNCH_EXPIRATION = 45; // in seconds - const int SSU2_MAX_HOLE_PUNCH_EXPIRATION = 181; // 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 From 7461b640e32fdd8cabe42557051f59fae166c3b5 Mon Sep 17 00:00:00 2001 From: orignal Date: Sat, 26 Oct 2024 19:26:25 -0400 Subject: [PATCH 16/21] reduce CPU usage --- libi2pd/Streaming.cpp | 16 +++++++++++++--- libi2pd/Streaming.h | 3 ++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/libi2pd/Streaming.cpp b/libi2pd/Streaming.cpp index f22e724d..f9257974 100644 --- a/libi2pd/Streaming.cpp +++ b/libi2pd/Streaming.cpp @@ -1237,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)); } @@ -1250,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) { diff --git a/libi2pd/Streaming.h b/libi2pd/Streaming.h index 8183bdad..a686d71b 100644 --- a/libi2pd/Streaming.h +++ b/libi2pd/Streaming.h @@ -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 From 608056dcd2dcf999a4541ca66bdb78caade4b9b1 Mon Sep 17 00:00:00 2001 From: orignal Date: Sun, 27 Oct 2024 11:55:10 -0400 Subject: [PATCH 17/21] don't handle RelayRequest and RelayIntro with same nonce twice --- libi2pd/SSU2.cpp | 4 +++- libi2pd/SSU2.h | 2 +- libi2pd/SSU2Session.cpp | 50 ++++++++++++++++++++++++----------------- 3 files changed, 33 insertions(+), 23 deletions(-) diff --git a/libi2pd/SSU2.cpp b/libi2pd/SSU2.cpp index 01a3bb6c..c8f2909b 100644 --- a/libi2pd/SSU2.cpp +++ b/libi2pd/SSU2.cpp @@ -477,7 +477,7 @@ namespace transport HandleReceivedPackets (std::move (receivedPackets)); } - void SSU2Server::AddSession (std::shared_ptr session) + bool SSU2Server::AddSession (std::shared_ptr session) { if (session) { @@ -485,8 +485,10 @@ namespace transport { if (session->GetState () != eSSU2SessionStatePeerTest) AddSessionByRouterHash (session); + return true; } } + return false; } void SSU2Server::RemoveSession (uint64_t connID) diff --git a/libi2pd/SSU2.h b/libi2pd/SSU2.h index 6f51f955..319a4780 100644 --- a/libi2pd/SSU2.h +++ b/libi2pd/SSU2.h @@ -85,7 +85,7 @@ namespace transport bool IsSyncClockFromPeers () const { return m_IsSyncClockFromPeers; }; void AdjustTimeOffset (int64_t offset, std::shared_ptr from); - void AddSession (std::shared_ptr session); + bool AddSession (std::shared_ptr session); void RemoveSession (uint64_t connID); void RequestRemoveSession (uint64_t connID); void AddSessionByRouterHash (std::shared_ptr session); diff --git a/libi2pd/SSU2Session.cpp b/libi2pd/SSU2Session.cpp index 8a1a44df..15cabac9 100644 --- a/libi2pd/SSU2Session.cpp +++ b/libi2pd/SSU2Session.cpp @@ -1931,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) @@ -2035,8 +2038,13 @@ namespace transport { // send HolePunch auto holePunchSession = std::make_shared(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); @@ -3043,7 +3051,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 From 79e8ccbb5b2f4306208f8fe776dd09566963fbea Mon Sep 17 00:00:00 2001 From: orignal Date: Sun, 27 Oct 2024 12:24:22 -0400 Subject: [PATCH 18/21] don't handle PeerTest 1 with same nonce twice --- libi2pd/SSU2Session.cpp | 42 ++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/libi2pd/SSU2Session.cpp b/libi2pd/SSU2Session.cpp index 15cabac9..3489a6ba 100644 --- a/libi2pd/SSU2Session.cpp +++ b/libi2pd/SSU2Session.cpp @@ -2162,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 { From ec4fe9a1e680e677b94fab21c7febd8151478ab4 Mon Sep 17 00:00:00 2001 From: orignal Date: Sun, 27 Oct 2024 18:17:28 -0400 Subject: [PATCH 19/21] set congesion cap G if symmetric NAT and ipv4 in only transport --- libi2pd/RouterContext.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp index 1efb25db..f5f8b4fb 100644 --- a/libi2pd/RouterContext.cpp +++ b/libi2pd/RouterContext.cpp @@ -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 { From 4c66608caf0971902df0a528ea5738802d2fe828 Mon Sep 17 00:00:00 2001 From: orignal Date: Sun, 27 Oct 2024 21:58:19 -0400 Subject: [PATCH 20/21] random tunnel reject when medium congestion --- libi2pd/I2NPProtocol.cpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/libi2pd/I2NPProtocol.cpp b/libi2pd/I2NPProtocol.cpp index 3b2c204e..198f798b 100644 --- a/libi2pd/I2NPProtocol.cpp +++ b/libi2pd/I2NPProtocol.cpp @@ -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), From 43939cedf4aa0ba2db17b96ba424f132daf9f079 Mon Sep 17 00:00:00 2001 From: orignal Date: Sun, 27 Oct 2024 22:19:06 -0400 Subject: [PATCH 21/21] random tunnel reject when medium congestion --- libi2pd/Tunnel.cpp | 3 ++- libi2pd/Tunnel.h | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/libi2pd/Tunnel.cpp b/libi2pd/Tunnel.cpp index c41bb775..1e2a75d9 100644 --- a/libi2pd/Tunnel.cpp +++ b/libi2pd/Tunnel.cpp @@ -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) { } diff --git a/libi2pd/Tunnel.h b/libi2pd/Tunnel.h index 00a05386..02bfb374 100644 --- a/libi2pd/Tunnel.h +++ b/libi2pd/Tunnel.h @@ -18,6 +18,7 @@ #include #include #include +#include #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 @@ -307,6 +310,7 @@ namespace tunnel int m_TotalNumSuccesiveTunnelCreations, m_TotalNumFailedTunnelCreations; double m_TunnelCreationSuccessRate; int m_TunnelCreationAttemptsNum; + std::mt19937 m_Rng; public: