From 78847306e9a96a2dfabce6058aaedb90ff8a48c5 Mon Sep 17 00:00:00 2001 From: orignal Date: Tue, 8 Oct 2024 13:44:25 -0400 Subject: [PATCH 01/50] use EVP_PKEY for family signature verification --- libi2pd/Family.cpp | 75 ++++++++++++++++------------------------------ libi2pd/Family.h | 6 ++-- 2 files changed, 29 insertions(+), 52 deletions(-) diff --git a/libi2pd/Family.cpp b/libi2pd/Family.cpp index 8c6d3ba4..89e825f1 100644 --- a/libi2pd/Family.cpp +++ b/libi2pd/Family.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -7,7 +7,6 @@ */ #include -#include #include #include "Crypto.h" #include "FS.h" @@ -25,6 +24,8 @@ namespace data Families::~Families () { + for (auto it : m_SigningKeys) + if (it.second.first) EVP_PKEY_free (it.second.first); } void Families::LoadCertificate (const std::string& filename) @@ -47,48 +48,16 @@ namespace data cn += 3; char * family = strstr (cn, ".family"); if (family) family[0] = 0; - } - auto pkey = X509_get_pubkey (cert); - int keyType = EVP_PKEY_base_id (pkey); - switch (keyType) - { - case EVP_PKEY_DSA: - // TODO: - break; - case EVP_PKEY_EC: - { - EC_KEY * ecKey = EVP_PKEY_get1_EC_KEY (pkey); - if (ecKey) + auto pkey = X509_get_pubkey (cert); + if (pkey) + { + if (!m_SigningKeys.emplace (cn, std::make_pair(pkey, (int)m_SigningKeys.size () + 1)).second) { - auto group = EC_KEY_get0_group (ecKey); - if (group) - { - int curve = EC_GROUP_get_curve_name (group); - if (curve == NID_X9_62_prime256v1) - { - uint8_t signingKey[64]; - BIGNUM * x = BN_new(), * y = BN_new(); - EC_POINT_get_affine_coordinates_GFp (group, - EC_KEY_get0_public_key (ecKey), x, y, NULL); - i2p::crypto::bn2buf (x, signingKey, 32); - i2p::crypto::bn2buf (y, signingKey + 32, 32); - BN_free (x); BN_free (y); - verifier = std::make_shared(); - verifier->SetPublicKey (signingKey); - } - else - LogPrint (eLogWarning, "Family: elliptic curve ", curve, " is not supported"); - } - EC_KEY_free (ecKey); - } - break; - } - default: - LogPrint (eLogWarning, "Family: Certificate key type ", keyType, " is not supported"); + EVP_PKEY_free (pkey); + LogPrint (eLogError, "Family: Duplicated family name ", cn); + } + } } - EVP_PKEY_free (pkey); - if (verifier && cn) - m_SigningKeys.emplace (cn, std::make_pair(verifier, (int)m_SigningKeys.size () + 1)); } SSL_free (ssl); } @@ -130,14 +99,22 @@ namespace data LogPrint (eLogError, "Family: ", family, " is too long"); return false; } - - memcpy (buf, family.c_str (), len); - memcpy (buf + len, (const uint8_t *)ident, 32); - len += 32; - Base64ToByteStream (signature, signatureLen, signatureBuf, 64); auto it = m_SigningKeys.find (family); - if (it != m_SigningKeys.end ()) - return it->second.first->Verify (buf, len, signatureBuf); + if (it != m_SigningKeys.end () && it->second.first) + { + memcpy (buf, family.c_str (), len); + memcpy (buf + len, (const uint8_t *)ident, 32); + len += 32; + auto signatureBufLen = Base64ToByteStream (signature, signatureLen, signatureBuf, 64); + if (signatureBufLen) + { + EVP_MD_CTX * ctx = EVP_MD_CTX_create (); + EVP_DigestVerifyInit (ctx, NULL, NULL, NULL, it->second.first); + auto ret = EVP_DigestVerify (ctx, signatureBuf, signatureBufLen, buf, len); + EVP_MD_CTX_destroy (ctx); + return ret; + } + } // TODO: process key return true; } diff --git a/libi2pd/Family.h b/libi2pd/Family.h index b19ea142..a82e3042 100644 --- a/libi2pd/Family.h +++ b/libi2pd/Family.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2022, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -12,7 +12,7 @@ #include #include #include -#include "Signature.h" +#include #include "Identity.h" namespace i2p @@ -37,7 +37,7 @@ namespace data private: - std::map, FamilyID> > m_SigningKeys; // family -> (verifier, id) + std::map > m_SigningKeys; // family -> (verification pkey, id) }; std::string CreateFamilySignature (const std::string& family, const IdentHash& ident); From 88a5f8b125f9404fb73c7b0d1a37007101d9e6b0 Mon Sep 17 00:00:00 2001 From: orignal Date: Tue, 8 Oct 2024 18:44:46 -0400 Subject: [PATCH 02/50] use EVP_PKEY for signing --- libi2pd/Family.cpp | 49 +++++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/libi2pd/Family.cpp b/libi2pd/Family.cpp index 89e825f1..26750e7d 100644 --- a/libi2pd/Family.cpp +++ b/libi2pd/Family.cpp @@ -136,35 +136,30 @@ namespace data if (ret) { SSL * ssl = SSL_new (ctx); - EVP_PKEY * pkey = SSL_get_privatekey (ssl); - EC_KEY * ecKey = EVP_PKEY_get1_EC_KEY (pkey); - if (ecKey) + auto pkey = SSL_get_privatekey (ssl); + if (pkey) { - auto group = EC_KEY_get0_group (ecKey); - if (group) + uint8_t buf[100], signature[128]; + size_t len = family.length (); + memcpy (buf, family.c_str (), len); + memcpy (buf + len, (const uint8_t *)ident, 32); + len += 32; + size_t l = 128; + EVP_MD_CTX * mdctx = EVP_MD_CTX_create (); + EVP_DigestSignInit (mdctx, NULL, NULL, NULL, pkey); + if (EVP_DigestSign (mdctx, signature, &l, buf, len)) { - int curve = EC_GROUP_get_curve_name (group); - if (curve == NID_X9_62_prime256v1) - { - uint8_t signingPrivateKey[32], buf[50], signature[64]; - i2p::crypto::bn2buf (EC_KEY_get0_private_key (ecKey), signingPrivateKey, 32); - i2p::crypto::ECDSAP256Signer signer (signingPrivateKey); - size_t len = family.length (); - memcpy (buf, family.c_str (), len); - memcpy (buf + len, (const uint8_t *)ident, 32); - len += 32; - signer.Sign (buf, len, signature); - len = Base64EncodingBufferSize (64); - char * b64 = new char[len+1]; - len = ByteStreamToBase64 (signature, 64, b64, len); - b64[len] = 0; - sig = b64; - delete[] b64; - } - else - LogPrint (eLogWarning, "Family: elliptic curve ", curve, " is not supported"); - } - } + len = Base64EncodingBufferSize (l); + char * b64 = new char[len+1]; + len = ByteStreamToBase64 (signature, l, b64, len); + b64[len] = 0; + sig = b64; + delete[] b64; + } + else + LogPrint (eLogError, "Family: signing failed"); + EVP_MD_CTX_destroy (mdctx); + } SSL_free (ssl); } else From 2321a897f5ec89ab18f8b206c7191293ece0df3d Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 9 Oct 2024 08:48:24 -0400 Subject: [PATCH 03/50] rollback --- libi2pd/Family.cpp | 49 +++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/libi2pd/Family.cpp b/libi2pd/Family.cpp index 26750e7d..89e825f1 100644 --- a/libi2pd/Family.cpp +++ b/libi2pd/Family.cpp @@ -136,30 +136,35 @@ namespace data if (ret) { SSL * ssl = SSL_new (ctx); - auto pkey = SSL_get_privatekey (ssl); - if (pkey) + EVP_PKEY * pkey = SSL_get_privatekey (ssl); + EC_KEY * ecKey = EVP_PKEY_get1_EC_KEY (pkey); + if (ecKey) { - uint8_t buf[100], signature[128]; - size_t len = family.length (); - memcpy (buf, family.c_str (), len); - memcpy (buf + len, (const uint8_t *)ident, 32); - len += 32; - size_t l = 128; - EVP_MD_CTX * mdctx = EVP_MD_CTX_create (); - EVP_DigestSignInit (mdctx, NULL, NULL, NULL, pkey); - if (EVP_DigestSign (mdctx, signature, &l, buf, len)) + auto group = EC_KEY_get0_group (ecKey); + if (group) { - len = Base64EncodingBufferSize (l); - char * b64 = new char[len+1]; - len = ByteStreamToBase64 (signature, l, b64, len); - b64[len] = 0; - sig = b64; - delete[] b64; - } - else - LogPrint (eLogError, "Family: signing failed"); - EVP_MD_CTX_destroy (mdctx); - } + int curve = EC_GROUP_get_curve_name (group); + if (curve == NID_X9_62_prime256v1) + { + uint8_t signingPrivateKey[32], buf[50], signature[64]; + i2p::crypto::bn2buf (EC_KEY_get0_private_key (ecKey), signingPrivateKey, 32); + i2p::crypto::ECDSAP256Signer signer (signingPrivateKey); + size_t len = family.length (); + memcpy (buf, family.c_str (), len); + memcpy (buf + len, (const uint8_t *)ident, 32); + len += 32; + signer.Sign (buf, len, signature); + len = Base64EncodingBufferSize (64); + char * b64 = new char[len+1]; + len = ByteStreamToBase64 (signature, 64, b64, len); + b64[len] = 0; + sig = b64; + delete[] b64; + } + else + LogPrint (eLogWarning, "Family: elliptic curve ", curve, " is not supported"); + } + } SSL_free (ssl); } else From 23bac4a4038b7053467a53b0d4e8bb8584228de9 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 9 Oct 2024 21:40:26 -0400 Subject: [PATCH 04/50] recreate certificate if invalid --- daemon/I2PControl.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/daemon/I2PControl.cpp b/daemon/I2PControl.cpp index fc7f2257..74229220 100644 --- a/daemon/I2PControl.cpp +++ b/daemon/I2PControl.cpp @@ -45,15 +45,23 @@ namespace client i2pcp_crt = i2p::fs::DataDirPath(i2pcp_crt); if (i2pcp_key.at(0) != '/') i2pcp_key = i2p::fs::DataDirPath(i2pcp_key); - if (!i2p::fs::Exists (i2pcp_crt) || !i2p::fs::Exists (i2pcp_key)) { + if (!i2p::fs::Exists (i2pcp_crt) || !i2p::fs::Exists (i2pcp_key)) + { LogPrint (eLogInfo, "I2PControl: Creating new certificate for control connection"); CreateCertificate (i2pcp_crt.c_str(), i2pcp_key.c_str()); - } else { + } + else LogPrint(eLogDebug, "I2PControl: Using cert from ", i2pcp_crt); - } m_SSLContext.set_options (boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::single_dh_use); - m_SSLContext.use_certificate_file (i2pcp_crt, boost::asio::ssl::context::pem); - m_SSLContext.use_private_key_file (i2pcp_key, boost::asio::ssl::context::pem); + boost::system::error_code ec; + m_SSLContext.use_certificate_file (i2pcp_crt, boost::asio::ssl::context::pem, ec); + if (!ec) + m_SSLContext.use_private_key_file (i2pcp_key, boost::asio::ssl::context::pem, ec); + if (ec) + { + LogPrint (eLogInfo, "I2PControl: Failed to load ceritifcate: ", ec.message (), ". Recreating"); + CreateCertificate (i2pcp_crt.c_str(), i2pcp_key.c_str()); + } // handlers m_MethodHandlers["Authenticate"] = &I2PControlService::AuthenticateHandler; From ac4c58bbe958d5ac6f14c93cdeffd51af28277ee Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 10 Oct 2024 15:46:22 -0400 Subject: [PATCH 05/50] reload cerificate again after re-creation attempt --- daemon/I2PControl.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/daemon/I2PControl.cpp b/daemon/I2PControl.cpp index 74229220..43c74199 100644 --- a/daemon/I2PControl.cpp +++ b/daemon/I2PControl.cpp @@ -61,6 +61,12 @@ namespace client { LogPrint (eLogInfo, "I2PControl: Failed to load ceritifcate: ", ec.message (), ". Recreating"); CreateCertificate (i2pcp_crt.c_str(), i2pcp_key.c_str()); + m_SSLContext.use_certificate_file (i2pcp_crt, boost::asio::ssl::context::pem, ec); + if (!ec) + m_SSLContext.use_private_key_file (i2pcp_key, boost::asio::ssl::context::pem, ec); + if (ec) + // give up + LogPrint (eLogError, "I2PControl: Can't load certificates"); } // handlers @@ -411,7 +417,7 @@ namespace client X509_NAME_add_entry_by_txt (name, "O", MBSTRING_ASC, (unsigned char *)I2P_CONTROL_CERTIFICATE_ORGANIZATION, -1, -1, 0); // organization X509_NAME_add_entry_by_txt (name, "CN", MBSTRING_ASC, (unsigned char *)I2P_CONTROL_CERTIFICATE_COMMON_NAME, -1, -1, 0); // common name X509_set_issuer_name (x509, name); // set issuer to ourselves - X509_sign (x509, pkey, EVP_sha1 ()); // sign + X509_sign (x509, pkey, EVP_sha1 ()); // sign, last param must be NULL for EdDSA // save cert if ((f = fopen (crt_path, "wb")) != NULL) { From c86e0ec37192ae2237d33f97938910cc4d5e4934 Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 10 Oct 2024 20:43:06 -0400 Subject: [PATCH 06/50] lock queue's mutex less often --- libi2pd/Queue.h | 17 +++++++++++++++-- libi2pd/Tunnel.cpp | 30 +++++++++++++++++------------- 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/libi2pd/Queue.h b/libi2pd/Queue.h index 441f8c3a..daca14c2 100644 --- a/libi2pd/Queue.h +++ b/libi2pd/Queue.h @@ -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 * @@ -107,8 +107,21 @@ namespace util return GetNonThreadSafe (true); } - private: + void GetWholeQueue (std::queue& queue) + { + if (!queue.empty ()) + { + std::queue newQueue; + queue.swap (newQueue); + } + { + std::unique_lock l(m_QueueMutex); + m_Queue.swap (queue); + } + } + private: + Element GetNonThreadSafe (bool peek = false) { if (!m_Queue.empty ()) diff --git a/libi2pd/Tunnel.cpp b/libi2pd/Tunnel.cpp index 1b63b7a7..1aa8528e 100644 --- a/libi2pd/Tunnel.cpp +++ b/libi2pd/Tunnel.cpp @@ -483,14 +483,17 @@ namespace tunnel { try { - auto msg = m_Queue.GetNextWithTimeout (1000); // 1 sec - if (msg) + std::queue > msgs; + if (m_Queue.Wait (1,0)) // 1 sec { + m_Queue.GetWholeQueue (msgs); int numMsgs = 0; uint32_t prevTunnelID = 0, tunnelID = 0; std::shared_ptr prevTunnel; - do + while (!msgs.empty ()) { + auto msg = msgs.front (); msgs.pop (); + if (!msg) continue; std::shared_ptr tunnel; uint8_t typeID = msg->GetTypeID (); switch (typeID) @@ -530,17 +533,18 @@ namespace tunnel LogPrint (eLogWarning, "Tunnel: Unexpected message type ", (int) typeID); } - msg = (numMsgs <= MAX_TUNNEL_MSGS_BATCH_SIZE) ? m_Queue.Get () : nullptr; - if (msg) - { - prevTunnelID = tunnelID; - prevTunnel = tunnel; - numMsgs++; - } - else if (tunnel) - tunnel->FlushTunnelDataMsgs (); + prevTunnelID = tunnelID; + prevTunnel = tunnel; + numMsgs++; + + if (msgs.empty ()) + { + if (numMsgs < MAX_TUNNEL_MSGS_BATCH_SIZE && !m_Queue.IsEmpty ()) + m_Queue.GetWholeQueue (msgs); // try more + else if (tunnel) + tunnel->FlushTunnelDataMsgs (); // otherwise flush last + } } - while (msg); } if (i2p::transport::transports.IsOnline()) From ab02f722af2b4355754349efda3138d4426937af Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 11 Oct 2024 11:27:36 -0400 Subject: [PATCH 07/50] print non-resolved address to log --- libi2pd_client/I2PTunnel.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libi2pd_client/I2PTunnel.cpp b/libi2pd_client/I2PTunnel.cpp index ad4e14b8..5ba83e1f 100644 --- a/libi2pd_client/I2PTunnel.cpp +++ b/libi2pd_client/I2PTunnel.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2023, The PurpleI2P Project +* Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -784,7 +784,7 @@ namespace client } if (!found) { - LogPrint (eLogError, "I2PTunnel: Unable to resolve to compatible address"); + LogPrint (eLogError, "I2PTunnel: Unable to resolve ", m_Address, " to compatible address"); return; } @@ -794,7 +794,7 @@ namespace client Accept (); } else - LogPrint (eLogError, "I2PTunnel: Unable to resolve server tunnel address: ", ecode.message ()); + LogPrint (eLogError, "I2PTunnel: Unable to resolve server tunnel address ", m_Address, ": ", ecode.message ()); } void I2PServerTunnel::SetAccessList (const std::set& accessList) From 4a5406b80383ca3e8dfcc3a4c6d7d454dd0d40aa Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 11 Oct 2024 13:41:37 -0400 Subject: [PATCH 08/50] lock queue's mutex less often --- libi2pd/NetDb.cpp | 13 ++++++------- libi2pd/Tunnel.cpp | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/libi2pd/NetDb.cpp b/libi2pd/NetDb.cpp index 24269015..8abcb4d2 100644 --- a/libi2pd/NetDb.cpp +++ b/libi2pd/NetDb.cpp @@ -122,16 +122,18 @@ namespace data uint64_t lastProfilesCleanup = i2p::util::GetMonotonicMilliseconds (), lastObsoleteProfilesCleanup = lastProfilesCleanup; int16_t profilesCleanupVariance = 0, obsoleteProfilesCleanVariance = 0; + std::queue > msgs; while (m_IsRunning) { try { - auto msg = m_Queue.GetNextWithTimeout (1000); // 1 sec - if (msg) + if (m_Queue.Wait (1,0)) // 1 sec { - int numMsgs = 0; - while (msg) + m_Queue.GetWholeQueue (msgs); + while (!msgs.empty ()) { + auto msg = msgs.front (); msgs.pop (); + if (!msg) continue; LogPrint(eLogDebug, "NetDb: Got request with type ", (int) msg->GetTypeID ()); switch (msg->GetTypeID ()) { @@ -145,9 +147,6 @@ namespace data LogPrint (eLogError, "NetDb: Unexpected message type ", (int) msg->GetTypeID ()); //i2p::HandleI2NPMessage (msg); } - if (numMsgs > 100) break; - msg = m_Queue.Get (); - numMsgs++; } } if (!m_IsRunning) break; diff --git a/libi2pd/Tunnel.cpp b/libi2pd/Tunnel.cpp index 1aa8528e..37d5a9b7 100644 --- a/libi2pd/Tunnel.cpp +++ b/libi2pd/Tunnel.cpp @@ -479,11 +479,11 @@ namespace tunnel std::this_thread::sleep_for (std::chrono::seconds(1)); // wait for other parts are ready uint64_t lastTs = 0, lastPoolsTs = 0, lastMemoryPoolTs = 0; + std::queue > msgs; while (m_IsRunning) { try { - std::queue > msgs; if (m_Queue.Wait (1,0)) // 1 sec { m_Queue.GetWholeQueue (msgs); From 8210911bc5fe3975e3fe364ab9619b4a95d9c2c8 Mon Sep 17 00:00:00 2001 From: orignal Date: Sat, 12 Oct 2024 17:51:26 -0400 Subject: [PATCH 09/50] use std::list and splice fr msg queue --- libi2pd/I2NPProtocol.cpp | 6 ------ libi2pd/I2NPProtocol.h | 3 ++- libi2pd/NetDb.cpp | 4 ++-- libi2pd/Queue.h | 27 ++++++++++++--------------- libi2pd/Tunnel.cpp | 6 +++--- libi2pd/Tunnel.h | 2 +- 6 files changed, 20 insertions(+), 28 deletions(-) diff --git a/libi2pd/I2NPProtocol.cpp b/libi2pd/I2NPProtocol.cpp index 4cceda8f..3b2c204e 100644 --- a/libi2pd/I2NPProtocol.cpp +++ b/libi2pd/I2NPProtocol.cpp @@ -932,14 +932,8 @@ namespace i2p void I2NPMessagesHandler::Flush () { if (!m_TunnelMsgs.empty ()) - { i2p::tunnel::tunnels.PostTunnelData (m_TunnelMsgs); - m_TunnelMsgs.clear (); - } if (!m_TunnelGatewayMsgs.empty ()) - { i2p::tunnel::tunnels.PostTunnelData (m_TunnelGatewayMsgs); - m_TunnelGatewayMsgs.clear (); - } } } diff --git a/libi2pd/I2NPProtocol.h b/libi2pd/I2NPProtocol.h index 4e26fc94..5971ce17 100644 --- a/libi2pd/I2NPProtocol.h +++ b/libi2pd/I2NPProtocol.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "Crypto.h" #include "I2PEndian.h" @@ -328,7 +329,7 @@ namespace tunnel private: - std::vector > m_TunnelMsgs, m_TunnelGatewayMsgs; + std::list > m_TunnelMsgs, m_TunnelGatewayMsgs; }; } diff --git a/libi2pd/NetDb.cpp b/libi2pd/NetDb.cpp index 8abcb4d2..20b2fd42 100644 --- a/libi2pd/NetDb.cpp +++ b/libi2pd/NetDb.cpp @@ -122,7 +122,7 @@ namespace data uint64_t lastProfilesCleanup = i2p::util::GetMonotonicMilliseconds (), lastObsoleteProfilesCleanup = lastProfilesCleanup; int16_t profilesCleanupVariance = 0, obsoleteProfilesCleanVariance = 0; - std::queue > msgs; + std::list > msgs; while (m_IsRunning) { try @@ -132,7 +132,7 @@ namespace data m_Queue.GetWholeQueue (msgs); while (!msgs.empty ()) { - auto msg = msgs.front (); msgs.pop (); + auto msg = msgs.front (); msgs.pop_front (); if (!msg) continue; LogPrint(eLogDebug, "NetDb: Got request with type ", (int) msg->GetTypeID ()); switch (msg->GetTypeID ()) diff --git a/libi2pd/Queue.h b/libi2pd/Queue.h index daca14c2..ec62bddf 100644 --- a/libi2pd/Queue.h +++ b/libi2pd/Queue.h @@ -9,8 +9,7 @@ #ifndef QUEUE_H__ #define QUEUE_H__ -#include -#include +#include #include #include #include @@ -29,22 +28,20 @@ namespace util void Put (Element e) { std::unique_lock l(m_QueueMutex); - m_Queue.push (std::move(e)); + m_Queue.push_back (std::move(e)); m_NonEmpty.notify_one (); } - templateclass Container, typename... R> - void Put (const Container& vec) + void Put (std::list& list) { - if (!vec.empty ()) + if (!list.empty ()) { std::unique_lock l(m_QueueMutex); - for (const auto& it: vec) - m_Queue.push (std::move(it)); + m_Queue.splice (m_Queue.end (), list); m_NonEmpty.notify_one (); - } - } - + } + } + Element GetNext () { std::unique_lock l(m_QueueMutex); @@ -107,11 +104,11 @@ namespace util return GetNonThreadSafe (true); } - void GetWholeQueue (std::queue& queue) + void GetWholeQueue (std::list& queue) { if (!queue.empty ()) { - std::queue newQueue; + std::list newQueue; queue.swap (newQueue); } { @@ -128,7 +125,7 @@ namespace util { auto el = m_Queue.front (); if (!peek) - m_Queue.pop (); + m_Queue.pop_front (); return el; } return nullptr; @@ -136,7 +133,7 @@ namespace util private: - std::queue m_Queue; + std::list m_Queue; std::mutex m_QueueMutex; std::condition_variable m_NonEmpty; }; diff --git a/libi2pd/Tunnel.cpp b/libi2pd/Tunnel.cpp index 37d5a9b7..ca110ccc 100644 --- a/libi2pd/Tunnel.cpp +++ b/libi2pd/Tunnel.cpp @@ -479,7 +479,7 @@ namespace tunnel std::this_thread::sleep_for (std::chrono::seconds(1)); // wait for other parts are ready uint64_t lastTs = 0, lastPoolsTs = 0, lastMemoryPoolTs = 0; - std::queue > msgs; + std::list > msgs; while (m_IsRunning) { try @@ -492,7 +492,7 @@ namespace tunnel std::shared_ptr prevTunnel; while (!msgs.empty ()) { - auto msg = msgs.front (); msgs.pop (); + auto msg = msgs.front (); msgs.pop_front (); if (!msg) continue; std::shared_ptr tunnel; uint8_t typeID = msg->GetTypeID (); @@ -830,7 +830,7 @@ namespace tunnel if (msg) m_Queue.Put (msg); } - void Tunnels::PostTunnelData (const std::vector >& msgs) + void Tunnels::PostTunnelData (std::list >& msgs) { m_Queue.Put (msgs); } diff --git a/libi2pd/Tunnel.h b/libi2pd/Tunnel.h index 6b014af2..00a05386 100644 --- a/libi2pd/Tunnel.h +++ b/libi2pd/Tunnel.h @@ -229,7 +229,7 @@ namespace tunnel std::shared_ptr CreateInboundTunnel (std::shared_ptr config, std::shared_ptr pool, std::shared_ptr outboundTunnel); std::shared_ptr CreateOutboundTunnel (std::shared_ptr config, std::shared_ptr pool); void PostTunnelData (std::shared_ptr msg); - void PostTunnelData (const std::vector >& msgs); + void PostTunnelData (std::list >& msgs); // and cleanup msgs void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr tunnel); void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr tunnel); std::shared_ptr CreateTunnelPool (int numInboundHops, From fbd07a5276ee12cf3ac28e123a8e57502ad422d6 Mon Sep 17 00:00:00 2001 From: orignal Date: Sun, 13 Oct 2024 19:53:40 -0400 Subject: [PATCH 10/50] SSU2Session/SSU2OutOfSession split --- libi2pd/SSU2.h | 1 + libi2pd/SSU2OutOfSession.cpp | 228 +++++++++++++++++++++++++++++++++++ libi2pd/SSU2OutOfSession.h | 57 +++++++++ libi2pd/SSU2Session.cpp | 218 +-------------------------------- libi2pd/SSU2Session.h | 43 +------ 5 files changed, 293 insertions(+), 254 deletions(-) create mode 100644 libi2pd/SSU2OutOfSession.cpp create mode 100644 libi2pd/SSU2OutOfSession.h diff --git a/libi2pd/SSU2.h b/libi2pd/SSU2.h index 2b97bd25..09f764c6 100644 --- a/libi2pd/SSU2.h +++ b/libi2pd/SSU2.h @@ -17,6 +17,7 @@ #include #include "util.h" #include "SSU2Session.h" +#include "SSU2OutOfSession.h" #include "Socks5.h" namespace i2p diff --git a/libi2pd/SSU2OutOfSession.cpp b/libi2pd/SSU2OutOfSession.cpp new file mode 100644 index 00000000..cf9f3f36 --- /dev/null +++ b/libi2pd/SSU2OutOfSession.cpp @@ -0,0 +1,228 @@ +/* +* Copyright (c) 2024, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +#include "Log.h" +#include "SSU2.h" +#include "SSU2OutOfSession.h" + +namespace i2p +{ +namespace transport +{ + SSU2PeerTestSession::SSU2PeerTestSession (SSU2Server& server, uint64_t sourceConnID, uint64_t destConnID): + SSU2Session (server, nullptr, nullptr, false), + m_MsgNumReceived (0), m_NumResends (0),m_IsConnectedRecently (false), m_IsStatusChanged (false), + m_PeerTestResendTimer (server.GetService ()) + { + if (!sourceConnID) sourceConnID = ~destConnID; + if (!destConnID) destConnID = ~sourceConnID; + SetSourceConnID (sourceConnID); + SetDestConnID (destConnID); + SetState (eSSU2SessionStatePeerTest); + SetTerminationTimeout (SSU2_PEER_TEST_EXPIRATION_TIMEOUT); + } + + bool SSU2PeerTestSession::ProcessPeerTest (uint8_t * buf, size_t len) + { + // we are Alice or Charlie, msgs 5,6,7 + Header header; + memcpy (header.buf, buf, 16); + header.ll[0] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 24)); + header.ll[1] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 12)); + if (header.h.type != eSSU2PeerTest) + { + LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type, " instead ", (int)eSSU2PeerTest); + return false; + } + if (len < 48) + { + LogPrint (eLogWarning, "SSU2: PeerTest message too short ", len); + return false; + } + uint8_t nonce[12] = {0}; + uint64_t headerX[2]; // sourceConnID, token + i2p::crypto::ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, (uint8_t *)headerX); + SetDestConnID (headerX[0]); + // decrypt and handle payload + uint8_t * payload = buf + 32; + CreateNonce (be32toh (header.h.packetNum), nonce); + uint8_t h[32]; + memcpy (h, header.buf, 16); + memcpy (h + 16, &headerX, 16); + if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 48, h, 32, + i2p::context.GetSSU2IntroKey (), nonce, payload, len - 48, false)) + { + LogPrint (eLogWarning, "SSU2: PeerTest AEAD verification failed "); + return false; + } + HandlePayload (payload, len - 48); + SetIsDataReceived (false); + return true; + } + + void SSU2PeerTestSession::HandlePeerTest (const uint8_t * buf, size_t len) + { + // msgs 5-7 + if (len < 8) return; + uint8_t msg = buf[0]; + if (msg <= m_MsgNumReceived) + { + LogPrint (eLogDebug, "SSU2: PeerTest msg num ", msg, " received after ", m_MsgNumReceived, ". Ignored"); + return; + } + size_t offset = 3; // points to signed data after msg + code + flag + uint32_t nonce = bufbe32toh (buf + offset + 1); // 1 - ver + switch (msg) // msg + { + case 5: // Alice from Charlie 1 + { + if (htobe64 (((uint64_t)nonce << 32) | nonce) == GetSourceConnID ()) + { + m_IsConnectedRecently = GetServer ().IsConnectedRecently (GetRemoteEndpoint ()); + if (GetAddress ()) + { + if (!m_IsConnectedRecently) + SetRouterStatus (eRouterStatusOK); + else if (m_IsStatusChanged && GetRouterStatus () == eRouterStatusFirewalled) + SetRouterStatus (eRouterStatusUnknown); + SendPeerTest (6, buf + offset, len - offset); + } + } + else + LogPrint (eLogWarning, "SSU2: Peer test 5 nonce mismatch ", nonce, " connID=", GetSourceConnID ()); + break; + } + case 6: // Charlie from Alice + { + m_PeerTestResendTimer.cancel (); // no more msg 5 resends + if (GetAddress ()) + SendPeerTest (7, buf + offset, len - offset); + else + LogPrint (eLogWarning, "SSU2: Unknown address for peer test 6"); + GetServer ().AddConnectedRecently (GetRemoteEndpoint (), i2p::util::GetSecondsSinceEpoch ()); + GetServer ().RequestRemoveSession (GetConnID ()); + break; + } + case 7: // Alice from Charlie 2 + { + m_PeerTestResendTimer.cancel (); // no more msg 6 resends + auto addr = GetAddress (); + if (addr && addr->IsV6 ()) + i2p::context.SetStatusV6 (eRouterStatusOK); // set status OK for ipv6 even if from SSU2 + GetServer ().AddConnectedRecently (GetRemoteEndpoint (), i2p::util::GetSecondsSinceEpoch ()); + GetServer ().RequestRemoveSession (GetConnID ()); + break; + } + default: + LogPrint (eLogWarning, "SSU2: PeerTest unexpected msg num ", msg); + return; + } + m_MsgNumReceived = msg; + } + + void SSU2PeerTestSession::SendPeerTest (uint8_t msg) + { + auto addr = GetAddress (); + if (!addr) return; + Header header; + uint8_t h[32], payload[SSU2_MAX_PACKET_SIZE]; + // fill packet + header.h.connID = GetDestConnID (); // dest id + RAND_bytes (header.buf + 8, 4); // random packet num + header.h.type = eSSU2PeerTest; + header.h.flags[0] = 2; // ver + header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID + header.h.flags[2] = 0; // flag + memcpy (h, header.buf, 16); + htobuf64 (h + 16, GetSourceConnID ()); // source id + // payload + payload[0] = eSSU2BlkDateTime; + htobe16buf (payload + 1, 4); + htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); + size_t payloadSize = 7; + if (msg == 6 || msg == 7) + payloadSize += CreateAddressBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize, GetRemoteEndpoint ()); + payloadSize += CreatePeerTestBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize, + msg, eSSU2PeerTestCodeAccept, nullptr, m_SignedData.data (), m_SignedData.size ()); + payloadSize += CreatePaddingBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize); + // encrypt + uint8_t n[12]; + CreateNonce (be32toh (header.h.packetNum), n); + i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, h, 32, addr->i, n, payload, payloadSize + 16, true); + payloadSize += 16; + header.ll[0] ^= CreateHeaderMask (addr->i, payload + (payloadSize - 24)); + header.ll[1] ^= CreateHeaderMask (addr->i, payload + (payloadSize - 12)); + memset (n, 0, 12); + i2p::crypto::ChaCha20 (h + 16, 16, addr->i, n, h + 16); + // send + GetServer ().Send (header.buf, 16, h + 16, 16, payload, payloadSize, GetRemoteEndpoint ()); + } + + void SSU2PeerTestSession::SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen) + { +#if __cplusplus >= 202002L // C++20 + m_SignedData.assign (signedData, signedData + signedDataLen); +#else + m_SignedData.resize (signedDataLen); + memcpy (m_SignedData.data (), signedData, signedDataLen); +#endif + SendPeerTest (msg); + // schedule resend for msgs 5 or 6 + if (msg == 5 || msg == 6) + ScheduleResend (); + } + + void SSU2PeerTestSession::SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, + std::shared_ptr addr) + { + if (!addr) return; + SetAddress (addr); + SendPeerTest (msg, signedData, signedDataLen); + } + + void SSU2PeerTestSession::Connect () + { + LogPrint (eLogError, "SSU2: Can't connect peer test session"); + } + + bool SSU2PeerTestSession::ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len) + { + LogPrint (eLogError, "SSU2: Can't handle incoming message in peer test session"); + return false; + } + + void SSU2PeerTestSession::ScheduleResend () + { + if (m_NumResends < SSU2_PEER_TEST_MAX_NUM_RESENDS) + { + m_PeerTestResendTimer.expires_from_now (boost::posix_time::milliseconds( + SSU2_PEER_TEST_RESEND_INTERVAL + GetServer ().GetRng ()() % SSU2_PEER_TEST_RESEND_INTERVAL_VARIANCE)); + std::weak_ptr s(std::static_pointer_cast(shared_from_this ())); + m_PeerTestResendTimer.async_wait ([s](const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + auto s1 = s.lock (); + if (s1) + { + int msg = 0; + if (s1->m_MsgNumReceived < 6) + msg = (s1->m_MsgNumReceived == 5) ? 6 : 5; + if (msg) // 5 or 6 + { + s1->SendPeerTest (msg); + s1->ScheduleResend (); + } + } + } + }); + m_NumResends++; + } + } +} +} diff --git a/libi2pd/SSU2OutOfSession.h b/libi2pd/SSU2OutOfSession.h new file mode 100644 index 00000000..29a35d52 --- /dev/null +++ b/libi2pd/SSU2OutOfSession.h @@ -0,0 +1,57 @@ +/* +* Copyright (c) 2024, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +#ifndef SSU2_OUT_OF_SESSION_H__ +#define SSU2_OUT_OF_SESSION_H__ + +#include +#include "SSU2Session.h" + +namespace i2p +{ +namespace transport +{ + const int SSU2_PEER_TEST_RESEND_INTERVAL = 3000; // in milliseconds + const int SSU2_PEER_TEST_RESEND_INTERVAL_VARIANCE = 2000; // in milliseconds + const int SSU2_PEER_TEST_MAX_NUM_RESENDS = 3; + + class SSU2PeerTestSession: public SSU2Session // for PeerTest msgs 5,6,7 + { + public: + + SSU2PeerTestSession (SSU2Server& server, uint64_t sourceConnID, uint64_t destConnID); + + uint8_t GetMsgNumReceived () const { return m_MsgNumReceived; } + bool IsConnectedRecently () const { return m_IsConnectedRecently; } + void SetStatusChanged () { m_IsStatusChanged = true; } + + void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, + std::shared_ptr addr); + bool ProcessPeerTest (uint8_t * buf, size_t len) override; + void Connect () override; // outgoing + bool ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len) override; // incoming + + private: + + void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen); // PeerTest message + void SendPeerTest (uint8_t msg); // send or resend m_SignedData + void HandlePeerTest (const uint8_t * buf, size_t len) override; + + void ScheduleResend (); + + private: + + uint8_t m_MsgNumReceived, m_NumResends; + bool m_IsConnectedRecently, m_IsStatusChanged; + std::vector m_SignedData; // for resends + boost::asio::deadline_timer m_PeerTestResendTimer; + }; +} +} + +#endif diff --git a/libi2pd/SSU2Session.cpp b/libi2pd/SSU2Session.cpp index 6213c614..37d078ac 100644 --- a/libi2pd/SSU2Session.cpp +++ b/libi2pd/SSU2Session.cpp @@ -13,17 +13,12 @@ #include "Gzip.h" #include "NetDb.hpp" #include "SSU2.h" +#include "SSU2Session.h" namespace i2p { namespace transport { - static inline void CreateNonce (uint64_t seqn, uint8_t * nonce) - { - memset (nonce, 0, 4); - htole64buf (nonce + 4, seqn); - } - void SSU2IncompleteMessage::AttachNextFragment (const uint8_t * fragment, size_t fragmentSize) { if (msg->len + fragmentSize > msg->maxLen) @@ -3086,216 +3081,5 @@ namespace transport else if (!sent && !m_SentPackets.empty ()) // if only acks received, nothing sent and we still have something to resend Resend (i2p::util::GetMillisecondsSinceEpoch ()); // than right time to resend } - - SSU2PeerTestSession::SSU2PeerTestSession (SSU2Server& server, uint64_t sourceConnID, uint64_t destConnID): - SSU2Session (server, nullptr, nullptr, false), - m_MsgNumReceived (0), m_NumResends (0),m_IsConnectedRecently (false), m_IsStatusChanged (false), - m_PeerTestResendTimer (server.GetService ()) - { - if (!sourceConnID) sourceConnID = ~destConnID; - if (!destConnID) destConnID = ~sourceConnID; - SetSourceConnID (sourceConnID); - SetDestConnID (destConnID); - SetState (eSSU2SessionStatePeerTest); - SetTerminationTimeout (SSU2_PEER_TEST_EXPIRATION_TIMEOUT); - } - - bool SSU2PeerTestSession::ProcessPeerTest (uint8_t * buf, size_t len) - { - // we are Alice or Charlie, msgs 5,6,7 - Header header; - memcpy (header.buf, buf, 16); - header.ll[0] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 24)); - header.ll[1] ^= CreateHeaderMask (i2p::context.GetSSU2IntroKey (), buf + (len - 12)); - if (header.h.type != eSSU2PeerTest) - { - LogPrint (eLogWarning, "SSU2: Unexpected message type ", (int)header.h.type, " instead ", (int)eSSU2PeerTest); - return false; - } - if (len < 48) - { - LogPrint (eLogWarning, "SSU2: PeerTest message too short ", len); - return false; - } - uint8_t nonce[12] = {0}; - uint64_t headerX[2]; // sourceConnID, token - i2p::crypto::ChaCha20 (buf + 16, 16, i2p::context.GetSSU2IntroKey (), nonce, (uint8_t *)headerX); - SetDestConnID (headerX[0]); - // decrypt and handle payload - uint8_t * payload = buf + 32; - CreateNonce (be32toh (header.h.packetNum), nonce); - uint8_t h[32]; - memcpy (h, header.buf, 16); - memcpy (h + 16, &headerX, 16); - if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len - 48, h, 32, - i2p::context.GetSSU2IntroKey (), nonce, payload, len - 48, false)) - { - LogPrint (eLogWarning, "SSU2: PeerTest AEAD verification failed "); - return false; - } - HandlePayload (payload, len - 48); - SetIsDataReceived (false); - return true; - } - - void SSU2PeerTestSession::HandlePeerTest (const uint8_t * buf, size_t len) - { - // msgs 5-7 - if (len < 8) return; - uint8_t msg = buf[0]; - if (msg <= m_MsgNumReceived) - { - LogPrint (eLogDebug, "SSU2: PeerTest msg num ", msg, " received after ", m_MsgNumReceived, ". Ignored"); - return; - } - size_t offset = 3; // points to signed data after msg + code + flag - uint32_t nonce = bufbe32toh (buf + offset + 1); // 1 - ver - switch (msg) // msg - { - case 5: // Alice from Charlie 1 - { - if (htobe64 (((uint64_t)nonce << 32) | nonce) == GetSourceConnID ()) - { - m_IsConnectedRecently = GetServer ().IsConnectedRecently (GetRemoteEndpoint ()); - if (GetAddress ()) - { - if (!m_IsConnectedRecently) - SetRouterStatus (eRouterStatusOK); - else if (m_IsStatusChanged && GetRouterStatus () == eRouterStatusFirewalled) - SetRouterStatus (eRouterStatusUnknown); - SendPeerTest (6, buf + offset, len - offset); - } - } - else - LogPrint (eLogWarning, "SSU2: Peer test 5 nonce mismatch ", nonce, " connID=", GetSourceConnID ()); - break; - } - case 6: // Charlie from Alice - { - m_PeerTestResendTimer.cancel (); // no more msg 5 resends - if (GetAddress ()) - SendPeerTest (7, buf + offset, len - offset); - else - LogPrint (eLogWarning, "SSU2: Unknown address for peer test 6"); - GetServer ().AddConnectedRecently (GetRemoteEndpoint (), i2p::util::GetSecondsSinceEpoch ()); - GetServer ().RequestRemoveSession (GetConnID ()); - break; - } - case 7: // Alice from Charlie 2 - { - m_PeerTestResendTimer.cancel (); // no more msg 6 resends - auto addr = GetAddress (); - if (addr && addr->IsV6 ()) - i2p::context.SetStatusV6 (eRouterStatusOK); // set status OK for ipv6 even if from SSU2 - GetServer ().AddConnectedRecently (GetRemoteEndpoint (), i2p::util::GetSecondsSinceEpoch ()); - GetServer ().RequestRemoveSession (GetConnID ()); - break; - } - default: - LogPrint (eLogWarning, "SSU2: PeerTest unexpected msg num ", msg); - return; - } - m_MsgNumReceived = msg; - } - - void SSU2PeerTestSession::SendPeerTest (uint8_t msg) - { - auto addr = GetAddress (); - if (!addr) return; - Header header; - uint8_t h[32], payload[SSU2_MAX_PACKET_SIZE]; - // fill packet - header.h.connID = GetDestConnID (); // dest id - RAND_bytes (header.buf + 8, 4); // random packet num - header.h.type = eSSU2PeerTest; - header.h.flags[0] = 2; // ver - header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID - header.h.flags[2] = 0; // flag - memcpy (h, header.buf, 16); - htobuf64 (h + 16, GetSourceConnID ()); // source id - // payload - payload[0] = eSSU2BlkDateTime; - htobe16buf (payload + 1, 4); - htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); - size_t payloadSize = 7; - if (msg == 6 || msg == 7) - payloadSize += CreateAddressBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize, GetRemoteEndpoint ()); - payloadSize += CreatePeerTestBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize, - msg, eSSU2PeerTestCodeAccept, nullptr, m_SignedData.data (), m_SignedData.size ()); - payloadSize += CreatePaddingBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize); - // encrypt - uint8_t n[12]; - CreateNonce (be32toh (header.h.packetNum), n); - i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, h, 32, addr->i, n, payload, payloadSize + 16, true); - payloadSize += 16; - header.ll[0] ^= CreateHeaderMask (addr->i, payload + (payloadSize - 24)); - header.ll[1] ^= CreateHeaderMask (addr->i, payload + (payloadSize - 12)); - memset (n, 0, 12); - i2p::crypto::ChaCha20 (h + 16, 16, addr->i, n, h + 16); - // send - GetServer ().Send (header.buf, 16, h + 16, 16, payload, payloadSize, GetRemoteEndpoint ()); - } - - void SSU2PeerTestSession::SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen) - { -#if __cplusplus >= 202002L // C++20 - m_SignedData.assign (signedData, signedData + signedDataLen); -#else - m_SignedData.resize (signedDataLen); - memcpy (m_SignedData.data (), signedData, signedDataLen); -#endif - SendPeerTest (msg); - // schedule resend for msgs 5 or 6 - if (msg == 5 || msg == 6) - ScheduleResend (); - } - - void SSU2PeerTestSession::SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, - std::shared_ptr addr) - { - if (!addr) return; - SetAddress (addr); - SendPeerTest (msg, signedData, signedDataLen); - } - - void SSU2PeerTestSession::Connect () - { - LogPrint (eLogError, "SSU2: Can't connect peer test session"); - } - - bool SSU2PeerTestSession::ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len) - { - LogPrint (eLogError, "SSU2: Can't handle incoming message in peer test session"); - return false; - } - - void SSU2PeerTestSession::ScheduleResend () - { - if (m_NumResends < SSU2_PEER_TEST_MAX_NUM_RESENDS) - { - m_PeerTestResendTimer.expires_from_now (boost::posix_time::milliseconds( - SSU2_PEER_TEST_RESEND_INTERVAL + GetServer ().GetRng ()() % SSU2_PEER_TEST_RESEND_INTERVAL_VARIANCE)); - std::weak_ptr s(std::static_pointer_cast(shared_from_this ())); - m_PeerTestResendTimer.async_wait ([s](const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - auto s1 = s.lock (); - if (s1) - { - int msg = 0; - if (s1->m_MsgNumReceived < 6) - msg = (s1->m_MsgNumReceived == 5) ? 6 : 5; - if (msg) // 5 or 6 - { - s1->SendPeerTest (msg); - s1->ScheduleResend (); - } - } - } - }); - m_NumResends++; - } - } } } diff --git a/libi2pd/SSU2Session.h b/libi2pd/SSU2Session.h index 49bd3be6..f0b6d7e6 100644 --- a/libi2pd/SSU2Session.h +++ b/libi2pd/SSU2Session.h @@ -390,43 +390,6 @@ namespace transport std::unordered_map m_ReceivedI2NPMsgIDs; // msgID -> timestamp in seconds uint64_t m_LastResendTime, m_LastResendAttemptTime; // in milliseconds }; - - - const int SSU2_PEER_TEST_RESEND_INTERVAL = 3000; // in milliseconds - const int SSU2_PEER_TEST_RESEND_INTERVAL_VARIANCE = 2000; // in milliseconds - const int SSU2_PEER_TEST_MAX_NUM_RESENDS = 3; - - class SSU2PeerTestSession: public SSU2Session // for PeerTest msgs 5,6,7 - { - public: - - SSU2PeerTestSession (SSU2Server& server, uint64_t sourceConnID, uint64_t destConnID); - - uint8_t GetMsgNumReceived () const { return m_MsgNumReceived; } - bool IsConnectedRecently () const { return m_IsConnectedRecently; } - void SetStatusChanged () { m_IsStatusChanged = true; } - - void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, - std::shared_ptr addr); - bool ProcessPeerTest (uint8_t * buf, size_t len) override; - void Connect () override; // outgoing - bool ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len) override; // incoming - - private: - - void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen); // PeerTest message - void SendPeerTest (uint8_t msg); // send or resend m_SignedData - void HandlePeerTest (const uint8_t * buf, size_t len) override; - - void ScheduleResend (); - - private: - - uint8_t m_MsgNumReceived, m_NumResends; - bool m_IsConnectedRecently, m_IsStatusChanged; - std::vector m_SignedData; // for resends - boost::asio::deadline_timer m_PeerTestResendTimer; - }; inline uint64_t CreateHeaderMask (const uint8_t * kh, const uint8_t * nonce) { @@ -434,6 +397,12 @@ namespace transport i2p::crypto::ChaCha20 ((uint8_t *)&data, 8, kh, nonce, (uint8_t *)&data); return data; } + + inline void CreateNonce (uint64_t seqn, uint8_t * nonce) + { + memset (nonce, 0, 4); + htole64buf (nonce + 4, seqn); + } } } From 48f7131a7dc6d2f6b0eac98fba4d4d45eba52bd0 Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 14 Oct 2024 18:55:41 -0400 Subject: [PATCH 11/50] received packets queue --- libi2pd/SSU2.cpp | 61 +++++++++++++++++++++++++++++++++++------------- libi2pd/SSU2.h | 9 ++++--- 2 files changed, 51 insertions(+), 19 deletions(-) diff --git a/libi2pd/SSU2.cpp b/libi2pd/SSU2.cpp index 83d23dd2..96658db5 100644 --- a/libi2pd/SSU2.cpp +++ b/libi2pd/SSU2.cpp @@ -157,6 +157,10 @@ namespace transport m_IntroducersV6.clear (); m_ConnectedRecently.clear (); m_RequestedPeerTests.clear (); + + for (auto it: m_ReceivedPacketsQueue) + m_PacketsArrayPool.ReleaseMt (it); + m_ReceivedPacketsQueue.clear (); } void SSU2Server::SetLocalAddress (const boost::asio::ip::address& localAddress) @@ -398,10 +402,25 @@ namespace transport break; } } - GetService ().post (std::bind (&SSU2Server::HandleReceivedPackets, this, packets)); + InsertToReceivedPacketsQueue (packets); } else - GetService ().post (std::bind (&SSU2Server::HandleReceivedPacket, this, packet)); + { + bool added = false; + { + // try to add single packet to existing packets array in queue + std::lock_guard l(m_ReceivedPacketsQueueMutex); + if (!m_ReceivedPacketsQueue.empty ()) + added = m_ReceivedPacketsQueue.back ()->AddPacket (packet); + } + if (!added) + { + // create new packets array for single packet + auto packets = m_PacketsArrayPool.AcquireMt (); + packets->AddPacket (packet); + InsertToReceivedPacketsQueue (packets); + } + } Receive (socket); } else @@ -428,20 +447,6 @@ namespace transport } } - void SSU2Server::HandleReceivedPacket (Packet * packet) - { - if (packet) - { - if (m_IsThroughProxy) - ProcessNextPacketFromProxy (packet->buf, packet->len); - else - ProcessNextPacket (packet->buf, packet->len, packet->from); - m_PacketsPool.ReleaseMt (packet); - if (m_LastSession && m_LastSession->GetState () != eSSU2SessionStateTerminated) - m_LastSession->FlushData (); - } - } - void SSU2Server::HandleReceivedPackets (Packets * packets) { if (!packets) return; @@ -463,6 +468,30 @@ namespace transport m_LastSession->FlushData (); } + void SSU2Server::InsertToReceivedPacketsQueue (Packets * packets) + { + if (!packets) return; + bool empty = false; + { + std::lock_guard l(m_ReceivedPacketsQueueMutex); + empty = m_ReceivedPacketsQueue.empty (); + m_ReceivedPacketsQueue.push_back (packets); + } + if (empty) + { + GetService ().post([this]() + { + std::list receivedPackets; + { + std::lock_guard l(m_ReceivedPacketsQueueMutex); + m_ReceivedPacketsQueue.swap (receivedPackets); + } + for (auto it: receivedPackets) + HandleReceivedPackets (it); + }); + } + } + void SSU2Server::AddSession (std::shared_ptr session) { if (session) diff --git a/libi2pd/SSU2.h b/libi2pd/SSU2.h index 09f764c6..494c11c6 100644 --- a/libi2pd/SSU2.h +++ b/libi2pd/SSU2.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -64,7 +65,7 @@ namespace transport return true; } return false; - } + } }; class ReceiveService: public i2p::util::RunnableService @@ -145,10 +146,10 @@ namespace transport void Receive (boost::asio::ip::udp::socket& socket); void HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred, Packet * packet, boost::asio::ip::udp::socket& socket); - void HandleReceivedPacket (Packet * packet); void HandleReceivedPackets (Packets * packets); void ProcessNextPacket (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); - + void InsertToReceivedPacketsQueue (Packets * packets); + void ScheduleTermination (); void HandleTerminationTimer (const boost::system::error_code& ecode); @@ -206,6 +207,8 @@ namespace transport std::mt19937 m_Rng; std::map m_ConnectedRecently; // endpoint -> last activity time in seconds std::unordered_map, uint64_t > > m_RequestedPeerTests; // nonce->(Alice, timestamp) + std::list m_ReceivedPacketsQueue; + mutable std::mutex m_ReceivedPacketsQueueMutex; // proxy bool m_IsThroughProxy; From 4e581af3ba7de4ee144c1e7f253eb23f20af092d Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 14 Oct 2024 21:21:07 -0400 Subject: [PATCH 12/50] plain list of received packets in queue --- libi2pd/SSU2.cpp | 72 ++++++++++++++---------------------------------- libi2pd/SSU2.h | 23 +++------------- 2 files changed, 24 insertions(+), 71 deletions(-) diff --git a/libi2pd/SSU2.cpp b/libi2pd/SSU2.cpp index 96658db5..f971e250 100644 --- a/libi2pd/SSU2.cpp +++ b/libi2pd/SSU2.cpp @@ -158,8 +158,7 @@ namespace transport m_ConnectedRecently.clear (); m_RequestedPeerTests.clear (); - for (auto it: m_ReceivedPacketsQueue) - m_PacketsArrayPool.ReleaseMt (it); + m_PacketsPool.ReleaseMt (m_ReceivedPacketsQueue); m_ReceivedPacketsQueue.clear (); } @@ -368,28 +367,23 @@ namespace transport return; } packet->len = bytes_transferred; + InsertToReceivedPacketsQueue (packet); + size_t numPackets = 1; boost::system::error_code ec; size_t moreBytes = socket.available (ec); if (!ec && moreBytes) { - auto packets = m_PacketsArrayPool.AcquireMt (); - packets->AddPacket (packet); - while (moreBytes && packets->numPackets < SSU2_MAX_NUM_PACKETS_PER_BATCH) - { + do + { packet = m_PacketsPool.AcquireMt (); packet->len = socket.receive_from (boost::asio::buffer (packet->buf, SSU2_MAX_PACKET_SIZE), packet->from, 0, ec); if (!ec) { i2p::transport::transports.UpdateReceivedBytes (packet->len); + numPackets++; if (packet->len >= SSU2_MIN_RECEIVED_PACKET_SIZE) - { - if (!packets->AddPacket (packet)) - { - LogPrint (eLogError, "SSU2: Received packets array is full"); - m_PacketsPool.ReleaseMt (packet); - } - } + InsertToReceivedPacketsQueue (packet); else // drop too short packets m_PacketsPool.ReleaseMt (packet); moreBytes = socket.available(ec); @@ -402,25 +396,8 @@ namespace transport break; } } - InsertToReceivedPacketsQueue (packets); + while (moreBytes && numPackets < SSU2_MAX_NUM_PACKETS_PER_BATCH); } - else - { - bool added = false; - { - // try to add single packet to existing packets array in queue - std::lock_guard l(m_ReceivedPacketsQueueMutex); - if (!m_ReceivedPacketsQueue.empty ()) - added = m_ReceivedPacketsQueue.back ()->AddPacket (packet); - } - if (!added) - { - // create new packets array for single packet - auto packets = m_PacketsArrayPool.AcquireMt (); - packets->AddPacket (packet); - InsertToReceivedPacketsQueue (packets); - } - } Receive (socket); } else @@ -447,47 +424,39 @@ namespace transport } } - void SSU2Server::HandleReceivedPackets (Packets * packets) + void SSU2Server::HandleReceivedPackets (std::list&& packets) { - if (!packets) return; + if (packets.empty ()) return; if (m_IsThroughProxy) - for (size_t i = 0; i < packets->numPackets; i++) - { - auto& packet = (*packets)[i]; - ProcessNextPacketFromProxy (packet->buf, packet->len); - } + for (auto it: packets) + ProcessNextPacketFromProxy (it->buf, it->len); else - for (size_t i = 0; i < packets->numPackets; i++) - { - auto& packet = (*packets)[i]; - ProcessNextPacket (packet->buf, packet->len, packet->from); - } - m_PacketsPool.ReleaseMt (packets->data (), packets->numPackets); - m_PacketsArrayPool.ReleaseMt (packets); + for (auto it: packets) + ProcessNextPacket (it->buf, it->len, it->from); + m_PacketsPool.ReleaseMt (packets); if (m_LastSession && m_LastSession->GetState () != eSSU2SessionStateTerminated) m_LastSession->FlushData (); } - void SSU2Server::InsertToReceivedPacketsQueue (Packets * packets) + void SSU2Server::InsertToReceivedPacketsQueue (Packet * packet) { - if (!packets) return; + if (!packet) return; bool empty = false; { std::lock_guard l(m_ReceivedPacketsQueueMutex); empty = m_ReceivedPacketsQueue.empty (); - m_ReceivedPacketsQueue.push_back (packets); + m_ReceivedPacketsQueue.push_back (packet); } if (empty) { GetService ().post([this]() { - std::list receivedPackets; + std::list receivedPackets; { std::lock_guard l(m_ReceivedPacketsQueueMutex); m_ReceivedPacketsQueue.swap (receivedPackets); } - for (auto it: receivedPackets) - HandleReceivedPackets (it); + HandleReceivedPackets (std::move (receivedPackets)); }); } } @@ -1167,7 +1136,6 @@ namespace transport } m_PacketsPool.CleanUpMt (); - m_PacketsArrayPool.CleanUpMt (); m_SentPacketsPool.CleanUp (); m_IncompleteMessagesPool.CleanUp (); m_FragmentsPool.CleanUp (); diff --git a/libi2pd/SSU2.h b/libi2pd/SSU2.h index 494c11c6..7a02730b 100644 --- a/libi2pd/SSU2.h +++ b/libi2pd/SSU2.h @@ -43,7 +43,7 @@ namespace transport 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 size_t SSU2_MAX_NUM_PACKETS_PER_BATCH = 32; + const size_t SSU2_MAX_NUM_PACKETS_PER_BATCH = 64; class SSU2Server: private i2p::util::RunnableServiceWithWork { @@ -53,20 +53,6 @@ namespace transport size_t len; boost::asio::ip::udp::endpoint from; }; - - struct Packets: public std::array - { - size_t numPackets = 0; - bool AddPacket (Packet *p) - { - if (p && numPackets < size ()) - { - data()[numPackets] = p; numPackets++; - return true; - } - return false; - } - }; class ReceiveService: public i2p::util::RunnableService { @@ -146,9 +132,9 @@ namespace transport void Receive (boost::asio::ip::udp::socket& socket); void HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred, Packet * packet, boost::asio::ip::udp::socket& socket); - void HandleReceivedPackets (Packets * packets); + void HandleReceivedPackets (std::list&& packets); void ProcessNextPacket (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); - void InsertToReceivedPacketsQueue (Packets * packets); + void InsertToReceivedPacketsQueue (Packet * packet); void ScheduleTermination (); void HandleTerminationTimer (const boost::system::error_code& ecode); @@ -193,7 +179,6 @@ namespace transport std::unordered_map, uint64_t > > m_PeerTests; // nonce->(Alice, timestamp). We are Bob std::list > m_Introducers, m_IntroducersV6; // introducers we are connected to i2p::util::MemoryPoolMt m_PacketsPool; - i2p::util::MemoryPoolMt m_PacketsArrayPool; i2p::util::MemoryPool m_SentPacketsPool; i2p::util::MemoryPool m_IncompleteMessagesPool; i2p::util::MemoryPool m_FragmentsPool; @@ -207,7 +192,7 @@ namespace transport std::mt19937 m_Rng; std::map m_ConnectedRecently; // endpoint -> last activity time in seconds std::unordered_map, uint64_t > > m_RequestedPeerTests; // nonce->(Alice, timestamp) - std::list m_ReceivedPacketsQueue; + std::list m_ReceivedPacketsQueue; mutable std::mutex m_ReceivedPacketsQueueMutex; // proxy From 7104d334fddbebc7d85150553c207ef0269809fa Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 14 Oct 2024 22:29:55 -0400 Subject: [PATCH 13/50] Do not increase the window size if the speed limit is reached when it is limited --- libi2pd/Streaming.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libi2pd/Streaming.cpp b/libi2pd/Streaming.cpp index c30c5d39..6a91e33f 100644 --- a/libi2pd/Streaming.cpp +++ b/libi2pd/Streaming.cpp @@ -1225,7 +1225,7 @@ namespace stream m_NumPacketsToSend = ((ts*1000 - m_LastSendTime*1000) + m_PacingTimeRem) / m_PacingTime; m_PacingTimeRem = ((ts*1000 - m_LastSendTime*1000) + m_PacingTimeRem) - (m_NumPacketsToSend * m_PacingTime); m_IsSendTime = true; - if (m_WindowIncCounter && m_WindowSize < MAX_WINDOW_SIZE && !m_SendBuffer.IsEmpty ()) + if (m_WindowIncCounter && m_WindowSize < MAX_WINDOW_SIZE && !m_SendBuffer.IsEmpty () && m_PacingTime > m_MinPacingTime) { for (int i = 0; i < m_NumPacketsToSend; i++) { From ec1f41b13cef0cef17936bbd6210b9c885d88b91 Mon Sep 17 00:00:00 2001 From: orignal Date: Tue, 15 Oct 2024 15:05:18 -0400 Subject: [PATCH 14/50] insert multiple packets to the queue using splice --- libi2pd/SSU2.cpp | 48 +++++++++++++++++++++++++++++++----------------- libi2pd/SSU2.h | 2 ++ 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/libi2pd/SSU2.cpp b/libi2pd/SSU2.cpp index f971e250..574aea55 100644 --- a/libi2pd/SSU2.cpp +++ b/libi2pd/SSU2.cpp @@ -367,23 +367,22 @@ namespace transport return; } packet->len = bytes_transferred; - InsertToReceivedPacketsQueue (packet); - - size_t numPackets = 1; + boost::system::error_code ec; size_t moreBytes = socket.available (ec); if (!ec && moreBytes) { - do + std::list packets; + packets.push_back (packet); + while (moreBytes && packets.size () < SSU2_MAX_NUM_PACKETS_PER_BATCH) { packet = m_PacketsPool.AcquireMt (); packet->len = socket.receive_from (boost::asio::buffer (packet->buf, SSU2_MAX_PACKET_SIZE), packet->from, 0, ec); if (!ec) { i2p::transport::transports.UpdateReceivedBytes (packet->len); - numPackets++; if (packet->len >= SSU2_MIN_RECEIVED_PACKET_SIZE) - InsertToReceivedPacketsQueue (packet); + packets.push_back (packet); else // drop too short packets m_PacketsPool.ReleaseMt (packet); moreBytes = socket.available(ec); @@ -396,8 +395,10 @@ namespace transport break; } } - while (moreBytes && numPackets < SSU2_MAX_NUM_PACKETS_PER_BATCH); + InsertToReceivedPacketsQueue (packets); } + else + InsertToReceivedPacketsQueue (packet); Receive (socket); } else @@ -448,17 +449,30 @@ namespace transport m_ReceivedPacketsQueue.push_back (packet); } if (empty) + GetService ().post([this]() { HandleReceivedPacketsQueue (); }); + } + + void SSU2Server::InsertToReceivedPacketsQueue (std::list& packets) + { + if (packets.empty ()) return; + bool empty = false; { - GetService ().post([this]() - { - std::list receivedPackets; - { - std::lock_guard l(m_ReceivedPacketsQueueMutex); - m_ReceivedPacketsQueue.swap (receivedPackets); - } - HandleReceivedPackets (std::move (receivedPackets)); - }); - } + std::lock_guard l(m_ReceivedPacketsQueueMutex); + empty = m_ReceivedPacketsQueue.empty (); + m_ReceivedPacketsQueue.splice (m_ReceivedPacketsQueue.end (), packets); + } + if (empty) + GetService ().post([this]() { HandleReceivedPacketsQueue (); }); + } + + void SSU2Server::HandleReceivedPacketsQueue () + { + std::list receivedPackets; + { + std::lock_guard l(m_ReceivedPacketsQueueMutex); + m_ReceivedPacketsQueue.swap (receivedPackets); + } + HandleReceivedPackets (std::move (receivedPackets)); } void SSU2Server::AddSession (std::shared_ptr session) diff --git a/libi2pd/SSU2.h b/libi2pd/SSU2.h index 7a02730b..426c6a10 100644 --- a/libi2pd/SSU2.h +++ b/libi2pd/SSU2.h @@ -135,6 +135,8 @@ namespace transport void HandleReceivedPackets (std::list&& packets); void ProcessNextPacket (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); void InsertToReceivedPacketsQueue (Packet * packet); + void InsertToReceivedPacketsQueue (std::list& packets); + void HandleReceivedPacketsQueue (); void ScheduleTermination (); void HandleTerminationTimer (const boost::system::error_code& ecode); From 97fdedfbe393ef2ce2195960d72bd0b5fe339413 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 16 Oct 2024 08:28:25 -0400 Subject: [PATCH 15/50] implement SSU2HolPunchSession --- libi2pd/SSU2.cpp | 11 +++++-- libi2pd/SSU2OutOfSession.cpp | 58 ++++++++++++++++++++++++++++++++++++ libi2pd/SSU2OutOfSession.h | 15 ++++++++++ libi2pd/SSU2Session.cpp | 48 +++-------------------------- libi2pd/SSU2Session.h | 5 ++-- 5 files changed, 88 insertions(+), 49 deletions(-) diff --git a/libi2pd/SSU2.cpp b/libi2pd/SSU2.cpp index 574aea55..e96a7233 100644 --- a/libi2pd/SSU2.cpp +++ b/libi2pd/SSU2.cpp @@ -479,9 +479,11 @@ namespace transport { if (session) { - m_Sessions.emplace (session->GetConnID (), session); - if (session->GetState () != eSSU2SessionStatePeerTest) - AddSessionByRouterHash (session); + if (m_Sessions.emplace (session->GetConnID (), session).second) + { + if (session->GetState () != eSSU2SessionStatePeerTest) + AddSessionByRouterHash (session); + } } } @@ -715,6 +717,9 @@ namespace transport m_LastSession->SetRemoteEndpoint (senderEndpoint); m_LastSession->ProcessPeerTest (buf, len); break; + case eSSU2SessionStateHolePunch: + m_LastSession->ProcessFirstIncomingMessage (connID, buf, len); // SessionRequest + break; case eSSU2SessionStateClosing: m_LastSession->ProcessData (buf, len, senderEndpoint); // we might receive termintaion block if (m_LastSession && m_LastSession->GetState () == eSSU2SessionStateClosing) diff --git a/libi2pd/SSU2OutOfSession.cpp b/libi2pd/SSU2OutOfSession.cpp index cf9f3f36..574ad75b 100644 --- a/libi2pd/SSU2OutOfSession.cpp +++ b/libi2pd/SSU2OutOfSession.cpp @@ -224,5 +224,63 @@ namespace transport m_NumResends++; } } + + SSU2HolePunchSession::SSU2HolePunchSession (SSU2Server& server, uint32_t nonce, + const boost::asio::ip::udp::endpoint& remoteEndpoint, + std::shared_ptr localAddr): + SSU2Session (server), // we create full incoming session + m_Nonce (nonce) + { + // we are Charlie + m_Token = GetServer ().GetIncomingToken (remoteEndpoint); + uint64_t destConnID = htobe64 (((uint64_t)nonce << 32) | nonce); // dest id + uint32_t sourceConnID = ~destConnID; + SetSourceConnID (sourceConnID); + SetDestConnID (destConnID); + SetState (eSSU2SessionStateHolePunch); + SetRemoteEndpoint (remoteEndpoint); + SetAddress (localAddr); + SetTerminationTimeout (SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT); + } + + void SSU2HolePunchSession::SendHolePunch () + { + auto addr = GetAddress (); + if (!addr) return; + auto& ep = GetRemoteEndpoint (); + LogPrint (eLogDebug, "SSU2: Sending HolePunch to ", ep); + Header header; + uint8_t h[32], payload[SSU2_MAX_PACKET_SIZE]; + // fill packet + header.h.connID = GetDestConnID (); // dest id + RAND_bytes (header.buf + 8, 4); // random packet num + header.h.type = eSSU2HolePunch; + header.h.flags[0] = 2; // ver + header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID + header.h.flags[2] = 0; // flag + memcpy (h, header.buf, 16); + htobuf64 (h + 16, GetSourceConnID ()); // source id + RAND_bytes (h + 24, 8); // header token, to be ignored by Alice + // payload + payload[0] = eSSU2BlkDateTime; + htobe16buf (payload + 1, 4); + htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); + size_t payloadSize = 7; + payloadSize += CreateAddressBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize, ep); + payloadSize += CreateRelayResponseBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize, + eSSU2RelayResponseCodeAccept, m_Nonce, m_Token, ep.address ().is_v4 ()); + payloadSize += CreatePaddingBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize); + // encrypt + uint8_t n[12]; + CreateNonce (be32toh (header.h.packetNum), n); + i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, h, 32, addr->i, n, payload, payloadSize + 16, true); + payloadSize += 16; + header.ll[0] ^= CreateHeaderMask (addr->i, payload + (payloadSize - 24)); + header.ll[1] ^= CreateHeaderMask (addr->i, payload + (payloadSize - 12)); + memset (n, 0, 12); + i2p::crypto::ChaCha20 (h + 16, 16, addr->i, n, h + 16); + // send + GetServer ().Send (header.buf, 16, h + 16, 16, payload, payloadSize, ep); + } } } diff --git a/libi2pd/SSU2OutOfSession.h b/libi2pd/SSU2OutOfSession.h index 29a35d52..d0d944d1 100644 --- a/libi2pd/SSU2OutOfSession.h +++ b/libi2pd/SSU2OutOfSession.h @@ -51,6 +51,21 @@ namespace transport std::vector m_SignedData; // for resends boost::asio::deadline_timer m_PeerTestResendTimer; }; + + class SSU2HolePunchSession: public SSU2Session // Charlie + { + public: + + SSU2HolePunchSession (SSU2Server& server, uint32_t nonce, const boost::asio::ip::udp::endpoint& remoteEndpoint, + std::shared_ptr localAddr); + + void SendHolePunch (); + + private: + + uint32_t m_Nonce; + uint64_t m_Token; // for RelayResponse block + }; } } diff --git a/libi2pd/SSU2Session.cpp b/libi2pd/SSU2Session.cpp index 37d078ac..958f7536 100644 --- a/libi2pd/SSU2Session.cpp +++ b/libi2pd/SSU2Session.cpp @@ -1352,46 +1352,6 @@ namespace transport return true; } - void SSU2Session::SendHolePunch (uint32_t nonce, const boost::asio::ip::udp::endpoint& ep, - const uint8_t * introKey, uint64_t token) - { - // we are Charlie - LogPrint (eLogDebug, "SSU2: Sending HolePunch to ", ep); - Header header; - uint8_t h[32], payload[SSU2_MAX_PACKET_SIZE]; - // fill packet - header.h.connID = htobe64 (((uint64_t)nonce << 32) | nonce); // dest id - RAND_bytes (header.buf + 8, 4); // random packet num - header.h.type = eSSU2HolePunch; - header.h.flags[0] = 2; // ver - header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID - header.h.flags[2] = 0; // flag - memcpy (h, header.buf, 16); - uint64_t c = ~header.h.connID; - memcpy (h + 16, &c, 8); // source id - RAND_bytes (h + 24, 8); // token - // payload - payload[0] = eSSU2BlkDateTime; - htobe16buf (payload + 1, 4); - htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); - size_t payloadSize = 7; - payloadSize += CreateAddressBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, ep); - payloadSize += CreateRelayResponseBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, - eSSU2RelayResponseCodeAccept, nonce, token, ep.address ().is_v4 ()); - payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); - // encrypt - uint8_t n[12]; - CreateNonce (be32toh (header.h.packetNum), n); - i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, h, 32, introKey, n, payload, payloadSize + 16, true); - payloadSize += 16; - header.ll[0] ^= CreateHeaderMask (introKey, payload + (payloadSize - 24)); - header.ll[1] ^= CreateHeaderMask (introKey, payload + (payloadSize - 12)); - memset (n, 0, 12); - i2p::crypto::ChaCha20 (h + 16, 16, introKey, n, h + 16); - // send - m_Server.Send (header.buf, 16, h + 16, 16, payload, payloadSize, ep); - } - bool SSU2Session::ProcessHolePunch (uint8_t * buf, size_t len) { // we are Alice @@ -1984,7 +1944,6 @@ namespace transport void SSU2Session::HandleRelayIntro (const uint8_t * buf, size_t len, int attempts) { // we are Charlie - auto mts = i2p::util::GetMillisecondsSinceEpoch (); SSU2RelayResponseCode code = eSSU2RelayResponseCodeAccept; uint64_t token = 0; bool isV4 = false; @@ -2011,10 +1970,11 @@ namespace transport { if (m_Server.IsSupported (ep.address ())) { - token = m_Server.GetIncomingToken (ep); isV4 = ep.address ().is_v4 (); - SendHolePunch (bufbe32toh (buf + 33), ep, addr->i, token); - m_Server.AddConnectedRecently (ep, mts/1000); + auto holePunchSession = std::make_shared( + m_Server, bufbe32toh (buf + 33), ep, addr); + m_Server.AddSession (holePunchSession); + holePunchSession->SendHolePunch (); } else { diff --git a/libi2pd/SSU2Session.h b/libi2pd/SSU2Session.h index f0b6d7e6..b3b81b4d 100644 --- a/libi2pd/SSU2Session.h +++ b/libi2pd/SSU2Session.h @@ -112,6 +112,7 @@ namespace transport eSSU2SessionStateTerminated, eSSU2SessionStateFailed, eSSU2SessionStateIntroduced, + eSSU2SessionStateHolePunch, eSSU2SessionStatePeerTest, eSSU2SessionStateTokenRequestReceived }; @@ -295,6 +296,8 @@ namespace transport size_t CreateAddressBlock (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep); size_t CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize = 0); size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint8_t msg, SSU2PeerTestCode code, const uint8_t * routerHash, const uint8_t * signedData, size_t signedDataLen); + + size_t CreateRelayResponseBlock (uint8_t * buf, size_t len, SSU2RelayResponseCode code, uint32_t nonce, uint64_t token, bool v4); private: @@ -320,7 +323,6 @@ namespace transport uint32_t SendData (const uint8_t * buf, size_t len, uint8_t flags = 0); // returns packet num void SendQuickAck (); void SendTermination (); - void SendHolePunch (uint32_t nonce, const boost::asio::ip::udp::endpoint& ep, const uint8_t * introKey, uint64_t token); void SendPathResponse (const uint8_t * data, size_t len); void SendPathChallenge (); @@ -352,7 +354,6 @@ namespace transport size_t CreateFirstFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr msg); size_t CreateFollowOnFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr msg, uint8_t& fragmentNum, uint32_t msgID); size_t CreateRelayIntroBlock (uint8_t * buf, size_t len, const uint8_t * introData, size_t introDataLen); - size_t CreateRelayResponseBlock (uint8_t * buf, size_t len, SSU2RelayResponseCode code, uint32_t nonce, uint64_t token, bool v4); size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint32_t nonce); // Alice size_t CreateTerminationBlock (uint8_t * buf, size_t len); From d69e957213f4ce3d7fd977fba42f73f6a60403cc Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 16 Oct 2024 09:04:35 -0400 Subject: [PATCH 16/50] rollback --- libi2pd/SSU2Session.cpp | 47 +++++++++++++++++++++++++++++++++++++---- libi2pd/SSU2Session.h | 2 ++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/libi2pd/SSU2Session.cpp b/libi2pd/SSU2Session.cpp index 958f7536..f68231db 100644 --- a/libi2pd/SSU2Session.cpp +++ b/libi2pd/SSU2Session.cpp @@ -1352,6 +1352,46 @@ namespace transport return true; } + void SSU2Session::SendHolePunch (uint32_t nonce, const boost::asio::ip::udp::endpoint& ep, + const uint8_t * introKey, uint64_t token) + { + // we are Charlie + LogPrint (eLogDebug, "SSU2: Sending HolePunch to ", ep); + Header header; + uint8_t h[32], payload[SSU2_MAX_PACKET_SIZE]; + // fill packet + header.h.connID = htobe64 (((uint64_t)nonce << 32) | nonce); // dest id + RAND_bytes (header.buf + 8, 4); // random packet num + header.h.type = eSSU2HolePunch; + header.h.flags[0] = 2; // ver + header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID + header.h.flags[2] = 0; // flag + memcpy (h, header.buf, 16); + uint64_t c = ~header.h.connID; + memcpy (h + 16, &c, 8); // source id + RAND_bytes (h + 24, 8); // token + // payload + payload[0] = eSSU2BlkDateTime; + htobe16buf (payload + 1, 4); + htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); + size_t payloadSize = 7; + payloadSize += CreateAddressBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, ep); + payloadSize += CreateRelayResponseBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, + eSSU2RelayResponseCodeAccept, nonce, token, ep.address ().is_v4 ()); + payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); + // encrypt + uint8_t n[12]; + CreateNonce (be32toh (header.h.packetNum), n); + i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, h, 32, introKey, n, payload, payloadSize + 16, true); + payloadSize += 16; + header.ll[0] ^= CreateHeaderMask (introKey, payload + (payloadSize - 24)); + header.ll[1] ^= CreateHeaderMask (introKey, payload + (payloadSize - 12)); + memset (n, 0, 12); + i2p::crypto::ChaCha20 (h + 16, 16, introKey, n, h + 16); + // send + m_Server.Send (header.buf, 16, h + 16, 16, payload, payloadSize, ep); + } + bool SSU2Session::ProcessHolePunch (uint8_t * buf, size_t len) { // we are Alice @@ -1970,11 +2010,10 @@ namespace transport { if (m_Server.IsSupported (ep.address ())) { + token = m_Server.GetIncomingToken (ep); isV4 = ep.address ().is_v4 (); - auto holePunchSession = std::make_shared( - m_Server, bufbe32toh (buf + 33), ep, addr); - m_Server.AddSession (holePunchSession); - holePunchSession->SendHolePunch (); + SendHolePunch (bufbe32toh (buf + 33), ep, addr->i, token); + m_Server.AddConnectedRecently (ep, i2p::util::GetSecondsSinceEpoch ()); } else { diff --git a/libi2pd/SSU2Session.h b/libi2pd/SSU2Session.h index b3b81b4d..6a5ae5e2 100644 --- a/libi2pd/SSU2Session.h +++ b/libi2pd/SSU2Session.h @@ -274,6 +274,8 @@ namespace transport bool ProcessSessionConfirmed (uint8_t * buf, size_t len); bool ProcessRetry (uint8_t * buf, size_t len); bool ProcessHolePunch (uint8_t * buf, size_t len); + void SendHolePunch (uint32_t nonce, const boost::asio::ip::udp::endpoint& ep, + const uint8_t * introKey, uint64_t token); virtual bool ProcessPeerTest (uint8_t * buf, size_t len); void ProcessData (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& from); From 4f73f60e5177a3b6add0a06934227bea47556bb0 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 16 Oct 2024 12:07:13 -0400 Subject: [PATCH 17/50] don't create relay response block twice --- libi2pd/SSU2OutOfSession.cpp | 24 +++++++++--- libi2pd/SSU2OutOfSession.h | 8 +++- libi2pd/SSU2Session.cpp | 74 ++++++++++-------------------------- libi2pd/SSU2Session.h | 6 +-- 4 files changed, 47 insertions(+), 65 deletions(-) diff --git a/libi2pd/SSU2OutOfSession.cpp b/libi2pd/SSU2OutOfSession.cpp index 574ad75b..46809217 100644 --- a/libi2pd/SSU2OutOfSession.cpp +++ b/libi2pd/SSU2OutOfSession.cpp @@ -227,19 +227,18 @@ namespace transport SSU2HolePunchSession::SSU2HolePunchSession (SSU2Server& server, uint32_t nonce, const boost::asio::ip::udp::endpoint& remoteEndpoint, - std::shared_ptr localAddr): + std::shared_ptr addr): SSU2Session (server), // we create full incoming session m_Nonce (nonce) { // we are Charlie - m_Token = GetServer ().GetIncomingToken (remoteEndpoint); uint64_t destConnID = htobe64 (((uint64_t)nonce << 32) | nonce); // dest id uint32_t sourceConnID = ~destConnID; SetSourceConnID (sourceConnID); SetDestConnID (destConnID); SetState (eSSU2SessionStateHolePunch); SetRemoteEndpoint (remoteEndpoint); - SetAddress (localAddr); + SetAddress (addr); SetTerminationTimeout (SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT); } @@ -267,8 +266,12 @@ namespace transport htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); size_t payloadSize = 7; payloadSize += CreateAddressBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize, ep); - payloadSize += CreateRelayResponseBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize, - eSSU2RelayResponseCodeAccept, m_Nonce, m_Token, ep.address ().is_v4 ()); + // relay response block + if (payloadSize + m_RelayResponseBlock.size () < GetMaxPayloadSize ()) + { + memcpy (payload + payloadSize, m_RelayResponseBlock.data (), m_RelayResponseBlock.size ()); + payloadSize += m_RelayResponseBlock.size (); + } payloadSize += CreatePaddingBlock (payload + payloadSize, GetMaxPayloadSize () - payloadSize); // encrypt uint8_t n[12]; @@ -282,5 +285,16 @@ namespace transport // send GetServer ().Send (header.buf, 16, h + 16, 16, payload, payloadSize, ep); } + + void SSU2HolePunchSession::SendHolePunch (const uint8_t * relayResponseBlock, size_t relayResponseBlockLen) + { +#if __cplusplus >= 202002L // C++20 + m_RelayResponseBlock.assign (relayResponseBlock, relayResponseBlock + relayResponseBlockLen); +#else + m_RelayResponseBlock.resize (relayResponseBlockLen); + memcpy (m_RelayResponseBlock.data (), relayResponseBlock, relayResponseBlockLen); +#endif + SendHolePunch (); + } } } diff --git a/libi2pd/SSU2OutOfSession.h b/libi2pd/SSU2OutOfSession.h index d0d944d1..1069ab40 100644 --- a/libi2pd/SSU2OutOfSession.h +++ b/libi2pd/SSU2OutOfSession.h @@ -57,14 +57,18 @@ namespace transport public: SSU2HolePunchSession (SSU2Server& server, uint32_t nonce, const boost::asio::ip::udp::endpoint& remoteEndpoint, - std::shared_ptr localAddr); + std::shared_ptr addr); + void SendHolePunch (const uint8_t * relayResponseBlock, size_t relayResponseBlockLen); + + private: + void SendHolePunch (); private: uint32_t m_Nonce; - uint64_t m_Token; // for RelayResponse block + std::vector m_RelayResponseBlock; }; } } diff --git a/libi2pd/SSU2Session.cpp b/libi2pd/SSU2Session.cpp index f68231db..47f6a9b2 100644 --- a/libi2pd/SSU2Session.cpp +++ b/libi2pd/SSU2Session.cpp @@ -1351,46 +1351,6 @@ namespace transport SendSessionRequest (token); return true; } - - void SSU2Session::SendHolePunch (uint32_t nonce, const boost::asio::ip::udp::endpoint& ep, - const uint8_t * introKey, uint64_t token) - { - // we are Charlie - LogPrint (eLogDebug, "SSU2: Sending HolePunch to ", ep); - Header header; - uint8_t h[32], payload[SSU2_MAX_PACKET_SIZE]; - // fill packet - header.h.connID = htobe64 (((uint64_t)nonce << 32) | nonce); // dest id - RAND_bytes (header.buf + 8, 4); // random packet num - header.h.type = eSSU2HolePunch; - header.h.flags[0] = 2; // ver - header.h.flags[1] = (uint8_t)i2p::context.GetNetID (); // netID - header.h.flags[2] = 0; // flag - memcpy (h, header.buf, 16); - uint64_t c = ~header.h.connID; - memcpy (h + 16, &c, 8); // source id - RAND_bytes (h + 24, 8); // token - // payload - payload[0] = eSSU2BlkDateTime; - htobe16buf (payload + 1, 4); - htobe32buf (payload + 3, (i2p::util::GetMillisecondsSinceEpoch () + 500)/1000); - size_t payloadSize = 7; - payloadSize += CreateAddressBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, ep); - payloadSize += CreateRelayResponseBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize, - eSSU2RelayResponseCodeAccept, nonce, token, ep.address ().is_v4 ()); - payloadSize += CreatePaddingBlock (payload + payloadSize, m_MaxPayloadSize - payloadSize); - // encrypt - uint8_t n[12]; - CreateNonce (be32toh (header.h.packetNum), n); - i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, h, 32, introKey, n, payload, payloadSize + 16, true); - payloadSize += 16; - header.ll[0] ^= CreateHeaderMask (introKey, payload + (payloadSize - 24)); - header.ll[1] ^= CreateHeaderMask (introKey, payload + (payloadSize - 12)); - memset (n, 0, 12); - i2p::crypto::ChaCha20 (h + 16, 16, introKey, n, h + 16); - // send - m_Server.Send (header.buf, 16, h + 16, 16, payload, payloadSize, ep); - } bool SSU2Session::ProcessHolePunch (uint8_t * buf, size_t len) { @@ -1985,8 +1945,8 @@ namespace transport { // we are Charlie SSU2RelayResponseCode code = eSSU2RelayResponseCodeAccept; - uint64_t token = 0; - bool isV4 = false; + boost::asio::ip::udp::endpoint ep; + std::shared_ptr addr; auto r = i2p::data::netdb.FindRouter (buf + 1); // Alice if (r) { @@ -1999,31 +1959,29 @@ namespace transport s.Insert (buf + 47, asz); // Alice Port, Alice IP if (s.Verify (r->GetIdentity (), buf + 47 + asz)) { - // send HolePunch - boost::asio::ip::udp::endpoint ep; + // obtain and check endpoint and address for HolePunch if (ExtractEndpoint (buf + 47, asz, ep)) { - std::shared_ptr addr; if (!ep.address ().is_unspecified () && ep.port ()) - addr = ep.address ().is_v6 () ? r->GetSSU2V6Address () : r->GetSSU2V4Address (); - if (addr) { if (m_Server.IsSupported (ep.address ())) { - token = m_Server.GetIncomingToken (ep); - isV4 = ep.address ().is_v4 (); - SendHolePunch (bufbe32toh (buf + 33), ep, addr->i, token); - m_Server.AddConnectedRecently (ep, i2p::util::GetSecondsSinceEpoch ()); + addr = ep.address ().is_v6 () ? r->GetSSU2V6Address () : r->GetSSU2V4Address (); + if (!addr) + { + LogPrint (eLogWarning, "SSU2: RelayIntro address for endpoint not found"); + code = eSSU2RelayResponseCodeCharlieAliceIsUnknown; + } } else { LogPrint (eLogWarning, "SSU2: RelayIntro unsupported address"); code = eSSU2RelayResponseCodeCharlieUnsupportedAddress; - } + } } else { - LogPrint (eLogWarning, "SSU2: RelayIntro unknown address"); + LogPrint (eLogWarning, "SSU2: RelayIntro invalid endpoint"); code = eSSU2RelayResponseCodeCharlieAliceIsUnknown; } } @@ -2059,8 +2017,16 @@ namespace transport } // send relay response to Bob auto packet = m_Server.GetSentPacketsPool ().AcquireShared (); + uint32_t nonce = bufbe32toh (buf + 33); packet->payloadSize = CreateRelayResponseBlock (packet->payload, m_MaxPayloadSize, - code, bufbe32toh (buf + 33), token, isV4); + code, nonce, m_Server.GetIncomingToken (ep), ep.address ().is_v4 ()); + if (code == eSSU2RelayResponseCodeAccept && addr) + { + // 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 + } 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 diff --git a/libi2pd/SSU2Session.h b/libi2pd/SSU2Session.h index 6a5ae5e2..ae0f18f4 100644 --- a/libi2pd/SSU2Session.h +++ b/libi2pd/SSU2Session.h @@ -274,8 +274,6 @@ namespace transport bool ProcessSessionConfirmed (uint8_t * buf, size_t len); bool ProcessRetry (uint8_t * buf, size_t len); bool ProcessHolePunch (uint8_t * buf, size_t len); - void SendHolePunch (uint32_t nonce, const boost::asio::ip::udp::endpoint& ep, - const uint8_t * introKey, uint64_t token); virtual bool ProcessPeerTest (uint8_t * buf, size_t len); void ProcessData (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& from); @@ -298,8 +296,6 @@ namespace transport size_t CreateAddressBlock (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep); size_t CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize = 0); size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint8_t msg, SSU2PeerTestCode code, const uint8_t * routerHash, const uint8_t * signedData, size_t signedDataLen); - - size_t CreateRelayResponseBlock (uint8_t * buf, size_t len, SSU2RelayResponseCode code, uint32_t nonce, uint64_t token, bool v4); private: @@ -356,6 +352,8 @@ namespace transport size_t CreateFirstFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr msg); size_t CreateFollowOnFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr msg, uint8_t& fragmentNum, uint32_t msgID); size_t CreateRelayIntroBlock (uint8_t * buf, size_t len, const uint8_t * introData, size_t introDataLen); + size_t CreateRelayResponseBlock (uint8_t * buf, size_t len, SSU2RelayResponseCode code, uint32_t nonce, uint64_t token, bool v4); + size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint32_t nonce); // Alice size_t CreateTerminationBlock (uint8_t * buf, size_t len); From 50d9252ba96c5456026036ec2d2f9c3f111ec9a7 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 16 Oct 2024 13:10:21 -0400 Subject: [PATCH 18/50] resend HolePunch 3 times or until SessionRequest received --- libi2pd/SSU2OutOfSession.cpp | 32 +++++++++++++++++++++++++++++++- libi2pd/SSU2OutOfSession.h | 9 +++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/libi2pd/SSU2OutOfSession.cpp b/libi2pd/SSU2OutOfSession.cpp index 46809217..a2e0941e 100644 --- a/libi2pd/SSU2OutOfSession.cpp +++ b/libi2pd/SSU2OutOfSession.cpp @@ -229,7 +229,7 @@ namespace transport const boost::asio::ip::udp::endpoint& remoteEndpoint, std::shared_ptr addr): SSU2Session (server), // we create full incoming session - m_Nonce (nonce) + m_Nonce (nonce), m_NumResends (0), m_HolePunchResendTimer (server.GetService ()) { // we are Charlie uint64_t destConnID = htobe64 (((uint64_t)nonce << 32) | nonce); // dest id @@ -295,6 +295,36 @@ namespace transport memcpy (m_RelayResponseBlock.data (), relayResponseBlock, relayResponseBlockLen); #endif SendHolePunch (); + ScheduleResend (); + } + + void SSU2HolePunchSession::ScheduleResend () + { + if (m_NumResends < SSU2_HOLE_PUNCH_MAX_NUM_RESENDS) + { + m_HolePunchResendTimer.expires_from_now (boost::posix_time::milliseconds( + SSU2_HOLE_PUNCH_RESEND_INTERVAL + GetServer ().GetRng ()() % SSU2_HOLE_PUNCH_RESEND_INTERVAL_VARIANCE)); + std::weak_ptr s(std::static_pointer_cast(shared_from_this ())); + m_HolePunchResendTimer.async_wait ([s](const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + auto s1 = s.lock (); + if (s1 && s1->GetState () == eSSU2SessionStateHolePunch) + { + s1->SendHolePunch (); + s1->ScheduleResend (); + } + } + }); + m_NumResends++; + } + } + + bool SSU2HolePunchSession::ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len) + { + m_HolePunchResendTimer.cancel (); + return SSU2Session::ProcessFirstIncomingMessage (connID, buf, len); } } } diff --git a/libi2pd/SSU2OutOfSession.h b/libi2pd/SSU2OutOfSession.h index 1069ab40..176a30f9 100644 --- a/libi2pd/SSU2OutOfSession.h +++ b/libi2pd/SSU2OutOfSession.h @@ -52,6 +52,10 @@ namespace transport boost::asio::deadline_timer m_PeerTestResendTimer; }; + const int SSU2_HOLE_PUNCH_RESEND_INTERVAL = 1000; // in milliseconds + const int SSU2_HOLE_PUNCH_RESEND_INTERVAL_VARIANCE = 500; // in milliseconds + const int SSU2_HOLE_PUNCH_MAX_NUM_RESENDS = 3; + class SSU2HolePunchSession: public SSU2Session // Charlie { public: @@ -60,15 +64,20 @@ namespace transport std::shared_ptr addr); void SendHolePunch (const uint8_t * relayResponseBlock, size_t relayResponseBlockLen); + + bool ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len) override; // SessionRequest private: void SendHolePunch (); + void ScheduleResend (); private: uint32_t m_Nonce; + int m_NumResends; std::vector m_RelayResponseBlock; + boost::asio::deadline_timer m_HolePunchResendTimer; }; } } From 8981e406f50e49561b5d209685880800ae6cfc3f Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 16 Oct 2024 13:51:48 -0400 Subject: [PATCH 19/50] don't delete RouterInfo's buffer right a way --- libi2pd/NetDb.cpp | 9 +++++++-- libi2pd/RouterInfo.cpp | 6 ++++-- libi2pd/RouterInfo.h | 6 ++++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/libi2pd/NetDb.cpp b/libi2pd/NetDb.cpp index 20b2fd42..f436a436 100644 --- a/libi2pd/NetDb.cpp +++ b/libi2pd/NetDb.cpp @@ -646,6 +646,11 @@ namespace data for (auto& it: m_RouterInfos) { if (!it.second || it.second == own) continue; // skip own + if (it.second->IsBufferScheduledToDelete ()) // from previous SaveUpdated, we assume m_PersistingRouters complete + { + std::lock_guard l(m_RouterInfosMutex); // possible collision between DeleteBuffer and Update + it.second->DeleteBuffer (); + } std::string ident = it.second->GetIdentHashBase64(); if (it.second->IsUpdated ()) { @@ -655,8 +660,8 @@ namespace data std::shared_ptr buffer; { std::lock_guard l(m_RouterInfosMutex); // possible collision between DeleteBuffer and Update - buffer = it.second->GetSharedBuffer (); - it.second->DeleteBuffer (); + buffer = it.second->CopyBuffer (); + it.second->ScheduleBufferToDelete (); } if (buffer && !it.second->IsUnreachable ()) // don't save bad router saveToDisk.push_back(std::make_pair(ident, buffer)); diff --git a/libi2pd/RouterInfo.cpp b/libi2pd/RouterInfo.cpp index 2da40ae8..168c5665 100644 --- a/libi2pd/RouterInfo.cpp +++ b/libi2pd/RouterInfo.cpp @@ -45,8 +45,9 @@ namespace data RouterInfo::RouterInfo (const std::string& fullPath): m_FamilyID (0), m_IsUpdated (false), m_IsUnreachable (false), m_IsFloodfill (false), - m_SupportedTransports (0),m_ReachableTransports (0), m_PublishedTransports (0), - m_Caps (0), m_Version (0), m_Congestion (eLowCongestion) + m_IsBufferScheduledToDelete (false), m_SupportedTransports (0), + m_ReachableTransports (0), m_PublishedTransports (0), m_Caps (0), m_Version (0), + m_Congestion (eLowCongestion) { m_Addresses = AddressesPtr(new Addresses ()); // create empty list m_Buffer = RouterInfo::NewBuffer (); // always RouterInfo's @@ -1140,6 +1141,7 @@ namespace data if (len > m_Buffer->size ()) len = m_Buffer->size (); memcpy (m_Buffer->data (), buf, len); m_Buffer->SetBufferLen (len); + m_IsBufferScheduledToDelete = false; } std::shared_ptr RouterInfo::CopyBuffer () const diff --git a/libi2pd/RouterInfo.h b/libi2pd/RouterInfo.h index 72521797..719d5795 100644 --- a/libi2pd/RouterInfo.h +++ b/libi2pd/RouterInfo.h @@ -290,9 +290,11 @@ namespace data const uint8_t * GetBuffer () const { return m_Buffer ? m_Buffer->data () : nullptr; }; const uint8_t * LoadBuffer (const std::string& fullPath); // load if necessary size_t GetBufferLen () const { return m_Buffer ? m_Buffer->GetBufferLen () : 0; }; - void DeleteBuffer () { m_Buffer = nullptr; }; + void DeleteBuffer () { m_Buffer = nullptr; m_IsBufferScheduledToDelete = false; }; std::shared_ptr GetSharedBuffer () const { return m_Buffer; }; std::shared_ptr CopyBuffer () const; + void ScheduleBufferToDelete () { m_IsBufferScheduledToDelete = false; }; + bool IsBufferScheduledToDelete () const { return m_IsBufferScheduledToDelete; }; bool IsUpdated () const { return m_IsUpdated; }; void SetUpdated (bool updated) { m_IsUpdated = updated; }; @@ -354,7 +356,7 @@ namespace data #else AddressesPtr m_Addresses; #endif - bool m_IsUpdated, m_IsUnreachable, m_IsFloodfill; + bool m_IsUpdated, m_IsUnreachable, m_IsFloodfill, m_IsBufferScheduledToDelete; CompatibleTransports m_SupportedTransports, m_ReachableTransports, m_PublishedTransports; uint8_t m_Caps; char m_BandwidthCap; From e26682f4cbba40230a12ab17f9f7133bbf45da68 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 16 Oct 2024 15:05:29 -0400 Subject: [PATCH 20/50] don't try to save invalid router --- libi2pd/NetDb.cpp | 5 ++--- libi2pd/RouterInfo.h | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/libi2pd/NetDb.cpp b/libi2pd/NetDb.cpp index f436a436..d32f113d 100644 --- a/libi2pd/NetDb.cpp +++ b/libi2pd/NetDb.cpp @@ -654,7 +654,7 @@ namespace data std::string ident = it.second->GetIdentHashBase64(); if (it.second->IsUpdated ()) { - if (it.second->GetBuffer ()) + if (it.second->GetBuffer () && !it.second->IsUnreachable ()) { // we have something to save std::shared_ptr buffer; @@ -663,9 +663,8 @@ namespace data buffer = it.second->CopyBuffer (); it.second->ScheduleBufferToDelete (); } - if (buffer && !it.second->IsUnreachable ()) // don't save bad router + if (buffer) saveToDisk.push_back(std::make_pair(ident, buffer)); - it.second->SetUnreachable (false); } it.second->SetUpdated (false); updatedCount++; diff --git a/libi2pd/RouterInfo.h b/libi2pd/RouterInfo.h index 719d5795..beeba5bf 100644 --- a/libi2pd/RouterInfo.h +++ b/libi2pd/RouterInfo.h @@ -293,7 +293,7 @@ namespace data void DeleteBuffer () { m_Buffer = nullptr; m_IsBufferScheduledToDelete = false; }; std::shared_ptr GetSharedBuffer () const { return m_Buffer; }; std::shared_ptr CopyBuffer () const; - void ScheduleBufferToDelete () { m_IsBufferScheduledToDelete = false; }; + void ScheduleBufferToDelete () { m_IsBufferScheduledToDelete = true; }; bool IsBufferScheduledToDelete () const { return m_IsBufferScheduledToDelete; }; bool IsUpdated () const { return m_IsUpdated; }; From 0ccf0a633943db416f93af4cd654aca5d2b08eb5 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 16 Oct 2024 17:57:52 -0400 Subject: [PATCH 21/50] use pointer to RouterInfo in SaveUpdated --- libi2pd/NetDb.cpp | 67 +++++++++++++++++++++--------------------- libi2pd/RouterInfo.cpp | 2 +- 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/libi2pd/NetDb.cpp b/libi2pd/NetDb.cpp index d32f113d..7266f44f 100644 --- a/libi2pd/NetDb.cpp +++ b/libi2pd/NetDb.cpp @@ -643,69 +643,68 @@ namespace data std::list removeFromDisk; auto own = i2p::context.GetSharedRouterInfo (); - for (auto& it: m_RouterInfos) + for (auto [ident, r]: m_RouterInfos) { - if (!it.second || it.second == own) continue; // skip own - if (it.second->IsBufferScheduledToDelete ()) // from previous SaveUpdated, we assume m_PersistingRouters complete + if (!r || r == own) continue; // skip own + if (r->IsBufferScheduledToDelete ()) // from previous SaveUpdated, we assume m_PersistingRouters complete { std::lock_guard l(m_RouterInfosMutex); // possible collision between DeleteBuffer and Update - it.second->DeleteBuffer (); + r->DeleteBuffer (); } - std::string ident = it.second->GetIdentHashBase64(); - if (it.second->IsUpdated ()) + if (r->IsUpdated ()) { - if (it.second->GetBuffer () && !it.second->IsUnreachable ()) + if (r->GetBuffer () && !r->IsUnreachable ()) { // we have something to save std::shared_ptr buffer; { std::lock_guard l(m_RouterInfosMutex); // possible collision between DeleteBuffer and Update - buffer = it.second->CopyBuffer (); - it.second->ScheduleBufferToDelete (); + buffer = r->CopyBuffer (); + r->ScheduleBufferToDelete (); } if (buffer) - saveToDisk.push_back(std::make_pair(ident, buffer)); + saveToDisk.push_back(std::make_pair(ident.ToBase64 (), buffer)); } - it.second->SetUpdated (false); + r->SetUpdated (false); updatedCount++; continue; } - if (it.second->GetProfile ()->IsUnreachable ()) - it.second->SetUnreachable (true); + if (r->GetProfile ()->IsUnreachable ()) + r->SetUnreachable (true); // make router reachable back if too few routers or floodfills - if (it.second->IsUnreachable () && (total - deletedCount < NETDB_MIN_ROUTERS || isLowRate || - (it.second->IsFloodfill () && totalFloodfills - deletedFloodfillsCount < NETDB_MIN_FLOODFILLS))) - it.second->SetUnreachable (false); - if (!it.second->IsUnreachable ()) + if (r->IsUnreachable () && (total - deletedCount < NETDB_MIN_ROUTERS || isLowRate || + (r->IsFloodfill () && totalFloodfills - deletedFloodfillsCount < NETDB_MIN_FLOODFILLS))) + r->SetUnreachable (false); + if (!r->IsUnreachable ()) { // find & mark expired routers - if (!it.second->GetCompatibleTransports (true)) // non reachable by any transport - it.second->SetUnreachable (true); - else if (ts + NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < it.second->GetTimestamp ()) + if (!r->GetCompatibleTransports (true)) // non reachable by any transport + r->SetUnreachable (true); + else if (ts + NETDB_EXPIRATION_TIMEOUT_THRESHOLD*1000LL < r->GetTimestamp ()) { - LogPrint (eLogWarning, "NetDb: RouterInfo is from future for ", (it.second->GetTimestamp () - ts)/1000LL, " seconds"); - it.second->SetUnreachable (true); + LogPrint (eLogWarning, "NetDb: RouterInfo is from future for ", (r->GetTimestamp () - ts)/1000LL, " seconds"); + r->SetUnreachable (true); } else if (checkForExpiration) { - if (ts > it.second->GetTimestamp () + expirationTimeout) - it.second->SetUnreachable (true); - else if ((ts > it.second->GetTimestamp () + expirationTimeout/2) && // more than half of expiration - total > NETDB_NUM_ROUTERS_THRESHOLD && !it.second->IsHighBandwidth() && // low bandwidth - !it.second->IsFloodfill() && (!i2p::context.IsFloodfill () || // non floodfill - (CreateRoutingKey (it.second->GetIdentHash ()) ^ i2p::context.GetIdentHash ()).metric[0] >= 0x02)) // different first 7 bits - it.second->SetUnreachable (true); + if (ts > r->GetTimestamp () + expirationTimeout) + r->SetUnreachable (true); + else if ((ts > r->GetTimestamp () + expirationTimeout/2) && // more than half of expiration + total > NETDB_NUM_ROUTERS_THRESHOLD && !r->IsHighBandwidth() && // low bandwidth + !r->IsFloodfill() && (!i2p::context.IsFloodfill () || // non floodfill + (CreateRoutingKey (ident) ^ i2p::context.GetIdentHash ()).metric[0] >= 0x02)) // different first 7 bits + r->SetUnreachable (true); } } // make router reachable back if connected now - if (it.second->IsUnreachable () && i2p::transport::transports.IsConnected (it.second->GetIdentHash ())) - it.second->SetUnreachable (false); + if (r->IsUnreachable () && i2p::transport::transports.IsConnected (ident)) + r->SetUnreachable (false); - if (it.second->IsUnreachable ()) + if (r->IsUnreachable ()) { - if (it.second->IsFloodfill ()) deletedFloodfillsCount++; + if (r->IsFloodfill ()) deletedFloodfillsCount++; // delete RI file - removeFromDisk.push_back (ident); + removeFromDisk.push_back (ident.ToBase64()); deletedCount++; if (total - deletedCount < NETDB_MIN_ROUTERS) checkForExpiration = false; } diff --git a/libi2pd/RouterInfo.cpp b/libi2pd/RouterInfo.cpp index 168c5665..2321a1e0 100644 --- a/libi2pd/RouterInfo.cpp +++ b/libi2pd/RouterInfo.cpp @@ -56,7 +56,7 @@ namespace data RouterInfo::RouterInfo (std::shared_ptr&& buf, size_t len): m_FamilyID (0), m_IsUpdated (true), m_IsUnreachable (false), m_IsFloodfill (false), - m_SupportedTransports (0), m_ReachableTransports (0), m_PublishedTransports (0), + m_IsBufferScheduledToDelete (false), m_SupportedTransports (0), m_ReachableTransports (0), m_PublishedTransports (0), m_Caps (0), m_Version (0), m_Congestion (eLowCongestion) { if (len <= MAX_RI_BUFFER_SIZE) From 0213f058d14f4d550aa857f619b44a4a56ef5f5f Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 16 Oct 2024 21:19:30 -0400 Subject: [PATCH 22/50] Send peer test msg 6 with delay if msg 4 was received before msg 5 --- libi2pd/SSU2OutOfSession.cpp | 32 ++++++++++++++------------------ libi2pd/SSU2OutOfSession.h | 7 +++---- libi2pd/SSU2Session.cpp | 4 +++- 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/libi2pd/SSU2OutOfSession.cpp b/libi2pd/SSU2OutOfSession.cpp index a2e0941e..c337938e 100644 --- a/libi2pd/SSU2OutOfSession.cpp +++ b/libi2pd/SSU2OutOfSession.cpp @@ -83,6 +83,7 @@ namespace transport { if (htobe64 (((uint64_t)nonce << 32) | nonce) == GetSourceConnID ()) { + m_PeerTestResendTimer.cancel (); // calcel delayed msg 6 if any m_IsConnectedRecently = GetServer ().IsConnectedRecently (GetRemoteEndpoint ()); if (GetAddress ()) { @@ -111,9 +112,6 @@ namespace transport case 7: // Alice from Charlie 2 { m_PeerTestResendTimer.cancel (); // no more msg 6 resends - auto addr = GetAddress (); - if (addr && addr->IsV6 ()) - i2p::context.SetStatusV6 (eRouterStatusOK); // set status OK for ipv6 even if from SSU2 GetServer ().AddConnectedRecently (GetRemoteEndpoint (), i2p::util::GetSecondsSinceEpoch ()); GetServer ().RequestRemoveSession (GetConnID ()); break; @@ -163,7 +161,7 @@ namespace transport GetServer ().Send (header.buf, 16, h + 16, 16, payload, payloadSize, GetRemoteEndpoint ()); } - void SSU2PeerTestSession::SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen) + void SSU2PeerTestSession::SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, bool delayed) { #if __cplusplus >= 202002L // C++20 m_SignedData.assign (signedData, signedData + signedDataLen); @@ -171,18 +169,19 @@ namespace transport m_SignedData.resize (signedDataLen); memcpy (m_SignedData.data (), signedData, signedDataLen); #endif - SendPeerTest (msg); + if (!delayed) + SendPeerTest (msg); // schedule resend for msgs 5 or 6 if (msg == 5 || msg == 6) - ScheduleResend (); + ScheduleResend (msg); } void SSU2PeerTestSession::SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, - std::shared_ptr addr) + std::shared_ptr addr, bool delayed) { if (!addr) return; SetAddress (addr); - SendPeerTest (msg, signedData, signedDataLen); + SendPeerTest (msg, signedData, signedDataLen, delayed); } void SSU2PeerTestSession::Connect () @@ -196,32 +195,29 @@ namespace transport return false; } - void SSU2PeerTestSession::ScheduleResend () + void SSU2PeerTestSession::ScheduleResend (uint8_t msg) { if (m_NumResends < SSU2_PEER_TEST_MAX_NUM_RESENDS) { m_PeerTestResendTimer.expires_from_now (boost::posix_time::milliseconds( SSU2_PEER_TEST_RESEND_INTERVAL + GetServer ().GetRng ()() % SSU2_PEER_TEST_RESEND_INTERVAL_VARIANCE)); std::weak_ptr s(std::static_pointer_cast(shared_from_this ())); - m_PeerTestResendTimer.async_wait ([s](const boost::system::error_code& ecode) + m_PeerTestResendTimer.async_wait ([s, msg](const boost::system::error_code& ecode) { if (ecode != boost::asio::error::operation_aborted) { auto s1 = s.lock (); if (s1) { - int msg = 0; - if (s1->m_MsgNumReceived < 6) - msg = (s1->m_MsgNumReceived == 5) ? 6 : 5; - if (msg) // 5 or 6 + if (msg > s1->m_MsgNumReceived) { s1->SendPeerTest (msg); - s1->ScheduleResend (); + s1->m_NumResends++; + s1->ScheduleResend (msg); } } } }); - m_NumResends++; } } @@ -229,7 +225,7 @@ namespace transport const boost::asio::ip::udp::endpoint& remoteEndpoint, std::shared_ptr addr): SSU2Session (server), // we create full incoming session - m_Nonce (nonce), m_NumResends (0), m_HolePunchResendTimer (server.GetService ()) + m_NumResends (0), m_HolePunchResendTimer (server.GetService ()) { // we are Charlie uint64_t destConnID = htobe64 (((uint64_t)nonce << 32) | nonce); // dest id @@ -313,11 +309,11 @@ namespace transport if (s1 && s1->GetState () == eSSU2SessionStateHolePunch) { s1->SendHolePunch (); + s1->m_NumResends++; s1->ScheduleResend (); } } }); - m_NumResends++; } } diff --git a/libi2pd/SSU2OutOfSession.h b/libi2pd/SSU2OutOfSession.h index 176a30f9..6f2a9fbd 100644 --- a/libi2pd/SSU2OutOfSession.h +++ b/libi2pd/SSU2OutOfSession.h @@ -31,18 +31,18 @@ namespace transport void SetStatusChanged () { m_IsStatusChanged = true; } void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, - std::shared_ptr addr); + std::shared_ptr addr, bool delayed = false); bool ProcessPeerTest (uint8_t * buf, size_t len) override; void Connect () override; // outgoing bool ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len) override; // incoming private: - void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen); // PeerTest message + void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, bool delayed = false); // PeerTest message void SendPeerTest (uint8_t msg); // send or resend m_SignedData void HandlePeerTest (const uint8_t * buf, size_t len) override; - void ScheduleResend (); + void ScheduleResend (uint8_t msg); private: @@ -74,7 +74,6 @@ namespace transport private: - uint32_t m_Nonce; int m_NumResends; std::vector m_RelayResponseBlock; boost::asio::deadline_timer m_HolePunchResendTimer; diff --git a/libi2pd/SSU2Session.cpp b/libi2pd/SSU2Session.cpp index 47f6a9b2..454c38d5 100644 --- a/libi2pd/SSU2Session.cpp +++ b/libi2pd/SSU2Session.cpp @@ -2312,7 +2312,7 @@ namespace transport { if (!session->IsConnectedRecently ()) SetRouterStatus (eRouterStatusOK); - // send msg 6 + // send msg 6 immeditely session->SendPeerTest (6, buf + offset, len - offset, addr); } else @@ -2323,6 +2323,8 @@ namespace transport session->m_Address = addr; if (GetTestingState ()) { + // schedule msg 6 with delay + session->SendPeerTest (6, buf + offset, len - offset, addr, true); SetTestingState (false); if (GetRouterStatus () != eRouterStatusFirewalled && addr->IsPeerTesting ()) { From fe71776b6f6a8f4bb9c94902b495d37a621d17c9 Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 17 Oct 2024 15:44:26 -0400 Subject: [PATCH 23/50] update LeaseSet if inbound tunnel was restored --- libi2pd/Tunnel.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/libi2pd/Tunnel.cpp b/libi2pd/Tunnel.cpp index ca110ccc..6e5ebdad 100644 --- a/libi2pd/Tunnel.cpp +++ b/libi2pd/Tunnel.cpp @@ -250,7 +250,18 @@ namespace tunnel void InboundTunnel::HandleTunnelDataMsg (std::shared_ptr&& msg) { - if (GetState () != eTunnelStateExpiring) SetState (eTunnelStateEstablished); // incoming messages means a tunnel is alive + if (!IsEstablished () && GetState () != eTunnelStateExpiring) + { + // incoming messages means a tunnel is alive + SetState (eTunnelStateEstablished); + auto pool = GetTunnelPool (); + if (pool) + { + // update LeaseSet + auto dest = pool->GetLocalDestination (); + if (dest) dest->SetLeaseSetUpdated (); + } + } EncryptTunnelMsg (msg, msg); msg->from = GetSharedFromThis (); m_Endpoint.HandleDecryptedTunnelDataMsg (msg); From bc9d25ec3b53b9c2baa2371ce5747de8a35e7e88 Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 17 Oct 2024 16:20:27 -0400 Subject: [PATCH 24/50] ability post LeaseSet update to destination's thread --- libi2pd/Destination.cpp | 7 +++++-- libi2pd/Destination.h | 2 +- libi2pd/Garlic.cpp | 2 +- libi2pd/Garlic.h | 2 +- libi2pd/Tunnel.cpp | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/libi2pd/Destination.cpp b/libi2pd/Destination.cpp index 28b23950..01ff2d2a 100644 --- a/libi2pd/Destination.cpp +++ b/libi2pd/Destination.cpp @@ -588,9 +588,12 @@ namespace client i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msgID); } - void LeaseSetDestination::SetLeaseSetUpdated () + void LeaseSetDestination::SetLeaseSetUpdated (bool post) { - UpdateLeaseSet (); + if (post) + m_Service.post([s = shared_from_this ()]() { s->UpdateLeaseSet (); }); + else + UpdateLeaseSet (); } void LeaseSetDestination::Publish () diff --git a/libi2pd/Destination.h b/libi2pd/Destination.h index 4a51a257..293cd75d 100644 --- a/libi2pd/Destination.h +++ b/libi2pd/Destination.h @@ -150,7 +150,7 @@ namespace client void SubmitECIESx25519Key (const uint8_t * key, uint64_t tag); void ProcessGarlicMessage (std::shared_ptr msg); void ProcessDeliveryStatusMessage (std::shared_ptr msg); - void SetLeaseSetUpdated (); + void SetLeaseSetUpdated (bool post) override; bool IsPublic () const { return m_IsPublic; }; void SetPublic (bool pub) { m_IsPublic = pub; }; diff --git a/libi2pd/Garlic.cpp b/libi2pd/Garlic.cpp index 1705b03a..04884acd 100644 --- a/libi2pd/Garlic.cpp +++ b/libi2pd/Garlic.cpp @@ -897,7 +897,7 @@ namespace garlic } } - void GarlicDestination::SetLeaseSetUpdated () + void GarlicDestination::SetLeaseSetUpdated (bool post) { { std::unique_lock l(m_SessionsMutex); diff --git a/libi2pd/Garlic.h b/libi2pd/Garlic.h index a4475dc7..80fc15da 100644 --- a/libi2pd/Garlic.h +++ b/libi2pd/Garlic.h @@ -253,7 +253,7 @@ namespace garlic virtual void ProcessGarlicMessage (std::shared_ptr msg); virtual void ProcessDeliveryStatusMessage (std::shared_ptr msg); - virtual void SetLeaseSetUpdated (); + virtual void SetLeaseSetUpdated (bool post = false); virtual std::shared_ptr GetLeaseSet () = 0; // TODO virtual std::shared_ptr GetTunnelPool () const = 0; diff --git a/libi2pd/Tunnel.cpp b/libi2pd/Tunnel.cpp index 6e5ebdad..c41bb775 100644 --- a/libi2pd/Tunnel.cpp +++ b/libi2pd/Tunnel.cpp @@ -259,7 +259,7 @@ namespace tunnel { // update LeaseSet auto dest = pool->GetLocalDestination (); - if (dest) dest->SetLeaseSetUpdated (); + if (dest) dest->SetLeaseSetUpdated (true); } } EncryptTunnelMsg (msg, msg); From 890fe77b1019b7dab0619a8138bbc844e34bccfa Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 17 Oct 2024 18:15:33 -0400 Subject: [PATCH 25/50] update leaseset in destination's thread --- libi2pd/TunnelPool.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libi2pd/TunnelPool.cpp b/libi2pd/TunnelPool.cpp index 5af42373..3d50738c 100644 --- a/libi2pd/TunnelPool.cpp +++ b/libi2pd/TunnelPool.cpp @@ -141,7 +141,7 @@ namespace tunnel m_InboundTunnels.insert (createdTunnel); } if (m_LocalDestination) - m_LocalDestination->SetLeaseSetUpdated (); + m_LocalDestination->SetLeaseSetUpdated (true); } void TunnelPool::TunnelExpired (std::shared_ptr expiredTunnel) @@ -330,7 +330,7 @@ namespace tunnel } if (num < m_NumInboundTunnels && m_NumInboundHops <= 0 && m_LocalDestination) // zero hops IB - m_LocalDestination->SetLeaseSetUpdated (); // update LeaseSet immediately + m_LocalDestination->SetLeaseSetUpdated (true); // update LeaseSet immediately } void TunnelPool::TestTunnels () @@ -377,10 +377,10 @@ namespace tunnel it.second.second->SetState (eTunnelStateTestFailed); } if (failed && m_LocalDestination) - m_LocalDestination->SetLeaseSetUpdated (); + m_LocalDestination->SetLeaseSetUpdated (true); } if (m_LocalDestination) - m_LocalDestination->SetLeaseSetUpdated (); + m_LocalDestination->SetLeaseSetUpdated (true); } else if (it.second.second->GetState () != eTunnelStateExpiring) it.second.second->SetState (eTunnelStateTestFailed); From 1419745a5d848ac283225230d1a76209253a4d61 Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 17 Oct 2024 21:09:37 -0400 Subject: [PATCH 26/50] recognize symmetric NAT from peer test msg 7 --- libi2pd/SSU2OutOfSession.cpp | 29 +++++++++++++++++++++++++++++ libi2pd/SSU2OutOfSession.h | 2 ++ libi2pd/SSU2Session.h | 5 +++-- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/libi2pd/SSU2OutOfSession.cpp b/libi2pd/SSU2OutOfSession.cpp index c337938e..262f93a9 100644 --- a/libi2pd/SSU2OutOfSession.cpp +++ b/libi2pd/SSU2OutOfSession.cpp @@ -65,6 +65,12 @@ namespace transport return true; } + void SSU2PeerTestSession::HandleAddress (const uint8_t * buf, size_t len) + { + if (!ExtractEndpoint (buf, len, m_OurEndpoint)) + LogPrint (eLogWarning, "SSU2: Can't hanlde address block from peer test message"); + } + void SSU2PeerTestSession::HandlePeerTest (const uint8_t * buf, size_t len) { // msgs 5-7 @@ -112,6 +118,29 @@ namespace transport case 7: // Alice from Charlie 2 { m_PeerTestResendTimer.cancel (); // no more msg 6 resends + if (m_MsgNumReceived < 5 && m_OurEndpoint.port ()) // msg 5 was not received + { + if (m_OurEndpoint.address ().is_v4 ()) // ipv4 + { + if (i2p::context.GetStatus () == eRouterStatusFirewalled) + { + if (m_OurEndpoint.port () != GetServer ().GetPort (true)) + i2p::context.SetError (eRouterErrorSymmetricNAT); + else if (i2p::context.GetError () == eRouterErrorSymmetricNAT) + i2p::context.SetError (eRouterErrorNone); + } + } + else + { + if (i2p::context.GetStatusV6 () == eRouterStatusFirewalled) + { + if (m_OurEndpoint.port () != GetServer ().GetPort (false)) + i2p::context.SetErrorV6 (eRouterErrorSymmetricNAT); + else if (i2p::context.GetErrorV6 () == eRouterErrorSymmetricNAT) + i2p::context.SetErrorV6 (eRouterErrorNone); + } + } + } GetServer ().AddConnectedRecently (GetRemoteEndpoint (), i2p::util::GetSecondsSinceEpoch ()); GetServer ().RequestRemoveSession (GetConnID ()); break; diff --git a/libi2pd/SSU2OutOfSession.h b/libi2pd/SSU2OutOfSession.h index 6f2a9fbd..e8c55c3c 100644 --- a/libi2pd/SSU2OutOfSession.h +++ b/libi2pd/SSU2OutOfSession.h @@ -41,6 +41,7 @@ namespace transport void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, bool delayed = false); // PeerTest message void SendPeerTest (uint8_t msg); // send or resend m_SignedData void HandlePeerTest (const uint8_t * buf, size_t len) override; + void HandleAddress (const uint8_t * buf, size_t len) override; void ScheduleResend (uint8_t msg); @@ -50,6 +51,7 @@ namespace transport bool m_IsConnectedRecently, m_IsStatusChanged; std::vector m_SignedData; // for resends boost::asio::deadline_timer m_PeerTestResendTimer; + boost::asio::ip::udp::endpoint m_OurEndpoint; // as seen by peer }; const int SSU2_HOLE_PUNCH_RESEND_INTERVAL = 1000; // in milliseconds diff --git a/libi2pd/SSU2Session.h b/libi2pd/SSU2Session.h index ae0f18f4..e27671f8 100644 --- a/libi2pd/SSU2Session.h +++ b/libi2pd/SSU2Session.h @@ -296,6 +296,8 @@ namespace transport size_t CreateAddressBlock (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep); size_t CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize = 0); size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint8_t msg, SSU2PeerTestCode code, const uint8_t * routerHash, const uint8_t * signedData, size_t signedDataLen); + + bool ExtractEndpoint (const uint8_t * buf, size_t size, boost::asio::ip::udp::endpoint& ep); private: @@ -328,8 +330,7 @@ namespace transport void HandleRouterInfo (const uint8_t * buf, size_t len); void HandleAck (const uint8_t * buf, size_t len); void HandleAckRange (uint32_t firstPacketNum, uint32_t lastPacketNum, uint64_t ts); - void HandleAddress (const uint8_t * buf, size_t len); - bool ExtractEndpoint (const uint8_t * buf, size_t size, boost::asio::ip::udp::endpoint& ep); + virtual void HandleAddress (const uint8_t * buf, size_t len); size_t CreateEndpoint (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep); std::shared_ptr FindLocalAddress () const; void AdjustMaxPayloadSize (); From f98a310235d7e96dd39a9423593e2fd3cbb546ed Mon Sep 17 00:00:00 2001 From: SystemFailure Date: Fri, 18 Oct 2024 13:17:47 +0000 Subject: [PATCH 27/50] 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 28/50] 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 29/50] 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 30/50] 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 31/50] 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 32/50] 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 33/50] 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 34/50] 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 35/50] 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 36/50] 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 37/50] 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 38/50] 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 39/50] 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 40/50] 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 41/50] 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 42/50] 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 43/50] 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) ); - - // 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"); + 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"); - 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 44/50] 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 45/50] 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 46/50] 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 47/50] 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: From 8f9874570a01698fe739b759feed4e0a8d67148b Mon Sep 17 00:00:00 2001 From: mittwerk Date: Mon, 28 Oct 2024 09:11:09 +0200 Subject: [PATCH 48/50] hardening iterator --- libi2pd_client/UDPTunnel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libi2pd_client/UDPTunnel.cpp b/libi2pd_client/UDPTunnel.cpp index cd17bbf0..9f7fe864 100644 --- a/libi2pd_client/UDPTunnel.cpp +++ b/libi2pd_client/UDPTunnel.cpp @@ -203,7 +203,7 @@ namespace client std::vector > sessions; std::lock_guard lock (m_SessionsMutex); - for (auto it: m_Sessions) + for (const auto &it: m_Sessions) { auto s = it.second; if (!s->m_Destination) continue; From 0e8d624d861bd08c705e8390f98609e3876d0acf Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 28 Oct 2024 08:38:04 -0400 Subject: [PATCH 49/50] move UpdatePacingTime out of loop --- libi2pd/Streaming.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libi2pd/Streaming.cpp b/libi2pd/Streaming.cpp index f9257974..966c172d 100644 --- a/libi2pd/Streaming.cpp +++ b/libi2pd/Streaming.cpp @@ -1276,10 +1276,12 @@ namespace stream else m_WindowSize += (m_WindowSize - (1 - PREV_SPEED_KEEP_TIME_COEFF)) / m_WindowSize; if (m_WindowSize > MAX_WINDOW_SIZE) m_WindowSize = MAX_WINDOW_SIZE; - m_WindowIncCounter --; - UpdatePacingTime (); + m_WindowIncCounter--; } + else + break; } + UpdatePacingTime (); } if (m_IsNAcked) ResendPacket (); From ec67f48d855c196669938a2da71064da8a501d4b Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 28 Oct 2024 08:46:01 -0400 Subject: [PATCH 50/50] fixed possible memory leak --- libi2pd/NTCP2.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libi2pd/NTCP2.cpp b/libi2pd/NTCP2.cpp index 728ac01d..a05469f1 100644 --- a/libi2pd/NTCP2.cpp +++ b/libi2pd/NTCP2.cpp @@ -1822,7 +1822,7 @@ namespace transport LogPrint(eLogError, "NTCP2: HTTP proxy write error ", ec.message()); }); - boost::asio::streambuf * readbuff = new boost::asio::streambuf; + auto readbuff = std::make_shared(); boost::asio::async_read_until(conn->GetSocket(), *readbuff, "\r\n\r\n", [readbuff, timer, conn] (const boost::system::error_code & ec, std::size_t transferred) { @@ -1842,7 +1842,6 @@ namespace transport { timer->cancel(); conn->ClientLogin(); - delete readbuff; return; } else @@ -1852,7 +1851,6 @@ namespace transport LogPrint(eLogError, "NTCP2: HTTP proxy gave malformed response"); timer->cancel(); conn->Terminate(); - delete readbuff; } }); break;