fixed another TCP issue: if we were in TIME_WAIT and we received a retransmission or delayed ack, TCP would wrongly bindly immediatly acknowledge.

* Moved TIME_WAIT and CLOSED handling to common Receive() path that does Sequence checking and further tests.
 * Moved setting FLAG_NO_RECEIVE to the end of Receive() when FIN is set so we can check for NO_RECEIVE before processing the segment text just after processing FIN.
 * Added a new action DELETE to be used when in TIME_WAIT and we receive a good RST


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20572 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Hugo Santos 2007-04-05 06:56:58 +00:00
parent 2cff90bf94
commit f2893088ba
3 changed files with 30 additions and 14 deletions

View File

@ -203,6 +203,7 @@ TCPEndpoint::Free()
if (fState <= SYNCHRONIZE_SENT || fState == TIME_WAIT)
return B_OK;
// we are only interested in the timer, not in changing state
_EnterTimeWait();
return B_BUSY;
// we'll be freed later when the 2MSL timer expires
@ -821,13 +822,26 @@ TCPEndpoint::Receive(tcp_segment_header &segment, net_buffer *buffer)
// is this a valid reset?
if (fLastAcknowledgeSent <= segment.sequence
&& tcp_sequence(segment.sequence) < fLastAcknowledgeSent + fReceiveWindow) {
// TODO: close connection depending on state
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) {
release_sem_etc(fReceiveLock, 1, B_DO_NOT_RESCHEDULE);
// TODO: real conditional locking needed!
gSocketModule->notify(socket, B_SELECT_READ, 1);
} else {
return DELETE | DROP;
}
fError = ECONNREFUSED;
fState = CLOSED;
release_sem_etc(fReceiveLock, 1, B_DO_NOT_RESCHEDULE);
// TODO: real conditional locking needed!
gSocketModule->notify(socket, B_SELECT_READ, fReceiveQueue.Available());
}
return DROP;
@ -904,7 +918,8 @@ TCPEndpoint::Receive(tcp_segment_header &segment, net_buffer *buffer)
// TODO: real conditional locking needed!
gSocketModule->notify(socket, B_SELECT_READ, fReceiveQueue.Available());
}
if (fSendMax < segment.acknowledge)
if (fSendMax < segment.acknowledge || fState == TIME_WAIT)
return DROP | IMMEDIATE_ACKNOWLEDGE;
if (fSendUnacknowledged >= segment.acknowledge) {
// this is a duplicate acknowledge
@ -977,7 +992,6 @@ TCPEndpoint::Receive(tcp_segment_header &segment, net_buffer *buffer)
if (segment.flags & TCP_FLAG_FINISH) {
TRACE("Receive(): peer is finishing connection!");
fReceiveNext++;
fFlags |= FLAG_NO_RECEIVE;
release_sem_etc(fReceiveLock, 1, B_DO_NOT_RESCHEDULE);
// TODO: real conditional locking needed!
@ -1015,7 +1029,7 @@ TCPEndpoint::Receive(tcp_segment_header &segment, net_buffer *buffer)
// state machine is done switching states and the data is good.
// put it in the receive buffer
if (buffer->size > 0) {
if (buffer->size > 0 && (fFlags & FLAG_NO_RECEIVE) == 0) {
fReceiveQueue.Add(buffer, segment.sequence);
fReceiveNext += buffer->size;
TRACE("Receive(): adding data, receive next = %lu!", (uint32)fReceiveNext);
@ -1029,6 +1043,9 @@ TCPEndpoint::Receive(tcp_segment_header &segment, net_buffer *buffer)
if (segment.flags & TCP_FLAG_PUSH)
fReceiveQueue.SetPushPointer(fReceiveQueue.LastSequence());
if (segment.flags & TCP_FLAG_FINISH)
fFlags |= FLAG_NO_RECEIVE;
return action;
}

View File

@ -555,12 +555,6 @@ tcp_receive_data(net_buffer *buffer)
TRACE(("Endpoint %p in state %s\n", endpoint, name_for_state(endpoint->State())));
switch (endpoint->State()) {
case TIME_WAIT:
segmentAction |= IMMEDIATE_ACKNOWLEDGE;
case CLOSED:
endpoint->UpdateTimeWait();
break;
case LISTEN:
segmentAction = endpoint->ListenReceive(segment, buffer);
break;
@ -576,6 +570,8 @@ tcp_receive_data(net_buffer *buffer)
case FINISH_SENT:
case FINISH_ACKNOWLEDGED:
case CLOSING:
case TIME_WAIT:
case CLOSED:
segmentAction = endpoint->Receive(segment, buffer);
break;
}
@ -585,6 +581,8 @@ tcp_receive_data(net_buffer *buffer)
endpoint->SendAcknowledge();
else if (segmentAction & ACKNOWLEDGE)
endpoint->DelayedAcknowledge();
else if (segmentAction & DELETE)
gSocketModule->delete_socket(endpoint->socket);
} else if ((segment.flags & TCP_FLAG_RESET) == 0)
segmentAction = DROP | RESET;

View File

@ -153,6 +153,7 @@ enum tcp_segment_action {
RESET = 0x02,
ACKNOWLEDGE = 0x04,
IMMEDIATE_ACKNOWLEDGE = 0x08,
DELETE = 0x10,
};