fix: fix TCP stack bugs

This commit is contained in:
2026-06-13 17:06:54 +02:00
parent 44f86a8b19
commit 25f9d6e401
2 changed files with 58 additions and 10 deletions
+1 -1
View File
@@ -12,4 +12,4 @@
#pragma once
#define MONTAUK_BUILD_NUMBER 13
#define MONTAUK_BUILD_NUMBER 21
+57 -9
View File
@@ -21,8 +21,8 @@ using namespace Kt;
namespace Net::Tcp {
// Receive buffer size per connection
static constexpr uint16_t RECV_BUFFER_SIZE = 4096;
static constexpr uint16_t WINDOW_SIZE = 4096;
static constexpr uint16_t RECV_BUFFER_SIZE = 32768;
static constexpr uint16_t WINDOW_SIZE = RECV_BUFFER_SIZE;
static constexpr uint32_t MAX_CONNECTIONS = 16;
static constexpr uint64_t RETRANSMIT_TIMEOUT_MS = 1000;
static constexpr int MAX_RETRANSMITS = 5;
@@ -120,7 +120,9 @@ namespace Net::Tcp {
hdr->AckNum = Htonl(conn->RecvNext);
hdr->DataOffset = (HEADER_SIZE / 4) << 4;
hdr->Flags = flags;
hdr->Window = Htons(WINDOW_SIZE);
// Advertise the actual remaining buffer space so the peer throttles
// itself instead of overrunning the ring.
hdr->Window = Htons((uint16_t)(RECV_BUFFER_SIZE - conn->RecvCount));
hdr->Checksum = 0;
hdr->UrgentPtr = 0;
@@ -287,17 +289,41 @@ namespace Net::Tcp {
notify = true;
}
if (payloadLen > 0 && seqNum == conn->RecvNext) {
RecvBufferWrite(conn, payload, payloadLen);
conn->RecvNext += payloadLen;
uint16_t accepted = 0;
bool inOrder = (seqNum == conn->RecvNext);
// Send ACK
if (payloadLen > 0 && inOrder) {
// Accept only what fits in the ring and ACK exactly that.
// ACKing dropped bytes loses them permanently (the peer
// never retransmits ACKed data), which corrupts the
// stream — fatal for TLS. Unaccepted bytes are simply
// retransmitted from RecvNext later.
uint16_t space = RECV_BUFFER_SIZE - conn->RecvCount;
accepted = payloadLen < space ? payloadLen : space;
if (accepted > 0) {
RecvBufferWrite(conn, payload, accepted);
conn->RecvNext += accepted;
}
SendSegment(conn, FLAG_ACK, nullptr, 0);
notify = true;
} else if (payloadLen > 0) {
// Out-of-order data: there is a gap before this segment.
// Drop it but send a duplicate ACK for the byte we are
// still waiting on, so the peer fast-retransmits the gap.
// Real networks reorder mid-stream bursts; without this
// the peer only recovers after a (slow) retransmit timeout.
SendSegment(conn, FLAG_ACK, nullptr, 0);
notify = true;
}
if (flags & FLAG_FIN) {
conn->RecvNext = seqNum + payloadLen + 1;
// Only accept the FIN when this segment is in order AND we
// consumed all of its payload — otherwise the FIN sits beyond
// a hole in the stream. Acknowledging it here would advance
// RecvNext past data we never received, silently truncating
// the stream (fatal for TLS). Leaving the connection
// Established makes the peer retransmit the gap and the FIN.
if ((flags & FLAG_FIN) && inOrder && accepted == payloadLen) {
conn->RecvNext += 1; // FIN occupies one sequence number
conn->CurrentState = State::CloseWait;
// Send ACK for the FIN
@@ -673,6 +699,9 @@ namespace Net::Tcp {
conn->Lock.Acquire();
if (conn->RecvCount > 0) {
bool wasNearFull =
(uint16_t)(RECV_BUFFER_SIZE - conn->RecvCount) < 2048;
uint16_t toRead = conn->RecvCount;
if (toRead > bufferSize) {
toRead = bufferSize;
@@ -683,9 +712,14 @@ namespace Net::Tcp {
conn->RecvHead = (conn->RecvHead + 1) % RECV_BUFFER_SIZE;
}
conn->RecvCount -= toRead;
bool windowReopened = wasNearFull &&
conn->CurrentState == State::Established;
conn->Lock.Release();
asm volatile("push %0; popfq" :: "r"(flags) : "memory");
if (windowReopened) {
SendSegment(conn, FLAG_ACK, nullptr, 0);
}
return toRead;
}
@@ -716,7 +750,13 @@ namespace Net::Tcp {
conn->Lock.Acquire();
int result;
bool windowReopened = false;
if (conn->RecvCount > 0) {
// If the advertised window was (nearly) closed, the peer is
// stalled waiting for it to reopen — announce the drain below.
bool wasNearFull =
(uint16_t)(RECV_BUFFER_SIZE - conn->RecvCount) < 2048;
uint16_t toRead = conn->RecvCount;
if (toRead > bufferSize) {
toRead = bufferSize;
@@ -728,6 +768,8 @@ namespace Net::Tcp {
}
conn->RecvCount -= toRead;
result = toRead;
windowReopened = wasNearFull &&
conn->CurrentState == State::Established;
} else if (conn->CurrentState == State::CloseWait ||
conn->CurrentState == State::Closed ||
conn->CurrentState == State::TimeWait) {
@@ -739,6 +781,12 @@ namespace Net::Tcp {
conn->Lock.Release();
asm volatile("push %0; popfq" :: "r"(flags) : "memory");
if (windowReopened) {
// Window-update ACK so the peer resumes without waiting for a
// zero-window probe.
SendSegment(conn, FLAG_ACK, nullptr, 0);
}
return result;
}