diff --git a/libi2pd/Datagram.cpp b/libi2pd/Datagram.cpp index 516dc057..fc628a6f 100644 --- a/libi2pd/Datagram.cpp +++ b/libi2pd/Datagram.cpp @@ -226,10 +226,10 @@ namespace datagram } } } - uint16_t flags = bufbe16toh (buf + identityLen); + const uint8_t * flags = buf + identityLen; size_t offset = identityLen + 2; bool isOptions = false; - if (flags & DATAGRAM2_FLAG_OPTIONS) + if (flags[1] & DATAGRAM2_FLAG_OPTIONS) { isOptions = true; m_Options.CleanUp (); @@ -250,7 +250,7 @@ namespace datagram if (!verified) { std::shared_ptr transientVerifier; - if (flags & DATAGRAM2_FLAG_OFFLINE_SIGNATURE) + if (flags[1] & DATAGRAM2_FLAG_OFFLINE_SIGNATURE) { transientVerifier = i2p::data::ProcessOfflineSignature (&identity, buf, len, offset); if (!transientVerifier) @@ -308,10 +308,10 @@ namespace datagram auto r = FindReceiver(toPort); if (r) { - uint16_t flags = bufbe16toh (buf + 32); + const uint8_t * flags = buf + 32; size_t offset = 34; bool isOptions = false; - if (flags & DATAGRAM3_FLAG_OPTIONS) + if (flags[1] & DATAGRAM3_FLAG_OPTIONS) { isOptions = true; m_Options.CleanUp (); diff --git a/libi2pd/Datagram.h b/libi2pd/Datagram.h index d9d5fedc..a90edeb8 100644 --- a/libi2pd/Datagram.h +++ b/libi2pd/Datagram.h @@ -53,9 +53,9 @@ namespace datagram eDatagramV3 = 3, }; - constexpr uint16_t DATAGRAM2_FLAG_OPTIONS = 0x10; - constexpr uint16_t DATAGRAM2_FLAG_OFFLINE_SIGNATURE = 0x20; - constexpr uint16_t DATAGRAM3_FLAG_OPTIONS = 0x10; + constexpr uint8_t DATAGRAM2_FLAG_OPTIONS = 0x10; + constexpr uint8_t DATAGRAM2_FLAG_OFFLINE_SIGNATURE = 0x20; + constexpr uint8_t DATAGRAM3_FLAG_OPTIONS = 0x10; class DatagramSession : public std::enable_shared_from_this { diff --git a/libi2pd/util.h b/libi2pd/util.h index fd52e39d..59901f2b 100644 --- a/libi2pd/util.h +++ b/libi2pd/util.h @@ -247,12 +247,12 @@ namespace util static size_t WriteOption (std::string_view param, std::string_view value, uint8_t * buf, size_t len); template - bool Get(std::string_view param, T& value) + bool Get(std::string_view param, T& value) const { auto s = (*this)[param]; if (s.empty ()) return false; auto res = std::from_chars(s.data(), s.data() + s.size(), value); - return res.ec != std::errc(); + return res.ec == std::errc(); } template bool Put (std::string_view param, T value) diff --git a/libi2pd_client/UDPTunnel.cpp b/libi2pd_client/UDPTunnel.cpp index 12e8e5be..1b0bd48c 100644 --- a/libi2pd_client/UDPTunnel.cpp +++ b/libi2pd_client/UDPTunnel.cpp @@ -6,6 +6,7 @@ * See full license text in LICENSE file at top of project tree */ +#include #include "Log.h" #include "util.h" #include "ClientContext.h" @@ -16,6 +17,9 @@ namespace i2p { namespace client { + constexpr std::string_view UDP_SESSION_SEQN { "seqn" }; + constexpr std::string_view UDP_SESSION_ACKED { "acked" }; + void I2PUDPServerTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len, const i2p::util::Mapping * options) { @@ -27,6 +31,15 @@ namespace client m_LastSession->LastActivity = i2p::util::GetMillisecondsSinceEpoch(); else LogPrint (eLogInfo, "UDP Server: Send exception: ", ec.message (), " to ", m_RemoteEndpoint); + if (options) + { + uint32_t seqn = 0; + if (options->Get (UDP_SESSION_SEQN, seqn) && seqn > m_LastSession->m_LastReceivedPacketNum) + { + m_LastSession->m_LastReceivedPacketNum = seqn; + LogPrint (eLogDebug, "UDP Server: Received packet with seqn ", seqn); + } + } } void I2PUDPServerTunnel::HandleRecvFromI2PRaw (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) @@ -123,11 +136,9 @@ namespace client const boost::asio::ip::udp::endpoint& endpoint, const i2p::data::IdentHash& to, uint16_t ourPort, uint16_t theirPort) : m_Destination(localDestination->GetDatagramDestination()), - IPSocket(localDestination->GetService(), localEndpoint), - Identity (to), SendEndpoint(endpoint), - LastActivity(i2p::util::GetMillisecondsSinceEpoch()), - LocalPort(ourPort), - RemotePort(theirPort) + IPSocket(localDestination->GetService(), localEndpoint), Identity (to), + SendEndpoint(endpoint), LastActivity(i2p::util::GetMillisecondsSinceEpoch()), + LocalPort(ourPort), RemotePort(theirPort), m_NextSendPacketNum (1), m_LastReceivedPacketNum (0) { IPSocket.set_option (boost::asio::socket_base::receive_buffer_size (I2P_UDP_MAX_MTU )); IPSocket.non_blocking (true); @@ -149,7 +160,13 @@ namespace client auto ts = i2p::util::GetMillisecondsSinceEpoch(); auto session = m_Destination->GetSession (Identity); if (ts > LastActivity + I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL) - m_Destination->SendDatagram(session, m_Buffer, len, LocalPort, RemotePort); + { + i2p::util::Mapping options; + options.Put (UDP_SESSION_SEQN, m_NextSendPacketNum); + if (m_LastReceivedPacketNum > 0) + options.Put (UDP_SESSION_ACKED, m_LastReceivedPacketNum); + m_Destination->SendDatagram(session, m_Buffer, len, LocalPort, RemotePort, &options); + } else m_Destination->SendRawDatagram(session, m_Buffer, len, LocalPort, RemotePort); size_t numPackets = 0; @@ -164,6 +181,7 @@ namespace client } if (numPackets > 0) LogPrint(eLogDebug, "UDPSession: Forward more ", numPackets, "packets B from ", FromEndpoint); + m_NextSendPacketNum += numPackets + 1; m_Destination->FlushSendQueue (session); LastActivity = ts; Receive(); @@ -238,7 +256,8 @@ namespace client uint16_t remotePort, bool gzip, i2p::datagram::DatagramVersion datagramVersion) : m_Name (name), m_RemoteDest (remoteDest), m_LocalDest (localDestination), m_LocalEndpoint (localEndpoint), m_ResolveThread (nullptr), m_LocalSocket (nullptr), RemotePort (remotePort), - m_LastPort (0), m_cancel_resolve (false), m_Gzip (gzip), m_DatagramVersion (datagramVersion) + m_LastPort (0), m_cancel_resolve (false), m_Gzip (gzip), m_DatagramVersion (datagramVersion), + m_NextSendPacketNum (1), m_LastReceivedPacketNum (0) { } @@ -339,7 +358,13 @@ namespace client LogPrint (eLogDebug, "UDP Client: Send ", transferred, " to ", m_RemoteAddr->identHash.ToBase32 (), ":", RemotePort); auto session = m_LocalDest->GetDatagramDestination ()->GetSession (m_RemoteAddr->identHash); if (ts > m_LastSession->second + I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL) - m_LocalDest->GetDatagramDestination ()->SendDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort); + { + i2p::util::Mapping options; + options.Put (UDP_SESSION_SEQN, m_NextSendPacketNum); + if (m_LastReceivedPacketNum > 0) + options.Put (UDP_SESSION_ACKED, m_LastReceivedPacketNum); + m_LocalDest->GetDatagramDestination ()->SendDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort, &options); + } else m_LocalDest->GetDatagramDestination ()->SendRawDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort); size_t numPackets = 0; @@ -356,6 +381,7 @@ namespace client } if (numPackets) LogPrint (eLogDebug, "UDP Client: Sent ", numPackets, " more packets to ", m_RemoteAddr->identHash.ToBase32 ()); + m_NextSendPacketNum += numPackets + 1; m_LocalDest->GetDatagramDestination ()->FlushSendQueue (session); // mark convo as active diff --git a/libi2pd_client/UDPTunnel.h b/libi2pd_client/UDPTunnel.h index f25c5362..848ed679 100644 --- a/libi2pd_client/UDPTunnel.h +++ b/libi2pd_client/UDPTunnel.h @@ -46,6 +46,7 @@ namespace client uint16_t RemotePort; uint8_t m_Buffer[I2P_UDP_MAX_MTU]; + uint32_t m_NextSendPacketNum, m_LastReceivedPacketNum; UDPSession(boost::asio::ip::udp::endpoint localEndpoint, const std::shared_ptr & localDestination, @@ -180,6 +181,7 @@ namespace client bool m_Gzip; i2p::datagram::DatagramVersion m_DatagramVersion; std::shared_ptr m_LastSession; + uint32_t m_NextSendPacketNum, m_LastReceivedPacketNum; public: