fixed an issue where TCP would RST a connection when a peer trying to connect us re-sent their SYN.

- In fact our SYN/ACK reply is being lost due to ARP resolving as we are waiting in the device's receive path, thus we never get replies before timing out. This requires queueing during ARP resolving.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20706 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Hugo Santos 2007-04-15 17:34:52 +00:00
parent c23db4e688
commit fbaad6fdb5
2 changed files with 277 additions and 228 deletions

View File

@ -90,6 +90,32 @@ posix_error(status_t error)
}
static inline bool
in_window(const tcp_sequence &sequence, const tcp_sequence &rcvNext,
uint32 rcvWindow)
{
return sequence >= rcvNext && sequence < (rcvNext + rcvWindow);
}
static inline bool
segment_in_sequence(const tcp_segment_header &segment, int size,
const tcp_sequence &rcvNext, uint32 rcvWindow)
{
tcp_sequence sequence(segment.sequence);
if (size == 0) {
if (rcvWindow == 0)
return sequence == rcvNext;
return in_window(sequence, rcvNext, rcvWindow);
} else {
if (rcvWindow == 0)
return false;
return in_window(sequence, rcvNext, rcvWindow)
|| in_window(sequence + size - 1, rcvNext, rcvWindow);
}
}
WaitList::WaitList(const char *name)
{
fSem = create_sem(0, name);
@ -761,7 +787,7 @@ TCPEndpoint::ListenReceive(tcp_segment_header &segment, net_buffer *buffer)
segment.flags &= ~TCP_FLAG_SYNCHRONIZE;
// we handled this flag now, it must not be set for further processing
return endpoint->Receive(segment, buffer);
return endpoint->_Receive(segment, buffer);
// TODO: here, the ack/delayed ack call will be made on the parent socket!
}
@ -894,237 +920,19 @@ TCPEndpoint::Receive(tcp_segment_header &segment, net_buffer *buffer)
// The fast path was not applicable, so we continue with the standard
// processing of the incoming segment
if (segment.flags & TCP_FLAG_RESET) {
// is this a valid reset?
if (fLastAcknowledgeSent <= segment.sequence
&& tcp_sequence(segment.sequence)
< (fLastAcknowledgeSent + fReceiveWindow)) {
if (fState == SYNCHRONIZE_RECEIVED) {
// TODO: if we came from SYN-SENT signal connection refused
// and remove all segments from tx queue
} else if (fState == ESTABLISHED || fState == FINISH_SENT
|| fState == FINISH_RECEIVED || fState == FINISH_ACKNOWLEDGED) {
// TODO: RFC 793 states that on ESTABLISHED, FIN-WAIT{1,2}
// or CLOSE-WAIT "All segment queues should be
// flushed".
}
if (fState != TIME_WAIT && fReceiveQueue.Available() > 0) {
_NotifyReader();
} else {
return DELETE | DROP;
}
fError = ECONNREFUSED;
fState = CLOSED;
}
if (fState != SYNCHRONIZE_SENT && fState != LISTEN && fState != CLOSED) {
// 1. check sequence number
if (!segment_in_sequence(segment, buffer->size, fReceiveNext,
fReceiveWindow)) {
TRACE(" Receive(): segment out of window, next: %lu wnd: %lu",
(uint32)fReceiveNext, fReceiveWindow);
if (segment.flags & TCP_FLAG_RESET)
return DROP;
}
if ((segment.flags & TCP_FLAG_SYNCHRONIZE) != 0
|| (fState == SYNCHRONIZE_RECEIVED
&& (fInitialReceiveSequence > segment.sequence
|| (segment.flags & TCP_FLAG_ACKNOWLEDGE) != 0
&& (fSendUnacknowledged > segment.acknowledge
|| fSendMax < segment.acknowledge)))) {
// reset the connection - either the initial SYN was faulty, or we
// received a SYN within the data stream
return DROP | RESET;
}
fReceiveWindow = max_c(fReceiveQueue.Free(), fReceiveWindow);
// the window must not shrink
// trim buffer to be within the receive window
int32 drop = fReceiveNext - segment.sequence;
if (drop > 0) {
if ((uint32)drop > buffer->size
|| ((uint32)drop == buffer->size
&& (segment.flags & TCP_FLAG_FINISH) == 0)) {
// don't accidently remove a FIN we shouldn't remove
segment.flags &= ~TCP_FLAG_FINISH;
drop = buffer->size;
}
// remove duplicate data at the start
TRACE("* remove %ld bytes from the start", drop);
gBufferModule->remove_header(buffer, drop);
segment.sequence += drop;
}
int32 action = KEEP;
drop = segment.sequence + buffer->size - (fReceiveNext + fReceiveWindow);
if (drop > 0) {
// remove data exceeding our window
if ((uint32)drop >= buffer->size) {
// if we can accept data, or the segment is not what we'd expect,
// drop the segment (an immediate acknowledge is always triggered)
if (fReceiveWindow != 0 || segment.sequence != fReceiveNext)
return DROP | IMMEDIATE_ACKNOWLEDGE;
action |= IMMEDIATE_ACKNOWLEDGE;
}
if ((segment.flags & TCP_FLAG_FINISH) != 0) {
// we need to remove the finish, too, as part of the data
drop--;
}
segment.flags &= ~(TCP_FLAG_FINISH | TCP_FLAG_PUSH);
TRACE("* remove %ld bytes from the end", drop);
gBufferModule->remove_trailer(buffer, drop);
}
// Then look at the acknowledgement for any updates
if ((segment.flags & TCP_FLAG_ACKNOWLEDGE) != 0) {
// process acknowledged data
if (fState == SYNCHRONIZE_RECEIVED) {
// TODO: window scaling!
if (socket->parent != NULL) {
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)
return DROP | IMMEDIATE_ACKNOWLEDGE;
if (fSendUnacknowledged >= segment.acknowledge) {
// this is a duplicate acknowledge
// TODO: handle this!
if (buffer->size == 0 && advertisedWindow == fSendWindow
&& (segment.flags & TCP_FLAG_FINISH) == 0) {
TRACE("Receive(): duplicate ack!");
fDuplicateAcknowledgeCount++;
gStackModule->cancel_timer(&fRetransmitTimer);
fSendNext = segment.acknowledge;
_SendQueued();
return DROP;
}
} else {
// this segment acknowledges in flight data
fDuplicateAcknowledgeCount = 0;
if (fSendMax == segment.acknowledge) {
// there is no outstanding data to be acknowledged
// TODO: if the transmit timer function is already waiting
// to acquire this endpoint's lock, we should stop it anyway
TRACE("Receive(): all inflight data ack'd!");
gStackModule->cancel_timer(&fRetransmitTimer);
} else {
TRACE("Receive(): set retransmit timer!");
// TODO: set retransmit timer correctly
if (!gStackModule->is_timer_active(&fRetransmitTimer))
gStackModule->set_timer(&fRetransmitTimer, 1000000LL);
}
fSendUnacknowledged = segment.acknowledge;
fSendQueue.RemoveUntil(segment.acknowledge);
if (fSendNext < fSendUnacknowledged)
fSendNext = fSendUnacknowledged;
if (segment.acknowledge > fSendQueue.LastSequence() && fState > ESTABLISHED) {
TRACE("Receive(): FIN has been acknowledged!");
switch (fState) {
case FINISH_SENT:
fState = FINISH_ACKNOWLEDGED;
break;
case CLOSING:
fState = TIME_WAIT;
_EnterTimeWait();
break;
case WAIT_FOR_FINISH_ACKNOWLEDGE:
fState = CLOSED;
break;
default:
break;
}
}
}
}
// TODO: update window
fSendWindow = advertisedWindow;
if (advertisedWindow > fSendMaxWindow)
fSendMaxWindow = advertisedWindow;
if (segment.flags & TCP_FLAG_URGENT) {
if (fState == ESTABLISHED || fState == FINISH_SENT
|| fState == FINISH_ACKNOWLEDGED) {
// TODO: Handle urgent data:
// - RCV.UP <- max(RCV.UP, SEG.UP)
// - signal the user that urgent data is available (SIGURG)
}
}
bool notify = false;
if (buffer->size > 0 && _ShouldReceive()) {
fReceiveQueue.Add(buffer, segment.sequence);
fReceiveNext += buffer->size;
notify = true;
TRACE("Receive(): adding data, receive next = %lu. Now have %lu bytes.",
(uint32)fReceiveNext, fReceiveQueue.Available());
if (segment.flags & TCP_FLAG_PUSH)
fReceiveQueue.SetPushPointer();
} else {
action = (action & ~KEEP) | DROP;
}
if (segment.flags & TCP_FLAG_FINISH) {
if (fState != CLOSED && fState != LISTEN && fState != SYNCHRONIZE_SENT) {
TRACE("Receive(): peer is finishing connection!");
fReceiveNext++;
notify = true;
// FIN implies PSH
fReceiveQueue.SetPushPointer();
// RFC 793 states that we must send an ACK to FIN
action |= IMMEDIATE_ACKNOWLEDGE;
// other side is closing connection; change states
switch (fState) {
case ESTABLISHED:
case SYNCHRONIZE_RECEIVED:
fState = FINISH_RECEIVED;
break;
case FINISH_SENT:
// simultaneous close
fState = CLOSING;
break;
case FINISH_ACKNOWLEDGED:
fState = TIME_WAIT;
_EnterTimeWait();
break;
default:
break;
}
}
}
if (notify)
_NotifyReader();
if (buffer->size > 0 || (segment.flags & TCP_FLAG_SYNCHRONIZE) != 0)
action |= ACKNOWLEDGE;
TRACE("Receive() Action %ld", action);
return action;
return _Receive(segment, buffer);
}
@ -1425,6 +1233,246 @@ TCPEndpoint::_ShouldReceive() const
}
int32
TCPEndpoint::_Receive(tcp_segment_header &segment, net_buffer *buffer)
{
uint32 advertisedWindow = (uint32)segment.advertised_window << fSendWindowShift;
if (segment.flags & TCP_FLAG_RESET) {
// is this a valid reset?
if (fLastAcknowledgeSent <= segment.sequence
&& tcp_sequence(segment.sequence)
< (fLastAcknowledgeSent + fReceiveWindow)) {
if (fState == SYNCHRONIZE_RECEIVED) {
// TODO: if we came from SYN-SENT signal connection refused
// and remove all segments from tx queue
} else if (fState == ESTABLISHED || fState == FINISH_SENT
|| fState == FINISH_RECEIVED || fState == FINISH_ACKNOWLEDGED) {
// TODO: RFC 793 states that on ESTABLISHED, FIN-WAIT{1,2}
// or CLOSE-WAIT "All segment queues should be
// flushed".
}
if (fState != TIME_WAIT && fReceiveQueue.Available() > 0) {
_NotifyReader();
} else {
return DELETE | DROP;
}
fError = ECONNREFUSED;
fState = CLOSED;
}
return DROP;
}
if ((segment.flags & TCP_FLAG_SYNCHRONIZE) != 0
|| (fState == SYNCHRONIZE_RECEIVED
&& (fInitialReceiveSequence > segment.sequence
|| (segment.flags & TCP_FLAG_ACKNOWLEDGE) != 0
&& (fSendUnacknowledged > segment.acknowledge
|| fSendMax < segment.acknowledge)))) {
// reset the connection - either the initial SYN was faulty, or we
// received a SYN within the data stream
return DROP | RESET;
}
fReceiveWindow = max_c(fReceiveQueue.Free(), fReceiveWindow);
// the window must not shrink
// trim buffer to be within the receive window
int32 drop = fReceiveNext - segment.sequence;
if (drop > 0) {
if ((uint32)drop > buffer->size
|| ((uint32)drop == buffer->size
&& (segment.flags & TCP_FLAG_FINISH) == 0)) {
// don't accidently remove a FIN we shouldn't remove
segment.flags &= ~TCP_FLAG_FINISH;
drop = buffer->size;
}
// remove duplicate data at the start
TRACE("* remove %ld bytes from the start", drop);
gBufferModule->remove_header(buffer, drop);
segment.sequence += drop;
}
int32 action = KEEP;
drop = segment.sequence + buffer->size - (fReceiveNext + fReceiveWindow);
if (drop > 0) {
// remove data exceeding our window
if ((uint32)drop >= buffer->size) {
// if we can accept data, or the segment is not what we'd expect,
// drop the segment (an immediate acknowledge is always triggered)
if (fReceiveWindow != 0 || segment.sequence != fReceiveNext)
return DROP | IMMEDIATE_ACKNOWLEDGE;
action |= IMMEDIATE_ACKNOWLEDGE;
}
if ((segment.flags & TCP_FLAG_FINISH) != 0) {
// we need to remove the finish, too, as part of the data
drop--;
}
segment.flags &= ~(TCP_FLAG_FINISH | TCP_FLAG_PUSH);
TRACE("* remove %ld bytes from the end", drop);
gBufferModule->remove_trailer(buffer, drop);
}
// Then look at the acknowledgement for any updates
if ((segment.flags & TCP_FLAG_ACKNOWLEDGE) != 0) {
// process acknowledged data
if (fState == SYNCHRONIZE_RECEIVED) {
// TODO: window scaling!
if (socket->parent != NULL) {
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)
return DROP | IMMEDIATE_ACKNOWLEDGE;
if (fSendUnacknowledged >= segment.acknowledge) {
// this is a duplicate acknowledge
// TODO: handle this!
if (buffer->size == 0 && advertisedWindow == fSendWindow
&& (segment.flags & TCP_FLAG_FINISH) == 0) {
TRACE("Receive(): duplicate ack!");
fDuplicateAcknowledgeCount++;
gStackModule->cancel_timer(&fRetransmitTimer);
fSendNext = segment.acknowledge;
_SendQueued();
return DROP;
}
} else {
// this segment acknowledges in flight data
fDuplicateAcknowledgeCount = 0;
if (fSendMax == segment.acknowledge) {
// there is no outstanding data to be acknowledged
// TODO: if the transmit timer function is already waiting
// to acquire this endpoint's lock, we should stop it anyway
TRACE("Receive(): all inflight data ack'd!");
gStackModule->cancel_timer(&fRetransmitTimer);
} else {
TRACE("Receive(): set retransmit timer!");
// TODO: set retransmit timer correctly
if (!gStackModule->is_timer_active(&fRetransmitTimer))
gStackModule->set_timer(&fRetransmitTimer, 1000000LL);
}
fSendUnacknowledged = segment.acknowledge;
fSendQueue.RemoveUntil(segment.acknowledge);
if (fSendNext < fSendUnacknowledged)
fSendNext = fSendUnacknowledged;
if (segment.acknowledge > fSendQueue.LastSequence()
&& fState > ESTABLISHED) {
TRACE("Receive(): FIN has been acknowledged!");
switch (fState) {
case FINISH_SENT:
fState = FINISH_ACKNOWLEDGED;
break;
case CLOSING:
fState = TIME_WAIT;
_EnterTimeWait();
break;
case WAIT_FOR_FINISH_ACKNOWLEDGE:
fState = CLOSED;
break;
default:
break;
}
}
}
}
// TODO: update window
fSendWindow = advertisedWindow;
if (advertisedWindow > fSendMaxWindow)
fSendMaxWindow = advertisedWindow;
if (segment.flags & TCP_FLAG_URGENT) {
if (fState == ESTABLISHED || fState == FINISH_SENT
|| fState == FINISH_ACKNOWLEDGED) {
// TODO: Handle urgent data:
// - RCV.UP <- max(RCV.UP, SEG.UP)
// - signal the user that urgent data is available (SIGURG)
}
}
bool notify = false;
if (buffer->size > 0 && _ShouldReceive()) {
fReceiveQueue.Add(buffer, segment.sequence);
fReceiveNext += buffer->size;
notify = true;
TRACE("Receive(): adding data, receive next = %lu. Now have %lu bytes.",
(uint32)fReceiveNext, fReceiveQueue.Available());
if (segment.flags & TCP_FLAG_PUSH)
fReceiveQueue.SetPushPointer();
} else {
action = (action & ~KEEP) | DROP;
}
if (segment.flags & TCP_FLAG_FINISH) {
if (fState != CLOSED && fState != LISTEN && fState != SYNCHRONIZE_SENT) {
TRACE("Receive(): peer is finishing connection!");
fReceiveNext++;
notify = true;
// FIN implies PSH
fReceiveQueue.SetPushPointer();
// RFC 793 states that we must send an ACK to FIN
action |= IMMEDIATE_ACKNOWLEDGE;
// other side is closing connection; change states
switch (fState) {
case ESTABLISHED:
case SYNCHRONIZE_RECEIVED:
fState = FINISH_RECEIVED;
break;
case FINISH_SENT:
// simultaneous close
fState = CLOSING;
break;
case FINISH_ACKNOWLEDGED:
fState = TIME_WAIT;
_EnterTimeWait();
break;
default:
break;
}
}
}
if (notify)
_NotifyReader();
if (buffer->size > 0 || (segment.flags & TCP_FLAG_SYNCHRONIZE) != 0)
action |= ACKNOWLEDGE;
TRACE("Receive() Action %ld", action);
return action;
}
// #pragma mark - timer

View File

@ -86,6 +86,7 @@ class TCPEndpoint : public net_protocol {
ssize_t _AvailableData() const;
void _NotifyReader();
bool _ShouldReceive() const;
int32 _Receive(tcp_segment_header& segment, net_buffer *buffer);
static void _TimeWaitTimer(net_timer *timer, void *data);
static void _RetransmitTimer(net_timer *timer, void *data);