Bugzilla – Bug 2285
The loss of TCP 3 way handshake ACK, and subsequent data segment causes receive callback to fire before connection is established
Last modified: 2017-09-17 20:10:52 EDT
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
Created attachment 2261 [details] Tcp Handshake Drop Test
Created attachment 2262 [details] Proposed patch
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().
Patch pushed in changeset 13073:0ad94b8b6fb4; the test is no longer aligned so I held that back.