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),
fReceiveQueue(socket->receive.buffer_size),
fRoundTripTime(TCP_INITIAL_RTT),
fReceivedTSval(0),
fUsingTimestamps(false),
fState(CLOSED),
fFlags(0), //FLAG_OPTION_WINDOW_SHIFT),
fError(B_OK),
@ -363,6 +365,10 @@ TCPEndpoint::Connect(const struct sockaddr *address)
fSendMax = fInitialSendSequence;
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
status = _SendQueued();
if (status != B_OK) {
@ -794,6 +800,8 @@ TCPEndpoint::ListenReceive(tcp_segment_header &segment, net_buffer *buffer)
TRACE(" ListenReceive() created new endpoint %p", endpoint);
endpoint->_UpdateTimestamps(segment, 0, false);
// send SYN+ACK
status_t status = endpoint->_SendQueued();
@ -866,10 +874,12 @@ TCPEndpoint::SynchronizeSentReceive(tcp_segment_header &segment, net_buffer *buf
fState = SYNCHRONIZE_RECEIVED;
}
_UpdateTimestamps(segment, 0, false);
segment.flags &= ~TCP_FLAG_SYNCHRONIZE;
// 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
&& advertisedWindow > 0 && advertisedWindow == fSendWindow
&& fSendNext == fSendMax) {
_UpdateTimestamps(segment, buffer->size, true);
if (buffer->size == 0) {
// this is a pure acknowledge segment - we're on the sending end
if (fSendUnacknowledged < segment.acknowledge
@ -1047,6 +1060,13 @@ TCPEndpoint::_SendQueued(bool force)
tcp_segment_header segment;
segment.flags = _CurrentFlags();
segment.urgent_offset = 0;
if (fUsingTimestamps) {
segment.has_timestamps = true;
segment.TSecr = fReceivedTSval;
segment.TSval = system_time();
}
uint32 sendWindow = fSendWindow;
uint32 available = fSendQueue.Available(fSendNext);
@ -1172,6 +1192,9 @@ TCPEndpoint::_SendQueued(bool force)
return status;
}
if (segment.flags & TCP_FLAG_ACKNOWLEDGE)
fLastAcknowledgeSent = segment.acknowledge;
length -= segmentLength;
if (length == 0)
break;
@ -1257,6 +1280,8 @@ TCPEndpoint::_Receive(tcp_segment_header &segment, net_buffer *buffer)
{
uint32 advertisedWindow = (uint32)segment.advertised_window << fSendWindowShift;
size_t segmentLength = buffer->size;
if (segment.flags & TCP_FLAG_RESET) {
// is this a valid reset?
if (fLastAcknowledgeSent <= segment.sequence
@ -1449,6 +1474,7 @@ TCPEndpoint::_Receive(tcp_segment_header &segment, net_buffer *buffer)
}
if (segment.flags & TCP_FLAG_FINISH) {
segmentLength++;
if (fState != CLOSED && fState != LISTEN && fState != SYNCHRONIZE_SENT) {
TRACE("Receive(): peer is finishing connection!");
fReceiveNext++;
@ -1486,12 +1512,30 @@ TCPEndpoint::_Receive(tcp_segment_header &segment, net_buffer *buffer)
if (buffer->size > 0 || (segment.flags & TCP_FLAG_SYNCHRONIZE) != 0)
action |= ACKNOWLEDGE;
_UpdateTimestamps(segment, segmentLength, true);
TRACE("Receive() Action %ld", 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

View File

@ -95,6 +95,8 @@ class TCPEndpoint : public net_protocol {
void _NotifyReader();
bool _ShouldReceive() const;
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 _RetransmitTimer(net_timer *timer, void *data);
@ -146,6 +148,9 @@ class TCPEndpoint : public net_protocol {
uint32 fTrackingSequence;
bool fTracking;
uint32 fReceivedTSval;
bool fUsingTimestamps;
uint32 fCongestionWindow;
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);
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) {
// insert one NOP so that the subsequent data is aligned on a 4 byte boundary
option->kind = TCP_OPTION_NOP;
bump_option(option, length);
option->kind = TCP_OPTION_WINDOW_SHIFT;
option->length = 3;
option->window_shift = segment.window_shift;
@ -171,8 +186,7 @@ add_tcp_header(net_address_module_info *addressModule,
header.flags = segment.flags;
header.advertised_window = htons(segment.advertised_window);
header.checksum = 0;
header.urgent_offset = 0;
// TODO: urgent pointer not yet supported
header.urgent_offset = segment.urgent_offset;
// we must detach before calculating the checksum as we may
// not have a contiguous buffer.
@ -226,7 +240,9 @@ process_options(tcp_segment_header &segment, net_buffer *buffer, int32 size)
length = 3;
break;
case TCP_OPTION_TIMESTAMP:
// TODO: support timestamp!
segment.has_timestamps = true;
segment.TSval = option->timestamp.TSval;
segment.TSecr = ntohl(option->timestamp.TSecr);
length = 10;
break;
@ -515,11 +531,7 @@ tcp_receive_data(net_buffer *buffer)
segment.advertised_window = header.AdvertisedWindow();
segment.urgent_offset = header.UrgentOffset();
segment.flags = header.flags;
if ((segment.flags & TCP_FLAG_SYNCHRONIZE) != 0) {
// 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));
}
process_options(segment, buffer, headerLength - sizeof(tcp_header));
bufferHeader.Remove(headerLength);
// we no longer need to keep the header around

View File

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