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) if (fState <= SYNCHRONIZE_SENT || fState == TIME_WAIT)
return B_OK; return B_OK;
// we are only interested in the timer, not in changing state
_EnterTimeWait(); _EnterTimeWait();
return B_BUSY; return B_BUSY;
// we'll be freed later when the 2MSL timer expires // 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? // is this a valid reset?
if (fLastAcknowledgeSent <= segment.sequence if (fLastAcknowledgeSent <= segment.sequence
&& tcp_sequence(segment.sequence) < fLastAcknowledgeSent + fReceiveWindow) { && tcp_sequence(segment.sequence) < fLastAcknowledgeSent + fReceiveWindow) {
// TODO: close connection depending on state if (fState == SYNCHRONIZE_RECEIVED) {
fError = ECONNREFUSED; // TODO: if we came from SYN-SENT signal connection refused
fState = CLOSED; // 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); release_sem_etc(fReceiveLock, 1, B_DO_NOT_RESCHEDULE);
// TODO: real conditional locking needed! // TODO: real conditional locking needed!
gSocketModule->notify(socket, B_SELECT_READ, fReceiveQueue.Available()); gSocketModule->notify(socket, B_SELECT_READ, 1);
} else {
return DELETE | DROP;
}
fError = ECONNREFUSED;
fState = CLOSED;
} }
return DROP; return DROP;
@ -904,7 +918,8 @@ TCPEndpoint::Receive(tcp_segment_header &segment, net_buffer *buffer)
// TODO: real conditional locking needed! // TODO: real conditional locking needed!
gSocketModule->notify(socket, B_SELECT_READ, fReceiveQueue.Available()); gSocketModule->notify(socket, B_SELECT_READ, fReceiveQueue.Available());
} }
if (fSendMax < segment.acknowledge)
if (fSendMax < segment.acknowledge || fState == TIME_WAIT)
return DROP | IMMEDIATE_ACKNOWLEDGE; return DROP | IMMEDIATE_ACKNOWLEDGE;
if (fSendUnacknowledged >= segment.acknowledge) { if (fSendUnacknowledged >= segment.acknowledge) {
// this is a duplicate 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) { if (segment.flags & TCP_FLAG_FINISH) {
TRACE("Receive(): peer is finishing connection!"); TRACE("Receive(): peer is finishing connection!");
fReceiveNext++; fReceiveNext++;
fFlags |= FLAG_NO_RECEIVE;
release_sem_etc(fReceiveLock, 1, B_DO_NOT_RESCHEDULE); release_sem_etc(fReceiveLock, 1, B_DO_NOT_RESCHEDULE);
// TODO: real conditional locking needed! // 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. // state machine is done switching states and the data is good.
// put it in the receive buffer // put it in the receive buffer
if (buffer->size > 0) { if (buffer->size > 0 && (fFlags & FLAG_NO_RECEIVE) == 0) {
fReceiveQueue.Add(buffer, segment.sequence); fReceiveQueue.Add(buffer, segment.sequence);
fReceiveNext += buffer->size; fReceiveNext += buffer->size;
TRACE("Receive(): adding data, receive next = %lu!", (uint32)fReceiveNext); 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) if (segment.flags & TCP_FLAG_PUSH)
fReceiveQueue.SetPushPointer(fReceiveQueue.LastSequence()); fReceiveQueue.SetPushPointer(fReceiveQueue.LastSequence());
if (segment.flags & TCP_FLAG_FINISH)
fFlags |= FLAG_NO_RECEIVE;
return action; 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()))); TRACE(("Endpoint %p in state %s\n", endpoint, name_for_state(endpoint->State())));
switch (endpoint->State()) { switch (endpoint->State()) {
case TIME_WAIT:
segmentAction |= IMMEDIATE_ACKNOWLEDGE;
case CLOSED:
endpoint->UpdateTimeWait();
break;
case LISTEN: case LISTEN:
segmentAction = endpoint->ListenReceive(segment, buffer); segmentAction = endpoint->ListenReceive(segment, buffer);
break; break;
@ -576,6 +570,8 @@ tcp_receive_data(net_buffer *buffer)
case FINISH_SENT: case FINISH_SENT:
case FINISH_ACKNOWLEDGED: case FINISH_ACKNOWLEDGED:
case CLOSING: case CLOSING:
case TIME_WAIT:
case CLOSED:
segmentAction = endpoint->Receive(segment, buffer); segmentAction = endpoint->Receive(segment, buffer);
break; break;
} }
@ -585,6 +581,8 @@ tcp_receive_data(net_buffer *buffer)
endpoint->SendAcknowledge(); endpoint->SendAcknowledge();
else if (segmentAction & ACKNOWLEDGE) else if (segmentAction & ACKNOWLEDGE)
endpoint->DelayedAcknowledge(); endpoint->DelayedAcknowledge();
else if (segmentAction & DELETE)
gSocketModule->delete_socket(endpoint->socket);
} else if ((segment.flags & TCP_FLAG_RESET) == 0) } else if ((segment.flags & TCP_FLAG_RESET) == 0)
segmentAction = DROP | RESET; segmentAction = DROP | RESET;

View File

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