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:
parent
2cff90bf94
commit
f2893088ba
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -153,6 +153,7 @@ 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
Block a user