Loading debian/changelog +7 −0 Original line number Diff line number Diff line libnetplus (20260403+3) unstable; urgency=medium * Fix QUIC handshake packet loss: retry sendPacket on EAGAIN up to 5 times with 1ms back-off instead of silently dropping packets -- Jan Koester <jan.koester@tuxist.de> Fri, 04 Apr 2026 03:00:00 +0200 libnetplus (20260403+2) unstable; urgency=medium * Add client-side handshake debug logging: Initial/Handshake decrypt Loading src/quic.cpp +24 −16 Original line number Diff line number Diff line Loading @@ -205,6 +205,9 @@ void quic::generateConnectionId(std::vector<uint8_t>& cid, size_t len) { // ============================================================================ // Helper: Send packet to peer (uses udp class methods) // Retries on EAGAIN up to 5 times with 1ms back-off to avoid losing // packets when the kernel TX buffer is temporarily full (e.g. after // sending the 1200-byte ServerHello, the Handshake flight may EAGAIN). // ============================================================================ ssize_t quic::sendPacket(const uint8_t* data, size_t len) { Loading @@ -212,15 +215,14 @@ ssize_t quic::sendPacket(const uint8_t* data, size_t len) { return -1; } for (int attempt = 0; attempt < 5; ++attempt) { buffer send_buf(reinterpret_cast<const char*>(data), len); try { size_t sent; if (_parent != nullptr) { // Child connection: shared unconnected parent socket, send to stored peer address sent = udp::sendTo(send_buf, MSG_DONTWAIT); } else { // Client connection: connected socket sent = udp::sendData(send_buf, MSG_DONTWAIT); } Loading @@ -228,11 +230,17 @@ ssize_t quic::sendPacket(const uint8_t* data, size_t len) { _last_activity = std::chrono::steady_clock::now(); } return static_cast<ssize_t>(sent); } catch (NetException&) { // EAGAIN/EWOULDBLOCK or other send failure } catch (NetException& e) { // EAGAIN/EWOULDBLOCK — retry after brief pause if (e.getErrorType() == NetException::Note && attempt < 4) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); continue; } return -1; } } return -1; } // ============================================================================ // Variable-length Integer Encoding/Decoding (RFC 9000 Section 16) Loading Loading
debian/changelog +7 −0 Original line number Diff line number Diff line libnetplus (20260403+3) unstable; urgency=medium * Fix QUIC handshake packet loss: retry sendPacket on EAGAIN up to 5 times with 1ms back-off instead of silently dropping packets -- Jan Koester <jan.koester@tuxist.de> Fri, 04 Apr 2026 03:00:00 +0200 libnetplus (20260403+2) unstable; urgency=medium * Add client-side handshake debug logging: Initial/Handshake decrypt Loading
src/quic.cpp +24 −16 Original line number Diff line number Diff line Loading @@ -205,6 +205,9 @@ void quic::generateConnectionId(std::vector<uint8_t>& cid, size_t len) { // ============================================================================ // Helper: Send packet to peer (uses udp class methods) // Retries on EAGAIN up to 5 times with 1ms back-off to avoid losing // packets when the kernel TX buffer is temporarily full (e.g. after // sending the 1200-byte ServerHello, the Handshake flight may EAGAIN). // ============================================================================ ssize_t quic::sendPacket(const uint8_t* data, size_t len) { Loading @@ -212,15 +215,14 @@ ssize_t quic::sendPacket(const uint8_t* data, size_t len) { return -1; } for (int attempt = 0; attempt < 5; ++attempt) { buffer send_buf(reinterpret_cast<const char*>(data), len); try { size_t sent; if (_parent != nullptr) { // Child connection: shared unconnected parent socket, send to stored peer address sent = udp::sendTo(send_buf, MSG_DONTWAIT); } else { // Client connection: connected socket sent = udp::sendData(send_buf, MSG_DONTWAIT); } Loading @@ -228,11 +230,17 @@ ssize_t quic::sendPacket(const uint8_t* data, size_t len) { _last_activity = std::chrono::steady_clock::now(); } return static_cast<ssize_t>(sent); } catch (NetException&) { // EAGAIN/EWOULDBLOCK or other send failure } catch (NetException& e) { // EAGAIN/EWOULDBLOCK — retry after brief pause if (e.getErrorType() == NetException::Note && attempt < 4) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); continue; } return -1; } } return -1; } // ============================================================================ // Variable-length Integer Encoding/Decoding (RFC 9000 Section 16) Loading