Bug 2285 - The loss of TCP 3 way handshake ACK, and subsequent data segment causes receive callback to fire before connection is established
The loss of TCP 3 way handshake ACK, and subsequent data segment causes recei...
Status: RESOLVED FIXED
Product: ns-3
Classification: Unclassified
Component: tcp
ns-3-dev
Mac Intel Mac OS
: P5 normal
Assigned To: natale.patriciello
:
Depends on:
Blocks:
  Show dependency treegraph
 
Reported: 2016-01-30 20:11 EST by l.salameh
Modified: 2017-09-17 20:10 EDT (History)
3 users (show)

See Also:


Attachments
Tcp Handshake Drop Test (5.04 KB, text/x-csrc)
2016-01-30 20:11 EST, l.salameh
Details
Tcp Handshake Drop Test (5.04 KB, text/x-csrc)
2016-01-30 20:58 EST, l.salameh
Details
Proposed patch (1.00 KB, patch)
2016-01-30 21:03 EST, l.salameh
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description l.salameh 2016-01-30 20:11:24 EST
Created attachment 2260 [details]
Tcp Handshake Drop Test

I am trying to model web downloads using HTTP in ns-3.

In my model, a client will issue an HTTP request to a server which listens on a socket.
Once the server accepts the new connection, it registers a receive callback on that 
accepted socket and receives the request.
After the request is received, the server will send a response.

I have hit a particular corner case in my experiments where a bug occurs when the following drops happen:

In the 3-way handshake, the final ACK is lost.
The HTTP request itself is also lost.

I tried to capture a simplified example of this behaviour in the test case attached.
The output from the test is shown below. Note the following: the receiver's ReceiveData
callback is never called, even though the sender times out
and correctly retransmits the data segment (of size 200, representing an HTTP request).

Looking at the cause of this bug, I found the following:
In the receiver's TcpSocketBase code, after it receives the initial SYN, it goes into the SYN_RCVD state.
It doesn't get the ACK from the 3-way handshake, but gets the next data segment after it gets retransmitted.
At this point, ProcessSynRcvd is called.

In the test case and my experiment, the receiver used its socket's accept callback (new connection created) to call the SetRecvCallback on the incoming socket.

But in ProcessSynRcvd, the method calls NotifyNewConnectionCreated which triggers the accept new connection created callback, AFTER
it calls the methods that notify a socket of received data.

In more detail, ProcessSynRcvd() calls ReceivedAck() which calls ReceivedData, which finally calls NotifyDataRecv, in order to trigger the socket's RecvCallback.
THEN it calls NotifyNewConnectionCreated.

Unfortunately, because we never called the socket's accept callback at this point, we haven't
registered the receive callback yet, i.e. as the test shows, we never detect the received data.

Finally, because we don't manage to empty the receive buffer, the receiver sends a RST when it attempts to close the socket.     

0 Sender TX: 49153 > 4477 [SYN] Seq=0 Ack=0 Win=32768 ns3::TcpOptionWinScale(2) size 24
0.02 Receiver RX: 49153 > 4477 [SYN] Seq=0 Ack=0 Win=32768 ns3::TcpOptionWinScale(2) ns3::TcpOptionEnd(EOL) size 0
0.02 Receiver TX: 4477 > 49153 [SYN|ACK] Seq=0 Ack=1 Win=32768 ns3::TcpOptionWinScale(2) size 24
0.04 Sender RX: 4477 > 49153 [SYN|ACK] Seq=0 Ack=1 Win=32768 ns3::TcpOptionWinScale(2) ns3::TcpOptionEnd(EOL) size 0
0.04 Sender TX: 49153 > 4477 [ACK] Seq=1 Ack=1 Win=32768 size 20
0.04 Sender TX: 49153 > 4477 [FIN|ACK] Seq=1 Ack=1 Win=32768 size 220
Dropped 49153 > 4477 [ACK] Seq=1 Ack=1 Win=32768 size 20
Dropped 49153 > 4477 [FIN|ACK] Seq=1 Ack=1 Win=32768 size 220
1.041 Sender TX: 49153 > 4477 [FIN|ACK] Seq=1 Ack=1 Win=32768 size 220
1.061 Receiver RX: 49153 > 4477 [FIN|ACK] Seq=1 Ack=1 Win=32768 size 200
1.061 Receiver TX: 4477 > 49153 [ACK] Seq=1 Ack=202 Win=32768 size 20
1.061 Receiver TX: 4477 > 49153 [RST] Seq=1 Ack=202 Win=32768 size 20
1.081 Sender RX: 4477 > 49153 [ACK] Seq=1 Ack=202 Win=32768 size 0
1.081 Sender RX: 4477 > 49153 [RST] Seq=1 Ack=202 Win=32768 size 0
FAIL tcp-handshake-drop 0.030 s
Comment 1 l.salameh 2016-01-30 20:58:36 EST
Created attachment 2261 [details]
Tcp Handshake Drop Test
Comment 2 l.salameh 2016-01-30 21:03:18 EST
Created attachment 2262 [details]
Proposed patch
Comment 3 l.salameh 2016-01-30 21:06:15 EST
I have attached a proposed patch to fix this bug. 

For TcpSocketBase::ProcessSynRcvd, in the first branch, we need to switch the order of ReceivedAck() and NotifyNewConnectionCreated(), so that we trigger the accept callback first.

In the branch dealing with receiving a FIN, we should add a call to NotifyNewConnectionCreated().
Comment 4 Tom Henderson 2017-09-17 20:10:52 EDT
Patch pushed in changeset 13073:0ad94b8b6fb4; the test is no longer aligned so I held that back.