TCP: added SACK definitions and option processing.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20775 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -1061,7 +1061,7 @@ TCPEndpoint::_SendQueued(bool force)
|
|||||||
|
|
||||||
if ((fOptions & TCP_NOOPT) == 0) {
|
if ((fOptions & TCP_NOOPT) == 0) {
|
||||||
if (fFlags & FLAG_OPTION_TIMESTAMP) {
|
if (fFlags & FLAG_OPTION_TIMESTAMP) {
|
||||||
segment.has_timestamps = true;
|
segment.options |= TCP_HAS_TIMESTAMPS;
|
||||||
segment.TSecr = fReceivedTSval;
|
segment.TSecr = fReceivedTSval;
|
||||||
segment.TSval = htonl(tcp_now());
|
segment.TSval = htonl(tcp_now());
|
||||||
}
|
}
|
||||||
@@ -1071,7 +1071,7 @@ TCPEndpoint::_SendQueued(bool force)
|
|||||||
// add connection establishment options
|
// add connection establishment options
|
||||||
segment.max_segment_size = fReceiveMaxSegmentSize;
|
segment.max_segment_size = fReceiveMaxSegmentSize;
|
||||||
if (fFlags & FLAG_OPTION_WINDOW_SCALE) {
|
if (fFlags & FLAG_OPTION_WINDOW_SCALE) {
|
||||||
segment.has_window_shift = true;
|
segment.options |= TCP_HAS_WINDOW_SCALE;
|
||||||
segment.window_shift = fReceiveWindowShift;
|
segment.window_shift = fReceiveWindowShift;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1185,7 +1185,7 @@ TCPEndpoint::_SendQueued(bool force)
|
|||||||
// for local connections as the answer is directly handled
|
// for local connections as the answer is directly handled
|
||||||
|
|
||||||
if (segment.flags & TCP_FLAG_SYNCHRONIZE) {
|
if (segment.flags & TCP_FLAG_SYNCHRONIZE) {
|
||||||
segment.has_window_shift = false;
|
segment.options &= ~TCP_HAS_WINDOW_SCALE;
|
||||||
segment.max_segment_size = 0;
|
segment.max_segment_size = 0;
|
||||||
size++;
|
size++;
|
||||||
}
|
}
|
||||||
@@ -1583,7 +1583,7 @@ TCPEndpoint::_PrepareReceivePath(tcp_segment_header &segment)
|
|||||||
if (segment.max_segment_size > 0)
|
if (segment.max_segment_size > 0)
|
||||||
fSendMaxSegmentSize = segment.max_segment_size;
|
fSendMaxSegmentSize = segment.max_segment_size;
|
||||||
|
|
||||||
if (segment.has_window_shift) {
|
if (segment.options & TCP_HAS_WINDOW_SCALE) {
|
||||||
fFlags |= FLAG_OPTION_WINDOW_SCALE;
|
fFlags |= FLAG_OPTION_WINDOW_SCALE;
|
||||||
fSendWindowShift = segment.window_shift;
|
fSendWindowShift = segment.window_shift;
|
||||||
} else {
|
} else {
|
||||||
@@ -1591,7 +1591,7 @@ TCPEndpoint::_PrepareReceivePath(tcp_segment_header &segment)
|
|||||||
fReceiveWindowShift = 0;
|
fReceiveWindowShift = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (segment.has_timestamps) {
|
if (segment.options & TCP_HAS_TIMESTAMPS) {
|
||||||
fFlags |= FLAG_OPTION_TIMESTAMP;
|
fFlags |= FLAG_OPTION_TIMESTAMP;
|
||||||
fReceivedTSval = segment.TSval;
|
fReceivedTSval = segment.TSval;
|
||||||
} else
|
} else
|
||||||
@@ -1654,7 +1654,7 @@ TCPEndpoint::_Acknowledged(tcp_segment_header &segment)
|
|||||||
if (fSendUnacknowledged == fSendMax)
|
if (fSendUnacknowledged == fSendMax)
|
||||||
gStackModule->cancel_timer(&fRetransmitTimer);
|
gStackModule->cancel_timer(&fRetransmitTimer);
|
||||||
|
|
||||||
if (segment.has_timestamps)
|
if (segment.options & TCP_HAS_TIMESTAMPS)
|
||||||
_UpdateSRTT(tcp_diff_timestamp(ntohl(segment.TSecr)));
|
_UpdateSRTT(tcp_diff_timestamp(ntohl(segment.TSecr)));
|
||||||
else {
|
else {
|
||||||
// TODO Fallback to RFC 793 type estimation
|
// TODO Fallback to RFC 793 type estimation
|
||||||
|
|||||||
@@ -57,6 +57,10 @@ static recursive_lock sEndpointManagersLock;
|
|||||||
static EndpointManagerList sEndpointManagers;
|
static EndpointManagerList sEndpointManagers;
|
||||||
|
|
||||||
|
|
||||||
|
// The TCP header length is at most 64 bytes.
|
||||||
|
static const int kMaxOptionSize = 64 - sizeof(tcp_header);
|
||||||
|
|
||||||
|
|
||||||
static EndpointManager *
|
static EndpointManager *
|
||||||
endpoint_manager_for(net_domain *domain)
|
endpoint_manager_for(net_domain *domain)
|
||||||
{
|
{
|
||||||
@@ -114,14 +118,15 @@ add_options(tcp_segment_header &segment, uint8 *buffer, size_t bufferSize)
|
|||||||
tcp_option *option = (tcp_option *)buffer;
|
tcp_option *option = (tcp_option *)buffer;
|
||||||
size_t length = 0;
|
size_t length = 0;
|
||||||
|
|
||||||
if (segment.max_segment_size > 0 && length + 8 < bufferSize) {
|
if (segment.max_segment_size > 0 && length + 8 <= bufferSize) {
|
||||||
option->kind = TCP_OPTION_MAX_SEGMENT_SIZE;
|
option->kind = TCP_OPTION_MAX_SEGMENT_SIZE;
|
||||||
option->length = 4;
|
option->length = 4;
|
||||||
option->max_segment_size = htons(segment.max_segment_size);
|
option->max_segment_size = htons(segment.max_segment_size);
|
||||||
bump_option(option, length);
|
bump_option(option, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (segment.has_timestamps && length + 12 < bufferSize) {
|
if ((segment.options & TCP_HAS_TIMESTAMPS)
|
||||||
|
&& length + 12 <= bufferSize) {
|
||||||
// two NOPs so the timestamps get aligned to a 4 byte boundary
|
// two NOPs so the timestamps get aligned to a 4 byte boundary
|
||||||
option->kind = TCP_OPTION_NOP;
|
option->kind = TCP_OPTION_NOP;
|
||||||
bump_option(option, length);
|
bump_option(option, length);
|
||||||
@@ -135,7 +140,8 @@ add_options(tcp_segment_header &segment, uint8 *buffer, size_t bufferSize)
|
|||||||
bump_option(option, length);
|
bump_option(option, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (segment.has_window_shift && length + 4 < bufferSize) {
|
if ((segment.options & TCP_HAS_WINDOW_SCALE)
|
||||||
|
&& length + 4 <= bufferSize) {
|
||||||
// insert one NOP so that the subsequent data is aligned on a 4 byte boundary
|
// insert one NOP so that the subsequent data is aligned on a 4 byte boundary
|
||||||
option->kind = TCP_OPTION_NOP;
|
option->kind = TCP_OPTION_NOP;
|
||||||
bump_option(option, length);
|
bump_option(option, length);
|
||||||
@@ -146,6 +152,30 @@ add_options(tcp_segment_header &segment, uint8 *buffer, size_t bufferSize)
|
|||||||
bump_option(option, length);
|
bump_option(option, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((segment.options & TCP_SACK_PERMITTED)
|
||||||
|
&& length + 2 <= bufferSize) {
|
||||||
|
option->kind = TCP_OPTION_SACK_PERMITTED;
|
||||||
|
option->length = 2;
|
||||||
|
bump_option(option, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (segment.sack_count > 0) {
|
||||||
|
int sackCount = ((int)(bufferSize - length) - 4) / sizeof(tcp_sack);
|
||||||
|
if (sackCount > segment.sack_count)
|
||||||
|
sackCount = segment.sack_count;
|
||||||
|
|
||||||
|
if (sackCount > 0) {
|
||||||
|
option->kind = TCP_OPTION_NOP;
|
||||||
|
bump_option(option, length);
|
||||||
|
option->kind = TCP_OPTION_NOP;
|
||||||
|
bump_option(option, length);
|
||||||
|
option->kind = TCP_OPTION_SACK;
|
||||||
|
option->length = 2 + sackCount * sizeof(tcp_sack);
|
||||||
|
memcpy(option->sack, segment.sacks, sackCount * sizeof(tcp_sack));
|
||||||
|
bump_option(option, length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ((length & 3) == 0) {
|
if ((length & 3) == 0) {
|
||||||
// options completely fill out the option space
|
// options completely fill out the option space
|
||||||
return length;
|
return length;
|
||||||
@@ -167,7 +197,7 @@ add_tcp_header(net_address_module_info *addressModule,
|
|||||||
{
|
{
|
||||||
buffer->protocol = IPPROTO_TCP;
|
buffer->protocol = IPPROTO_TCP;
|
||||||
|
|
||||||
uint8 optionsBuffer[32];
|
uint8 optionsBuffer[kMaxOptionSize];
|
||||||
uint32 optionsLength = add_options(segment, optionsBuffer, sizeof(optionsBuffer));
|
uint32 optionsLength = add_options(segment, optionsBuffer, sizeof(optionsBuffer));
|
||||||
|
|
||||||
NetBufferPrepend<tcp_header> bufferHeader(buffer, sizeof(tcp_header) + optionsLength);
|
NetBufferPrepend<tcp_header> bufferHeader(buffer, sizeof(tcp_header) + optionsLength);
|
||||||
@@ -213,12 +243,22 @@ tcp_options_length(tcp_segment_header &segment)
|
|||||||
if (segment.max_segment_size > 0)
|
if (segment.max_segment_size > 0)
|
||||||
length += 4;
|
length += 4;
|
||||||
|
|
||||||
if (segment.has_timestamps)
|
if (segment.options & TCP_HAS_TIMESTAMPS)
|
||||||
length += 12;
|
length += 12;
|
||||||
|
|
||||||
if (segment.has_window_shift)
|
if (segment.options & TCP_HAS_WINDOW_SCALE)
|
||||||
length += 4;
|
length += 4;
|
||||||
|
|
||||||
|
if (segment.options & TCP_SACK_PERMITTED)
|
||||||
|
length += 2;
|
||||||
|
|
||||||
|
if (segment.sack_count > 0) {
|
||||||
|
int sackCount = min_c((int)((kMaxOptionSize - length - 4)
|
||||||
|
/ sizeof(tcp_sack)), segment.sack_count);
|
||||||
|
if (sackCount > 0)
|
||||||
|
length += 4 + sackCount * sizeof(tcp_sack);
|
||||||
|
}
|
||||||
|
|
||||||
if ((length & 3) == 0)
|
if ((length & 3) == 0)
|
||||||
return length;
|
return length;
|
||||||
|
|
||||||
@@ -227,17 +267,20 @@ tcp_options_length(tcp_segment_header &segment)
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
process_options(tcp_segment_header &segment, net_buffer *buffer, int32 size)
|
process_options(tcp_segment_header &segment, net_buffer *buffer, size_t size)
|
||||||
{
|
{
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
tcp_option *option;
|
tcp_option *option;
|
||||||
uint8 optionsBuffer[32];
|
|
||||||
|
uint8 optionsBuffer[kMaxOptionSize];
|
||||||
if (gBufferModule->direct_access(buffer, sizeof(tcp_header), size,
|
if (gBufferModule->direct_access(buffer, sizeof(tcp_header), size,
|
||||||
(void **)&option) != B_OK) {
|
(void **)&option) != B_OK) {
|
||||||
if (size > 32)
|
if (size > sizeof(optionsBuffer)) {
|
||||||
panic("UNIMPLEMENTED: TCP option processing across data nodes");
|
dprintf("Ignoring TCP options larger than expected.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
gBufferModule->read(buffer, sizeof(tcp_header), optionsBuffer, size);
|
gBufferModule->read(buffer, sizeof(tcp_header), optionsBuffer, size);
|
||||||
option = (tcp_option *)optionsBuffer;
|
option = (tcp_option *)optionsBuffer;
|
||||||
@@ -257,17 +300,20 @@ process_options(tcp_segment_header &segment, net_buffer *buffer, int32 size)
|
|||||||
break;
|
break;
|
||||||
case TCP_OPTION_WINDOW_SHIFT:
|
case TCP_OPTION_WINDOW_SHIFT:
|
||||||
if (option->length == 3 && (size - 3) >= 0) {
|
if (option->length == 3 && (size - 3) >= 0) {
|
||||||
segment.has_window_shift = true;
|
segment.options |= TCP_HAS_WINDOW_SCALE;
|
||||||
segment.window_shift = option->window_shift;
|
segment.window_shift = option->window_shift;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TCP_OPTION_TIMESTAMP:
|
case TCP_OPTION_TIMESTAMP:
|
||||||
if (option->length == 10 && (size - 10) >= 0) {
|
if (option->length == 10 && (size - 10) >= 0) {
|
||||||
segment.has_timestamps = true;
|
segment.options |= TCP_HAS_TIMESTAMPS;
|
||||||
segment.TSval = option->timestamp.TSval;
|
segment.TSval = option->timestamp.TSval;
|
||||||
segment.TSecr = ntohl(option->timestamp.TSecr);
|
segment.TSecr = ntohl(option->timestamp.TSecr);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case TCP_OPTION_SACK_PERMITTED:
|
||||||
|
if (option->length == 2 && (size - 2) >= 0)
|
||||||
|
segment.options |= TCP_SACK_PERMITTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (length < 0) {
|
if (length < 0) {
|
||||||
|
|||||||
@@ -106,6 +106,11 @@ class tcp_sequence {
|
|||||||
#define TCP_MAX_WINDOW 65535
|
#define TCP_MAX_WINDOW 65535
|
||||||
#define TCP_MAX_SEGMENT_LIFETIME 60000000 // 60 secs
|
#define TCP_MAX_SEGMENT_LIFETIME 60000000 // 60 secs
|
||||||
|
|
||||||
|
struct tcp_sack {
|
||||||
|
uint32 left_edge;
|
||||||
|
uint32 right_edge;
|
||||||
|
} _PACKED;
|
||||||
|
|
||||||
struct tcp_option {
|
struct tcp_option {
|
||||||
uint8 kind;
|
uint8 kind;
|
||||||
uint8 length;
|
uint8 length;
|
||||||
@@ -116,6 +121,7 @@ struct tcp_option {
|
|||||||
uint32 TSval;
|
uint32 TSval;
|
||||||
uint32 TSecr;
|
uint32 TSecr;
|
||||||
} timestamp;
|
} timestamp;
|
||||||
|
tcp_sack sack[0];
|
||||||
};
|
};
|
||||||
} _PACKED;
|
} _PACKED;
|
||||||
|
|
||||||
@@ -124,19 +130,27 @@ enum tcp_option_kind {
|
|||||||
TCP_OPTION_NOP = 1,
|
TCP_OPTION_NOP = 1,
|
||||||
TCP_OPTION_MAX_SEGMENT_SIZE = 2,
|
TCP_OPTION_MAX_SEGMENT_SIZE = 2,
|
||||||
TCP_OPTION_WINDOW_SHIFT = 3,
|
TCP_OPTION_WINDOW_SHIFT = 3,
|
||||||
|
TCP_OPTION_SACK_PERMITTED = 4,
|
||||||
|
TCP_OPTION_SACK = 5,
|
||||||
TCP_OPTION_TIMESTAMP = 8,
|
TCP_OPTION_TIMESTAMP = 8,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define TCP_MAX_WINDOW_SHIFT 14
|
#define TCP_MAX_WINDOW_SHIFT 14
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TCP_HAS_WINDOW_SCALE = 1 << 0,
|
||||||
|
TCP_HAS_TIMESTAMPS = 1 << 1,
|
||||||
|
TCP_SACK_PERMITTED = 1 << 2,
|
||||||
|
};
|
||||||
|
|
||||||
struct tcp_segment_header {
|
struct tcp_segment_header {
|
||||||
tcp_segment_header(uint8 _flags)
|
tcp_segment_header(uint8 _flags)
|
||||||
:
|
:
|
||||||
flags(_flags),
|
flags(_flags),
|
||||||
window_shift(0),
|
window_shift(0),
|
||||||
max_segment_size(0),
|
max_segment_size(0),
|
||||||
has_window_shift(false),
|
sack_count(0),
|
||||||
has_timestamps(false)
|
options(0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
uint32 sequence;
|
uint32 sequence;
|
||||||
@@ -150,8 +164,10 @@ struct tcp_segment_header {
|
|||||||
uint32 TSval;
|
uint32 TSval;
|
||||||
uint32 TSecr;
|
uint32 TSecr;
|
||||||
|
|
||||||
bool has_window_shift : 1;
|
tcp_sack *sacks;
|
||||||
bool has_timestamps : 1;
|
int sack_count;
|
||||||
|
|
||||||
|
uint32 options;
|
||||||
|
|
||||||
bool AcknowledgeOnly() const
|
bool AcknowledgeOnly() const
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user