From ac508fcb4a0f89b429a2c3b439e1b0a7c946c9c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Duval?= Date: Fri, 9 Jan 2026 21:25:36 +0100 Subject: [PATCH] tcp: _Receive: reset connection when received another ack packet while finish acknowledged and zero receive window. * SendQueued: try to get window update when send window is zero. * Golang test TestServerGracefulClose now passes. tcp_receive_data generates a RST packet with a random sequence, etc. with _SendReset, the actual connection will be used to generate the segment information, like Linux, ie: 18:07:17.657812 IP localhost.localdomain.40009 > localhost.localdomain.40034: Flags [R.], seq 184, ack 69632, win 0, options [nop,nop,TS val 34750 ecr 33649], length 0 instead of: 18:10:50.816193 IP localhost.localdomain.40051 > localhost.localdomain.40062: Flags [R], seq 1736711, win 0, length 0 Change-Id: Ib621605f74cb6d1cbc2add050bcddbf471402254 Reviewed-on: https://review.haiku-os.org/c/haiku/+/10223 Tested-by: Commit checker robot Reviewed-by: waddlesplash --- .../network/protocols/tcp/TCPEndpoint.cpp | 49 +++++++++++++++++-- .../network/protocols/tcp/TCPEndpoint.h | 1 + 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp b/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp index 88c4bf53fc..02eaadfcf9 100644 --- a/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp +++ b/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp @@ -1755,6 +1755,16 @@ TCPEndpoint::_Receive(tcp_segment_header& segment, net_buffer* buffer) return DROP; } + if (fState == FINISH_ACKNOWLEDGED + && segment.AcknowledgeOnly() + && (fReceiveMaxAdvertised - fReceiveNext).Number() == 0 + && segmentLength == 0 + && segment.acknowledge == fSendUnacknowledged) { + // reset the connection - received another ack packet + // while finish acknowledged and zero receive window + return DROP | RESET; + } + if ((segment.flags & TCP_FLAG_SYNCHRONIZE) != 0 || (fState == SYNCHRONIZE_RECEIVED && (fInitialReceiveSequence > segment.sequence @@ -1979,7 +1989,7 @@ TCPEndpoint::_Receive(tcp_segment_header& segment, net_buffer* buffer) _UpdateTimestamps(segment, segmentLength); - TRACE("Receive() Action %" B_PRId32, action); + TRACE("Receive() Action 0x%" B_PRIx32, action); return action; } @@ -2031,6 +2041,13 @@ TCPEndpoint::SegmentReceived(tcp_segment_header& segment, net_buffer* buffer) if (segmentAction & SEND_QUEUED) _SendQueued(); + // handle RESET action separately to use actual connection + // to generate the segment information + if ((segmentAction & RESET) != 0 && _SendReset(true) == B_OK) { + fState = CLOSED; + segmentAction &= ~RESET; + } + if ((fFlags & (FLAG_CLOSED | FLAG_DELETE_ON_CLOSE)) == (FLAG_CLOSED | FLAG_DELETE_ON_CLOSE)) { @@ -2296,6 +2313,29 @@ TCPEndpoint::_SendAcknowledge(bool force) } +/*! Sends a RST with segment information related to the connection. */ +status_t +TCPEndpoint::_SendReset(bool force) +{ + if (fRoute == NULL || fState == LISTEN) + return B_ERROR; + + tcp_segment_header segment = _PrepareSendSegment(); + + // Is there actually anything to do? + if (!force && (fState != FINISH_ACKNOWLEDGED || (fFlags & FLAG_CLOSED) == 0)) + return B_OK; + + segment.flags |= TCP_FLAG_RESET; + + net_buffer* buffer = gBufferModule->create(256); + if (buffer == NULL) + return B_NO_MEMORY; + + return _PrepareAndSend(segment, buffer, false); +} + + /*! Sends one or more TCP segments with the data waiting in the queue. */ status_t TCPEndpoint::_SendQueued(bool force) @@ -2328,12 +2368,15 @@ TCPEndpoint::_SendQueued(bool force) if (consumedWindow > sendWindow) { sendWindow = 0; - // TODO: enter persist state? try to get a window update. } else sendWindow -= consumedWindow; uint32 length = min_c(fSendQueue.Available(fSendNext), sendWindow); - if (length == 0 && !state_needs_finish(fState)) { + if (!force && length == 0 && !state_needs_finish(fState)) { + // try to get a window update + if (sendWindow == 0 && !gStackModule->is_timer_active(&fPersistTimer)) + _StartPersistTimer(); + // Nothing to send. return B_OK; } diff --git a/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.h b/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.h index f9ade7821d..5bbdef3a6b 100644 --- a/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.h +++ b/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.h @@ -83,6 +83,7 @@ private: status_t _PrepareAndSend(tcp_segment_header& segment, net_buffer* buffer, bool isRetransmit); status_t _SendAcknowledge(bool force = false); + status_t _SendReset(bool force = false); status_t _SendQueued(bool force = false); status_t _Disconnect(bool closing);