Bug 1166 - IPV4 TCP Failed to send a RST when a Connect comes over a binded socket after bind but before a listen/accept
IPV4 TCP Failed to send a RST when a Connect comes over a binded socket after...
Status: RESOLVED FIXED
Product: ns-3
Classification: Unclassified
Component: internet
ns-3.10
All All
: P5 normal
Assigned To: Adrian S.-W. Tam
:
Depends on:
Blocks:
  Show dependency treegraph
 
Reported: 2011-05-19 03:52 EDT by Frederic Urbani
Modified: 2011-12-07 20:48 EST (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Frederic Urbani 2011-05-19 03:52:55 EDT
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.
Comment 1 Adrian S.-W. Tam 2011-12-07 20:48:14 EST
Fix is committed to the development codebase.