From f2893088ba25b514acf67d0fdf0d965d6aa4b317 Mon Sep 17 00:00:00 2001 From: Hugo Santos Date: Thu, 5 Apr 2007 06:56:58 +0000 Subject: [PATCH] fixed another TCP issue: if we were in TIME_WAIT and we received a retransmission or delayed ack, TCP would wrongly bindly immediatly acknowledge. * Moved TIME_WAIT and CLOSED handling to common Receive() path that does Sequence checking and further tests. * Moved setting FLAG_NO_RECEIVE to the end of Receive() when FIN is set so we can check for NO_RECEIVE before processing the segment text just after processing FIN. * Added a new action DELETE to be used when in TIME_WAIT and we receive a good RST git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20572 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../network/protocols/tcp/TCPEndpoint.cpp | 33 ++++++++++++++----- .../kernel/network/protocols/tcp/tcp.cpp | 10 +++--- .../kernel/network/protocols/tcp/tcp.h | 1 + 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp b/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp index ede57c2f93..56e611affd 100644 --- a/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp +++ b/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp @@ -203,6 +203,7 @@ TCPEndpoint::Free() if (fState <= SYNCHRONIZE_SENT || fState == TIME_WAIT) return B_OK; + // we are only interested in the timer, not in changing state _EnterTimeWait(); return B_BUSY; // we'll be freed later when the 2MSL timer expires @@ -821,13 +822,26 @@ TCPEndpoint::Receive(tcp_segment_header &segment, net_buffer *buffer) // is this a valid reset? if (fLastAcknowledgeSent <= segment.sequence && tcp_sequence(segment.sequence) < fLastAcknowledgeSent + fReceiveWindow) { - // TODO: close connection depending on state + if (fState == SYNCHRONIZE_RECEIVED) { + // TODO: if we came from SYN-SENT signal connection refused + // and remove all segments from tx queue + } else if (fState == ESTABLISHED || fState == FINISH_SENT + || fState == FINISH_RECEIVED || fState == FINISH_ACKNOWLEDGED) { + // TODO: RFC 793 states that on ESTABLISHED, FIN-WAIT{1,2} + // or CLOSE-WAIT "All segment queues should be + // flushed". + } + + if (fState != TIME_WAIT && fReceiveQueue.Available() > 0) { + release_sem_etc(fReceiveLock, 1, B_DO_NOT_RESCHEDULE); + // TODO: real conditional locking needed! + gSocketModule->notify(socket, B_SELECT_READ, 1); + } else { + return DELETE | DROP; + } + fError = ECONNREFUSED; fState = CLOSED; - - release_sem_etc(fReceiveLock, 1, B_DO_NOT_RESCHEDULE); - // TODO: real conditional locking needed! - gSocketModule->notify(socket, B_SELECT_READ, fReceiveQueue.Available()); } return DROP; @@ -904,7 +918,8 @@ TCPEndpoint::Receive(tcp_segment_header &segment, net_buffer *buffer) // TODO: real conditional locking needed! gSocketModule->notify(socket, B_SELECT_READ, fReceiveQueue.Available()); } - if (fSendMax < segment.acknowledge) + + if (fSendMax < segment.acknowledge || fState == TIME_WAIT) return DROP | IMMEDIATE_ACKNOWLEDGE; if (fSendUnacknowledged >= segment.acknowledge) { // this is a duplicate acknowledge @@ -977,7 +992,6 @@ TCPEndpoint::Receive(tcp_segment_header &segment, net_buffer *buffer) if (segment.flags & TCP_FLAG_FINISH) { TRACE("Receive(): peer is finishing connection!"); fReceiveNext++; - fFlags |= FLAG_NO_RECEIVE; release_sem_etc(fReceiveLock, 1, B_DO_NOT_RESCHEDULE); // TODO: real conditional locking needed! @@ -1015,7 +1029,7 @@ TCPEndpoint::Receive(tcp_segment_header &segment, net_buffer *buffer) // state machine is done switching states and the data is good. // put it in the receive buffer - if (buffer->size > 0) { + if (buffer->size > 0 && (fFlags & FLAG_NO_RECEIVE) == 0) { fReceiveQueue.Add(buffer, segment.sequence); fReceiveNext += buffer->size; TRACE("Receive(): adding data, receive next = %lu!", (uint32)fReceiveNext); @@ -1029,6 +1043,9 @@ TCPEndpoint::Receive(tcp_segment_header &segment, net_buffer *buffer) if (segment.flags & TCP_FLAG_PUSH) fReceiveQueue.SetPushPointer(fReceiveQueue.LastSequence()); + if (segment.flags & TCP_FLAG_FINISH) + fFlags |= FLAG_NO_RECEIVE; + return action; } diff --git a/src/add-ons/kernel/network/protocols/tcp/tcp.cpp b/src/add-ons/kernel/network/protocols/tcp/tcp.cpp index 017d41cab3..40376826bf 100644 --- a/src/add-ons/kernel/network/protocols/tcp/tcp.cpp +++ b/src/add-ons/kernel/network/protocols/tcp/tcp.cpp @@ -555,12 +555,6 @@ tcp_receive_data(net_buffer *buffer) TRACE(("Endpoint %p in state %s\n", endpoint, name_for_state(endpoint->State()))); switch (endpoint->State()) { - case TIME_WAIT: - segmentAction |= IMMEDIATE_ACKNOWLEDGE; - case CLOSED: - endpoint->UpdateTimeWait(); - break; - case LISTEN: segmentAction = endpoint->ListenReceive(segment, buffer); break; @@ -576,6 +570,8 @@ tcp_receive_data(net_buffer *buffer) case FINISH_SENT: case FINISH_ACKNOWLEDGED: case CLOSING: + case TIME_WAIT: + case CLOSED: segmentAction = endpoint->Receive(segment, buffer); break; } @@ -585,6 +581,8 @@ tcp_receive_data(net_buffer *buffer) endpoint->SendAcknowledge(); else if (segmentAction & ACKNOWLEDGE) endpoint->DelayedAcknowledge(); + else if (segmentAction & DELETE) + gSocketModule->delete_socket(endpoint->socket); } else if ((segment.flags & TCP_FLAG_RESET) == 0) segmentAction = DROP | RESET; diff --git a/src/add-ons/kernel/network/protocols/tcp/tcp.h b/src/add-ons/kernel/network/protocols/tcp/tcp.h index df2d775fc5..67229f1423 100644 --- a/src/add-ons/kernel/network/protocols/tcp/tcp.h +++ b/src/add-ons/kernel/network/protocols/tcp/tcp.h @@ -153,6 +153,7 @@ enum tcp_segment_action { RESET = 0x02, ACKNOWLEDGE = 0x04, IMMEDIATE_ACKNOWLEDGE = 0x08, + DELETE = 0x10, };