Bugzilla – Bug 1166
IPV4 TCP Failed to send a RST when a Connect comes over a binded socket after bind but before a listen/accept
Last modified: 2011-12-07 20:48:14 EST
To reproduce this bug I use DCE and I do this (in pseudo code): I launch a client and a server (using threads) and they do this : SERVER: bind sleep(10) listen accept CLIENT: sleep(1) connect sleep(15) connect close The wanted result is that : 1 . the first connect should failed immediatly (at second 1) and return an errno of ECONNREFUSED, 2 . the second connect is successfull, But using NS3/DCE and NS3 TCP Stack I obtain the following result: 1 . the first connect failed after 3 seconds and return an errno of ECONNREFUSED, 2 . the second connect failed immediatly with an errno of ECONNREFUSED. trace extracts: 1s 0 SimuFd:dce_connect(0x7f14dc036800, 0, 4, 0x7f14e1db2dc0, 16) 1s 0 UnixStreamSocketFd:Connect(0x7f14d8001040, 0x7f14dc036800) 1s 0 UnixSocketFd:Connect(0x7f14d8001040, 0x7f14dc036800) 1s 0 1 [node 0] TcpSocketBase:Connect(0x7f14d8000d00, 02-06-7f:00:00:01:d4:04) 1s 0 1 [node 0] TcpSocketBase:Bind() 1s 0 1 [node 0] TcpSocketBase:SetupCallback(0x7f14d8000d00) 1s 0 1 [node 0] TcpSocketBase:SetupEndpoint(0x7f14d8000d00) 1s 0 1 [node 0] TcpSocketBase:SetupEndpoint(): Route exists 1s 0 1 [node 0] TcpSocketBase:DoConnect(0x7f14d8000d00) 1s 0 1 [node 0] TcpSocketBase:SendEmptyPacket(0x7f14d8000d00, 2) 1s 0 Simulator:GetSystemId() 1s 0 Simulator:IsExpired(0x7f14d8000d78) 1s 0 1 [node 0] TcpSocketBase:SendEmptyPacket(): Schedule retransmission timeout at time 1 to expire at time 4 1s 0 1 [node 0] TcpSocketBase:DoConnect(): CLOSED -> SYN_SENT 1s 0 1 [node 0] TcpSocketBase:ForwardUp(): Socket 0x7f14d4000c50 forward up 0.0.0.0:0 to 127.0.0.1:1236 1s 0 1 [node 0] TcpSocketBase:ForwardUp(): 0x7f14d4000c50 Leaving zerowindow persist state 1s 0 Simulator:Cancel(0x7f14d4000d10) 1s 0 1 [node 0] TcpSocketBase:SendRST(0x7f14d4000c50) 1s 0 1 [node 0] TcpSocketBase:SendEmptyPacket(0x7f14d4000c50, 4) 1s 0 Simulator:GetSystemId() 1s 0 Simulator:IsExpired(0x7f14d4000cc8) 1s 0 UnixStreamSocketFd:CloseError(0x7f14d4000f90, 0) 1s 0 Simulator:Cancel(0x7f14d4000cc8) 1s 0 Simulator:Cancel(0x7f14d4000d10) 1s 0 Simulator:Cancel(0x7f14d4000cf8) 1s 0 Simulator:Cancel(0x7f14d4000ce0) 4s 0 4 [node 0] TcpSocketBase:SendEmptyPacket(0x7f14d8000d00, 2) 4s 0 Simulator:GetSystemId() 4s 0 Simulator:IsExpired(0x7f14d8000d78) 4s 0 4 [node 0] TcpSocketBase:SendEmptyPacket(): Schedule retransmission timeout at time 4 to expire at time 10 4s 0 Simulator:GetSystemId() 4s 0 4 [node 0] TcpSocketBase:ForwardUp(): Socket 0x7f14d8000d00 forward up 127.0.0.1:1236 to 127.0.0.1:49153 4s 0 4 [node 0] TcpSocketBase:ForwardUp(): 0x7f14d8000d00 Leaving zerowindow persist state 4s 0 Simulator:Cancel(0x7f14d8000dc0) 4s 0 4 [node 0] TcpSocketBase:ProcessSynSent(0x7f14d8000d00, 1236 > 49153 [ RST ACK ] Seq=0 Ack=1 Win=65535) 4s 0 4 [node 0] TcpSocketBase:CloseAndNotify(0x7f14d8000d00) 4s 0 UnixStreamSocketFd:CloseSuccess(0x7f14d8001040, 1) 4s 0 Simulator:IsExpired(0xa6dd78) 4s 0 4 [node 0] TcpSocketBase:CloseAndNotify(): SYN_SENT -> CLOSED 4s 0 Simulator:Cancel(0x7f14d8000d78) 4s 0 Simulator:Cancel(0x7f14d8000dc0) 4s 0 Simulator:Cancel(0x7f14d8000da8) 4s 0 Simulator:Cancel(0x7f14d8000d90) 4s 0 4 [node 0] TcpSocketBase:ProcessSynSent(): Illegal flag received. Reset packet is sent. 4s 0 4 [node 0] TcpSocketBase:SendRST(0x7f14d8000d00) 4s 0 4 [node 0] TcpSocketBase:SendEmptyPacket(0x7f14d8000d00, 4) 4s 0 Simulator:GetSystemId() 4s 0 4 [node 0] TcpSocketBase:SendEmptyPacket(): Failed to send empty packet due to null endpoint 4s 0 UnixStreamSocketFd:CloseError(0x7f14d8001040, 4) 4s 0 Simulator:Cancel(0x7f14dc036960) 4s 0 UnixStreamSocketFd:Connect(): Connect: wait result:2 10s 0 Simulator:IsExpired(0xa6dd78) 10s 0 Simulator:Cancel(0x7f14dc036500) ----------------------------------------------- After code and trace investigations, I think I found why we have the suspect log : 4s 0 4 [node 0] TcpSocketBase:SendEmptyPacket(): Failed to send empty packet due to null endpoint and the reason is that while doing the first SendRST : 1s 0 1 [node 0] TcpSocketBase:ForwardUp(): Socket 0x7f14d4000c50 forward up 0.0.0.0:0 to 127.0.0.1:1236 1s 0 1 [node 0] TcpSocketBase:ForwardUp(): 0x7f14d4000c50 Leaving zerowindow persist state 1s 0 Simulator:Cancel(0x7f14d4000d10) 1s 0 1 [node 0] TcpSocketBase:SendRST(0x7f14d4000c50) the m_endpoint exists but with a null peer address and port so the RST packet sending failed, and the endpoint is deallocated as in SendRST code : void TcpSocketBase::SendRST (void) { NS_LOG_FUNCTION (this); SendEmptyPacket (TcpHeader::RST); NotifyErrorClose (); DeallocateEndPoint (); } and When the retransmission occurs the m_endpoint is NULL so the first message : 4s 0 4 [node 0] TcpSocketBase:SendEmptyPacket(): Failed to send empty packet due to null endpoint in consequence I do a fix in TcpSocketBase::ForwardUp method consisting of : before calling SendRST I check the value of peer and if is null I use new version of SendEmptyPacket which take in args the peer address and port, also I do not call DeallocateEndPoint nether NotifyErrorClose : // Send RST if the incoming packet is not a RST if ((tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG)) != TcpHeader::RST) { - SendRST (); + if ( m_endPoint->GetPeerAddress ().IsEqual( Ipv4Address::GetZero() ) ) + { + SendEmptyPacket2 ( TcpHeader::RST, header.GetSource(), port); + } + else + { + SendRST (); + } } break; case SYN_SENT: --- and SendEmptyPacket2 (I was not able to use the same name because of callbacks): /** Send an empty packet with specified TCP flags to a destination */ void TcpSocketBase::SendEmptyPacket2 (uint8_t flags, Ipv4Address daddr, uint16_t port ) { NS_LOG_FUNCTION (this << (uint32_t)flags); Ptr<Packet> p = Create<Packet> (); TcpHeader header; SequenceNumber32 s = m_nextTxSequence; if (m_endPoint == 0) { NS_LOG_WARN ("Failed to send empty packet due to null endpoint"); return; } if (flags & TcpHeader::FIN) { flags |= TcpHeader::ACK; } else if (m_state == FIN_WAIT_1 || m_state == LAST_ACK || m_state == CLOSING) { ++s; } header.SetFlags (flags); header.SetSequenceNumber (s); header.SetAckNumber (m_rxBuffer.NextRxSequence ()); header.SetSourcePort (m_endPoint->GetLocalPort ()); header.SetDestinationPort (port); header.SetWindowSize (AdvertisedWindowSize ()); m_tcp->SendPacket (p, header, m_endPoint->GetLocalAddress (), daddr, m_boundnetdevice); m_rto = m_rtt->RetransmitTimeout (); bool hasSyn = flags & TcpHeader::SYN; bool hasFin = flags & TcpHeader::FIN; bool isAck = flags == TcpHeader::ACK; if (hasSyn) { // Exponential backoff of connection time out m_rto = m_cnTimeout; m_cnTimeout = m_cnTimeout + m_cnTimeout; m_cnCount--; } if (flags & TcpHeader::ACK) { // If sending an ACK, cancel the delay ACK as well m_delAckEvent.Cancel (); m_delAckCount = 0; } if (m_retxEvent.IsExpired () && (hasSyn || hasFin) && !isAck ) { // Retransmit SYN / SYN+ACK / FIN / FIN+ACK to guard against lost NS_LOG_LOGIC ("Schedule retransmission timeout at time " << Simulator::Now ().GetSeconds () << " to expire at time " << (Simulator::Now () + m_rto.Get ()).GetSeconds ()); m_retxEvent = Simulator::Schedule (m_rto, &TcpSocketBase::SendEmptyPacket2, this, flags, daddr, port); } } ----------------------------------------------------------- This fix correct the behavior and do not introduice bug in my others TCP socket test cases. But as I am not yet an TCP expert I am not 100% sure of this fix.
Fix is committed to the development codebase.