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:
Hugo Santos 2007-04-17 03:39:24 +00:00
parent aa07700795
commit f19e1f47da
2 changed files with 101 additions and 119 deletions

View File

@ -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;
} }

View File

@ -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);