support RFC 1323's TCP Timestamps (we are still not updating our estimator though).

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20719 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Hugo Santos 2007-04-16 03:19:59 +00:00
parent 03d7f17e07
commit ca1b900bdb
4 changed files with 89 additions and 16 deletions

View File

@ -179,6 +179,8 @@ TCPEndpoint::TCPEndpoint(net_socket *socket)
fReceiveMaxSegmentSize(TCP_DEFAULT_MAX_SEGMENT_SIZE), fReceiveMaxSegmentSize(TCP_DEFAULT_MAX_SEGMENT_SIZE),
fReceiveQueue(socket->receive.buffer_size), fReceiveQueue(socket->receive.buffer_size),
fRoundTripTime(TCP_INITIAL_RTT), fRoundTripTime(TCP_INITIAL_RTT),
fReceivedTSval(0),
fUsingTimestamps(false),
fState(CLOSED), fState(CLOSED),
fFlags(0), //FLAG_OPTION_WINDOW_SHIFT), fFlags(0), //FLAG_OPTION_WINDOW_SHIFT),
fError(B_OK), fError(B_OK),
@ -363,6 +365,10 @@ TCPEndpoint::Connect(const struct sockaddr *address)
fSendMax = fInitialSendSequence; fSendMax = fInitialSendSequence;
fSendQueue.SetInitialSequence(fSendNext + 1); fSendQueue.SetInitialSequence(fSendNext + 1);
// try to use timestamps, if the peer doesn't reply with the TS
// option as well we'll stop using them.
fUsingTimestamps = true;
// send SYN // send SYN
status = _SendQueued(); status = _SendQueued();
if (status != B_OK) { if (status != B_OK) {
@ -794,6 +800,8 @@ TCPEndpoint::ListenReceive(tcp_segment_header &segment, net_buffer *buffer)
TRACE(" ListenReceive() created new endpoint %p", endpoint); TRACE(" ListenReceive() created new endpoint %p", endpoint);
endpoint->_UpdateTimestamps(segment, 0, false);
// send SYN+ACK // send SYN+ACK
status_t status = endpoint->_SendQueued(); status_t status = endpoint->_SendQueued();
@ -866,10 +874,12 @@ TCPEndpoint::SynchronizeSentReceive(tcp_segment_header &segment, net_buffer *buf
fState = SYNCHRONIZE_RECEIVED; fState = SYNCHRONIZE_RECEIVED;
} }
_UpdateTimestamps(segment, 0, false);
segment.flags &= ~TCP_FLAG_SYNCHRONIZE; segment.flags &= ~TCP_FLAG_SYNCHRONIZE;
// we handled this flag now, it must not be set for further processing // we handled this flag now, it must not be set for further processing
return Receive(segment, buffer) | IMMEDIATE_ACKNOWLEDGE; return _Receive(segment, buffer) | IMMEDIATE_ACKNOWLEDGE;
} }
@ -892,6 +902,9 @@ TCPEndpoint::Receive(tcp_segment_header &segment, net_buffer *buffer)
&& fReceiveNext == segment.sequence && fReceiveNext == segment.sequence
&& advertisedWindow > 0 && advertisedWindow == fSendWindow && advertisedWindow > 0 && advertisedWindow == fSendWindow
&& fSendNext == fSendMax) { && fSendNext == fSendMax) {
_UpdateTimestamps(segment, buffer->size, true);
if (buffer->size == 0) { if (buffer->size == 0) {
// this is a pure acknowledge segment - we're on the sending end // this is a pure acknowledge segment - we're on the sending end
if (fSendUnacknowledged < segment.acknowledge if (fSendUnacknowledged < segment.acknowledge
@ -1047,6 +1060,13 @@ TCPEndpoint::_SendQueued(bool force)
tcp_segment_header segment; tcp_segment_header segment;
segment.flags = _CurrentFlags(); segment.flags = _CurrentFlags();
segment.urgent_offset = 0;
if (fUsingTimestamps) {
segment.has_timestamps = true;
segment.TSecr = fReceivedTSval;
segment.TSval = system_time();
}
uint32 sendWindow = fSendWindow; uint32 sendWindow = fSendWindow;
uint32 available = fSendQueue.Available(fSendNext); uint32 available = fSendQueue.Available(fSendNext);
@ -1172,6 +1192,9 @@ TCPEndpoint::_SendQueued(bool force)
return status; return status;
} }
if (segment.flags & TCP_FLAG_ACKNOWLEDGE)
fLastAcknowledgeSent = segment.acknowledge;
length -= segmentLength; length -= segmentLength;
if (length == 0) if (length == 0)
break; break;
@ -1257,6 +1280,8 @@ TCPEndpoint::_Receive(tcp_segment_header &segment, net_buffer *buffer)
{ {
uint32 advertisedWindow = (uint32)segment.advertised_window << fSendWindowShift; uint32 advertisedWindow = (uint32)segment.advertised_window << fSendWindowShift;
size_t segmentLength = buffer->size;
if (segment.flags & TCP_FLAG_RESET) { if (segment.flags & TCP_FLAG_RESET) {
// is this a valid reset? // is this a valid reset?
if (fLastAcknowledgeSent <= segment.sequence if (fLastAcknowledgeSent <= segment.sequence
@ -1449,6 +1474,7 @@ TCPEndpoint::_Receive(tcp_segment_header &segment, net_buffer *buffer)
} }
if (segment.flags & TCP_FLAG_FINISH) { if (segment.flags & TCP_FLAG_FINISH) {
segmentLength++;
if (fState != CLOSED && fState != LISTEN && fState != SYNCHRONIZE_SENT) { if (fState != CLOSED && fState != LISTEN && fState != SYNCHRONIZE_SENT) {
TRACE("Receive(): peer is finishing connection!"); TRACE("Receive(): peer is finishing connection!");
fReceiveNext++; fReceiveNext++;
@ -1486,12 +1512,30 @@ TCPEndpoint::_Receive(tcp_segment_header &segment, net_buffer *buffer)
if (buffer->size > 0 || (segment.flags & TCP_FLAG_SYNCHRONIZE) != 0) if (buffer->size > 0 || (segment.flags & TCP_FLAG_SYNCHRONIZE) != 0)
action |= ACKNOWLEDGE; action |= ACKNOWLEDGE;
_UpdateTimestamps(segment, segmentLength, true);
TRACE("Receive() Action %ld", action); TRACE("Receive() Action %ld", action);
return action; return action;
} }
void
TCPEndpoint::_UpdateTimestamps(tcp_segment_header &segment, size_t segmentLength,
bool checkSequence)
{
fUsingTimestamps = segment.has_timestamps;
if (fUsingTimestamps) {
tcp_sequence sequence(segment.sequence);
if (!checkSequence || (fLastAcknowledgeSent >= sequence
&& fLastAcknowledgeSent < (sequence + segmentLength)))
fReceivedTSval = segment.TSval;
}
}
// #pragma mark - timer // #pragma mark - timer

View File

@ -95,6 +95,8 @@ class TCPEndpoint : public net_protocol {
void _NotifyReader(); void _NotifyReader();
bool _ShouldReceive() const; bool _ShouldReceive() const;
int32 _Receive(tcp_segment_header& segment, net_buffer *buffer); int32 _Receive(tcp_segment_header& segment, net_buffer *buffer);
void _UpdateTimestamps(tcp_segment_header& segment,
size_t segmentLength, bool checkSequence);
static void _TimeWaitTimer(net_timer *timer, void *data); static void _TimeWaitTimer(net_timer *timer, void *data);
static void _RetransmitTimer(net_timer *timer, void *data); static void _RetransmitTimer(net_timer *timer, void *data);
@ -146,6 +148,9 @@ class TCPEndpoint : public net_protocol {
uint32 fTrackingSequence; uint32 fTrackingSequence;
bool fTracking; bool fTracking;
uint32 fReceivedTSval;
bool fUsingTimestamps;
uint32 fCongestionWindow; uint32 fCongestionWindow;
uint32 fSlowStartThreshold; uint32 fSlowStartThreshold;

View File

@ -120,11 +120,26 @@ add_options(tcp_segment_header &segment, uint8 *buffer, size_t bufferSize)
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) {
// two NOPs so the timestamps get aligned to a 4 byte boundary
option->kind = TCP_OPTION_NOP;
bump_option(option, length);
option->kind = TCP_OPTION_NOP;
bump_option(option, length);
option->kind = TCP_OPTION_TIMESTAMP;
option->length = 10;
option->timestamp.TSval = htonl(segment.TSval);
// TSecr is opaque to us, we send it as we received it.
option->timestamp.TSecr = segment.TSecr;
bump_option(option, length);
}
if (segment.has_window_shift && length + 4 < bufferSize) { if (segment.has_window_shift && 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);
option->kind = TCP_OPTION_WINDOW_SHIFT; option->kind = TCP_OPTION_WINDOW_SHIFT;
option->length = 3; option->length = 3;
option->window_shift = segment.window_shift; option->window_shift = segment.window_shift;
@ -171,8 +186,7 @@ add_tcp_header(net_address_module_info *addressModule,
header.flags = segment.flags; header.flags = segment.flags;
header.advertised_window = htons(segment.advertised_window); header.advertised_window = htons(segment.advertised_window);
header.checksum = 0; header.checksum = 0;
header.urgent_offset = 0; header.urgent_offset = segment.urgent_offset;
// TODO: urgent pointer not yet supported
// we must detach before calculating the checksum as we may // we must detach before calculating the checksum as we may
// not have a contiguous buffer. // not have a contiguous buffer.
@ -226,7 +240,9 @@ process_options(tcp_segment_header &segment, net_buffer *buffer, int32 size)
length = 3; length = 3;
break; break;
case TCP_OPTION_TIMESTAMP: case TCP_OPTION_TIMESTAMP:
// TODO: support timestamp! segment.has_timestamps = true;
segment.TSval = option->timestamp.TSval;
segment.TSecr = ntohl(option->timestamp.TSecr);
length = 10; length = 10;
break; break;
@ -515,11 +531,7 @@ tcp_receive_data(net_buffer *buffer)
segment.advertised_window = header.AdvertisedWindow(); segment.advertised_window = header.AdvertisedWindow();
segment.urgent_offset = header.UrgentOffset(); segment.urgent_offset = header.UrgentOffset();
segment.flags = header.flags; segment.flags = header.flags;
if ((segment.flags & TCP_FLAG_SYNCHRONIZE) != 0) { process_options(segment, buffer, headerLength - sizeof(tcp_header));
// for now, we only process the options in the SYN segment
// TODO: when we support timestamps, they could be handled specifically
process_options(segment, buffer, headerLength - sizeof(tcp_header));
}
bufferHeader.Remove(headerLength); bufferHeader.Remove(headerLength);
// we no longer need to keep the header around // we no longer need to keep the header around

View File

@ -112,9 +112,11 @@ struct tcp_option {
union { union {
uint8 window_shift; uint8 window_shift;
uint16 max_segment_size; uint16 max_segment_size;
uint32 timestamp; struct {
uint32 TSval;
uint32 TSecr;
} timestamp;
}; };
uint32 timestamp_reply;
} _PACKED; } _PACKED;
enum tcp_option_kind { enum tcp_option_kind {
@ -128,18 +130,28 @@ enum tcp_option_kind {
#define TCP_MAX_WINDOW_SHIFT 14 #define TCP_MAX_WINDOW_SHIFT 14
struct tcp_segment_header { struct tcp_segment_header {
tcp_segment_header() : has_window_shift(false), window_shift(0), max_segment_size(0) {} tcp_segment_header()
// constructor zeros options :
window_shift(0),
max_segment_size(0),
has_window_shift(false),
has_timestamps(false)
{}
uint32 sequence; uint32 sequence;
uint32 acknowledge; uint32 acknowledge;
uint16 advertised_window; uint16 advertised_window;
uint16 urgent_offset; uint16 urgent_offset;
uint8 flags; uint8 flags;
uint8 has_window_shift : 1; uint8 window_shift;
uint8 window_shift : 7;
uint16 max_segment_size; uint16 max_segment_size;
uint32 TSval;
uint32 TSecr;
bool has_window_shift : 1;
bool has_timestamps : 1;
bool AcknowledgeOnly() const bool AcknowledgeOnly() const
{ {
return (flags & (TCP_FLAG_SYNCHRONIZE | TCP_FLAG_FINISH | TCP_FLAG_RESET return (flags & (TCP_FLAG_SYNCHRONIZE | TCP_FLAG_FINISH | TCP_FLAG_RESET