diff --git a/src/add-ons/kernel/network/protocols/tcp/BufferQueue.cpp b/src/add-ons/kernel/network/protocols/tcp/BufferQueue.cpp index 4d5ada0a27..73d65b802f 100644 --- a/src/add-ons/kernel/network/protocols/tcp/BufferQueue.cpp +++ b/src/add-ons/kernel/network/protocols/tcp/BufferQueue.cpp @@ -10,6 +10,7 @@ #include "BufferQueue.h" #include +#include //#define TRACE_BUFFER_QUEUE @@ -436,6 +437,41 @@ BufferQueue::SetPushPointer() fPushPointer = fList.Tail()->sequence + fList.Tail()->size; } + +int +BufferQueue::PopulateSackInfo(tcp_sequence sequence, int maxSackCount, tcp_sack* sacks) +{ + SegmentList::ReverseIterator iterator = fList.GetReverseIterator(); + net_buffer* buffer = iterator.Next(); + + int sackCount = 0; + while (buffer != NULL && buffer->sequence > sequence) { + if (buffer->sequence + buffer->size < sacks[sackCount].left_edge) { + if (sackCount + 1 == maxSackCount) + break; + ++sackCount; + sacks[sackCount].left_edge = buffer->sequence; + sacks[sackCount].right_edge = buffer->sequence + buffer->size; + } else { + sacks[sackCount].left_edge = buffer->sequence; + if (sacks[sackCount].right_edge == 0) + sacks[sackCount].right_edge = buffer->sequence + buffer->size; + } + + buffer = iterator.Next(); + } + + if (sacks[0].left_edge != 0) { + for (int i = 0; i <= sackCount; ++i) { + sacks[i].left_edge = htonl(sacks[i].left_edge); + sacks[i].right_edge = htonl(sacks[i].right_edge); + } + ++sackCount; + } + + return sackCount; +} + #if DEBUG_TCP_BUFFER_QUEUE /*! Perform a sanity check of the whole queue. diff --git a/src/add-ons/kernel/network/protocols/tcp/BufferQueue.h b/src/add-ons/kernel/network/protocols/tcp/BufferQueue.h index f428ae921b..f9f3ef9617 100644 --- a/src/add-ons/kernel/network/protocols/tcp/BufferQueue.h +++ b/src/add-ons/kernel/network/protocols/tcp/BufferQueue.h @@ -38,6 +38,7 @@ public: inline size_t PushedData() const; void SetPushPointer(); + int PopulateSackInfo(tcp_sequence sequence, int maxSackCount, tcp_sack* sacks); size_t Used() const { return fNumBytes; } inline size_t Free() const; diff --git a/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp b/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp index 5e69336787..0cd396d617 100644 --- a/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp +++ b/src/add-ons/kernel/network/protocols/tcp/TCPEndpoint.cpp @@ -319,7 +319,8 @@ enum { FLAG_CLOSED = 0x08, FLAG_DELETE_ON_CLOSE = 0x10, FLAG_LOCAL = 0x20, - FLAG_RECOVERY = 0x40 + FLAG_RECOVERY = 0x40, + FLAG_OPTION_SACK_PERMITTED = 0x80, }; @@ -451,7 +452,7 @@ TCPEndpoint::TCPEndpoint(net_socket* socket) fCongestionWindow(0), fSlowStartThreshold(0), fState(CLOSED), - fFlags(FLAG_OPTION_WINDOW_SCALE | FLAG_OPTION_TIMESTAMP) + fFlags(FLAG_OPTION_WINDOW_SCALE | FLAG_OPTION_TIMESTAMP | FLAG_OPTION_SACK_PERMITTED) { // TODO: to be replaced with a real read/write locking strategy! mutex_init(&fLock, "tcp lock"); @@ -1413,6 +1414,9 @@ TCPEndpoint::_PrepareReceivePath(tcp_segment_header& segment) fReceivedTimestamp = segment.timestamp_value; } else fFlags &= ~FLAG_OPTION_TIMESTAMP; + + if ((segment.options & TCP_SACK_PERMITTED) == 0) + fFlags &= ~FLAG_OPTION_SACK_PERMITTED; } if (fSendMaxSegmentSize > 2190) @@ -2019,6 +2023,18 @@ TCPEndpoint::_SendQueued(bool force, uint32 sendWindow) segment.timestamp_value = tcp_now(); } + // SACK information is embedded with duplicate acknowledgements + if (!fReceiveQueue.IsContiguous() && fLastAcknowledgeSent == fReceiveNext + && (fFlags & FLAG_OPTION_SACK_PERMITTED) != 0) { + segment.options |= TCP_HAS_SACK; + int maxSackCount = 4 - ((fFlags & FLAG_OPTION_TIMESTAMP) != 0); + segment.sacks = (tcp_sack*)calloc(maxSackCount, sizeof(tcp_sack)); + if (segment.sacks != NULL) + segment.sackCount = fReceiveQueue.PopulateSackInfo(fReceiveNext, maxSackCount, segment.sacks); + else + segment.sackCount = 0; + } + if ((segment.flags & TCP_FLAG_SYNCHRONIZE) != 0 && fSendNext == fInitialSendSequence) { // add connection establishment options @@ -2027,6 +2043,8 @@ TCPEndpoint::_SendQueued(bool force, uint32 sendWindow) segment.options |= TCP_HAS_WINDOW_SCALE; segment.window_shift = fReceiveWindowShift; } + if ((fFlags & FLAG_OPTION_SACK_PERMITTED) != 0) + segment.options |= TCP_SACK_PERMITTED; } } diff --git a/src/add-ons/kernel/network/protocols/tcp/tcp.cpp b/src/add-ons/kernel/network/protocols/tcp/tcp.cpp index e0dcbe2e83..dabd26becf 100644 --- a/src/add-ons/kernel/network/protocols/tcp/tcp.cpp +++ b/src/add-ons/kernel/network/protocols/tcp/tcp.cpp @@ -139,10 +139,10 @@ add_options(tcp_segment_header &segment, uint8 *buffer, size_t bufferSize) bump_option(option, length); } - if (segment.sack_count > 0) { + if (segment.sackCount > 0) { int sackCount = ((int)(bufferSize - length) - 4) / sizeof(tcp_sack); - if (sackCount > segment.sack_count) - sackCount = segment.sack_count; + if (sackCount > segment.sackCount) + sackCount = segment.sackCount; if (sackCount > 0) { option->kind = TCP_OPTION_NOP; @@ -153,6 +153,7 @@ add_options(tcp_segment_header &segment, uint8 *buffer, size_t bufferSize) option->length = 2 + sackCount * sizeof(tcp_sack); memcpy(option->sack, segment.sacks, sackCount * sizeof(tcp_sack)); bump_option(option, length); + free(segment.sacks); } } @@ -217,6 +218,18 @@ process_options(tcp_segment_header &segment, net_buffer *buffer, size_t size) if (option->length == 2 && size >= 2) segment.options |= TCP_SACK_PERMITTED; break; + case TCP_OPTION_SACK: + if (size >= option->length) { + segment.options |= TCP_HAS_SACK; + segment.sackCount = (option->length - 2) / sizeof(tcp_sack); + segment.sacks = option->sack; + + for(int i = 0; i < segment.sackCount; ++i) { + segment.sacks[i].left_edge = ntohl(segment.sacks[i].left_edge); + segment.sacks[i].right_edge = ntohl(segment.sacks[i].right_edge); + } + } + break; } if (length < 0) { @@ -431,9 +444,9 @@ tcp_options_length(tcp_segment_header& segment) if (segment.options & TCP_SACK_PERMITTED) length += 2; - if (segment.sack_count > 0) { + if (segment.sackCount > 0) { int sackCount = min_c((int)((kMaxOptionSize - length - 4) - / sizeof(tcp_sack)), segment.sack_count); + / sizeof(tcp_sack)), segment.sackCount); if (sackCount > 0) length += 4 + sackCount * sizeof(tcp_sack); } diff --git a/src/add-ons/kernel/network/protocols/tcp/tcp.h b/src/add-ons/kernel/network/protocols/tcp/tcp.h index f261f11da0..057462350a 100644 --- a/src/add-ons/kernel/network/protocols/tcp/tcp.h +++ b/src/add-ons/kernel/network/protocols/tcp/tcp.h @@ -231,6 +231,7 @@ enum { TCP_HAS_WINDOW_SCALE = 1 << 0, TCP_HAS_TIMESTAMPS = 1 << 1, TCP_SACK_PERMITTED = 1 << 2, + TCP_HAS_SACK = 1 << 3, }; struct tcp_segment_header { @@ -239,7 +240,7 @@ struct tcp_segment_header { flags(_flags), window_shift(0), max_segment_size(0), - sack_count(0), + sackCount(0), options(0) {} @@ -255,7 +256,7 @@ struct tcp_segment_header { uint32 timestamp_reply; tcp_sack *sacks; - int sack_count; + int sackCount; uint32 options;