assorted TCP fixes.
- fixed the locking for spawned connections and accept()s. - return EMSGSIZE if the user is trying to write more data than the send buffer can hold. - fixed a crash when receiving a RST while the connection is being closed. - don't wake up readers when the connection gets established. - endpoint managers lock must be recursive to properly work with spawn'ed sockets. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20727 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
f56b0aa00d
commit
4ee088419f
|
@ -45,6 +45,7 @@ class BufferQueue {
|
||||||
|
|
||||||
size_t Used() const { return fNumBytes; }
|
size_t Used() const { return fNumBytes; }
|
||||||
size_t Free() const { return fMaxBytes - fNumBytes; }
|
size_t Free() const { return fMaxBytes - fNumBytes; }
|
||||||
|
size_t Size() const { return fMaxBytes; }
|
||||||
|
|
||||||
bool IsContiguous() const { return fNumBytes == fContiguousBytes; }
|
bool IsContiguous() const { return fNumBytes == fContiguousBytes; }
|
||||||
|
|
||||||
|
|
|
@ -256,6 +256,7 @@ status_t
|
||||||
TCPEndpoint::Close()
|
TCPEndpoint::Close()
|
||||||
{
|
{
|
||||||
TRACE("Close()");
|
TRACE("Close()");
|
||||||
|
|
||||||
RecursiveLocker lock(fLock);
|
RecursiveLocker lock(fLock);
|
||||||
|
|
||||||
if (fState == LISTEN)
|
if (fState == LISTEN)
|
||||||
|
@ -270,8 +271,6 @@ TCPEndpoint::Close()
|
||||||
if (status != B_OK)
|
if (status != B_OK)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
TRACE("Close() after Shutdown()");
|
|
||||||
|
|
||||||
if (socket->options & SO_LINGER) {
|
if (socket->options & SO_LINGER) {
|
||||||
TRACE("Close(): Lingering for %i secs", socket->linger);
|
TRACE("Close(): Lingering for %i secs", socket->linger);
|
||||||
|
|
||||||
|
@ -299,6 +298,8 @@ TCPEndpoint::Free()
|
||||||
{
|
{
|
||||||
TRACE("Free()");
|
TRACE("Free()");
|
||||||
|
|
||||||
|
RecursiveLocker _(fLock);
|
||||||
|
|
||||||
if (fState <= SYNCHRONIZE_SENT || fState == TIME_WAIT)
|
if (fState <= SYNCHRONIZE_SENT || fState == TIME_WAIT)
|
||||||
return B_OK;
|
return B_OK;
|
||||||
|
|
||||||
|
@ -319,7 +320,7 @@ TCPEndpoint::Connect(const struct sockaddr *address)
|
||||||
TRACE("Connect() on address %s",
|
TRACE("Connect() on address %s",
|
||||||
AddressString(Domain(), address, true).Data());
|
AddressString(Domain(), address, true).Data());
|
||||||
|
|
||||||
RecursiveLocker locker(&fLock);
|
RecursiveLocker locker(fLock);
|
||||||
|
|
||||||
// Can only call connect() from CLOSED or LISTEN states
|
// Can only call connect() from CLOSED or LISTEN states
|
||||||
// otherwise endpoint is considered already connected
|
// otherwise endpoint is considered already connected
|
||||||
|
@ -391,11 +392,8 @@ TCPEndpoint::Connect(const struct sockaddr *address)
|
||||||
return EINPROGRESS;
|
return EINPROGRESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (status == B_OK && fState != ESTABLISHED)
|
status = _WaitForEstablished(locker, absolute_timeout(timeout));
|
||||||
status = fSendList.Wait(locker, absolute_timeout(timeout));
|
|
||||||
|
|
||||||
TRACE(" Connect(): Connection complete: %s", strerror(status));
|
TRACE(" Connect(): Connection complete: %s", strerror(status));
|
||||||
|
|
||||||
return posix_error(status);
|
return posix_error(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -405,15 +403,20 @@ TCPEndpoint::Accept(struct net_socket **_acceptedSocket)
|
||||||
{
|
{
|
||||||
TRACE("Accept()");
|
TRACE("Accept()");
|
||||||
|
|
||||||
|
RecursiveLocker locker(fLock);
|
||||||
|
|
||||||
status_t status;
|
status_t status;
|
||||||
bigtime_t timeout = absolute_timeout(socket->receive.timeout);
|
bigtime_t timeout = absolute_timeout(socket->receive.timeout);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
status = acquire_sem_etc(fAcceptSemaphore, 1, B_ABSOLUTE_TIMEOUT |
|
locker.Unlock();
|
||||||
B_CAN_INTERRUPT, timeout);
|
|
||||||
|
status = acquire_sem_etc(fAcceptSemaphore, 1, B_ABSOLUTE_TIMEOUT
|
||||||
|
| B_CAN_INTERRUPT, timeout);
|
||||||
if (status < B_OK)
|
if (status < B_OK)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
|
locker.Lock();
|
||||||
status = gSocketModule->dequeue_connected(socket, _acceptedSocket);
|
status = gSocketModule->dequeue_connected(socket, _acceptedSocket);
|
||||||
if (status == B_OK)
|
if (status == B_OK)
|
||||||
TRACE(" Accept() returning %p", (*_acceptedSocket)->first_protocol);
|
TRACE(" Accept() returning %p", (*_acceptedSocket)->first_protocol);
|
||||||
|
@ -429,11 +432,11 @@ TCPEndpoint::Bind(sockaddr *address)
|
||||||
if (address == NULL)
|
if (address == NULL)
|
||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
RecursiveLocker lock(fLock);
|
||||||
|
|
||||||
TRACE("Bind() on address %s",
|
TRACE("Bind() on address %s",
|
||||||
AddressString(Domain(), address, true).Data());
|
AddressString(Domain(), address, true).Data());
|
||||||
|
|
||||||
RecursiveLocker lock(fLock);
|
|
||||||
|
|
||||||
if (fState != CLOSED)
|
if (fState != CLOSED)
|
||||||
return EISCONN;
|
return EISCONN;
|
||||||
|
|
||||||
|
@ -478,6 +481,9 @@ TCPEndpoint::Listen(int count)
|
||||||
return EDESTADDRREQ;
|
return EDESTADDRREQ;
|
||||||
|
|
||||||
fAcceptSemaphore = create_sem(0, "tcp accept");
|
fAcceptSemaphore = create_sem(0, "tcp accept");
|
||||||
|
if (fAcceptSemaphore < B_OK)
|
||||||
|
return ENOBUFS;
|
||||||
|
|
||||||
fState = LISTEN;
|
fState = LISTEN;
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
@ -524,6 +530,9 @@ TCPEndpoint::SendData(net_buffer *buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buffer->size > 0) {
|
if (buffer->size > 0) {
|
||||||
|
if (buffer->size > fSendQueue.Size())
|
||||||
|
return EMSGSIZE;
|
||||||
|
|
||||||
bigtime_t timeout = absolute_timeout(socket->send.timeout);
|
bigtime_t timeout = absolute_timeout(socket->send.timeout);
|
||||||
|
|
||||||
while (fSendQueue.Free() < buffer->size) {
|
while (fSendQueue.Free() < buffer->size) {
|
||||||
|
@ -579,12 +588,9 @@ TCPEndpoint::ReadData(size_t numBytes, uint32 flags, net_buffer** _buffer)
|
||||||
if (flags & MSG_DONTWAIT)
|
if (flags & MSG_DONTWAIT)
|
||||||
return B_WOULD_BLOCK;
|
return B_WOULD_BLOCK;
|
||||||
|
|
||||||
while (fState != ESTABLISHED) {
|
status_t status = _WaitForEstablished(locker, timeout);
|
||||||
// we need to wait until the connection becomes established
|
if (status < B_OK)
|
||||||
status_t status = fSendList.Wait(locker, timeout);
|
return posix_error(status);
|
||||||
if (status < B_OK)
|
|
||||||
return posix_error(status);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t dataNeeded = socket->receive.low_water_mark;
|
size_t dataNeeded = socket->receive.low_water_mark;
|
||||||
|
@ -727,10 +733,9 @@ TCPEndpoint::UpdateTimeWait()
|
||||||
|
|
||||||
|
|
||||||
int32
|
int32
|
||||||
TCPEndpoint::ListenReceive(tcp_segment_header &segment, net_buffer *buffer)
|
TCPEndpoint::_ListenReceive(tcp_segment_header &segment, net_buffer *buffer)
|
||||||
{
|
{
|
||||||
TRACE("ListenReceive(): packet %p (%lu bytes) with flags 0x%x, seq %lu, ack %lu!",
|
TRACE("ListenReceive()");
|
||||||
buffer, buffer->size, segment.flags, segment.sequence, segment.acknowledge);
|
|
||||||
|
|
||||||
// Essentially, we accept only TCP_FLAG_SYNCHRONIZE in this state,
|
// Essentially, we accept only TCP_FLAG_SYNCHRONIZE in this state,
|
||||||
// but the error behaviour differs
|
// but the error behaviour differs
|
||||||
|
@ -753,60 +758,71 @@ 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);
|
||||||
|
|
||||||
TCPEndpoint *endpoint = (TCPEndpoint *)newSocket->first_protocol;
|
return ((TCPEndpoint *)newSocket->first_protocol)->Spawn(this, segment, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
endpoint->fSpawned = true;
|
|
||||||
|
int32
|
||||||
|
TCPEndpoint::Spawn(TCPEndpoint *parent, tcp_segment_header &segment,
|
||||||
|
net_buffer *buffer)
|
||||||
|
{
|
||||||
|
RecursiveLocker _(fLock);
|
||||||
|
|
||||||
|
fState = SYNCHRONIZE_RECEIVED;
|
||||||
|
fManager = parent->fManager;
|
||||||
|
|
||||||
|
TRACE("Spawn()");
|
||||||
|
|
||||||
|
fSpawned = true;
|
||||||
|
|
||||||
|
sockaddr *local = (sockaddr *)&socket->address;
|
||||||
|
sockaddr *peer = (sockaddr *)&socket->peer;
|
||||||
|
|
||||||
// TODO: proper error handling!
|
// TODO: proper error handling!
|
||||||
|
|
||||||
endpoint->fRoute = gDatalinkModule->get_route(Domain(),
|
fRoute = gDatalinkModule->get_route(Domain(), peer);
|
||||||
(sockaddr *)&newSocket->peer);
|
if (fRoute == NULL)
|
||||||
if (endpoint->fRoute == NULL)
|
|
||||||
return DROP;
|
return DROP;
|
||||||
|
|
||||||
if (fManager->SetConnection(endpoint, (sockaddr *)&buffer->destination,
|
if (fManager->SetConnection(this, local, peer, NULL) < B_OK)
|
||||||
(sockaddr *)&buffer->source, NULL) < B_OK)
|
|
||||||
return DROP;
|
return DROP;
|
||||||
|
|
||||||
endpoint->fInitialReceiveSequence = segment.sequence;
|
fInitialReceiveSequence = segment.sequence;
|
||||||
endpoint->fReceiveQueue.SetInitialSequence(segment.sequence + 1);
|
fReceiveQueue.SetInitialSequence(segment.sequence + 1);
|
||||||
endpoint->fState = SYNCHRONIZE_RECEIVED;
|
fAcceptSemaphore = parent->fAcceptSemaphore;
|
||||||
endpoint->fAcceptSemaphore = fAcceptSemaphore;
|
fReceiveMaxSegmentSize = _GetMSS(peer);
|
||||||
endpoint->fReceiveMaxSegmentSize = _GetMSS((sockaddr *)&newSocket->peer);
|
|
||||||
// 40 bytes for IP and TCP header without any options
|
// 40 bytes for IP and TCP header without any options
|
||||||
// TODO: make this depending on the RTF_LOCAL flag?
|
// TODO: make this depending on the RTF_LOCAL flag?
|
||||||
endpoint->fReceiveNext = segment.sequence + 1;
|
fReceiveNext = segment.sequence + 1;
|
||||||
// account for the extra sequence number for the synchronization
|
// account for the extra sequence number for the synchronization
|
||||||
|
|
||||||
endpoint->fInitialSendSequence = system_time() >> 4;
|
fInitialSendSequence = system_time() >> 4;
|
||||||
endpoint->fSendNext = endpoint->fInitialSendSequence;
|
fSendNext = fInitialSendSequence;
|
||||||
endpoint->fSendUnacknowledged = endpoint->fSendNext;
|
fSendUnacknowledged = fSendNext;
|
||||||
endpoint->fSendMax = endpoint->fSendNext;
|
fSendMax = fSendNext;
|
||||||
|
|
||||||
// set options
|
// set options
|
||||||
if ((fOptions & TCP_NOOPT) == 0) {
|
if ((parent->fOptions & TCP_NOOPT) == 0) {
|
||||||
if (segment.max_segment_size > 0)
|
if (segment.max_segment_size > 0)
|
||||||
endpoint->fSendMaxSegmentSize = segment.max_segment_size;
|
fSendMaxSegmentSize = segment.max_segment_size;
|
||||||
else
|
else
|
||||||
endpoint->fReceiveMaxSegmentSize = TCP_DEFAULT_MAX_SEGMENT_SIZE;
|
fReceiveMaxSegmentSize = TCP_DEFAULT_MAX_SEGMENT_SIZE;
|
||||||
if (segment.has_window_shift) {
|
if (segment.has_window_shift) {
|
||||||
endpoint->fFlags |= FLAG_OPTION_WINDOW_SHIFT;
|
fFlags |= FLAG_OPTION_WINDOW_SHIFT;
|
||||||
endpoint->fSendWindowShift = segment.window_shift;
|
fSendWindowShift = segment.window_shift;
|
||||||
} else {
|
} else {
|
||||||
endpoint->fFlags &= ~FLAG_OPTION_WINDOW_SHIFT;
|
fFlags &= ~FLAG_OPTION_WINDOW_SHIFT;
|
||||||
endpoint->fReceiveWindowShift = 0;
|
fReceiveWindowShift = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE(" ListenReceive() created new endpoint %p", endpoint);
|
_UpdateTimestamps(segment, 0, false);
|
||||||
|
|
||||||
endpoint->_UpdateTimestamps(segment, 0, false);
|
|
||||||
|
|
||||||
// send SYN+ACK
|
// send SYN+ACK
|
||||||
status_t status = endpoint->_SendQueued();
|
status_t status = _SendQueued();
|
||||||
|
|
||||||
endpoint->fInitialSendSequence = endpoint->fSendNext;
|
fInitialSendSequence = fSendNext;
|
||||||
endpoint->fSendQueue.SetInitialSequence(endpoint->fSendNext);
|
fSendQueue.SetInitialSequence(fSendNext);
|
||||||
|
|
||||||
if (status < B_OK)
|
if (status < B_OK)
|
||||||
return DROP;
|
return DROP;
|
||||||
|
@ -814,16 +830,14 @@ TCPEndpoint::ListenReceive(tcp_segment_header &segment, net_buffer *buffer)
|
||||||
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 endpoint->_Receive(segment, buffer);
|
return _Receive(segment, buffer);
|
||||||
// TODO: here, the ack/delayed ack call will be made on the parent socket!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int32
|
int32
|
||||||
TCPEndpoint::SynchronizeSentReceive(tcp_segment_header &segment, net_buffer *buffer)
|
TCPEndpoint::_SynchronizeSentReceive(tcp_segment_header &segment, net_buffer *buffer)
|
||||||
{
|
{
|
||||||
TRACE("SynchronizeSentReceive(): packet %p (%lu bytes) with flags 0x%x, seq %lu, ack %lu!",
|
TRACE("SynchronizeSentReceive()");
|
||||||
buffer, buffer->size, segment.flags, segment.sequence, segment.acknowledge);
|
|
||||||
|
|
||||||
if ((segment.flags & TCP_FLAG_ACKNOWLEDGE) != 0
|
if ((segment.flags & TCP_FLAG_ACKNOWLEDGE) != 0
|
||||||
&& (fInitialSendSequence >= segment.acknowledge
|
&& (fInitialSendSequence >= segment.acknowledge
|
||||||
|
@ -859,16 +873,7 @@ TCPEndpoint::SynchronizeSentReceive(tcp_segment_header &segment, net_buffer *buf
|
||||||
}
|
}
|
||||||
|
|
||||||
if (segment.flags & TCP_FLAG_ACKNOWLEDGE) {
|
if (segment.flags & TCP_FLAG_ACKNOWLEDGE) {
|
||||||
// the connection has been established
|
_MarkEstablished();
|
||||||
fState = ESTABLISHED;
|
|
||||||
|
|
||||||
if (socket->parent != NULL) {
|
|
||||||
gSocketModule->set_connected(socket);
|
|
||||||
release_sem_etc(fAcceptSemaphore, 1, B_DO_NOT_RESCHEDULE);
|
|
||||||
}
|
|
||||||
|
|
||||||
fSendList.Signal();
|
|
||||||
_NotifyReader();
|
|
||||||
} else {
|
} else {
|
||||||
// simultaneous open
|
// simultaneous open
|
||||||
fState = SYNCHRONIZE_RECEIVED;
|
fState = SYNCHRONIZE_RECEIVED;
|
||||||
|
@ -884,13 +889,50 @@ TCPEndpoint::SynchronizeSentReceive(tcp_segment_header &segment, net_buffer *buf
|
||||||
|
|
||||||
|
|
||||||
int32
|
int32
|
||||||
TCPEndpoint::Receive(tcp_segment_header &segment, net_buffer *buffer)
|
TCPEndpoint::SegmentReceived(tcp_segment_header &segment, net_buffer *buffer)
|
||||||
{
|
{
|
||||||
TRACE("Receive(): packet %p (%lu bytes) with flags 0x%x, seq %lu, ack %lu!",
|
RecursiveLocker locker(fLock);
|
||||||
buffer, buffer->size, segment.flags, segment.sequence, segment.acknowledge);
|
|
||||||
|
|
||||||
// TODO: rethink locking!
|
TRACE("SegmentReceived(): packet %p (%lu bytes) with flags 0x%x, seq %lu, "
|
||||||
|
"ack %lu", buffer, buffer->size, segment.flags, segment.sequence,
|
||||||
|
segment.acknowledge);
|
||||||
|
|
||||||
|
int32 segmentAction = DROP;
|
||||||
|
|
||||||
|
switch (fState) {
|
||||||
|
case LISTEN:
|
||||||
|
segmentAction = _ListenReceive(segment, buffer);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SYNCHRONIZE_SENT:
|
||||||
|
segmentAction = _SynchronizeSentReceive(segment, buffer);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SYNCHRONIZE_RECEIVED:
|
||||||
|
case ESTABLISHED:
|
||||||
|
case FINISH_RECEIVED:
|
||||||
|
case WAIT_FOR_FINISH_ACKNOWLEDGE:
|
||||||
|
case FINISH_SENT:
|
||||||
|
case FINISH_ACKNOWLEDGED:
|
||||||
|
case CLOSING:
|
||||||
|
case TIME_WAIT:
|
||||||
|
case CLOSED:
|
||||||
|
segmentAction = _SegmentReceived(segment, buffer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// process acknowledge action as asked for by the *Receive() method
|
||||||
|
if (segmentAction & IMMEDIATE_ACKNOWLEDGE)
|
||||||
|
SendAcknowledge();
|
||||||
|
else if (segmentAction & ACKNOWLEDGE)
|
||||||
|
DelayedAcknowledge();
|
||||||
|
|
||||||
|
return segmentAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32
|
||||||
|
TCPEndpoint::_SegmentReceived(tcp_segment_header &segment, net_buffer *buffer)
|
||||||
|
{
|
||||||
uint32 advertisedWindow = (uint32)segment.advertised_window << fSendWindowShift;
|
uint32 advertisedWindow = (uint32)segment.advertised_window << fSendWindowShift;
|
||||||
|
|
||||||
// First, handle the most common case for uni-directional data transfer
|
// First, handle the most common case for uni-directional data transfer
|
||||||
|
@ -1287,23 +1329,15 @@ TCPEndpoint::_Receive(tcp_segment_header &segment, net_buffer *buffer)
|
||||||
if (fLastAcknowledgeSent <= segment.sequence
|
if (fLastAcknowledgeSent <= segment.sequence
|
||||||
&& tcp_sequence(segment.sequence)
|
&& tcp_sequence(segment.sequence)
|
||||||
< (fLastAcknowledgeSent + fReceiveWindow)) {
|
< (fLastAcknowledgeSent + fReceiveWindow)) {
|
||||||
if (fState == SYNCHRONIZE_RECEIVED) {
|
if (fState == SYNCHRONIZE_RECEIVED)
|
||||||
// TODO: if we came from SYN-SENT signal connection refused
|
fError = ECONNREFUSED;
|
||||||
// and remove all segments from tx queue
|
else if (fState == CLOSING || fState == TIME_WAIT
|
||||||
} else if (fState == ESTABLISHED || fState == FINISH_SENT
|
|| fState == WAIT_FOR_FINISH_ACKNOWLEDGE)
|
||||||
|| fState == FINISH_RECEIVED || fState == FINISH_ACKNOWLEDGED) {
|
fError = ENOTCONN;
|
||||||
// TODO: RFC 793 states that on ESTABLISHED, FIN-WAIT{1,2}
|
else
|
||||||
// or CLOSE-WAIT "All segment queues should be
|
fError = ECONNRESET;
|
||||||
// flushed".
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fState != TIME_WAIT && fReceiveQueue.Available() > 0) {
|
_NotifyReader();
|
||||||
_NotifyReader();
|
|
||||||
} else {
|
|
||||||
return DELETE | DROP;
|
|
||||||
}
|
|
||||||
|
|
||||||
fError = ECONNREFUSED;
|
|
||||||
fState = CLOSED;
|
fState = CLOSED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1371,15 +1405,7 @@ TCPEndpoint::_Receive(tcp_segment_header &segment, net_buffer *buffer)
|
||||||
// process acknowledged data
|
// process acknowledged data
|
||||||
if (fState == SYNCHRONIZE_RECEIVED) {
|
if (fState == SYNCHRONIZE_RECEIVED) {
|
||||||
// TODO: window scaling!
|
// TODO: window scaling!
|
||||||
if (socket->parent != NULL) {
|
_MarkEstablished();
|
||||||
gSocketModule->set_connected(socket);
|
|
||||||
release_sem_etc(fAcceptSemaphore, 1, B_DO_NOT_RESCHEDULE);
|
|
||||||
}
|
|
||||||
|
|
||||||
fState = ESTABLISHED;
|
|
||||||
|
|
||||||
fSendList.Signal();
|
|
||||||
_NotifyReader();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fSendMax < segment.acknowledge || fState == TIME_WAIT)
|
if (fSendMax < segment.acknowledge || fState == TIME_WAIT)
|
||||||
|
@ -1536,6 +1562,33 @@ TCPEndpoint::_UpdateTimestamps(tcp_segment_header &segment, size_t segmentLength
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
TCPEndpoint::_MarkEstablished()
|
||||||
|
{
|
||||||
|
fState = ESTABLISHED;
|
||||||
|
|
||||||
|
if (socket->parent != NULL) {
|
||||||
|
gSocketModule->set_connected(socket);
|
||||||
|
release_sem_etc(fAcceptSemaphore, 1, B_DO_NOT_RESCHEDULE);
|
||||||
|
}
|
||||||
|
|
||||||
|
fSendList.Signal();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
TCPEndpoint::_WaitForEstablished(RecursiveLocker &locker, bigtime_t timeout)
|
||||||
|
{
|
||||||
|
while (fState != ESTABLISHED) {
|
||||||
|
status_t status = fSendList.Wait(locker, timeout);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark - timer
|
// #pragma mark - timer
|
||||||
|
|
||||||
|
|
||||||
|
@ -1544,7 +1597,7 @@ TCPEndpoint::_RetransmitTimer(net_timer *timer, void *data)
|
||||||
{
|
{
|
||||||
TCPEndpoint *endpoint = (TCPEndpoint *)data;
|
TCPEndpoint *endpoint = (TCPEndpoint *)data;
|
||||||
|
|
||||||
RecursiveLocker locker(endpoint->Lock());
|
RecursiveLocker locker(endpoint->fLock);
|
||||||
if (!locker.IsLocked())
|
if (!locker.IsLocked())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1559,7 +1612,7 @@ TCPEndpoint::_PersistTimer(net_timer *timer, void *data)
|
||||||
{
|
{
|
||||||
TCPEndpoint *endpoint = (TCPEndpoint *)data;
|
TCPEndpoint *endpoint = (TCPEndpoint *)data;
|
||||||
|
|
||||||
RecursiveLocker locker(endpoint->Lock());
|
RecursiveLocker locker(endpoint->fLock);
|
||||||
if (!locker.IsLocked())
|
if (!locker.IsLocked())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1572,7 +1625,7 @@ TCPEndpoint::_DelayedAcknowledgeTimer(struct net_timer *timer, void *data)
|
||||||
{
|
{
|
||||||
TCPEndpoint *endpoint = (TCPEndpoint *)data;
|
TCPEndpoint *endpoint = (TCPEndpoint *)data;
|
||||||
|
|
||||||
RecursiveLocker locker(endpoint->Lock());
|
RecursiveLocker locker(endpoint->fLock);
|
||||||
if (!locker.IsLocked())
|
if (!locker.IsLocked())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1585,7 +1638,7 @@ TCPEndpoint::_TimeWaitTimer(struct net_timer *timer, void *data)
|
||||||
{
|
{
|
||||||
TCPEndpoint *endpoint = (TCPEndpoint *)data;
|
TCPEndpoint *endpoint = (TCPEndpoint *)data;
|
||||||
|
|
||||||
if (recursive_lock_lock(&endpoint->Lock()) < B_OK)
|
if (recursive_lock_lock(&endpoint->fLock) < B_OK)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
endpoint->DeleteSocket();
|
endpoint->DeleteSocket();
|
||||||
|
|
|
@ -45,8 +45,6 @@ class TCPEndpoint : public net_protocol {
|
||||||
|
|
||||||
status_t InitCheck() const;
|
status_t InitCheck() const;
|
||||||
|
|
||||||
recursive_lock &Lock() { return fLock; }
|
|
||||||
|
|
||||||
status_t Open();
|
status_t Open();
|
||||||
status_t Close();
|
status_t Close();
|
||||||
status_t Free();
|
status_t Free();
|
||||||
|
@ -69,10 +67,10 @@ class TCPEndpoint : public net_protocol {
|
||||||
status_t DelayedAcknowledge();
|
status_t DelayedAcknowledge();
|
||||||
status_t SendAcknowledge();
|
status_t SendAcknowledge();
|
||||||
status_t UpdateTimeWait();
|
status_t UpdateTimeWait();
|
||||||
int32 ListenReceive(tcp_segment_header& segment, net_buffer *buffer);
|
|
||||||
int32 SynchronizeSentReceive(tcp_segment_header& segment,
|
int32 SegmentReceived(tcp_segment_header& segment, net_buffer *buffer);
|
||||||
|
int32 Spawn(TCPEndpoint *parent, tcp_segment_header& segment,
|
||||||
net_buffer *buffer);
|
net_buffer *buffer);
|
||||||
int32 Receive(tcp_segment_header& segment, net_buffer *buffer);
|
|
||||||
|
|
||||||
net_domain *Domain() const
|
net_domain *Domain() const
|
||||||
{ return socket->first_protocol->module->get_domain(
|
{ return socket->first_protocol->module->get_domain(
|
||||||
|
@ -94,9 +92,15 @@ class TCPEndpoint : public net_protocol {
|
||||||
ssize_t _AvailableData() const;
|
ssize_t _AvailableData() const;
|
||||||
void _NotifyReader();
|
void _NotifyReader();
|
||||||
bool _ShouldReceive() const;
|
bool _ShouldReceive() const;
|
||||||
|
int32 _ListenReceive(tcp_segment_header& segment, net_buffer *buffer);
|
||||||
|
int32 _SynchronizeSentReceive(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, bool checkSequence);
|
||||||
|
void _MarkEstablished();
|
||||||
|
status_t _WaitForEstablished(RecursiveLocker &lock, bigtime_t timeout);
|
||||||
|
|
||||||
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);
|
||||||
|
|
|
@ -53,7 +53,7 @@ net_stack_module_info *gStackModule;
|
||||||
// protocol cookie, so we don't have to go through the list
|
// protocol cookie, so we don't have to go through the list
|
||||||
// for each segment.
|
// for each segment.
|
||||||
typedef DoublyLinkedList<EndpointManager> EndpointManagerList;
|
typedef DoublyLinkedList<EndpointManager> EndpointManagerList;
|
||||||
static benaphore sEndpointManagersLock;
|
static recursive_lock sEndpointManagersLock;
|
||||||
static EndpointManagerList sEndpointManagers;
|
static EndpointManagerList sEndpointManagers;
|
||||||
|
|
||||||
|
|
||||||
|
@ -536,7 +536,7 @@ tcp_receive_data(net_buffer *buffer)
|
||||||
bufferHeader.Remove(headerLength);
|
bufferHeader.Remove(headerLength);
|
||||||
// we no longer need to keep the header around
|
// we no longer need to keep the header around
|
||||||
|
|
||||||
BenaphoreLocker _(sEndpointManagersLock);
|
RecursiveLocker _(sEndpointManagersLock);
|
||||||
|
|
||||||
EndpointManager *endpointManager = endpoint_manager_for(domain);
|
EndpointManager *endpointManager = endpoint_manager_for(domain);
|
||||||
if (endpointManager == NULL)
|
if (endpointManager == NULL)
|
||||||
|
@ -547,40 +547,9 @@ tcp_receive_data(net_buffer *buffer)
|
||||||
|
|
||||||
TCPEndpoint *endpoint = endpointManager->FindConnection(
|
TCPEndpoint *endpoint = endpointManager->FindConnection(
|
||||||
(sockaddr *)&buffer->destination, (sockaddr *)&buffer->source);
|
(sockaddr *)&buffer->destination, (sockaddr *)&buffer->source);
|
||||||
if (endpoint != NULL) {
|
if (endpoint != NULL)
|
||||||
RecursiveLocker locker(endpoint->Lock());
|
segmentAction = endpoint->SegmentReceived(segment, buffer);
|
||||||
TRACE(("Endpoint %p in state %s\n", endpoint, name_for_state(endpoint->State())));
|
else if ((segment.flags & TCP_FLAG_RESET) == 0)
|
||||||
|
|
||||||
switch (endpoint->State()) {
|
|
||||||
case LISTEN:
|
|
||||||
segmentAction = endpoint->ListenReceive(segment, buffer);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SYNCHRONIZE_SENT:
|
|
||||||
segmentAction = endpoint->SynchronizeSentReceive(segment, buffer);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SYNCHRONIZE_RECEIVED:
|
|
||||||
case ESTABLISHED:
|
|
||||||
case FINISH_RECEIVED:
|
|
||||||
case WAIT_FOR_FINISH_ACKNOWLEDGE:
|
|
||||||
case FINISH_SENT:
|
|
||||||
case FINISH_ACKNOWLEDGED:
|
|
||||||
case CLOSING:
|
|
||||||
case TIME_WAIT:
|
|
||||||
case CLOSED:
|
|
||||||
segmentAction = endpoint->Receive(segment, buffer);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// process acknowledge action as asked for by the *Receive() method
|
|
||||||
if (segmentAction & IMMEDIATE_ACKNOWLEDGE)
|
|
||||||
endpoint->SendAcknowledge();
|
|
||||||
else if (segmentAction & ACKNOWLEDGE)
|
|
||||||
endpoint->DelayedAcknowledge();
|
|
||||||
else if (segmentAction & DELETE)
|
|
||||||
endpoint->DeleteSocket();
|
|
||||||
} else if ((segment.flags & TCP_FLAG_RESET) == 0)
|
|
||||||
segmentAction = DROP | RESET;
|
segmentAction = DROP | RESET;
|
||||||
|
|
||||||
if (segmentAction & RESET) {
|
if (segmentAction & RESET) {
|
||||||
|
@ -615,7 +584,7 @@ tcp_error_reply(net_protocol *protocol, net_buffer *causedError, uint32 code,
|
||||||
static status_t
|
static status_t
|
||||||
tcp_init()
|
tcp_init()
|
||||||
{
|
{
|
||||||
status_t status = benaphore_init(&sEndpointManagersLock,
|
status_t status = recursive_lock_init(&sEndpointManagersLock,
|
||||||
"endpoint managers lock");
|
"endpoint managers lock");
|
||||||
|
|
||||||
if (status < B_OK)
|
if (status < B_OK)
|
||||||
|
@ -647,7 +616,7 @@ tcp_init()
|
||||||
static status_t
|
static status_t
|
||||||
tcp_uninit()
|
tcp_uninit()
|
||||||
{
|
{
|
||||||
benaphore_destroy(&sEndpointManagersLock);
|
recursive_lock_destroy(&sEndpointManagersLock);
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -165,7 +165,6 @@ enum tcp_segment_action {
|
||||||
RESET = 0x02,
|
RESET = 0x02,
|
||||||
ACKNOWLEDGE = 0x04,
|
ACKNOWLEDGE = 0x04,
|
||||||
IMMEDIATE_ACKNOWLEDGE = 0x08,
|
IMMEDIATE_ACKNOWLEDGE = 0x08,
|
||||||
DELETE = 0x10,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue