reorganized some of the TCP logic so we have common init paths for receive and send.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20731 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
aa07700795
commit
f19e1f47da
@ -314,7 +314,7 @@ TCPEndpoint::Free()
|
|||||||
until the connection has been established or refused.
|
until the connection has been established or refused.
|
||||||
*/
|
*/
|
||||||
status_t
|
status_t
|
||||||
TCPEndpoint::Connect(const struct sockaddr *address)
|
TCPEndpoint::Connect(const sockaddr *address)
|
||||||
{
|
{
|
||||||
TRACE("Connect() on address %s",
|
TRACE("Connect() on address %s",
|
||||||
AddressString(Domain(), address, true).Data());
|
AddressString(Domain(), address, true).Data());
|
||||||
@ -329,41 +329,13 @@ TCPEndpoint::Connect(const struct sockaddr *address)
|
|||||||
} else if (fState != CLOSED)
|
} else if (fState != CLOSED)
|
||||||
return EISCONN;
|
return EISCONN;
|
||||||
|
|
||||||
// get a net_route if there isn't one
|
status_t status = _PrepareSendPath(address);
|
||||||
// TODO: get a net_route_info instead!
|
if (status < B_OK)
|
||||||
if (fRoute == NULL) {
|
|
||||||
fRoute = gDatalinkModule->get_route(Domain(), (sockaddr *)address);
|
|
||||||
TRACE(" Connect(): Using Route %p", fRoute);
|
|
||||||
if (fRoute == NULL)
|
|
||||||
return ENETUNREACH;
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure connection does not already exist
|
|
||||||
status_t status = fManager->SetConnection(this,
|
|
||||||
(sockaddr *)&socket->address, address, fRoute->interface->address);
|
|
||||||
if (status < B_OK) {
|
|
||||||
TRACE(" Connect(): could not add connection: %s!", strerror(status));
|
|
||||||
return status;
|
return status;
|
||||||
}
|
|
||||||
|
|
||||||
fReceiveMaxSegmentSize = _GetMSS(address);
|
|
||||||
|
|
||||||
// Compute the window shift we advertise to our peer - if it doesn't support
|
|
||||||
// this option, this will be reset to 0 (when its SYN is received)
|
|
||||||
fReceiveWindowShift = 0;
|
|
||||||
while (fReceiveWindowShift < TCP_MAX_WINDOW_SHIFT
|
|
||||||
&& (0xffffUL << fReceiveWindowShift) < socket->receive.buffer_size) {
|
|
||||||
fReceiveWindowShift++;
|
|
||||||
}
|
|
||||||
|
|
||||||
TRACE(" Connect(): starting 3-way handshake...");
|
TRACE(" Connect(): starting 3-way handshake...");
|
||||||
|
|
||||||
fState = SYNCHRONIZE_SENT;
|
fState = SYNCHRONIZE_SENT;
|
||||||
fInitialSendSequence = system_time() >> 4;
|
|
||||||
fSendNext = fInitialSendSequence;
|
|
||||||
fSendUnacknowledged = fInitialSendSequence;
|
|
||||||
fSendMax = fInitialSendSequence;
|
|
||||||
fSendQueue.SetInitialSequence(fSendNext + 1);
|
|
||||||
|
|
||||||
// send SYN
|
// send SYN
|
||||||
status = _SendQueued();
|
status = _SendQueued();
|
||||||
@ -515,8 +487,7 @@ TCPEndpoint::SendData(net_buffer *buffer)
|
|||||||
if (fState == CLOSED)
|
if (fState == CLOSED)
|
||||||
return ENOTCONN;
|
return ENOTCONN;
|
||||||
else if (fState == LISTEN) {
|
else if (fState == LISTEN) {
|
||||||
// TODO change socket from passive to active.
|
return EDESTADDRREQ;
|
||||||
return EOPNOTSUPP;
|
|
||||||
} else if (fState == FINISH_SENT || fState == FINISH_ACKNOWLEDGED
|
} else if (fState == FINISH_SENT || fState == FINISH_ACKNOWLEDGED
|
||||||
|| fState == CLOSING || fState == WAIT_FOR_FINISH_ACKNOWLEDGE
|
|| fState == CLOSING || fState == WAIT_FOR_FINISH_ACKNOWLEDGE
|
||||||
|| fState == TIME_WAIT) {
|
|| fState == TIME_WAIT) {
|
||||||
@ -753,7 +724,8 @@ TCPEndpoint::_ListenReceive(tcp_segment_header &segment, net_buffer *buffer)
|
|||||||
AddressModule()->set_to((sockaddr *)&newSocket->peer,
|
AddressModule()->set_to((sockaddr *)&newSocket->peer,
|
||||||
(sockaddr *)&buffer->source);
|
(sockaddr *)&buffer->source);
|
||||||
|
|
||||||
return ((TCPEndpoint *)newSocket->first_protocol)->Spawn(this, segment, buffer);
|
return ((TCPEndpoint *)newSocket->first_protocol)->Spawn(this,
|
||||||
|
segment, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -765,55 +737,18 @@ TCPEndpoint::Spawn(TCPEndpoint *parent, tcp_segment_header &segment,
|
|||||||
|
|
||||||
fState = SYNCHRONIZE_RECEIVED;
|
fState = SYNCHRONIZE_RECEIVED;
|
||||||
fManager = parent->fManager;
|
fManager = parent->fManager;
|
||||||
|
fSpawned = true;
|
||||||
|
|
||||||
TRACE("Spawn()");
|
TRACE("Spawn()");
|
||||||
|
|
||||||
fSpawned = true;
|
|
||||||
|
|
||||||
sockaddr *local = (sockaddr *)&socket->address;
|
|
||||||
sockaddr *peer = (sockaddr *)&socket->peer;
|
|
||||||
|
|
||||||
// TODO: proper error handling!
|
// TODO: proper error handling!
|
||||||
|
if (_PrepareSendPath((sockaddr *)&socket->peer) < B_OK)
|
||||||
fRoute = gDatalinkModule->get_route(Domain(), peer);
|
|
||||||
if (fRoute == NULL)
|
|
||||||
return DROP;
|
return DROP;
|
||||||
|
|
||||||
if (fManager->SetConnection(this, local, peer, NULL) < B_OK)
|
_PrepareReceivePath(parent, segment);
|
||||||
return DROP;
|
|
||||||
|
|
||||||
fInitialReceiveSequence = segment.sequence;
|
|
||||||
fReceiveQueue.SetInitialSequence(segment.sequence + 1);
|
|
||||||
fAcceptSemaphore = parent->fAcceptSemaphore;
|
|
||||||
fReceiveMaxSegmentSize = _GetMSS(peer);
|
|
||||||
// 40 bytes for IP and TCP header without any options
|
|
||||||
// TODO: make this depending on the RTF_LOCAL flag?
|
|
||||||
fReceiveNext = segment.sequence + 1;
|
|
||||||
// account for the extra sequence number for the synchronization
|
|
||||||
|
|
||||||
fInitialSendSequence = system_time() >> 4;
|
|
||||||
fSendNext = fInitialSendSequence;
|
|
||||||
fSendUnacknowledged = fSendNext;
|
|
||||||
fSendMax = fSendNext;
|
|
||||||
|
|
||||||
// set options
|
|
||||||
if ((parent->fOptions & TCP_NOOPT) == 0) {
|
|
||||||
if (segment.max_segment_size > 0)
|
|
||||||
fSendMaxSegmentSize = segment.max_segment_size;
|
|
||||||
else
|
|
||||||
fReceiveMaxSegmentSize = TCP_DEFAULT_MAX_SEGMENT_SIZE;
|
|
||||||
_CheckWindowScale(segment);
|
|
||||||
}
|
|
||||||
|
|
||||||
_UpdateTimestamps(segment, 0, false);
|
|
||||||
|
|
||||||
// send SYN+ACK
|
// send SYN+ACK
|
||||||
status_t status = _SendQueued();
|
if (_SendQueued() < B_OK)
|
||||||
|
|
||||||
fInitialSendSequence = fSendNext;
|
|
||||||
fSendQueue.SetInitialSequence(fSendNext);
|
|
||||||
|
|
||||||
if (status < B_OK)
|
|
||||||
return DROP;
|
return DROP;
|
||||||
|
|
||||||
segment.flags &= ~TCP_FLAG_SYNCHRONIZE;
|
segment.flags &= ~TCP_FLAG_SYNCHRONIZE;
|
||||||
@ -842,19 +777,7 @@ TCPEndpoint::_SynchronizeSentReceive(tcp_segment_header &segment, net_buffer *bu
|
|||||||
if ((segment.flags & TCP_FLAG_SYNCHRONIZE) == 0)
|
if ((segment.flags & TCP_FLAG_SYNCHRONIZE) == 0)
|
||||||
return DROP;
|
return DROP;
|
||||||
|
|
||||||
fInitialReceiveSequence = segment.sequence;
|
_PrepareReceivePath(NULL, segment);
|
||||||
segment.sequence++;
|
|
||||||
|
|
||||||
fSendUnacknowledged = segment.acknowledge;
|
|
||||||
fReceiveNext = segment.sequence;
|
|
||||||
fReceiveQueue.SetInitialSequence(fReceiveNext);
|
|
||||||
|
|
||||||
if (segment.max_segment_size > 0)
|
|
||||||
fSendMaxSegmentSize = segment.max_segment_size;
|
|
||||||
else
|
|
||||||
fReceiveMaxSegmentSize = TCP_DEFAULT_MAX_SEGMENT_SIZE;
|
|
||||||
|
|
||||||
_CheckWindowScale(segment);
|
|
||||||
|
|
||||||
if (segment.flags & TCP_FLAG_ACKNOWLEDGE) {
|
if (segment.flags & TCP_FLAG_ACKNOWLEDGE) {
|
||||||
_MarkEstablished();
|
_MarkEstablished();
|
||||||
@ -863,8 +786,6 @@ TCPEndpoint::_SynchronizeSentReceive(tcp_segment_header &segment, net_buffer *bu
|
|||||||
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
|
||||||
|
|
||||||
@ -929,7 +850,7 @@ TCPEndpoint::_SegmentReceived(tcp_segment_header &segment, net_buffer *buffer)
|
|||||||
&& advertisedWindow > 0 && advertisedWindow == fSendWindow
|
&& advertisedWindow > 0 && advertisedWindow == fSendWindow
|
||||||
&& fSendNext == fSendMax) {
|
&& fSendNext == fSendMax) {
|
||||||
|
|
||||||
_UpdateTimestamps(segment, buffer->size, true);
|
_UpdateTimestamps(segment, buffer->size);
|
||||||
|
|
||||||
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
|
||||||
@ -1079,7 +1000,7 @@ TCPEndpoint::_SendQueued(bool force)
|
|||||||
segment.flags = _CurrentFlags();
|
segment.flags = _CurrentFlags();
|
||||||
segment.urgent_offset = 0;
|
segment.urgent_offset = 0;
|
||||||
|
|
||||||
if (fOptions & FLAG_OPTION_TIMESTAMP) {
|
if (fFlags & FLAG_OPTION_TIMESTAMP) {
|
||||||
segment.has_timestamps = true;
|
segment.has_timestamps = true;
|
||||||
segment.TSecr = fReceivedTSval;
|
segment.TSecr = fReceivedTSval;
|
||||||
segment.TSval = 0;
|
segment.TSval = 0;
|
||||||
@ -1115,7 +1036,7 @@ TCPEndpoint::_SendQueued(bool force)
|
|||||||
|
|
||||||
size_t availableBytes = fReceiveQueue.Free();
|
size_t availableBytes = fReceiveQueue.Free();
|
||||||
|
|
||||||
if (fOptions & FLAG_OPTION_WINDOW_SCALE)
|
if (fFlags & FLAG_OPTION_WINDOW_SCALE)
|
||||||
segment.advertised_window = availableBytes >> fReceiveWindowShift;
|
segment.advertised_window = availableBytes >> fReceiveWindowShift;
|
||||||
else
|
else
|
||||||
segment.advertised_window = min_c(TCP_MAX_WINDOW, availableBytes);
|
segment.advertised_window = min_c(TCP_MAX_WINDOW, availableBytes);
|
||||||
@ -1166,10 +1087,13 @@ TCPEndpoint::_SendQueued(bool force)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE("SendQueued() flags %x, buffer %p, size %lu, from address %s to %s",
|
TRACE("SendQueued() buffer %p (%lu bytes) address %s to %s",
|
||||||
segment.flags, buffer, buffer->size,
|
buffer, buffer->size, AddressString(Domain(),
|
||||||
AddressString(Domain(), (sockaddr *)&buffer->source, true).Data(),
|
(sockaddr *)&buffer->source, true).Data(), AddressString(Domain(),
|
||||||
AddressString(Domain(), (sockaddr *)&buffer->destination, true).Data());
|
(sockaddr *)&buffer->destination, true).Data());
|
||||||
|
TRACE(" flags 0x%x, seq %lu, ack %lu, rwnd %hu",
|
||||||
|
segment.flags, segment.sequence, segment.acknowledge,
|
||||||
|
segment.advertised_window);
|
||||||
|
|
||||||
status = add_tcp_header(AddressModule(), segment, buffer);
|
status = add_tcp_header(AddressModule(), segment, buffer);
|
||||||
if (status != B_OK) {
|
if (status != B_OK) {
|
||||||
@ -1382,10 +1306,8 @@ TCPEndpoint::_Receive(tcp_segment_header &segment, net_buffer *buffer)
|
|||||||
|
|
||||||
if ((segment.flags & TCP_FLAG_ACKNOWLEDGE) != 0) {
|
if ((segment.flags & TCP_FLAG_ACKNOWLEDGE) != 0) {
|
||||||
// process acknowledged data
|
// process acknowledged data
|
||||||
if (fState == SYNCHRONIZE_RECEIVED) {
|
if (fState == SYNCHRONIZE_RECEIVED)
|
||||||
_MarkEstablished();
|
_MarkEstablished();
|
||||||
_CheckWindowScale(segment);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fSendMax < segment.acknowledge || fState == TIME_WAIT)
|
if (fSendMax < segment.acknowledge || fState == TIME_WAIT)
|
||||||
return DROP | IMMEDIATE_ACKNOWLEDGE;
|
return DROP | IMMEDIATE_ACKNOWLEDGE;
|
||||||
@ -1510,7 +1432,7 @@ 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);
|
_UpdateTimestamps(segment, segmentLength);
|
||||||
|
|
||||||
TRACE("Receive() Action %ld", action);
|
TRACE("Receive() Action %ld", action);
|
||||||
|
|
||||||
@ -1519,18 +1441,13 @@ TCPEndpoint::_Receive(tcp_segment_header &segment, net_buffer *buffer)
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
TCPEndpoint::_UpdateTimestamps(tcp_segment_header &segment, size_t segmentLength,
|
TCPEndpoint::_UpdateTimestamps(tcp_segment_header &segment,
|
||||||
bool checkSequence)
|
size_t segmentLength)
|
||||||
{
|
{
|
||||||
if (segment.has_timestamps)
|
if (fFlags & FLAG_OPTION_TIMESTAMP) {
|
||||||
fOptions |= FLAG_OPTION_TIMESTAMP;
|
|
||||||
else
|
|
||||||
fOptions &= ~FLAG_OPTION_TIMESTAMP;
|
|
||||||
|
|
||||||
if (fOptions & FLAG_OPTION_TIMESTAMP) {
|
|
||||||
tcp_sequence sequence(segment.sequence);
|
tcp_sequence sequence(segment.sequence);
|
||||||
|
|
||||||
if (!checkSequence || (fLastAcknowledgeSent >= sequence
|
if ((fLastAcknowledgeSent >= sequence
|
||||||
&& fLastAcknowledgeSent < (sequence + segmentLength)))
|
&& fLastAcknowledgeSent < (sequence + segmentLength)))
|
||||||
fReceivedTSval = segment.TSval;
|
fReceivedTSval = segment.TSval;
|
||||||
}
|
}
|
||||||
@ -1579,15 +1496,78 @@ TCPEndpoint::_AddData(tcp_segment_header &segment, net_buffer *buffer)
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
TCPEndpoint::_CheckWindowScale(tcp_segment_header &segment)
|
TCPEndpoint::_PrepareReceivePath(TCPEndpoint *parent,
|
||||||
|
tcp_segment_header &segment)
|
||||||
{
|
{
|
||||||
if (segment.has_window_shift) {
|
fInitialReceiveSequence = segment.sequence;
|
||||||
fFlags |= FLAG_OPTION_WINDOW_SCALE;
|
|
||||||
fSendWindowShift = segment.window_shift;
|
// count the received SYN
|
||||||
} else {
|
segment.sequence++;
|
||||||
fFlags &= ~FLAG_OPTION_WINDOW_SCALE;
|
|
||||||
fReceiveWindowShift = 0;
|
if (parent == NULL)
|
||||||
|
fSendUnacknowledged = segment.acknowledge;
|
||||||
|
fReceiveNext = segment.sequence;
|
||||||
|
fReceiveQueue.SetInitialSequence(segment.sequence);
|
||||||
|
|
||||||
|
if (parent) {
|
||||||
|
fOptions = parent->fOptions;
|
||||||
|
fAcceptSemaphore = parent->fAcceptSemaphore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((fOptions & TCP_NOOPT) == 0) {
|
||||||
|
if (segment.max_segment_size > 0)
|
||||||
|
fSendMaxSegmentSize = segment.max_segment_size;
|
||||||
|
|
||||||
|
if (segment.has_window_shift) {
|
||||||
|
fFlags |= FLAG_OPTION_WINDOW_SCALE;
|
||||||
|
fSendWindowShift = segment.window_shift;
|
||||||
|
} else {
|
||||||
|
fFlags &= ~FLAG_OPTION_WINDOW_SCALE;
|
||||||
|
fReceiveWindowShift = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (segment.has_timestamps)
|
||||||
|
fFlags |= FLAG_OPTION_TIMESTAMP;
|
||||||
|
else
|
||||||
|
fFlags &= ~FLAG_OPTION_TIMESTAMP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
TCPEndpoint::_PrepareSendPath(const sockaddr *peer)
|
||||||
|
{
|
||||||
|
if (fRoute == NULL) {
|
||||||
|
fRoute = gDatalinkModule->get_route(Domain(), peer);
|
||||||
|
if (fRoute == NULL)
|
||||||
|
return ENETUNREACH;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure connection does not already exist
|
||||||
|
status_t status = fManager->SetConnection(this,
|
||||||
|
(sockaddr *)&socket->address, peer, fRoute->interface->address);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
fInitialSendSequence = system_time() >> 4;
|
||||||
|
fSendNext = fInitialSendSequence;
|
||||||
|
fSendUnacknowledged = fInitialSendSequence;
|
||||||
|
fSendMax = fInitialSendSequence;
|
||||||
|
|
||||||
|
// we are counting the SYN here
|
||||||
|
fSendQueue.SetInitialSequence(fSendNext + 1);
|
||||||
|
|
||||||
|
fReceiveMaxSegmentSize = _GetMSS(peer);
|
||||||
|
|
||||||
|
// Compute the window shift we advertise to our peer - if it doesn't support
|
||||||
|
// this option, this will be reset to 0 (when its SYN is received)
|
||||||
|
fReceiveWindowShift = 0;
|
||||||
|
while (fReceiveWindowShift < TCP_MAX_WINDOW_SHIFT
|
||||||
|
&& (0xffffUL << fReceiveWindowShift) < socket->receive.buffer_size) {
|
||||||
|
fReceiveWindowShift++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -98,11 +98,13 @@ class TCPEndpoint : public net_protocol {
|
|||||||
int32 _SegmentReceived(tcp_segment_header& segment, net_buffer *buffer);
|
int32 _SegmentReceived(tcp_segment_header& segment, net_buffer *buffer);
|
||||||
int32 _Receive(tcp_segment_header& segment, net_buffer *buffer);
|
int32 _Receive(tcp_segment_header& segment, net_buffer *buffer);
|
||||||
void _UpdateTimestamps(tcp_segment_header& segment,
|
void _UpdateTimestamps(tcp_segment_header& segment,
|
||||||
size_t segmentLength, bool checkSequence);
|
size_t segmentLength);
|
||||||
void _MarkEstablished();
|
void _MarkEstablished();
|
||||||
status_t _WaitForEstablished(RecursiveLocker &lock, bigtime_t timeout);
|
status_t _WaitForEstablished(RecursiveLocker &lock, bigtime_t timeout);
|
||||||
void _AddData(tcp_segment_header &segment, net_buffer *buffer);
|
void _AddData(tcp_segment_header &segment, net_buffer *buffer);
|
||||||
void _CheckWindowScale(tcp_segment_header &segment);
|
void _PrepareReceivePath(TCPEndpoint *parent,
|
||||||
|
tcp_segment_header &segment);
|
||||||
|
status_t _PrepareSendPath(const sockaddr *peer);
|
||||||
|
|
||||||
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);
|
||||||
|
Loading…
Reference in New Issue
Block a user