tcp: rfc 2018: implemented SACK option
Change-Id: I269ad2682446c4d37bae21dcf9f3036de964ff2c Reviewed-on: https://review.haiku-os.org/c/haiku/+/53 Reviewed-by: Jérôme Duval <[email protected]>
This commit is contained in:
committed by
Jérôme Duval
parent
a6e1e7e941
commit
745830a01b
@@ -10,6 +10,7 @@
|
|||||||
#include "BufferQueue.h"
|
#include "BufferQueue.h"
|
||||||
|
|
||||||
#include <KernelExport.h>
|
#include <KernelExport.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
|
||||||
//#define TRACE_BUFFER_QUEUE
|
//#define TRACE_BUFFER_QUEUE
|
||||||
@@ -436,6 +437,41 @@ BufferQueue::SetPushPointer()
|
|||||||
fPushPointer = fList.Tail()->sequence + fList.Tail()->size;
|
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
|
#if DEBUG_TCP_BUFFER_QUEUE
|
||||||
|
|
||||||
/*! Perform a sanity check of the whole queue.
|
/*! Perform a sanity check of the whole queue.
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ public:
|
|||||||
|
|
||||||
inline size_t PushedData() const;
|
inline size_t PushedData() const;
|
||||||
void SetPushPointer();
|
void SetPushPointer();
|
||||||
|
int PopulateSackInfo(tcp_sequence sequence, int maxSackCount, tcp_sack* sacks);
|
||||||
|
|
||||||
size_t Used() const { return fNumBytes; }
|
size_t Used() const { return fNumBytes; }
|
||||||
inline size_t Free() const;
|
inline size_t Free() const;
|
||||||
|
|||||||
@@ -319,7 +319,8 @@ enum {
|
|||||||
FLAG_CLOSED = 0x08,
|
FLAG_CLOSED = 0x08,
|
||||||
FLAG_DELETE_ON_CLOSE = 0x10,
|
FLAG_DELETE_ON_CLOSE = 0x10,
|
||||||
FLAG_LOCAL = 0x20,
|
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),
|
fCongestionWindow(0),
|
||||||
fSlowStartThreshold(0),
|
fSlowStartThreshold(0),
|
||||||
fState(CLOSED),
|
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!
|
// TODO: to be replaced with a real read/write locking strategy!
|
||||||
mutex_init(&fLock, "tcp lock");
|
mutex_init(&fLock, "tcp lock");
|
||||||
@@ -1413,6 +1414,9 @@ TCPEndpoint::_PrepareReceivePath(tcp_segment_header& segment)
|
|||||||
fReceivedTimestamp = segment.timestamp_value;
|
fReceivedTimestamp = segment.timestamp_value;
|
||||||
} else
|
} else
|
||||||
fFlags &= ~FLAG_OPTION_TIMESTAMP;
|
fFlags &= ~FLAG_OPTION_TIMESTAMP;
|
||||||
|
|
||||||
|
if ((segment.options & TCP_SACK_PERMITTED) == 0)
|
||||||
|
fFlags &= ~FLAG_OPTION_SACK_PERMITTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fSendMaxSegmentSize > 2190)
|
if (fSendMaxSegmentSize > 2190)
|
||||||
@@ -2019,6 +2023,18 @@ TCPEndpoint::_SendQueued(bool force, uint32 sendWindow)
|
|||||||
segment.timestamp_value = tcp_now();
|
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
|
if ((segment.flags & TCP_FLAG_SYNCHRONIZE) != 0
|
||||||
&& fSendNext == fInitialSendSequence) {
|
&& fSendNext == fInitialSendSequence) {
|
||||||
// add connection establishment options
|
// add connection establishment options
|
||||||
@@ -2027,6 +2043,8 @@ TCPEndpoint::_SendQueued(bool force, uint32 sendWindow)
|
|||||||
segment.options |= TCP_HAS_WINDOW_SCALE;
|
segment.options |= TCP_HAS_WINDOW_SCALE;
|
||||||
segment.window_shift = fReceiveWindowShift;
|
segment.window_shift = fReceiveWindowShift;
|
||||||
}
|
}
|
||||||
|
if ((fFlags & FLAG_OPTION_SACK_PERMITTED) != 0)
|
||||||
|
segment.options |= TCP_SACK_PERMITTED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -139,10 +139,10 @@ add_options(tcp_segment_header &segment, uint8 *buffer, size_t bufferSize)
|
|||||||
bump_option(option, length);
|
bump_option(option, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (segment.sack_count > 0) {
|
if (segment.sackCount > 0) {
|
||||||
int sackCount = ((int)(bufferSize - length) - 4) / sizeof(tcp_sack);
|
int sackCount = ((int)(bufferSize - length) - 4) / sizeof(tcp_sack);
|
||||||
if (sackCount > segment.sack_count)
|
if (sackCount > segment.sackCount)
|
||||||
sackCount = segment.sack_count;
|
sackCount = segment.sackCount;
|
||||||
|
|
||||||
if (sackCount > 0) {
|
if (sackCount > 0) {
|
||||||
option->kind = TCP_OPTION_NOP;
|
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);
|
option->length = 2 + sackCount * sizeof(tcp_sack);
|
||||||
memcpy(option->sack, segment.sacks, sackCount * sizeof(tcp_sack));
|
memcpy(option->sack, segment.sacks, sackCount * sizeof(tcp_sack));
|
||||||
bump_option(option, length);
|
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)
|
if (option->length == 2 && size >= 2)
|
||||||
segment.options |= TCP_SACK_PERMITTED;
|
segment.options |= TCP_SACK_PERMITTED;
|
||||||
break;
|
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) {
|
if (length < 0) {
|
||||||
@@ -431,9 +444,9 @@ tcp_options_length(tcp_segment_header& segment)
|
|||||||
if (segment.options & TCP_SACK_PERMITTED)
|
if (segment.options & TCP_SACK_PERMITTED)
|
||||||
length += 2;
|
length += 2;
|
||||||
|
|
||||||
if (segment.sack_count > 0) {
|
if (segment.sackCount > 0) {
|
||||||
int sackCount = min_c((int)((kMaxOptionSize - length - 4)
|
int sackCount = min_c((int)((kMaxOptionSize - length - 4)
|
||||||
/ sizeof(tcp_sack)), segment.sack_count);
|
/ sizeof(tcp_sack)), segment.sackCount);
|
||||||
if (sackCount > 0)
|
if (sackCount > 0)
|
||||||
length += 4 + sackCount * sizeof(tcp_sack);
|
length += 4 + sackCount * sizeof(tcp_sack);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -231,6 +231,7 @@ enum {
|
|||||||
TCP_HAS_WINDOW_SCALE = 1 << 0,
|
TCP_HAS_WINDOW_SCALE = 1 << 0,
|
||||||
TCP_HAS_TIMESTAMPS = 1 << 1,
|
TCP_HAS_TIMESTAMPS = 1 << 1,
|
||||||
TCP_SACK_PERMITTED = 1 << 2,
|
TCP_SACK_PERMITTED = 1 << 2,
|
||||||
|
TCP_HAS_SACK = 1 << 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct tcp_segment_header {
|
struct tcp_segment_header {
|
||||||
@@ -239,7 +240,7 @@ struct tcp_segment_header {
|
|||||||
flags(_flags),
|
flags(_flags),
|
||||||
window_shift(0),
|
window_shift(0),
|
||||||
max_segment_size(0),
|
max_segment_size(0),
|
||||||
sack_count(0),
|
sackCount(0),
|
||||||
options(0)
|
options(0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@@ -255,7 +256,7 @@ struct tcp_segment_header {
|
|||||||
uint32 timestamp_reply;
|
uint32 timestamp_reply;
|
||||||
|
|
||||||
tcp_sack *sacks;
|
tcp_sack *sacks;
|
||||||
int sack_count;
|
int sackCount;
|
||||||
|
|
||||||
uint32 options;
|
uint32 options;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user