* TIME_WAIT endpoints must not send an acknowledgement for known data; this

fixes the "endless discussions" when closing a local connection - only
  happened on a simultaneous close.
* A FIN in TIME_WAIT now updates the time-wait timer, as required by the
  TCP specification.
* Entering TIME_WAIT now cancels all connection timers. We might want to
  think about putting time wait connections into a separate hash, and delete
  the socket early on.
* Added tracing support for send/receive, timers, and state changes.
* Cleanup.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25236 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2008-04-29 07:39:58 +00:00
parent 5abeea69a5
commit 0cadc931d2
2 changed files with 236 additions and 79 deletions
@@ -10,7 +10,16 @@
#include "TCPEndpoint.h" #include "TCPEndpoint.h"
#include "EndpointManager.h"
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <new>
#include <stdlib.h>
#include <string.h>
#include <KernelExport.h>
#include <Select.h>
#include <net_buffer.h> #include <net_buffer.h>
#include <net_datalink.h> #include <net_datalink.h>
@@ -19,19 +28,12 @@
#include <NetUtilities.h> #include <NetUtilities.h>
#include <lock.h> #include <lock.h>
#include <tracing.h>
#include <util/AutoLock.h> #include <util/AutoLock.h>
#include <util/khash.h> #include <util/khash.h>
#include <util/list.h> #include <util/list.h>
#include <KernelExport.h> #include "EndpointManager.h"
#include <Select.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <new>
#include <stdlib.h>
#include <string.h>
// References: // References:
@@ -53,7 +55,7 @@
#define PrintAddress(address) \ #define PrintAddress(address) \
AddressString(Domain(), address, true).Data() AddressString(Domain(), address, true).Data()
//#define TRACE_TCP #define TRACE_TCP
//#define PROBE_TCP //#define PROBE_TCP
#ifdef TRACE_TCP #ifdef TRACE_TCP
@@ -77,6 +79,133 @@
# define PROBE(buffer, window) do { } while (0) # define PROBE(buffer, window) do { } while (0)
#endif #endif
#if TCP_TRACING
namespace TCPTracing {
class Receive : public AbstractTraceEntry {
public:
Receive(TCPEndpoint* endpoint, tcp_segment_header& segment, uint32 window,
net_buffer* buffer)
:
fEndpoint(endpoint),
fBuffer(buffer),
fBufferSize(buffer->size),
fSequence(segment.sequence),
fAcknowledge(segment.acknowledge),
fWindow(window),
fState(endpoint->State()),
fFlags(segment.flags)
{
Initialized();
}
virtual void AddDump(TraceOutput& out)
{
out.Print("tcp:%p (%12s) receive buffer %p (%lu bytes), flags %x, "
"seq %lu, ack %lu, wnd %lu", fEndpoint, name_for_state(fState),
fBuffer, fBufferSize, fFlags, fSequence, fAcknowledge, fWindow);
}
protected:
TCPEndpoint* fEndpoint;
net_buffer* fBuffer;
uint32 fBufferSize;
uint32 fSequence;
uint32 fAcknowledge;
uint32 fWindow;
tcp_state fState;
uint8 fFlags;
};
class Send : public AbstractTraceEntry {
public:
Send(TCPEndpoint* endpoint, tcp_segment_header& segment, net_buffer* buffer,
uint32 firstSequence, uint32 lastSequence)
:
fEndpoint(endpoint),
fBuffer(buffer),
fBufferSize(buffer->size),
fSequence(segment.sequence),
fAcknowledge(segment.acknowledge),
fFirstSequence(firstSequence),
fLastSequence(lastSequence),
fState(endpoint->State()),
fFlags(segment.flags)
{
Initialized();
}
virtual void AddDump(TraceOutput& out)
{
out.Print("tcp:%p (%12s) send buffer %p (%lu bytes), flags %x, "
"seq %lu, ack %lu, first %lu, last %lu",
fEndpoint, name_for_state(fState), fBuffer, fBufferSize, fFlags,
fSequence, fAcknowledge, fFirstSequence, fLastSequence);
}
protected:
TCPEndpoint* fEndpoint;
net_buffer* fBuffer;
uint32 fBufferSize;
uint32 fSequence;
uint32 fAcknowledge;
uint32 fFirstSequence;
uint32 fLastSequence;
tcp_state fState;
uint8 fFlags;
};
class State : public AbstractTraceEntry {
public:
State(TCPEndpoint* endpoint)
:
fEndpoint(endpoint),
fState(endpoint->State())
{
Initialized();
}
virtual void AddDump(TraceOutput& out)
{
out.Print("tcp:%p (%12s) state change", fEndpoint,
name_for_state(fState));
}
protected:
TCPEndpoint* fEndpoint;
tcp_state fState;
};
class Timer : public AbstractTraceEntry {
public:
Timer(TCPEndpoint* endpoint, const char* which)
:
fEndpoint(endpoint),
fWhich(which),
fState(endpoint->State())
{
Initialized();
}
virtual void AddDump(TraceOutput& out)
{
out.Print("tcp:%p (%12s) %s timer", fEndpoint,
name_for_state(fState), fWhich);
}
protected:
TCPEndpoint* fEndpoint;
const char* fWhich;
tcp_state fState;
};
} // namespace TCPTracing
# define T(x) new(std::nothrow) TCPTracing::x
#else
# define T(x)
#endif // TCP_TRACING
// Initial estimate for packet round trip time (RTT) // Initial estimate for packet round trip time (RTT)
#define TCP_INITIAL_RTT 2000000 #define TCP_INITIAL_RTT 2000000
@@ -343,6 +472,7 @@ TCPEndpoint::Close()
if (fState == SYNCHRONIZE_SENT || fState == LISTEN) { if (fState == SYNCHRONIZE_SENT || fState == LISTEN) {
// TODO: what about linger in case of SYNCHRONIZE_SENT? // TODO: what about linger in case of SYNCHRONIZE_SENT?
fState = CLOSED; fState = CLOSED;
T(State(this));
return B_OK; return B_OK;
} }
@@ -434,11 +564,13 @@ TCPEndpoint::Connect(const sockaddr* address)
TRACE(" Connect(): starting 3-way handshake..."); TRACE(" Connect(): starting 3-way handshake...");
fState = SYNCHRONIZE_SENT; fState = SYNCHRONIZE_SENT;
T(State(this));
// send SYN // send SYN
status = _SendQueued(); status = _SendQueued();
if (status != B_OK) { if (status != B_OK) {
fState = CLOSED; fState = CLOSED;
T(State(this));
return status; return status;
} }
@@ -550,6 +682,7 @@ TCPEndpoint::Listen(int count)
} }
fState = LISTEN; fState = LISTEN;
T(State(this));
return B_OK; return B_OK;
} }
@@ -837,20 +970,21 @@ TCPEndpoint::_EnterTimeWait()
{ {
TRACE("_EnterTimeWait()\n"); TRACE("_EnterTimeWait()\n");
_CancelConnectionTimers();
#if 0 #if 0
if (fState == TIME_WAIT && fRoute != NULL if (fState == TIME_WAIT && fRoute != NULL
&& (fRoute->flags & RTF_LOCAL) != 0) && (fRoute->flags & RTF_LOCAL) != 0)
return; return;
#endif #endif
gStackModule->set_timer(&fTimeWaitTimer, TCP_MAX_SEGMENT_LIFETIME << 1); _UpdateTimeWait();
} }
status_t void
TCPEndpoint::_UpdateTimeWait() TCPEndpoint::_UpdateTimeWait()
{ {
return B_OK; gStackModule->set_timer(&fTimeWaitTimer, TCP_MAX_SEGMENT_LIFETIME << 1);
} }
@@ -878,9 +1012,12 @@ TCPEndpoint::_Disconnect(bool closing)
else else
return B_OK; return B_OK;
T(State(this));
status_t status = _SendQueued(); status_t status = _SendQueued();
if (status != B_OK) { if (status != B_OK) {
fState = previousState; fState = previousState;
T(State(this));
return status; return status;
} }
@@ -892,6 +1029,7 @@ void
TCPEndpoint::_MarkEstablished() TCPEndpoint::_MarkEstablished()
{ {
fState = ESTABLISHED; fState = ESTABLISHED;
T(State(this));
if (socket->parent != NULL) { if (socket->parent != NULL) {
gSocketModule->set_connected(socket); gSocketModule->set_connected(socket);
@@ -928,6 +1066,7 @@ TCPEndpoint::_HandleReset(status_t error)
socket->error = error; socket->error = error;
fState = CLOSED; fState = CLOSED;
T(State(this));
fSendList.Signal(); fSendList.Signal();
_NotifyReader(); _NotifyReader();
@@ -997,6 +1136,57 @@ TCPEndpoint::_NotifyReader()
} }
bool
TCPEndpoint::_AddData(tcp_segment_header& segment, net_buffer* buffer)
{
fReceiveQueue.Add(buffer, segment.sequence);
fReceiveNext = fReceiveQueue.NextSequence();
TRACE(" _AddData(): adding data, receive next = %lu. Now have %lu bytes.",
(uint32)fReceiveNext, fReceiveQueue.Available());
if (segment.flags & TCP_FLAG_PUSH)
fReceiveQueue.SetPushPointer();
return fReceiveQueue.Available() > 0;
}
void
TCPEndpoint::_PrepareReceivePath(tcp_segment_header& segment)
{
fInitialReceiveSequence = segment.sequence;
// count the received SYN
segment.sequence++;
fReceiveNext = segment.sequence;
fReceiveQueue.SetInitialSequence(segment.sequence);
if ((fOptions & TCP_NOOPT) == 0) {
if (segment.max_segment_size > 0)
fSendMaxSegmentSize = segment.max_segment_size;
if (segment.options & TCP_HAS_WINDOW_SCALE) {
fFlags |= FLAG_OPTION_WINDOW_SCALE;
fSendWindowShift = segment.window_shift;
} else {
fFlags &= ~FLAG_OPTION_WINDOW_SCALE;
fReceiveWindowShift = 0;
}
if (segment.options & TCP_HAS_TIMESTAMPS) {
fFlags |= FLAG_OPTION_TIMESTAMP;
fReceivedTimestamp = segment.timestamp_value;
} else
fFlags &= ~FLAG_OPTION_TIMESTAMP;
}
fCongestionWindow = 2 * fSendMaxSegmentSize;
fSlowStartThreshold = (uint32)segment.advertised_window << fSendWindowShift;
}
bool bool
TCPEndpoint::_ShouldReceive() const TCPEndpoint::_ShouldReceive() const
{ {
@@ -1018,6 +1208,7 @@ TCPEndpoint::_Spawn(TCPEndpoint* parent, tcp_segment_header& segment,
ProtocolSocket::Open(); ProtocolSocket::Open();
fState = SYNCHRONIZE_RECEIVED; fState = SYNCHRONIZE_RECEIVED;
T(State(this));
fManager = parent->fManager; fManager = parent->fManager;
LocalAddress().SetTo(buffer->destination); LocalAddress().SetTo(buffer->destination);
@@ -1101,6 +1292,7 @@ TCPEndpoint::_SynchronizeSentReceive(tcp_segment_header &segment,
} else { } else {
// simultaneous open // simultaneous open
fState = SYNCHRONIZE_RECEIVED; fState = SYNCHRONIZE_RECEIVED;
T(State(this));
} }
segment.flags &= ~TCP_FLAG_SYNCHRONIZE; segment.flags &= ~TCP_FLAG_SYNCHRONIZE;
@@ -1260,7 +1452,7 @@ TCPEndpoint::_Receive(tcp_segment_header& segment, net_buffer* buffer)
if (fState == SYNCHRONIZE_RECEIVED) if (fState == SYNCHRONIZE_RECEIVED)
_MarkEstablished(); _MarkEstablished();
if (fSendMax < segment.acknowledge || fState == TIME_WAIT) if (fSendMax < segment.acknowledge)
return DROP | IMMEDIATE_ACKNOWLEDGE; return DROP | IMMEDIATE_ACKNOWLEDGE;
if (segment.acknowledge < fSendUnacknowledged) { if (segment.acknowledge < fSendUnacknowledged) {
@@ -1295,6 +1487,7 @@ TCPEndpoint::_Receive(tcp_segment_header& segment, net_buffer* buffer)
break; break;
case CLOSING: case CLOSING:
fState = TIME_WAIT; fState = TIME_WAIT;
T(State(this));
_EnterTimeWait(); _EnterTimeWait();
return DROP; return DROP;
case WAIT_FOR_FINISH_ACKNOWLEDGE: case WAIT_FOR_FINISH_ACKNOWLEDGE:
@@ -1305,6 +1498,8 @@ TCPEndpoint::_Receive(tcp_segment_header& segment, net_buffer* buffer)
default: default:
break; break;
} }
T(State(this));
} }
if (fState != CLOSED) if (fState != CLOSED)
@@ -1338,7 +1533,7 @@ TCPEndpoint::_Receive(tcp_segment_header& segment, net_buffer* buffer)
// FIN implies PSH // FIN implies PSH
fReceiveQueue.SetPushPointer(); fReceiveQueue.SetPushPointer();
// we'll reply immediatly to the FIN if we are not // we'll reply immediately to the FIN if we are not
// transitioning to TIME WAIT so we immediatly ACK it. // transitioning to TIME WAIT so we immediatly ACK it.
action |= IMMEDIATE_ACKNOWLEDGE; action |= IMMEDIATE_ACKNOWLEDGE;
@@ -1347,15 +1542,22 @@ TCPEndpoint::_Receive(tcp_segment_header& segment, net_buffer* buffer)
case ESTABLISHED: case ESTABLISHED:
case SYNCHRONIZE_RECEIVED: case SYNCHRONIZE_RECEIVED:
fState = FINISH_RECEIVED; fState = FINISH_RECEIVED;
T(State(this));
break; break;
case FINISH_SENT: case FINISH_SENT:
// simultaneous close // simultaneous close
fState = CLOSING; fState = CLOSING;
T(State(this));
break; break;
case FINISH_ACKNOWLEDGED: case FINISH_ACKNOWLEDGED:
fState = TIME_WAIT; fState = TIME_WAIT;
T(State(this));
_EnterTimeWait(); _EnterTimeWait();
break; break;
case TIME_WAIT:
_UpdateTimeWait();
break;
default: default:
break; break;
} }
@@ -1381,13 +1583,14 @@ TCPEndpoint::SegmentReceived(tcp_segment_header& segment, net_buffer* buffer)
{ {
MutexLocker locker(fLock); MutexLocker locker(fLock);
TRACE("SegmentReceived(): buffer %p (%lu bytes) address %s to %s", TRACE("SegmentReceived(): buffer %p (%lu bytes) address %s to %s\n"
"\tflags 0x%x, seq %lu, ack %lu, wnd %lu",
buffer, buffer->size, PrintAddress(buffer->source), buffer, buffer->size, PrintAddress(buffer->source),
PrintAddress(buffer->destination)); PrintAddress(buffer->destination), segment.flags, segment.sequence,
TRACE(" flags 0x%x, seq %lu, ack %lu, wnd %lu", segment.acknowledge,
segment.flags, segment.sequence, segment.acknowledge,
(uint32)segment.advertised_window << fSendWindowShift); (uint32)segment.advertised_window << fSendWindowShift);
T(Receive(this, segment,
(uint32)segment.advertised_window << fSendWindowShift, buffer));
int32 segmentAction = DROP; int32 segmentAction = DROP;
switch (fState) { switch (fState) {
@@ -1619,16 +1822,17 @@ TCPEndpoint::_SendQueued(bool force, uint32 sendWindow)
uint32 size = buffer->size; uint32 size = buffer->size;
segment.sequence = fSendNext; segment.sequence = fSendNext;
TRACE("SendQueued(): buffer %p (%lu bytes) address %s to %s", TRACE("SendQueued(): buffer %p (%lu bytes) address %s to %s\n"
"\tflags 0x%x, seq %lu, ack %lu, rwnd %hu, cwnd %lu, ssthresh %lu\n"
"\tlen %lu first %lu last %lu",
buffer, buffer->size, PrintAddress(buffer->source), buffer, buffer->size, PrintAddress(buffer->source),
PrintAddress(buffer->destination)); PrintAddress(buffer->destination), segment.flags, segment.sequence,
TRACE(" flags 0x%x, seq %lu, ack %lu, rwnd %hu, cwnd %lu"
", ssthresh %lu", segment.flags, segment.sequence,
segment.acknowledge, segment.advertised_window, segment.acknowledge, segment.advertised_window,
fCongestionWindow, fSlowStartThreshold); fCongestionWindow, fSlowStartThreshold, segmentLength,
TRACE(" len %lu first %lu last %lu", segmentLength,
(uint32)fSendQueue.FirstSequence(), (uint32)fSendQueue.FirstSequence(),
(uint32)fSendQueue.LastSequence()); (uint32)fSendQueue.LastSequence());
T(Send(this, segment, buffer, fSendQueue.FirstSequence(),
fSendQueue.LastSequence()));
PROBE(buffer, sendWindow); PROBE(buffer, sendWindow);
sendWindow -= buffer->size; sendWindow -= buffer->size;
@@ -1698,57 +1902,6 @@ TCPEndpoint::_MaxSegmentSize(const sockaddr* address) const
} }
bool
TCPEndpoint::_AddData(tcp_segment_header& segment, net_buffer* buffer)
{
fReceiveQueue.Add(buffer, segment.sequence);
fReceiveNext = fReceiveQueue.NextSequence();
TRACE(" _AddData(): adding data, receive next = %lu. Now have %lu bytes.",
(uint32)fReceiveNext, fReceiveQueue.Available());
if (segment.flags & TCP_FLAG_PUSH)
fReceiveQueue.SetPushPointer();
return fReceiveQueue.Available() > 0;
}
void
TCPEndpoint::_PrepareReceivePath(tcp_segment_header& segment)
{
fInitialReceiveSequence = segment.sequence;
// count the received SYN
segment.sequence++;
fReceiveNext = segment.sequence;
fReceiveQueue.SetInitialSequence(segment.sequence);
if ((fOptions & TCP_NOOPT) == 0) {
if (segment.max_segment_size > 0)
fSendMaxSegmentSize = segment.max_segment_size;
if (segment.options & TCP_HAS_WINDOW_SCALE) {
fFlags |= FLAG_OPTION_WINDOW_SCALE;
fSendWindowShift = segment.window_shift;
} else {
fFlags &= ~FLAG_OPTION_WINDOW_SCALE;
fReceiveWindowShift = 0;
}
if (segment.options & TCP_HAS_TIMESTAMPS) {
fFlags |= FLAG_OPTION_TIMESTAMP;
fReceivedTimestamp = segment.timestamp_value;
} else
fFlags &= ~FLAG_OPTION_TIMESTAMP;
}
fCongestionWindow = 2 * fSendMaxSegmentSize;
fSlowStartThreshold = (uint32)segment.advertised_window << fSendWindowShift;
}
status_t status_t
TCPEndpoint::_PrepareSendPath(const sockaddr* peer) TCPEndpoint::_PrepareSendPath(const sockaddr* peer)
{ {
@@ -1883,6 +2036,7 @@ TCPEndpoint::_ResetSlowStart()
TCPEndpoint::_RetransmitTimer(net_timer* timer, void* _endpoint) TCPEndpoint::_RetransmitTimer(net_timer* timer, void* _endpoint)
{ {
TCPEndpoint* endpoint = (TCPEndpoint*)_endpoint; TCPEndpoint* endpoint = (TCPEndpoint*)_endpoint;
T(Timer(endpoint, "retransmit"));
MutexLocker locker(endpoint->fLock); MutexLocker locker(endpoint->fLock);
if (!locker.IsLocked()) if (!locker.IsLocked())
@@ -1896,6 +2050,7 @@ TCPEndpoint::_RetransmitTimer(net_timer* timer, void* _endpoint)
TCPEndpoint::_PersistTimer(net_timer* timer, void* _endpoint) TCPEndpoint::_PersistTimer(net_timer* timer, void* _endpoint)
{ {
TCPEndpoint* endpoint = (TCPEndpoint*)_endpoint; TCPEndpoint* endpoint = (TCPEndpoint*)_endpoint;
T(Timer(endpoint, "persist"));
MutexLocker locker(endpoint->fLock); MutexLocker locker(endpoint->fLock);
if (!locker.IsLocked()) if (!locker.IsLocked())
@@ -1913,6 +2068,7 @@ TCPEndpoint::_PersistTimer(net_timer* timer, void* _endpoint)
TCPEndpoint::_DelayedAcknowledgeTimer(net_timer* timer, void* _endpoint) TCPEndpoint::_DelayedAcknowledgeTimer(net_timer* timer, void* _endpoint)
{ {
TCPEndpoint* endpoint = (TCPEndpoint*)_endpoint; TCPEndpoint* endpoint = (TCPEndpoint*)_endpoint;
T(Timer(endpoint, "delayed ack"));
MutexLocker locker(endpoint->fLock); MutexLocker locker(endpoint->fLock);
if (!locker.IsLocked()) if (!locker.IsLocked())
@@ -1930,6 +2086,7 @@ TCPEndpoint::_DelayedAcknowledgeTimer(net_timer* timer, void* _endpoint)
TCPEndpoint::_TimeWaitTimer(net_timer* timer, void* _endpoint) TCPEndpoint::_TimeWaitTimer(net_timer* timer, void* _endpoint)
{ {
TCPEndpoint* endpoint = (TCPEndpoint*)_endpoint; TCPEndpoint* endpoint = (TCPEndpoint*)_endpoint;
T(Timer(endpoint, "time-wait"));
gSocketModule->delete_socket(endpoint->socket); gSocketModule->delete_socket(endpoint->socket);
} }
@@ -84,7 +84,7 @@ public:
private: private:
void _StartPersistTimer(); void _StartPersistTimer();
void _EnterTimeWait(); void _EnterTimeWait();
status_t _UpdateTimeWait(); void _UpdateTimeWait();
void _CancelConnectionTimers(); void _CancelConnectionTimers();
uint8 _CurrentFlags(); uint8 _CurrentFlags();
bool _ShouldSendSegment(tcp_segment_header& segment, bool _ShouldSendSegment(tcp_segment_header& segment,