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:
parent
03d7f17e07
commit
ca1b900bdb
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user