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:
parent
530017c6c9
commit
739e994278
@ -1061,7 +1061,7 @@ TCPEndpoint::_SendQueued(bool force)
|
||||
|
||||
if ((fOptions & TCP_NOOPT) == 0) {
|
||||
if (fFlags & FLAG_OPTION_TIMESTAMP) {
|
||||
segment.has_timestamps = true;
|
||||
segment.options |= TCP_HAS_TIMESTAMPS;
|
||||
segment.TSecr = fReceivedTSval;
|
||||
segment.TSval = htonl(tcp_now());
|
||||
}
|
||||
@ -1071,7 +1071,7 @@ TCPEndpoint::_SendQueued(bool force)
|
||||
// add connection establishment options
|
||||
segment.max_segment_size = fReceiveMaxSegmentSize;
|
||||
if (fFlags & FLAG_OPTION_WINDOW_SCALE) {
|
||||
segment.has_window_shift = true;
|
||||
segment.options |= TCP_HAS_WINDOW_SCALE;
|
||||
segment.window_shift = fReceiveWindowShift;
|
||||
}
|
||||
}
|
||||
@ -1185,7 +1185,7 @@ TCPEndpoint::_SendQueued(bool force)
|
||||
// for local connections as the answer is directly handled
|
||||
|
||||
if (segment.flags & TCP_FLAG_SYNCHRONIZE) {
|
||||
segment.has_window_shift = false;
|
||||
segment.options &= ~TCP_HAS_WINDOW_SCALE;
|
||||
segment.max_segment_size = 0;
|
||||
size++;
|
||||
}
|
||||
@ -1583,7 +1583,7 @@ TCPEndpoint::_PrepareReceivePath(tcp_segment_header &segment)
|
||||
if (segment.max_segment_size > 0)
|
||||
fSendMaxSegmentSize = segment.max_segment_size;
|
||||
|
||||
if (segment.has_window_shift) {
|
||||
if (segment.options & TCP_HAS_WINDOW_SCALE) {
|
||||
fFlags |= FLAG_OPTION_WINDOW_SCALE;
|
||||
fSendWindowShift = segment.window_shift;
|
||||
} else {
|
||||
@ -1591,7 +1591,7 @@ TCPEndpoint::_PrepareReceivePath(tcp_segment_header &segment)
|
||||
fReceiveWindowShift = 0;
|
||||
}
|
||||
|
||||
if (segment.has_timestamps) {
|
||||
if (segment.options & TCP_HAS_TIMESTAMPS) {
|
||||
fFlags |= FLAG_OPTION_TIMESTAMP;
|
||||
fReceivedTSval = segment.TSval;
|
||||
} else
|
||||
@ -1654,7 +1654,7 @@ TCPEndpoint::_Acknowledged(tcp_segment_header &segment)
|
||||
if (fSendUnacknowledged == fSendMax)
|
||||
gStackModule->cancel_timer(&fRetransmitTimer);
|
||||
|
||||
if (segment.has_timestamps)
|
||||
if (segment.options & TCP_HAS_TIMESTAMPS)
|
||||
_UpdateSRTT(tcp_diff_timestamp(ntohl(segment.TSecr)));
|
||||
else {
|
||||
// TODO Fallback to RFC 793 type estimation
|
||||
|
@ -57,6 +57,10 @@ static recursive_lock sEndpointManagersLock;
|
||||
static EndpointManagerList sEndpointManagers;
|
||||
|
||||
|
||||
// The TCP header length is at most 64 bytes.
|
||||
static const int kMaxOptionSize = 64 - sizeof(tcp_header);
|
||||
|
||||
|
||||
static EndpointManager *
|
||||
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;
|
||||
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->length = 4;
|
||||
option->max_segment_size = htons(segment.max_segment_size);
|
||||
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
|
||||
option->kind = TCP_OPTION_NOP;
|
||||
bump_option(option, length);
|
||||
@ -135,7 +140,8 @@ add_options(tcp_segment_header &segment, uint8 *buffer, size_t bufferSize)
|
||||
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
|
||||
option->kind = TCP_OPTION_NOP;
|
||||
bump_option(option, length);
|
||||
@ -146,6 +152,30 @@ add_options(tcp_segment_header &segment, uint8 *buffer, size_t bufferSize)
|
||||
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) {
|
||||
// options completely fill out the option space
|
||||
return length;
|
||||
@ -167,7 +197,7 @@ add_tcp_header(net_address_module_info *addressModule,
|
||||
{
|
||||
buffer->protocol = IPPROTO_TCP;
|
||||
|
||||
uint8 optionsBuffer[32];
|
||||
uint8 optionsBuffer[kMaxOptionSize];
|
||||
uint32 optionsLength = add_options(segment, optionsBuffer, sizeof(optionsBuffer));
|
||||
|
||||
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)
|
||||
length += 4;
|
||||
|
||||
if (segment.has_timestamps)
|
||||
if (segment.options & TCP_HAS_TIMESTAMPS)
|
||||
length += 12;
|
||||
|
||||
if (segment.has_window_shift)
|
||||
if (segment.options & TCP_HAS_WINDOW_SCALE)
|
||||
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)
|
||||
return length;
|
||||
|
||||
@ -227,17 +267,20 @@ tcp_options_length(tcp_segment_header &segment)
|
||||
|
||||
|
||||
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)
|
||||
return;
|
||||
|
||||
tcp_option *option;
|
||||
uint8 optionsBuffer[32];
|
||||
|
||||
uint8 optionsBuffer[kMaxOptionSize];
|
||||
if (gBufferModule->direct_access(buffer, sizeof(tcp_header), size,
|
||||
(void **)&option) != B_OK) {
|
||||
if (size > 32)
|
||||
panic("UNIMPLEMENTED: TCP option processing across data nodes");
|
||||
if (size > sizeof(optionsBuffer)) {
|
||||
dprintf("Ignoring TCP options larger than expected.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
gBufferModule->read(buffer, sizeof(tcp_header), optionsBuffer, size);
|
||||
option = (tcp_option *)optionsBuffer;
|
||||
@ -257,17 +300,20 @@ process_options(tcp_segment_header &segment, net_buffer *buffer, int32 size)
|
||||
break;
|
||||
case TCP_OPTION_WINDOW_SHIFT:
|
||||
if (option->length == 3 && (size - 3) >= 0) {
|
||||
segment.has_window_shift = true;
|
||||
segment.options |= TCP_HAS_WINDOW_SCALE;
|
||||
segment.window_shift = option->window_shift;
|
||||
}
|
||||
break;
|
||||
case TCP_OPTION_TIMESTAMP:
|
||||
if (option->length == 10 && (size - 10) >= 0) {
|
||||
segment.has_timestamps = true;
|
||||
segment.options |= TCP_HAS_TIMESTAMPS;
|
||||
segment.TSval = option->timestamp.TSval;
|
||||
segment.TSecr = ntohl(option->timestamp.TSecr);
|
||||
}
|
||||
break;
|
||||
case TCP_OPTION_SACK_PERMITTED:
|
||||
if (option->length == 2 && (size - 2) >= 0)
|
||||
segment.options |= TCP_SACK_PERMITTED;
|
||||
}
|
||||
|
||||
if (length < 0) {
|
||||
|
@ -106,6 +106,11 @@ class tcp_sequence {
|
||||
#define TCP_MAX_WINDOW 65535
|
||||
#define TCP_MAX_SEGMENT_LIFETIME 60000000 // 60 secs
|
||||
|
||||
struct tcp_sack {
|
||||
uint32 left_edge;
|
||||
uint32 right_edge;
|
||||
} _PACKED;
|
||||
|
||||
struct tcp_option {
|
||||
uint8 kind;
|
||||
uint8 length;
|
||||
@ -116,6 +121,7 @@ struct tcp_option {
|
||||
uint32 TSval;
|
||||
uint32 TSecr;
|
||||
} timestamp;
|
||||
tcp_sack sack[0];
|
||||
};
|
||||
} _PACKED;
|
||||
|
||||
@ -124,19 +130,27 @@ enum tcp_option_kind {
|
||||
TCP_OPTION_NOP = 1,
|
||||
TCP_OPTION_MAX_SEGMENT_SIZE = 2,
|
||||
TCP_OPTION_WINDOW_SHIFT = 3,
|
||||
TCP_OPTION_SACK_PERMITTED = 4,
|
||||
TCP_OPTION_SACK = 5,
|
||||
TCP_OPTION_TIMESTAMP = 8,
|
||||
};
|
||||
|
||||
#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 {
|
||||
tcp_segment_header(uint8 _flags)
|
||||
:
|
||||
flags(_flags),
|
||||
window_shift(0),
|
||||
max_segment_size(0),
|
||||
has_window_shift(false),
|
||||
has_timestamps(false)
|
||||
sack_count(0),
|
||||
options(0)
|
||||
{}
|
||||
|
||||
uint32 sequence;
|
||||
@ -150,8 +164,10 @@ struct tcp_segment_header {
|
||||
uint32 TSval;
|
||||
uint32 TSecr;
|
||||
|
||||
bool has_window_shift : 1;
|
||||
bool has_timestamps : 1;
|
||||
tcp_sack *sacks;
|
||||
int sack_count;
|
||||
|
||||
uint32 options;
|
||||
|
||||
bool AcknowledgeOnly() const
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user