From 272e1a2f974e34f7d711063ac924e10d250524b7 Mon Sep 17 00:00:00 2001 From: A-star-ayush Date: Fri, 10 Nov 2017 01:46:37 +0530 Subject: [PATCH] tcp: fixed no response from window update, removed ideal timer The reason for the erratic behavior was that the tcp implementation silently drops window update messages after noting the update but without triggering any data send event. Before the new TCP patches were applied, the implementation relied on a retransmission timeout to trigger a send event after a window update. One of the new patches dealing with the ideal timer changed the semantic of the restransmit function call and caused the behavior witnessed. But a retransmission timeout is not the correct solution to window update. In fact a retransmission is not a desired effect of window update. So in the patch attached, I have changed the behavior of the implementation to immediately acknowledge the window update (along with data from SendQueue) and thus solving the problem of complete halt in data transmission. The patch also has the changes re-implemented that were reverted back but had nothing to do with the issue at hand. For the time being, I have also removed the "ideal timer" part from the patch (although it wasn't creating any conflict). I initially decided to implement the ideal timer using the same timer used for retransmission to avoid adding an additional timer. But as I have seen, it can be problematic. So I will be re-implementing the ideal timer and thus it was not included in this patch. Signed-off-by: Augustin Cavalier Fixes #13704. --- .../network/protocols/tcp/TCPEndpoint.cpp | 106 ++++++++++++------ .../network/protocols/tcp/TCPEndpoint.h | 1 + .../kernel/network/protocols/tcp/tcp.h | 2 + 3 files changed, 76 insertions(+), 33 deletions(-) diff --git a/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp b/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp index 49e85d286a..e3194da423 100644 --- a/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp +++ b/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -428,6 +429,7 @@ TCPEndpoint::TCPEndpoint(net_socket* socket) fSendWindow(0), fSendMaxWindow(0), fSendMaxSegmentSize(TCP_DEFAULT_MAX_SEGMENT_SIZE), + fSendMaxSegments(0), fSendQueue(socket->send.buffer_size), fInitialSendSequence(0), fPreviousHighestAcknowledge(0), @@ -1384,13 +1386,6 @@ TCPEndpoint::_AddData(tcp_segment_header& segment, net_buffer* buffer) if ((segment.flags & TCP_FLAG_PUSH) != 0) fReceiveQueue.SetPushPointer(); - if (fSendUnacknowledged == fSendMax) { - TRACE("data received, resetting ideal timer to: %" - B_PRIdBIGTIME, fRetransmitTimeout); - gStackModule->set_timer(&fRetransmitTimer, fRetransmitTimeout); - T(TimerSet(this, "ideal", fRetransmitTimeout)); - } - return fReceiveQueue.Available() > 0; } @@ -1426,7 +1421,14 @@ TCPEndpoint::_PrepareReceivePath(tcp_segment_header& segment) fFlags &= ~FLAG_OPTION_TIMESTAMP; } - fCongestionWindow = 2 * fSendMaxSegmentSize; + if (fSendMaxSegmentSize > 2190) + fCongestionWindow = 2 * fSendMaxSegmentSize; + else if (fSendMaxSegmentSize > 1095) + fCongestionWindow = 3 * fSendMaxSegmentSize; + else + fCongestionWindow = 4 * fSendMaxSegmentSize; + + fSendMaxSegments = fCongestionWindow / fSendMaxSegmentSize; fSlowStartThreshold = (uint32)segment.advertised_window << fSendWindowShift; } @@ -1709,6 +1711,9 @@ TCPEndpoint::_Receive(tcp_segment_header& segment, net_buffer* buffer) } #endif + if (advertisedWindow > fSendWindow) + action |= IMMEDIATE_ACKNOWLEDGE; + fSendWindow = advertisedWindow; if (advertisedWindow > fSendMaxWindow) fSendMaxWindow = advertisedWindow; @@ -1946,6 +1951,9 @@ inline bool TCPEndpoint::_ShouldSendSegment(tcp_segment_header& segment, uint32 length, uint32 segmentMaxSize, uint32 flightSize) { + if (fState == ESTABLISHED && fSendMaxSegments == 0) + return false; + if (length > 0) { // Avoid the silly window syndrome - we only send a segment in case: // - we have a full segment to send, or @@ -2168,6 +2176,9 @@ TCPEndpoint::_SendQueued(bool force, uint32 sendWindow) fReceiveMaxAdvertised = fReceiveNext + ((uint32)segment.advertised_window << fReceiveWindowShift); + if (segmentLength != 0 && fState == ESTABLISHED) + --fSendMaxSegments; + status = next->module->send_routed_data(next, fRoute, buffer); if (status < B_OK) { gBufferModule->free(buffer); @@ -2265,7 +2276,46 @@ TCPEndpoint::_Acknowledged(tcp_segment_header& segment) if (fSendUnacknowledged < segment.acknowledge) { fSendQueue.RemoveUntil(segment.acknowledge); + + uint32 bytesAcknowledged = segment.acknowledge - fSendUnacknowledged.Number(); + fPreviousHighestAcknowledge = fSendUnacknowledged; fSendUnacknowledged = segment.acknowledge; + + if (fPreviousHighestAcknowledge > fSendUnacknowledged) { + // need to update the recover variable upon a sequence wraparound + fRecover = segment.acknowledge - 1; + } + + // the acknowledgment of the SYN/ACK MUST NOT increase the size of the congestion window + if (fSendUnacknowledged != fInitialSendSequence) { + if (fCongestionWindow < fSlowStartThreshold) + fCongestionWindow += min_c(bytesAcknowledged, fSendMaxSegmentSize); + else { + uint32 increment = fSendMaxSegmentSize * fSendMaxSegmentSize; + + if (increment < fCongestionWindow) + increment = 1; + else + increment /= fCongestionWindow; + + fCongestionWindow += increment; + } + + fSendMaxSegments = UINT32_MAX; + } + + if ((fFlags & FLAG_RECOVERY) != 0) { + fSendNext = fSendUnacknowledged; + _SendQueued(); + fCongestionWindow -= bytesAcknowledged; + + if (bytesAcknowledged > fSendMaxSegmentSize) + fCongestionWindow += fSendMaxSegmentSize; + + fSendNext = fSendMax; + } else + fDuplicateAcknowledgeCount = 0; + if (fSendNext < fSendUnacknowledged) fSendNext = fSendUnacknowledged; @@ -2282,10 +2332,9 @@ TCPEndpoint::_Acknowledged(tcp_segment_header& segment) } if (fSendUnacknowledged == fSendMax) { - TRACE("all acknowledged, cancelling retransmission timer. Using it as ideal timer for: %" - B_PRIdBIGTIME, fRetransmitTimeout); - gStackModule->set_timer(&fRetransmitTimer, fRetransmitTimeout); - T(TimerSet(this, "ideal", fRetransmitTimeout)); + TRACE("all acknowledged, cancelling retransmission timer."); + gStackModule->cancel_timer(&fRetransmitTimer); + T(TimerSet(this, "retransmit", -1)); fSendTime = 0; } else { TRACE("data acknowledged, resetting retransmission timer to: %" @@ -2299,20 +2348,6 @@ TCPEndpoint::_Acknowledged(tcp_segment_header& segment) fSendCondition.NotifyAll(); gSocketModule->notify(socket, B_SELECT_WRITE, fSendQueue.Free()); } - - if (fCongestionWindow < fSlowStartThreshold) - fCongestionWindow += fSendMaxSegmentSize; - } - - if (fCongestionWindow >= fSlowStartThreshold) { - uint32 increment = fSendMaxSegmentSize * fSendMaxSegmentSize; - - if (increment < fCongestionWindow) - increment = 1; - else - increment /= fCongestionWindow; - - fCongestionWindow += increment; } // if there is data left to be sent, send it now @@ -2326,14 +2361,19 @@ TCPEndpoint::_Retransmit() { TRACE("Retransmit()"); - _ResetSlowStart(); + if (fState < ESTABLISHED) { + fRetransmitTimeout = TCP_SYN_RETRANSMIT_TIMEOUT; + fCongestionWindow = fSendMaxSegmentSize; + } else { + _ResetSlowStart(); + fDuplicateAcknowledgeCount = 0; + // Do exponential back off of the retransmit timeout + fRetransmitTimeout *= 2; + if (fRetransmitTimeout > TCP_MAX_RETRANSMIT_TIMEOUT) + fRetransmitTimeout = TCP_MAX_RETRANSMIT_TIMEOUT; + } + fSendNext = fSendUnacknowledged; - - // Do exponential back off of the retransmit timeout - fRetransmitTimeout *= 2; - if (fRetransmitTimeout > TCP_MAX_RETRANSMIT_TIMEOUT) - fRetransmitTimeout = TCP_MAX_RETRANSMIT_TIMEOUT; - _SendQueued(); fRecover = fSendNext.Number() - 1; diff --git a/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.h b/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.h index 22bf8597d7..6a62e07708 100644 --- a/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.h +++ b/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.h @@ -145,6 +145,7 @@ private: uint32 fSendWindow; uint32 fSendMaxWindow; uint32 fSendMaxSegmentSize; + uint32 fSendMaxSegments; BufferQueue fSendQueue; tcp_sequence fLastAcknowledgeSent; tcp_sequence fInitialSendSequence; diff --git a/src/add-ons/kernel/network/protocols/tcp/tcp.h b/src/add-ons/kernel/network/protocols/tcp/tcp.h index 6f30ec2943..24890a32aa 100644 --- a/src/add-ons/kernel/network/protocols/tcp/tcp.h +++ b/src/add-ons/kernel/network/protocols/tcp/tcp.h @@ -193,6 +193,8 @@ operator==(tcp_sequence a, tcp_sequence b) #define TCP_MIN_RETRANSMIT_TIMEOUT 200000 // 200 msecs // Maximum retransmit timeout (per RFC6298) #define TCP_MAX_RETRANSMIT_TIMEOUT 60000000 // 60 secs +// New value for timeout in case of lost SYN (RFC 6298) +#define TCP_SYN_RETRANSMIT_TIMEOUT 3000000 // 3 secs struct tcp_sack { uint32 left_edge;