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 <[email protected]> Reviewed-by: waddlesplash <[email protected]>
This commit is contained in:
committed by
waddlesplash
parent
c30e5cf106
commit
ac508fcb4a
@@ -1755,6 +1755,16 @@ TCPEndpoint::_Receive(tcp_segment_header& segment, net_buffer* buffer)
|
|||||||
return DROP;
|
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
|
if ((segment.flags & TCP_FLAG_SYNCHRONIZE) != 0
|
||||||
|| (fState == SYNCHRONIZE_RECEIVED
|
|| (fState == SYNCHRONIZE_RECEIVED
|
||||||
&& (fInitialReceiveSequence > segment.sequence
|
&& (fInitialReceiveSequence > segment.sequence
|
||||||
@@ -1979,7 +1989,7 @@ TCPEndpoint::_Receive(tcp_segment_header& segment, net_buffer* buffer)
|
|||||||
|
|
||||||
_UpdateTimestamps(segment, segmentLength);
|
_UpdateTimestamps(segment, segmentLength);
|
||||||
|
|
||||||
TRACE("Receive() Action %" B_PRId32, action);
|
TRACE("Receive() Action 0x%" B_PRIx32, action);
|
||||||
|
|
||||||
return action;
|
return action;
|
||||||
}
|
}
|
||||||
@@ -2031,6 +2041,13 @@ TCPEndpoint::SegmentReceived(tcp_segment_header& segment, net_buffer* buffer)
|
|||||||
if (segmentAction & SEND_QUEUED)
|
if (segmentAction & SEND_QUEUED)
|
||||||
_SendQueued();
|
_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))
|
if ((fFlags & (FLAG_CLOSED | FLAG_DELETE_ON_CLOSE))
|
||||||
== (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. */
|
/*! Sends one or more TCP segments with the data waiting in the queue. */
|
||||||
status_t
|
status_t
|
||||||
TCPEndpoint::_SendQueued(bool force)
|
TCPEndpoint::_SendQueued(bool force)
|
||||||
@@ -2328,12 +2368,15 @@ TCPEndpoint::_SendQueued(bool force)
|
|||||||
|
|
||||||
if (consumedWindow > sendWindow) {
|
if (consumedWindow > sendWindow) {
|
||||||
sendWindow = 0;
|
sendWindow = 0;
|
||||||
// TODO: enter persist state? try to get a window update.
|
|
||||||
} else
|
} else
|
||||||
sendWindow -= consumedWindow;
|
sendWindow -= consumedWindow;
|
||||||
|
|
||||||
uint32 length = min_c(fSendQueue.Available(fSendNext), sendWindow);
|
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.
|
// Nothing to send.
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ private:
|
|||||||
status_t _PrepareAndSend(tcp_segment_header& segment, net_buffer* buffer,
|
status_t _PrepareAndSend(tcp_segment_header& segment, net_buffer* buffer,
|
||||||
bool isRetransmit);
|
bool isRetransmit);
|
||||||
status_t _SendAcknowledge(bool force = false);
|
status_t _SendAcknowledge(bool force = false);
|
||||||
|
status_t _SendReset(bool force = false);
|
||||||
status_t _SendQueued(bool force = false);
|
status_t _SendQueued(bool force = false);
|
||||||
|
|
||||||
status_t _Disconnect(bool closing);
|
status_t _Disconnect(bool closing);
|
||||||
|
|||||||
Reference in New Issue
Block a user