Mixed bugfixes on TCP, closes bug 1166, 1227, 1242.
1. Make connection count (m_cnCount) a separate variable from the number of
connection retries (m_cnRetries), so that the number of connection retries
can be cloned without affecting the number of connections to be made in the
cloned sockets.
2. There was a case that the m_highTxMark was mistaken as m_nextTxSequence
3. Update m_lastRtt correctly, fixes bug 1242
4. The endpoint allocation/deallocation is rewritten so that the endpoint
remembers the correct peer's address and it is deleted upon close.
RecvFrom() call now use the data in endpoint to return the peer's address,
and the socket closes nicely.
5. TcpL4Protocol::m_sockets now holds a complete list of all existing sockets
6. RST packet is sent before CloseAndNotify() is called, so that m_endPoint is
not yet destroyed, fixes bug 1166
7. Fix cwnd deflation bug in partial ACK handling in TcpNewReno
8. Created attributes for fast retransmit threshold (m_retxThresh) and max
advertised window size (m_maxWinSize)
9. Refactor SendPendingData() and DoRetransmit(), created SendDataPacket()
10. Call NotifySend() after connection established in ProcessSynSent() and
ProcessSynRcvd()
11. Defines new flags in TcpHeader: ECE and CWR (see RFC3168)
12. Prepared hooks for TCP option handling
13. Code tidy up
14. Fix the definition of out-of-order packets
15. TCP can now move from TIME_WAIT state to CLOSED state after 2*MSL
16. Implemented limited transmit (RFC3042) in TcpNewReno, c.f. bug 1227
17. Added Raj's email to the preamble, as TcpSocketBase was modified from
TcpSocketImpl.
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2007 Georgia Tech Research Corporation
* Copyright (c) 2010 Adrian Sai-wah Tam
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Adrian Sai-wah Tam <adrian.sw.tam@gmail.com>
*/
#define NS_LOG_APPEND_CONTEXT \
if (m_node) { std::clog << Simulator::Now ().GetSeconds () << " [node " << m_node->GetId () << "] "; }
#include "ns3/abort.h"
#include "ns3/node.h"
#include "ns3/inet-socket-address.h"
#include "ns3/log.h"
#include "ns3/ipv4.h"
#include "ns3/ipv4-interface-address.h"
#include "ns3/ipv4-route.h"
#include "ns3/ipv4-routing-protocol.h"
#include "ns3/simulation-singleton.h"
#include "ns3/simulator.h"
#include "ns3/packet.h"
#include "ns3/uinteger.h"
#include "ns3/double.h"
#include "ns3/trace-source-accessor.h"
#include "tcp-socket-base.h"
#include "tcp-l4-protocol.h"
#include "ipv4-end-point.h"
#include "tcp-header.h"
#include "rtt-estimator.h"
#include <algorithm>
NS_LOG_COMPONENT_DEFINE ("TcpSocketBase");
namespace ns3 {
NS_OBJECT_ENSURE_REGISTERED (TcpSocketBase);
TypeId
TcpSocketBase::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::TcpSocketBase")
.SetParent<TcpSocket> ()
// .AddAttribute ("TcpState", "State in TCP state machine",
// TypeId::ATTR_GET,
// EnumValue (CLOSED),
// MakeEnumAccessor (&TcpSocketBase::m_state),
// MakeEnumChecker (CLOSED, "Closed"))
.AddAttribute ("MaxSegLifetime",
"Maximum segment lifetime in seconds, use for TIME_WAIT state transition to CLOSED state",
DoubleValue (120), /* RFC793 says MSL=2 minutes*/
MakeDoubleAccessor (&TcpSocketBase::m_msl),
MakeDoubleChecker<double> (0))
.AddAttribute ("MaxWindowSize", "Max size of advertised window",
UintegerValue (65535),
MakeUintegerAccessor (&TcpSocketBase::m_maxWinSize),
MakeUintegerChecker<uint16_t> ())
.AddTraceSource ("RTO",
"Retransmission timeout",
MakeTraceSourceAccessor (&TcpSocketBase::m_rto))
.AddTraceSource ("RTT",
"Last RTT sample",
MakeTraceSourceAccessor (&TcpSocketBase::m_lastRtt))
.AddTraceSource ("NextTxSequence",
"Next sequence number to send (SND.NXT)",
MakeTraceSourceAccessor (&TcpSocketBase::m_nextTxSequence))
.AddTraceSource ("HighestSequence",
"Highest sequence number ever sent in socket's life time",
MakeTraceSourceAccessor (&TcpSocketBase::m_highTxMark))
.AddTraceSource ("State",
"TCP state",
MakeTraceSourceAccessor (&TcpSocketBase::m_state))
.AddTraceSource ("RWND",
"Remote side's flow control window",
MakeTraceSourceAccessor (&TcpSocketBase::m_rWnd))
;
return tid;
}
TcpSocketBase::TcpSocketBase (void)
: m_dupAckCount (0),
m_delAckCount (0),
m_endPoint (0),
m_node (0),
m_tcp (0),
m_rtt (0),
m_nextTxSequence (0), // Change this for non-zero initial sequence number
m_highTxMark (0),
m_rxBuffer (0),
m_txBuffer (0),
m_state (CLOSED),
m_errno (ERROR_NOTERROR),
m_closeNotified (false),
m_closeOnEmpty (false),
m_shutdownSend (false),
m_shutdownRecv (false),
m_connected (false),
m_segmentSize (0), // For attribute initialization consistency (quiet valgrind)
m_rWnd (0)
{
NS_LOG_FUNCTION (this);
}
TcpSocketBase::TcpSocketBase (const TcpSocketBase& sock)
: TcpSocket (sock), //copy object::m_tid and socket::callbacks
m_dupAckCount (sock.m_dupAckCount),
m_delAckCount (0),
m_delAckMaxCount (sock.m_delAckMaxCount),
m_cnRetries (sock.m_cnRetries),
m_delAckTimeout (sock.m_delAckTimeout),
m_persistTimeout (sock.m_persistTimeout),
m_cnTimeout (sock.m_cnTimeout),
m_endPoint (0),
m_node (sock.m_node),
m_tcp (sock.m_tcp),
m_rtt (0),
m_nextTxSequence (sock.m_nextTxSequence),
m_highTxMark (sock.m_highTxMark),
m_rxBuffer (sock.m_rxBuffer),
m_txBuffer (sock.m_txBuffer),
m_state (sock.m_state),
m_errno (sock.m_errno),
m_closeNotified (sock.m_closeNotified),
m_closeOnEmpty (sock.m_closeOnEmpty),
m_shutdownSend (sock.m_shutdownSend),
m_shutdownRecv (sock.m_shutdownRecv),
m_connected (sock.m_connected),
m_msl (sock.m_msl),
m_segmentSize (sock.m_segmentSize),
m_maxWinSize (sock.m_maxWinSize),
m_rWnd (sock.m_rWnd)
{
NS_LOG_FUNCTION (this);
NS_LOG_LOGIC ("Invoked the copy constructor");
// Copy the rtt estimator if it is set
if (sock.m_rtt)
{
m_rtt = sock.m_rtt->Copy ();
}
// Reset all callbacks to null
Callback<void, Ptr< Socket > > vPS = MakeNullCallback<void, Ptr<Socket> > ();
Callback<void, Ptr<Socket>, const Address &> vPSA = MakeNullCallback<void, Ptr<Socket>, const Address &> ();
Callback<void, Ptr<Socket>, uint32_t> vPSUI = MakeNullCallback<void, Ptr<Socket>, uint32_t> ();
SetConnectCallback (vPS, vPS);
SetDataSentCallback (vPSUI);
SetSendCallback (vPSUI);
SetRecvCallback (vPS);
}
TcpSocketBase::~TcpSocketBase (void)
{
NS_LOG_FUNCTION (this);
m_node = 0;
if (m_endPoint != 0)
{
NS_ASSERT (m_tcp != 0);
/*
* Upon Bind, an Ipv4Endpoint is allocated and set to m_endPoint, and
* DestroyCallback is set to TcpSocketBase::Destroy. If we called
* m_tcp->DeAllocate, it wil destroy its Ipv4EndpointDemux::DeAllocate,
* which in turn destroys my m_endPoint, and in turn invokes
* TcpSocketBase::Destroy to nullify m_node, m_endPoint, and m_tcp.
*/
NS_ASSERT (m_endPoint != 0);
m_tcp->DeAllocate (m_endPoint);
NS_ASSERT (m_endPoint == 0);
}
m_tcp = 0;
CancelAllTimers ();
}
/** Associate a node with this TCP socket */
void
TcpSocketBase::SetNode (Ptr<Node> node)
{
m_node = node;
}
/** Associate the L4 protocol (e.g. mux/demux) with this socket */
void
TcpSocketBase::SetTcp (Ptr<TcpL4Protocol> tcp)
{
m_tcp = tcp;
}
/** Set an RTT estimator with this socket */
void
TcpSocketBase::SetRtt (Ptr<RttEstimator> rtt)
{
m_rtt = rtt;
}
/** Inherit from Socket class: Returns error code */
enum Socket::SocketErrno
TcpSocketBase::GetErrno (void) const
{
return m_errno;
}
/** Inherit from Socket class: Returns socket type, NS3_SOCK_STREAM */
enum Socket::SocketType
TcpSocketBase::GetSocketType (void) const
{
return NS3_SOCK_STREAM;
}
/** Inherit from Socket class: Returns associated node */
Ptr<Node>
TcpSocketBase::GetNode (void) const
{
NS_LOG_FUNCTION_NOARGS ();
return m_node;
}
/** Inherit from Socket class: Bind socket to an end-point in TcpL4Protocol */
int
TcpSocketBase::Bind (void)
{
NS_LOG_FUNCTION_NOARGS ();
m_endPoint = m_tcp->Allocate ();
if (0 == m_endPoint)
{
m_errno = ERROR_ADDRNOTAVAIL;
return -1;
}
m_tcp->m_sockets.push_back (this);
return SetupCallback ();
}
/** Inherit from Socket class: Bind socket (with specific address) to an end-point in TcpL4Protocol */
int
TcpSocketBase::Bind (const Address &address)
{
NS_LOG_FUNCTION (this << address);
if (!InetSocketAddress::IsMatchingType (address))
{
m_errno = ERROR_INVAL;
return -1;
}
InetSocketAddress transport = InetSocketAddress::ConvertFrom (address);
Ipv4Address ipv4 = transport.GetIpv4 ();
uint16_t port = transport.GetPort ();
if (ipv4 == Ipv4Address::GetAny () && port == 0)
{
m_endPoint = m_tcp->Allocate ();
if (0 == m_endPoint)
{
m_errno = ERROR_ADDRNOTAVAIL;
return -1;
}
}
else if (ipv4 == Ipv4Address::GetAny () && port != 0)
{
m_endPoint = m_tcp->Allocate (port);
if (0 == m_endPoint)
{
m_errno = ERROR_ADDRINUSE;
return -1;
}
}
else if (ipv4 != Ipv4Address::GetAny () && port == 0)
{
m_endPoint = m_tcp->Allocate (ipv4);
if (0 == m_endPoint)
{
m_errno = ERROR_ADDRNOTAVAIL;
return -1;
}
}
else if (ipv4 != Ipv4Address::GetAny () && port != 0)
{
m_endPoint = m_tcp->Allocate (ipv4, port);
if (0 == m_endPoint)
{
m_errno = ERROR_ADDRINUSE;
return -1;
}
}
m_tcp->m_sockets.push_back (this);
NS_LOG_LOGIC ("TcpSocketBase " << this << " got an endpoint: " << m_endPoint);
return SetupCallback ();
}
/** Inherit from Socket class: Initiate connection to a remote address:port */
int
TcpSocketBase::Connect (const Address & address)
{
NS_LOG_FUNCTION (this << address);
// If haven't do so, Bind() this socket first
if (m_endPoint == 0)
{
if (Bind () == -1)
{
NS_ASSERT (m_endPoint == 0);
return -1; // Bind() failed
}
NS_ASSERT (m_endPoint != 0);
}
InetSocketAddress transport = InetSocketAddress::ConvertFrom (address);
m_endPoint->SetPeer (transport.GetIpv4 (), transport.GetPort ());
// Get the appropriate local address and port number from the routing protocol and set up endpoint
if (SetupEndpoint () != 0)
{ // Route to destination does not exist
return -1;
}
// Re-initialize parameters in case this socket is being reused after CLOSE
m_rtt->Reset ();
m_cnCount = m_cnRetries;
// DoConnect() will do state-checking and send a SYN packet
return DoConnect ();
}
/** Inherit from Socket class: Listen on the endpoint for an incoming connection */
int
TcpSocketBase::Listen (void)
{
NS_LOG_FUNCTION (this);
// Linux quits EINVAL if we're not in CLOSED state, so match what they do
if (m_state != CLOSED)
{
m_errno = ERROR_INVAL;
return -1;
}
// In other cases, set the state to LISTEN and done
NS_LOG_INFO ("CLOSED -> LISTEN");
m_state = LISTEN;
return 0;
}
/** Inherit from Socket class: Kill this socket and signal the peer (if any) */
int
TcpSocketBase::Close (void)
{
NS_LOG_FUNCTION (this);
// First we check to see if there is any unread rx data
// Bug number 426 claims we should send reset in this case.
if (m_rxBuffer.Size () != 0)
{
SendRST ();
return 0;
}
if (m_txBuffer.SizeFromSequence (m_nextTxSequence) > 0)
{ // App close with pending data must wait until all data transmitted
if (m_closeOnEmpty == false)
{
m_closeOnEmpty = true;
NS_LOG_INFO ("Socket " << this << " deferring close, state " << TcpStateName[m_state]);
}
return 0;
}
return DoClose ();
}
/** Inherit from Socket class: Signal a termination of send */
int
TcpSocketBase::ShutdownSend (void)
{
NS_LOG_FUNCTION (this);
m_shutdownSend = true;
return 0;
}
/** Inherit from Socket class: Signal a termination of receive */
int
TcpSocketBase::ShutdownRecv (void)
{
NS_LOG_FUNCTION (this);
m_shutdownRecv = true;
return 0;
}
/** Inherit from Socket class: Send a packet. Parameter flags is not used.
Packet has no TCP header. Invoked by upper-layer application */
int
TcpSocketBase::Send (Ptr<Packet> p, uint32_t flags)
{
NS_LOG_FUNCTION (this << p);
NS_ABORT_MSG_IF (flags, "use of flags is not supported in TcpSocketBase::Send()");
if (m_state == ESTABLISHED || m_state == SYN_SENT || m_state == CLOSE_WAIT)
{
// Store the packet into Tx buffer
if (!m_txBuffer.Add (p))
{ // TxBuffer overflow, send failed
m_errno = ERROR_MSGSIZE;
return -1;
}
// Submit the data to lower layers
NS_LOG_LOGIC ("txBufSize=" << m_txBuffer.Size () << " state " << TcpStateName[m_state]);
if (m_state == ESTABLISHED || m_state == CLOSE_WAIT)
{ // Try to send the data out
SendPendingData (m_connected);
}
return p->GetSize ();
}
else
{ // Connection not established yet
m_errno = ERROR_NOTCONN;
return -1; // Send failure
}
}
/** Inherit from Socket class: In TcpSocketBase, it is same as Send() call */
int
TcpSocketBase::SendTo (Ptr<Packet> p, uint32_t flags, const Address &address)
{
return Send (p, flags); // SendTo() and Send() are the same
}
/** Inherit from Socket class: Return data to upper-layer application. Parameter flags
is not used. Data is returned as a packet of size no larger than maxSize */
Ptr<Packet>
TcpSocketBase::Recv (uint32_t maxSize, uint32_t flags)
{
NS_LOG_FUNCTION (this);
NS_ABORT_MSG_IF (flags, "use of flags is not supported in TcpSocketBase::Recv()");
if (m_rxBuffer.Size () == 0 && m_state == CLOSE_WAIT)
{
return Create<Packet> (); // Send EOF on connection close
}
Ptr<Packet> outPacket = m_rxBuffer.Extract (maxSize);
if (outPacket != 0 && outPacket->GetSize () != 0)
{
SocketAddressTag tag;
tag.SetAddress (InetSocketAddress (m_endPoint->GetPeerAddress (), m_endPoint->GetPeerPort ()));
outPacket->AddPacketTag (tag);
}
return outPacket;
}
/** Inherit from Socket class: Recv and return the remote's address */
Ptr<Packet>
TcpSocketBase::RecvFrom (uint32_t maxSize, uint32_t flags, Address &fromAddress)
{
NS_LOG_FUNCTION (this << maxSize << flags);
Ptr<Packet> packet = Recv (maxSize, flags);
// Null packet means no data to read, and an empty packet indicates EOF
if (packet != 0 && packet->GetSize () != 0)
{
if (m_endPoint != 0)
{
fromAddress = InetSocketAddress (m_endPoint->GetPeerAddress (), m_endPoint->GetPeerPort ());
}
else
{
fromAddress = InetSocketAddress (Ipv4Address::GetZero (), 0);
}
}
return packet;
}
/** Inherit from Socket class: Get the max number of bytes an app can send */
uint32_t
TcpSocketBase::GetTxAvailable (void) const
{
NS_LOG_FUNCTION (this);
return m_txBuffer.Available ();
}
/** Inherit from Socket class: Get the max number of bytes an app can read */
uint32_t
TcpSocketBase::GetRxAvailable (void) const
{
NS_LOG_FUNCTION (this);
return m_rxBuffer.Available ();
}
/** Inherit from Socket class: Return local address:port */
int
TcpSocketBase::GetSockName (Address &address) const
{
NS_LOG_FUNCTION (this);
if (m_endPoint != 0)
{
address = InetSocketAddress (m_endPoint->GetLocalAddress (), m_endPoint->GetLocalPort ());
}
else
{ // It is possible to call this method on a socket without a name
// in which case, behavior is unspecified
address = InetSocketAddress (Ipv4Address::GetZero (), 0);
}
return 0;
}
/** Inherit from Socket class: Bind this socket to the specified NetDevice */
void
TcpSocketBase::BindToNetDevice (Ptr<NetDevice> netdevice)
{
NS_LOG_FUNCTION (netdevice);
Socket::BindToNetDevice (netdevice); // Includes sanity check
if (m_endPoint == 0)
{
if (Bind () == -1)
{
NS_ASSERT (m_endPoint == 0);
return;
}
NS_ASSERT (m_endPoint != 0);
}
m_endPoint->BindToNetDevice (netdevice);
return;
}
/** Clean up after Bind. Set up callback functions in the end-point. */
int
TcpSocketBase::SetupCallback (void)
{
NS_LOG_FUNCTION (this);
if (m_endPoint == 0)
{
return -1;
}
m_endPoint->SetRxCallback (MakeCallback (&TcpSocketBase::ForwardUp, Ptr<TcpSocketBase> (this)));
m_endPoint->SetDestroyCallback (MakeCallback (&TcpSocketBase::Destroy, Ptr<TcpSocketBase> (this)));
return 0;
}
/** Perform the real connection tasks: Send SYN if allowed, RST if invalid */
int
TcpSocketBase::DoConnect (void)
{
NS_LOG_FUNCTION (this);
// A new connection is allowed only if this socket does not have a connection
if (m_state == CLOSED || m_state == LISTEN || m_state == SYN_SENT || m_state == LAST_ACK || m_state == CLOSE_WAIT)
{ // send a SYN packet and change state into SYN_SENT
SendEmptyPacket (TcpHeader::SYN);
NS_LOG_INFO (TcpStateName[m_state] << " -> SYN_SENT");
m_state = SYN_SENT;
}
else if (m_state != TIME_WAIT)
{ // In states SYN_RCVD, ESTABLISHED, FIN_WAIT_1, FIN_WAIT_2, and CLOSING, an connection
// exists. We send RST, tear down everything, and close this socket.
SendRST ();
CloseAndNotify ();
}
return 0;
}
/** Do the action to close the socket. Usually send a packet with appropriate
flags depended on the current m_state. */
int
TcpSocketBase::DoClose (void)
{
NS_LOG_FUNCTION (this);
switch (m_state)
{
case SYN_RCVD:
case ESTABLISHED:
// send FIN to close the peer
SendEmptyPacket (TcpHeader::FIN);
NS_LOG_INFO ("ESTABLISHED -> FIN_WAIT_1");
m_state = FIN_WAIT_1;
break;
case CLOSE_WAIT:
// send FIN+ACK to close the peer
SendEmptyPacket (TcpHeader::FIN | TcpHeader::ACK);
NS_LOG_INFO ("CLOSE_WAIT -> LAST_ACK");
m_state = LAST_ACK;
break;
case SYN_SENT:
case CLOSING:
// Send RST if application closes in SYN_SENT and CLOSING
SendRST ();
CloseAndNotify ();
break;
case LISTEN:
case LAST_ACK:
// In these three states, move to CLOSED and tear down the end point
CloseAndNotify ();
break;
case CLOSED:
case FIN_WAIT_1:
case FIN_WAIT_2:
case TIME_WAIT:
default: /* mute compiler */
// Do nothing in these four states
break;
}
return 0;
}
/** Peacefully close the socket by notifying the upper layer and deallocate end point */
void
TcpSocketBase::CloseAndNotify (void)
{
NS_LOG_FUNCTION (this);
if (!m_closeNotified) NotifyNormalClose ();
if (m_state != TIME_WAIT) DeallocateEndPoint ();
m_closeNotified = true;
NS_LOG_INFO (TcpStateName[m_state] << " -> CLOSED");
CancelAllTimers ();
m_state = CLOSED;
}
/** Tell if a sequence number range is out side the range that my rx buffer can
accpet */
bool
TcpSocketBase::OutOfRange (SequenceNumber32 head, SequenceNumber32 tail) const
{
if (m_state == LISTEN || m_state == SYN_SENT || m_state == SYN_RCVD)
{ // Rx buffer in these states are not initialized.
return false;
}
if (m_state == LAST_ACK || m_state == CLOSING || m_state == CLOSE_WAIT)
{ // In LAST_ACK and CLOSING states, it only wait for an ACK and the
// sequence number must equals to m_rxBuffer.NextRxSequence ()
return (m_rxBuffer.NextRxSequence () != head);
};
// In all other cases, check if the sequence number is in range
return (tail < m_rxBuffer.NextRxSequence () || m_rxBuffer.MaxRxSequence () <= head);
}
/** Function called by the L3 protocol when it received a packet to pass on to
the TCP. This function is registered as the "RxCallback" function in
SetupCallback(), which invoked by Bind(), and CompleteFork() */
void
TcpSocketBase::ForwardUp (Ptr<Packet> packet, Ipv4Header header, uint16_t port,
Ptr<Ipv4Interface> incomingInterface)
{
DoForwardUp (packet, header, port, incomingInterface);
}
/** The real function to handle the incoming packet from lower layers. This is
wrapped by ForwardUp() so that this function can be overloaded by daughter
classes. */
void
TcpSocketBase::DoForwardUp (Ptr<Packet> packet, Ipv4Header header, uint16_t port,
Ptr<Ipv4Interface> incomingInterface)
{
NS_LOG_LOGIC ("Socket " << this << " forward up " <<
m_endPoint->GetPeerAddress () <<
":" << m_endPoint->GetPeerPort () <<
" to " << m_endPoint->GetLocalAddress () <<
":" << m_endPoint->GetLocalPort ());
Address fromAddress = InetSocketAddress (header.GetSource (), port);
Address toAddress = InetSocketAddress (header.GetDestination (), m_endPoint->GetLocalPort ());
// Peel off TCP header and do validity checking
TcpHeader tcpHeader;
packet->RemoveHeader (tcpHeader);
if (tcpHeader.GetFlags () & TcpHeader::ACK)
{
EstimateRtt (tcpHeader);
}
ReadOptions (tcpHeader);
// Update Rx window size, i.e. the flow control window
if (m_rWnd.Get () == 0 && tcpHeader.GetWindowSize () != 0)
{ // persist probes end
NS_LOG_LOGIC (this << " Leaving zerowindow persist state");
m_persistEvent.Cancel ();
}
m_rWnd = tcpHeader.GetWindowSize ();
// Discard fully out of range packets
if (OutOfRange (tcpHeader.GetSequenceNumber (), tcpHeader.GetSequenceNumber () + packet->GetSize ()))
{
NS_LOG_LOGIC ("At state " << TcpStateName[m_state] <<
" received packet of seq [" << tcpHeader.GetSequenceNumber () <<
":" << tcpHeader.GetSequenceNumber () + packet->GetSize() <<
") out of range [" << m_rxBuffer.NextRxSequence () << ":" <<
m_rxBuffer.MaxRxSequence () << ")");
return;
}
// TCP state machine code in different process functions
// C.f.: tcp_rcv_state_process() in tcp_input.c in Linux kernel
switch (m_state)
{
case ESTABLISHED:
ProcessEstablished (packet, tcpHeader);
break;
case LISTEN:
ProcessListen (packet, tcpHeader, fromAddress, toAddress);
break;
case TIME_WAIT:
// Do nothing
break;
case CLOSED:
// Send RST if the incoming packet is not a RST
if ((tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG)) != TcpHeader::RST)
{ // Since m_endPoint is not configured yet, we cannot use SendRST here
TcpHeader h;
h.SetFlags (TcpHeader::RST);
h.SetSequenceNumber (m_nextTxSequence);
h.SetAckNumber (m_rxBuffer.NextRxSequence ());
h.SetSourcePort (tcpHeader.GetDestinationPort ());
h.SetDestinationPort (tcpHeader.GetSourcePort ());
h.SetWindowSize (AdvertisedWindowSize ());
AddOptions (h);
m_tcp->SendPacket (Create<Packet> (), h, header.GetDestination (), header.GetSource (), m_boundnetdevice);
}
break;
case SYN_SENT:
ProcessSynSent (packet, tcpHeader);
break;
case SYN_RCVD:
ProcessSynRcvd (packet, tcpHeader, fromAddress, toAddress);
break;
case FIN_WAIT_1:
case FIN_WAIT_2:
case CLOSE_WAIT:
ProcessWait (packet, tcpHeader);
break;
case CLOSING:
ProcessClosing (packet, tcpHeader);
break;
case LAST_ACK:
ProcessLastAck (packet, tcpHeader);
break;
default: // mute compiler
break;
}
}
/** Received a packet upon ESTABLISHED state. This function is mimicking the
role of tcp_rcv_established() in tcp_input.c in Linux kernel. */
void
TcpSocketBase::ProcessEstablished (Ptr<Packet> packet, const TcpHeader& tcpHeader)
{
NS_LOG_FUNCTION (this << tcpHeader);
// Extract the flags. PSH and URG are not honoured.
uint8_t tcpflags = tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG);
// Different flags are different events
if (tcpflags == TcpHeader::ACK)
{
ReceivedAck (packet, tcpHeader);
}
else if (tcpflags == TcpHeader::SYN)
{ // Received SYN, old NS-3 behaviour is to set state to SYN_RCVD and
// respond with a SYN+ACK. But it is not a legal state transition as of
// RFC793. Thus this is ignored.
}
else if (tcpflags == (TcpHeader::SYN | TcpHeader::ACK))
{ // No action for received SYN+ACK, it is probably a duplicated packet
}
else if (tcpflags == TcpHeader::FIN || tcpflags == (TcpHeader::FIN | TcpHeader::ACK))
{ // Received FIN or FIN+ACK, bring down this socket nicely
PeerClose (packet, tcpHeader);
}
else if (tcpflags == 0)
{ // No flags means there is only data
ReceivedData (packet, tcpHeader);
if (m_rxBuffer.Finished ())
{
PeerClose (packet, tcpHeader);
}
}
else
{ // Received RST or the TCP flags is invalid, in either case, terminate this socket
if (tcpflags != TcpHeader::RST)
{ // this must be an invalid flag, send reset
NS_LOG_LOGIC ("Illegal flag " << tcpflags << " received. Reset packet is sent.");
SendRST ();
}
CloseAndNotify ();
}
}
/** Process the newly received ACK */
void
TcpSocketBase::ReceivedAck (Ptr<Packet> packet, const TcpHeader& tcpHeader)
{
NS_LOG_FUNCTION (this << tcpHeader);
// Received ACK. Compare the ACK number against highest unacked seqno
if (0 == (tcpHeader.GetFlags () & TcpHeader::ACK))
{ // Ignore if no ACK flag
}
else if (tcpHeader.GetAckNumber () < m_txBuffer.HeadSequence ())
{ // Case 1: Old ACK, ignored.
NS_LOG_LOGIC ("Ignored ack of " << tcpHeader.GetAckNumber ());
}
else if (tcpHeader.GetAckNumber () == m_txBuffer.HeadSequence ())
{ // Case 2: Potentially a duplicated ACK
if (tcpHeader.GetAckNumber () < m_nextTxSequence)
{
NS_LOG_LOGIC ("Dupack of " << tcpHeader.GetAckNumber ());
DupAck (tcpHeader, ++m_dupAckCount);
}
// otherwise, the ACK is precisely equal to the nextTxSequence
NS_ASSERT (tcpHeader.GetAckNumber () <= m_nextTxSequence);
}
else if (tcpHeader.GetAckNumber () > m_txBuffer.HeadSequence ())
{ // Case 3: New ACK, reset m_dupAckCount and update m_txBuffer
NS_LOG_LOGIC ("New ack of " << tcpHeader.GetAckNumber ());
NewAck (tcpHeader.GetAckNumber ());
m_dupAckCount = 0;
}
// If there is any data piggybacked, store it into m_rxBuffer
if (packet->GetSize () > 0)
{
ReceivedData (packet, tcpHeader);
}
}
/** Received a packet upon LISTEN state. */
void
TcpSocketBase::ProcessListen (Ptr<Packet> packet, const TcpHeader& tcpHeader,
const Address& fromAddress, const Address& toAddress)
{
NS_LOG_FUNCTION (this << tcpHeader);
// Extract the flags. PSH and URG are not honoured.
uint8_t tcpflags = tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG);
// Fork a socket if received a SYN. Do nothing otherwise.
// C.f.: the LISTEN part in tcp_v4_do_rcv() in tcp_ipv4.c in Linux kernel
if (tcpflags != TcpHeader::SYN) return;
// Call socket's notify function to let the server app know we got a SYN
// If the server app refuses the connection, do nothing
if (!NotifyConnectionRequest (fromAddress)) return;
// Clone the socket, simulate fork
Ptr<TcpSocketBase> newSock = Fork ();
NS_LOG_LOGIC ("Cloned a TcpSocketBase " << newSock);
Simulator::ScheduleNow (&TcpSocketBase::CompleteFork, newSock,
packet, tcpHeader, fromAddress, toAddress);
}
/** Received a packet upon SYN_SENT */
void
TcpSocketBase::ProcessSynSent (Ptr<Packet> packet, const TcpHeader& tcpHeader)
{
NS_LOG_FUNCTION (this << tcpHeader);
// Extract the flags. PSH and URG are not honoured.
uint8_t tcpflags = tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG);
if (tcpflags == 0)
{ // Bare data, accept it and move to ESTABLISHED state. This is not a normal behaviour. Remove this?
NS_LOG_INFO ("SYN_SENT -> ESTABLISHED");
m_state = ESTABLISHED;
m_connected = true;
m_retxEvent.Cancel ();
m_delAckCount = m_delAckMaxCount;
ReceivedData (packet, tcpHeader);
Simulator::ScheduleNow (&TcpSocketBase::ConnectionSucceeded, this);
}
else if (tcpflags == TcpHeader::ACK)
{ // Ignore ACK in SYN_SENT
}
else if (tcpflags == TcpHeader::SYN)
{ // Received SYN, move to SYN_RCVD state and respond with SYN+ACK
NS_LOG_INFO ("SYN_SENT -> SYN_RCVD");
m_state = SYN_RCVD;
m_cnCount = m_cnRetries;
m_rxBuffer.SetNextRxSequence (tcpHeader.GetSequenceNumber () + SequenceNumber32 (1));
SendEmptyPacket (TcpHeader::SYN | TcpHeader::ACK);
}
else if (tcpflags == (TcpHeader::SYN | TcpHeader::ACK)
&& m_nextTxSequence + SequenceNumber32 (1) == tcpHeader.GetAckNumber ())
{ // Handshake completed
NS_LOG_INFO ("SYN_SENT -> ESTABLISHED");
m_state = ESTABLISHED;
m_connected = true;
m_retxEvent.Cancel ();
m_rxBuffer.SetNextRxSequence (tcpHeader.GetSequenceNumber () + SequenceNumber32 (1));
m_highTxMark = ++m_nextTxSequence;
m_txBuffer.SetHeadSequence (m_nextTxSequence);
SendEmptyPacket (TcpHeader::ACK);
SendPendingData (m_connected);
Simulator::ScheduleNow (&TcpSocketBase::ConnectionSucceeded, this);
// Always respond to first data packet to speed up the connection.
// Remove to get the behaviour of old NS-3 code.
m_delAckCount = m_delAckMaxCount;
}
else
{ // Other in-sequence input
if (tcpflags != TcpHeader::RST)
{ // When (1) rx of FIN+ACK; (2) rx of FIN; (3) rx of bad flags
NS_LOG_LOGIC ("Illegal flag " << std::hex << static_cast<uint32_t>(tcpflags) << std::dec << " received. Reset packet is sent.");
SendRST ();
}
CloseAndNotify ();
}
}
/** Received a packet upon SYN_RCVD */
void
TcpSocketBase::ProcessSynRcvd (Ptr<Packet> packet, const TcpHeader& tcpHeader,
const Address& fromAddress, const Address& toAddress)
{
NS_LOG_FUNCTION (this << tcpHeader);
// Extract the flags. PSH and URG are not honoured.
uint8_t tcpflags = tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG);
if (tcpflags == 0 ||
(tcpflags == TcpHeader::ACK
&& m_nextTxSequence + SequenceNumber32 (1) == tcpHeader.GetAckNumber ()))
{ // If it is bare data, accept it and move to ESTABLISHED state. This is
// possibly due to ACK lost in 3WHS. If in-sequence ACK is received, the
// handshake is completed nicely.
NS_LOG_INFO ("SYN_RCVD -> ESTABLISHED");
m_state = ESTABLISHED;
m_connected = true;
m_retxEvent.Cancel ();
m_highTxMark = ++m_nextTxSequence;
m_txBuffer.SetHeadSequence (m_nextTxSequence);
m_endPoint->SetPeer (InetSocketAddress::ConvertFrom (fromAddress).GetIpv4 (),
InetSocketAddress::ConvertFrom (fromAddress).GetPort ());
// Always respond to first data packet to speed up the connection.
// Remove to get the behaviour of old NS-3 code.
m_delAckCount = m_delAckMaxCount;
ReceivedAck (packet, tcpHeader);
NotifyNewConnectionCreated (this, fromAddress);
// As this connection is established, the socket is available to send data now
if (GetTxAvailable () > 0)
{
NotifySend (GetTxAvailable ());
}
}
else if (tcpflags == TcpHeader::SYN)
{ // Probably the peer lost my SYN+ACK
m_rxBuffer.SetNextRxSequence (tcpHeader.GetSequenceNumber () + SequenceNumber32 (1));
SendEmptyPacket (TcpHeader::SYN | TcpHeader::ACK);
}
else if (tcpflags == (TcpHeader::FIN | TcpHeader::ACK))
{
if (tcpHeader.GetSequenceNumber () == m_rxBuffer.NextRxSequence ())
{ // In-sequence FIN before connection complete. Set up connection and close.
m_connected = true;
m_retxEvent.Cancel ();
m_highTxMark = ++m_nextTxSequence;
m_txBuffer.SetHeadSequence (m_nextTxSequence);
m_endPoint->SetPeer (InetSocketAddress::ConvertFrom (fromAddress).GetIpv4 (),
InetSocketAddress::ConvertFrom (fromAddress).GetPort ());
PeerClose (packet, tcpHeader);
}
}
else
{ // Other in-sequence input
if (tcpflags != TcpHeader::RST)
{ // When (1) rx of SYN+ACK; (2) rx of FIN; (3) rx of bad flags
NS_LOG_LOGIC ("Illegal flag " << tcpflags << " received. Reset packet is sent.");
m_endPoint->SetPeer (InetSocketAddress::ConvertFrom (fromAddress).GetIpv4 (),
InetSocketAddress::ConvertFrom (fromAddress).GetPort ());
SendRST ();
}
CloseAndNotify ();
}
}
/** Received a packet upon CLOSE_WAIT, FIN_WAIT_1, or FIN_WAIT_2 states */
void
TcpSocketBase::ProcessWait (Ptr<Packet> packet, const TcpHeader& tcpHeader)
{
NS_LOG_FUNCTION (this << tcpHeader);
// Extract the flags. PSH and URG are not honoured.
uint8_t tcpflags = tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG);
if (packet->GetSize () > 0)
{ // Bare data, accept it
ReceivedData (packet, tcpHeader);
}
else if (tcpflags == TcpHeader::ACK)
{ // Process the ACK, and if in FIN_WAIT_1, conditionally move to FIN_WAIT_2
ReceivedAck (packet, tcpHeader);
if (m_state == FIN_WAIT_1 && m_txBuffer.Size () == 0 &&
tcpHeader.GetAckNumber () == m_highTxMark + SequenceNumber32 (1))
{ // This ACK corresponds to the FIN sent
NS_LOG_INFO ("FIN_WAIT_1 -> FIN_WAIT_2");
m_state = FIN_WAIT_2;
}
}
else if (tcpflags == TcpHeader::FIN || tcpflags == (TcpHeader::FIN | TcpHeader::ACK))
{ // Got FIN, respond with ACK and move to next state
if (tcpflags & TcpHeader::ACK)
{ // Process the ACK first
ReceivedAck (packet, tcpHeader);
}
m_rxBuffer.SetFinSequence (tcpHeader.GetSequenceNumber ());
}
else if (tcpflags == TcpHeader::SYN || tcpflags == (TcpHeader::SYN | TcpHeader::ACK))
{ // Duplicated SYN or SYN+ACK, possibly due to spurious retransmission
return;
}
else
{ // This is a RST or bad flags
if (tcpflags != TcpHeader::RST)
{
NS_LOG_LOGIC ("Illegal flag " << tcpflags << " received. Reset packet is sent.");
SendRST ();
}
CloseAndNotify ();
return;
}
// Check if the close responder sent an in-sequence FIN, if so, respond ACK
if ((m_state == FIN_WAIT_1 || m_state == FIN_WAIT_2) && m_rxBuffer.Finished ())
{
if (m_state == FIN_WAIT_1)
{
NS_LOG_INFO ("FIN_WAIT_1 -> CLOSING");
m_state = CLOSING;
if (m_txBuffer.Size () == 0 &&
tcpHeader.GetAckNumber () == m_highTxMark + SequenceNumber32 (1))
{ // This ACK corresponds to the FIN sent
TimeWait ();
}
}
else if (m_state == FIN_WAIT_2)
{
TimeWait ();
};
SendEmptyPacket (TcpHeader::ACK);
if (!m_shutdownRecv) NotifyDataRecv ();
}
}
/** Received a packet upon CLOSING */
void
TcpSocketBase::ProcessClosing (Ptr<Packet> packet, const TcpHeader& tcpHeader)
{
NS_LOG_FUNCTION (this << tcpHeader);
// Extract the flags. PSH and URG are not honoured.
uint8_t tcpflags = tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG);
if (tcpflags == TcpHeader::ACK)
{
if (tcpHeader.GetSequenceNumber () == m_rxBuffer.NextRxSequence ())
{ // This ACK corresponds to the FIN sent
TimeWait ();
}
}
else
{ // CLOSING state means simultaneous close, i.e. no one is sending data to
// anyone. If anything other than ACK is received, respond with a reset.
if (tcpflags == TcpHeader::FIN || tcpflags == (TcpHeader::FIN | TcpHeader::ACK))
{ // FIN from the peer as well. We can close immediately.
SendEmptyPacket (TcpHeader::ACK);
}
else if (tcpflags != TcpHeader::RST)
{ // Receive of SYN or SYN+ACK or bad flags or pure data
NS_LOG_LOGIC ("Illegal flag " << tcpflags << " received. Reset packet is sent.");
SendRST ();
}
CloseAndNotify ();
}
}
/** Received a packet upon LAST_ACK */
void
TcpSocketBase::ProcessLastAck (Ptr<Packet> packet, const TcpHeader& tcpHeader)
{
NS_LOG_FUNCTION (this << tcpHeader);
// Extract the flags. PSH and URG are not honoured.
uint8_t tcpflags = tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG);
if (tcpflags == 0)
{
ReceivedData (packet, tcpHeader);
}
else if (tcpflags == TcpHeader::ACK)
{
if (tcpHeader.GetSequenceNumber () == m_rxBuffer.NextRxSequence ())
{ // This ACK corresponds to the FIN sent. This socket closed peacefully.
CloseAndNotify ();
}
}
else if (tcpflags == TcpHeader::FIN)
{ // Received FIN again, the peer probably lost the FIN+ACK
SendEmptyPacket (TcpHeader::FIN | TcpHeader::ACK);
}
else if (tcpflags == (TcpHeader::FIN | TcpHeader::ACK) || tcpflags == TcpHeader::RST)
{
CloseAndNotify ();
}
else
{ // Received a SYN or SYN+ACK or bad flags
NS_LOG_LOGIC ("Illegal flag " << tcpflags << " received. Reset packet is sent.");
SendRST ();
CloseAndNotify ();
}
}
/** Peer sent me a FIN. Remember its sequence in rx buffer. */
void
TcpSocketBase::PeerClose (Ptr<Packet> p, const TcpHeader& tcpHeader)
{
NS_LOG_FUNCTION (this << tcpHeader);
// Ignore all out of range packets
if (tcpHeader.GetSequenceNumber () < m_rxBuffer.NextRxSequence () ||
tcpHeader.GetSequenceNumber () > m_rxBuffer.MaxRxSequence ())
{
return;
};
// For any case, remember the FIN position in rx buffer first
m_rxBuffer.SetFinSequence (tcpHeader.GetSequenceNumber () + SequenceNumber32 (p->GetSize ()));
NS_LOG_LOGIC ("Accepted FIN at seq " << tcpHeader.GetSequenceNumber () + SequenceNumber32 (p->GetSize ()));
// If there is any piggybacked data, process it
if (p->GetSize ())
{
ReceivedData (p, tcpHeader);
}
// Return if FIN is out of sequence, otherwise move to CLOSE_WAIT state by DoPeerClose
if (!m_rxBuffer.Finished ())
{
return;
};
// Simultaneous close: Application invoked Close() when we are processing this FIN packet
if (m_state == FIN_WAIT_1)
{
NS_LOG_INFO ("FIN_WAIT_1 -> CLOSING");
m_state = CLOSING;
return;
}
DoPeerClose (); // Change state, respond with ACK
}
/** Received a in-sequence FIN. Close down this socket. */
void
TcpSocketBase::DoPeerClose (void)
{
NS_ASSERT (m_state == ESTABLISHED || m_state == SYN_RCVD);
// Move the state to CLOSE_WAIT
NS_LOG_INFO (TcpStateName[m_state] << " -> CLOSE_WAIT");
m_state = CLOSE_WAIT;
if (!m_closeNotified)
{
// The normal behaviour for an application is that, when the peer sent a in-sequence
// FIN, the app should prepare to close. The app has two choices at this point: either
// respond with ShutdownSend() call to declare that it has nothing more to send and
// the socket can be closed immediately; or remember the peer's close request, wait
// until all its existing data are pushed into the TCP socket, then call Close()
// explicitly.
NS_LOG_LOGIC ("TCP " << this << " calling NotifyNormalClose");
NotifyNormalClose ();
m_closeNotified = true;
}
if (m_shutdownSend)
{ // The application declares that it would not sent any more, close this socket
Close ();
}
else
{ // Need to ack, the application will close later
SendEmptyPacket (TcpHeader::ACK);
}
if (m_state == LAST_ACK)
{
NS_LOG_LOGIC ("TcpSocketBase " << this << " scheduling LATO1");
m_lastAckEvent = Simulator::Schedule (m_rtt->RetransmitTimeout (),
&TcpSocketBase::LastAckTimeout, this);
}
}
/** Kill this socket. This is a callback function configured to m_endpoint in
SetupCallback(), invoked when the endpoint is destroyed. */
void
TcpSocketBase::Destroy (void)
{
NS_LOG_FUNCTION (this);
m_node = 0;
m_endPoint = 0;
std::vector<Ptr<TcpSocketBase> >::iterator it
= std::find (m_tcp->m_sockets.begin (), m_tcp->m_sockets.end (), this);
if (it != m_tcp->m_sockets.end ())
{
m_tcp->m_sockets.erase (it);
}
m_tcp = 0;
NS_LOG_LOGIC (this << " Cancelled ReTxTimeout event which was set to expire at " <<
(Simulator::Now () + Simulator::GetDelayLeft (m_retxEvent)).GetSeconds ());
CancelAllTimers ();
}
/** Send an empty packet with specified TCP flags */
void
TcpSocketBase::SendEmptyPacket (uint8_t flags)
{
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 (m_endPoint->GetPeerPort ());
header.SetWindowSize (AdvertisedWindowSize ());
AddOptions (header);
m_rto = m_rtt->RetransmitTimeout ();
bool hasSyn = flags & TcpHeader::SYN;
bool hasFin = flags & TcpHeader::FIN;
bool isAck = flags == TcpHeader::ACK;
if (hasSyn)
{
if (m_cnCount == 0)
{ // No more connection retries, give up
NS_LOG_LOGIC ("Connection failed.");
CloseAndNotify ();
return;
}
else
{ // Exponential backoff of connection time out
int backoffCount = 0x1 << (m_cnRetries - m_cnCount);
m_rto = m_cnTimeout * backoffCount;
m_cnCount--;
}
}
m_tcp->SendPacket (p, header, m_endPoint->GetLocalAddress (), m_endPoint->GetPeerAddress (), m_boundnetdevice);
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::SendEmptyPacket, this, flags);
}
}
/** This function closes the endpoint completely. Called upon RST_TX action. */
void
TcpSocketBase::SendRST (void)
{
NS_LOG_FUNCTION (this);
SendEmptyPacket (TcpHeader::RST);
NotifyErrorClose ();
DeallocateEndPoint ();
}
/** Deallocate the end point and cancel all the timers */
void
TcpSocketBase::DeallocateEndPoint (void)
{
if (m_endPoint != 0)
{
m_endPoint->SetDestroyCallback (MakeNullCallback<void> ());
m_tcp->DeAllocate (m_endPoint);
m_endPoint = 0;
std::vector<Ptr<TcpSocketBase> >::iterator it
= std::find (m_tcp->m_sockets.begin (), m_tcp->m_sockets.end (), this);
if (it != m_tcp->m_sockets.end ())
{
m_tcp->m_sockets.erase (it);
}
CancelAllTimers ();
}
}
/** Configure the endpoint to a local address. Called by Connect() if Bind() didn't specify one. */
int
TcpSocketBase::SetupEndpoint ()
{
NS_LOG_FUNCTION (this);
Ptr<Ipv4> ipv4 = m_node->GetObject<Ipv4> ();
NS_ASSERT (ipv4 != 0);
if (ipv4->GetRoutingProtocol () == 0)
{
NS_FATAL_ERROR ("No Ipv4RoutingProtocol in the node");
}
// Create a dummy packet, then ask the routing function for the best output
// interface's address
Ipv4Header header;
header.SetDestination (m_endPoint->GetPeerAddress ());
Socket::SocketErrno errno_;
Ptr<Ipv4Route> route;
Ptr<NetDevice> oif = m_boundnetdevice;
route = ipv4->GetRoutingProtocol ()->RouteOutput (Ptr<Packet> (), header, oif, errno_);
if (route == 0)
{
NS_LOG_LOGIC ("Route to " << m_endPoint->GetPeerAddress () << " does not exist");
NS_LOG_ERROR (errno_);
m_errno = errno_;
return -1;
}
NS_LOG_LOGIC ("Route exists");
m_endPoint->SetLocalAddress (route->GetSource ());
return 0;
}
/** This function is called only if a SYN received in LISTEN state. After
TcpSocketBase cloned, allocate a new end point to handle the incoming
connection and send a SYN+ACK to complete the handshake. */
void
TcpSocketBase::CompleteFork (Ptr<Packet> p, const TcpHeader& h,
const Address& fromAddress, const Address& toAddress)
{
// Get port and address from peer (connecting host)
m_endPoint = m_tcp->Allocate (InetSocketAddress::ConvertFrom (toAddress).GetIpv4 (),
InetSocketAddress::ConvertFrom (toAddress).GetPort (),
InetSocketAddress::ConvertFrom (fromAddress).GetIpv4 (),
InetSocketAddress::ConvertFrom (fromAddress).GetPort ());
m_tcp->m_sockets.push_back (this);
// Change the cloned socket from LISTEN state to SYN_RCVD
NS_LOG_INFO ("LISTEN -> SYN_RCVD");
m_state = SYN_RCVD;
m_cnCount = m_cnRetries;
SetupCallback ();
// Set the sequence number and send SYN+ACK
m_rxBuffer.SetNextRxSequence (h.GetSequenceNumber () + SequenceNumber32 (1));
SendEmptyPacket (TcpHeader::SYN | TcpHeader::ACK);
}
void
TcpSocketBase::ConnectionSucceeded ()
{ // Wrapper to protected function NotifyConnectionSucceeded() so that it can
// be called as a scheduled event
NotifyConnectionSucceeded ();
// The if-block below was moved from ProcessSynSent() to here because we need
// to invoke the NotifySend() only after NotifyConnectionSucceeded() to
// reflect the behaviour in the real world.
if (GetTxAvailable () > 0)
{
NotifySend (GetTxAvailable ());
}
}
/** Extract at most maxSize bytes from the TxBuffer at sequence seq, add the
TCP header, and send to TcpL4Protocol */
uint32_t
TcpSocketBase::SendDataPacket (SequenceNumber32 seq, uint32_t maxSize, bool withAck)
{
NS_LOG_FUNCTION (this << seq << maxSize << withAck);
Ptr<Packet> p = m_txBuffer.CopyFromSequence (maxSize, seq);
uint32_t sz = p->GetSize (); // Size of packet
uint8_t flags = withAck ? TcpHeader::ACK : 0;
uint32_t remainingData = m_txBuffer.SizeFromSequence (seq + SequenceNumber32 (sz));
if (m_closeOnEmpty && (remainingData == 0))
{
flags |= TcpHeader::FIN;
if (m_state == ESTABLISHED)
{ // On active close: I am the first one to send FIN
NS_LOG_INFO ("ESTABLISHED -> FIN_WAIT_1");
m_state = FIN_WAIT_1;
}
else if (m_state == CLOSE_WAIT)
{ // On passive close: Peer sent me FIN already
NS_LOG_INFO ("CLOSE_WAIT -> LAST_ACK");
m_state = LAST_ACK;
}
}
TcpHeader header;
header.SetFlags (flags);
header.SetSequenceNumber (seq);
header.SetAckNumber (m_rxBuffer.NextRxSequence ());
header.SetSourcePort (m_endPoint->GetLocalPort ());
header.SetDestinationPort (m_endPoint->GetPeerPort ());
header.SetWindowSize (AdvertisedWindowSize ());
AddOptions (header);
if (m_retxEvent.IsExpired () )
{ // Schedule retransmit
m_rto = m_rtt->RetransmitTimeout ();
NS_LOG_LOGIC (this << " SendDataPacket Schedule ReTxTimeout at time " <<
Simulator::Now ().GetSeconds () << " to expire at time " <<
(Simulator::Now () + m_rto.Get ()).GetSeconds () );
m_retxEvent = Simulator::Schedule (m_rto, &TcpSocketBase::ReTxTimeout, this);
}
NS_LOG_LOGIC ("Send packet via TcpL4Protocol with flags 0x" << std::hex << static_cast<uint32_t> (flags) << std::dec);
m_tcp->SendPacket (p, header, m_endPoint->GetLocalAddress (),
m_endPoint->GetPeerAddress (), m_boundnetdevice);
m_rtt->SentSeq (seq, sz); // notify the RTT
// Notify the application of the data being sent unless this is a retransmit
if (seq == m_nextTxSequence)
{
Simulator::ScheduleNow (&TcpSocketBase::NotifyDataSent, this, sz);
}
// Update highTxMark
m_highTxMark = std::max (seq + sz, m_highTxMark.Get ());
return sz;
}
/** Send as much pending data as possible according to the Tx window. Note that
* this function did not implement the PSH flag
*/
bool
TcpSocketBase::SendPendingData (bool withAck)
{
NS_LOG_FUNCTION (this << withAck);
if (m_txBuffer.Size () == 0) return false; // Nothing to send
if (m_endPoint == 0)
{
NS_LOG_INFO ("TcpSocketBase::SendPendingData: No endpoint; m_shutdownSend=" << m_shutdownSend);
return false; // Is this the right way to handle this condition?
}
uint32_t nPacketsSent = 0;
while (m_txBuffer.SizeFromSequence (m_nextTxSequence))
{
uint32_t w = AvailableWindow (); // Get available window size
NS_LOG_LOGIC ("TcpSocketBase " << this << " SendPendingData" <<
" w " << w <<
" rxwin " << m_rWnd <<
" segsize " << m_segmentSize <<
" nextTxSeq " << m_nextTxSequence <<
" highestRxAck " << m_txBuffer.HeadSequence () <<
" pd->Size " << m_txBuffer.Size () <<
" pd->SFS " << m_txBuffer.SizeFromSequence (m_nextTxSequence));
// Quit if send disallowed
if (m_shutdownSend)
{
m_errno = ERROR_SHUTDOWN;
return false;
}
// Stop sending if we need to wait for a larger Tx window
if (w < m_segmentSize && m_txBuffer.SizeFromSequence (m_nextTxSequence) > w)
{
break; // No more
}
uint32_t s = std::min (w, m_segmentSize); // Send no more than window
uint32_t sz = SendDataPacket (m_nextTxSequence, s, withAck);
nPacketsSent++; // Count sent this loop
m_nextTxSequence += sz; // Advance next tx sequence
}
NS_LOG_LOGIC ("SendPendingData sent " << nPacketsSent << " packets");
return (nPacketsSent > 0);
}
uint32_t
TcpSocketBase::UnAckDataCount ()
{
NS_LOG_FUNCTION (this);
return m_nextTxSequence.Get () - m_txBuffer.HeadSequence ();
}
uint32_t
TcpSocketBase::BytesInFlight ()
{
NS_LOG_FUNCTION (this);
return m_highTxMark.Get () - m_txBuffer.HeadSequence ();
}
uint32_t
TcpSocketBase::Window ()
{
NS_LOG_FUNCTION (this);
return m_rWnd;
}
uint32_t
TcpSocketBase::AvailableWindow ()
{
NS_LOG_FUNCTION_NOARGS ();
uint32_t unack = UnAckDataCount (); // Number of outstanding bytes
uint32_t win = Window (); // Number of bytes allowed to be outstanding
NS_LOG_LOGIC ("UnAckCount=" << unack << ", Win=" << win);
return (win < unack) ? 0 : (win - unack);
}
uint16_t
TcpSocketBase::AdvertisedWindowSize ()
{
return std::min (m_rxBuffer.MaxBufferSize () - m_rxBuffer.Size (), (uint32_t)m_maxWinSize);
}
// Receipt of new packet, put into Rx buffer
void
TcpSocketBase::ReceivedData (Ptr<Packet> p, const TcpHeader& tcpHeader)
{
NS_LOG_FUNCTION (this << tcpHeader);
NS_LOG_LOGIC ("seq " << tcpHeader.GetSequenceNumber () <<
" ack " << tcpHeader.GetAckNumber () <<
" pkt size " << p->GetSize () );
// Put into Rx buffer
SequenceNumber32 expectedSeq = m_rxBuffer.NextRxSequence ();
if (!m_rxBuffer.Add (p, tcpHeader))
{ // Insert failed: No data or RX buffer full
SendEmptyPacket (TcpHeader::ACK);
return;
}
// Now send a new ACK packet acknowledging all received and delivered data
if (m_rxBuffer.Size () > m_rxBuffer.Available () || m_rxBuffer.NextRxSequence () > expectedSeq + p->GetSize ())
{ // A gap exists in the buffer, or we filled a gap: Always ACK
SendEmptyPacket (TcpHeader::ACK);
}
else
{ // In-sequence packet: ACK if delayed ack count allows
if (++m_delAckCount >= m_delAckMaxCount)
{
m_delAckEvent.Cancel ();
m_delAckCount = 0;
SendEmptyPacket (TcpHeader::ACK);
}
else if (m_delAckEvent.IsExpired ())
{
m_delAckEvent = Simulator::Schedule (m_delAckTimeout,
&TcpSocketBase::DelAckTimeout, this);
NS_LOG_LOGIC (this << " scheduled delayed ACK at " << (Simulator::Now () + Simulator::GetDelayLeft (m_delAckEvent)).GetSeconds ());
}
}
// Notify app to receive if necessary
if (expectedSeq < m_rxBuffer.NextRxSequence ())
{ // NextRxSeq advanced, we have something to send to the app
if (!m_shutdownRecv) NotifyDataRecv ();
// Handle exceptions
if (m_closeNotified)
{
NS_LOG_WARN ("Why TCP " << this << " got data after close notification?");
}
// If we received FIN before and now completed all "holes" in rx buffer,
// invoke peer close procedure
if (m_rxBuffer.Finished () && (tcpHeader.GetFlags () & TcpHeader::FIN) == 0)
{
DoPeerClose ();
}
}
}
/** Called by ForwardUp() to estimate RTT */
void
TcpSocketBase::EstimateRtt (const TcpHeader& tcpHeader)
{
// Use m_rtt for the estimation. Note, RTT of duplicated acknowledgement
// (which should be ignored) is handled by m_rtt. Once timestamp option
// is implemented, this function would be more elaborated.
m_lastRtt = m_rtt->AckSeq (tcpHeader.GetAckNumber () );
};
// Called by the ReceivedAck() when new ACK received and by ProcessSynRcvd()
// when the three-way handshake completed. This cancels retransmission timer
// and advances Tx window
void
TcpSocketBase::NewAck (SequenceNumber32 const& ack)
{
NS_LOG_FUNCTION (this << ack);
if (m_state != SYN_RCVD)
{ // Set RTO unless the ACK is received in SYN_RCVD state
NS_LOG_LOGIC (this << " Cancelled ReTxTimeout event which was set to expire at " <<
(Simulator::Now () + Simulator::GetDelayLeft (m_retxEvent)).GetSeconds ());
m_retxEvent.Cancel ();
// On recieving a "New" ack we restart retransmission timer .. RFC 2988
m_rto = m_rtt->RetransmitTimeout ();
NS_LOG_LOGIC (this << " Schedule ReTxTimeout at time " <<
Simulator::Now ().GetSeconds () << " to expire at time " <<
(Simulator::Now () + m_rto.Get ()).GetSeconds ());
m_retxEvent = Simulator::Schedule (m_rto, &TcpSocketBase::ReTxTimeout, this);
}
if (m_rWnd.Get () == 0 && m_persistEvent.IsExpired ())
{ // Zero window: Enter persist state to send 1 byte to probe
NS_LOG_LOGIC (this << "Enter zerowindow persist state");
NS_LOG_LOGIC (this << "Cancelled ReTxTimeout event which was set to expire at " <<
(Simulator::Now () + Simulator::GetDelayLeft (m_retxEvent)).GetSeconds ());
m_retxEvent.Cancel ();
NS_LOG_LOGIC ("Schedule persist timeout at time " <<
Simulator::Now ().GetSeconds () << " to expire at time " <<
(Simulator::Now () + m_persistTimeout).GetSeconds ());
m_persistEvent = Simulator::Schedule (m_persistTimeout, &TcpSocketBase::PersistTimeout, this);
NS_ASSERT (m_persistTimeout == Simulator::GetDelayLeft (m_persistEvent));
}
// Note the highest ACK and tell app to send more
NS_LOG_LOGIC ("TCP " << this << " NewAck " << ack <<
" numberAck " << (ack - m_txBuffer.HeadSequence ())); // Number bytes ack'ed
m_txBuffer.DiscardUpTo (ack);
if (GetTxAvailable () > 0)
{
NotifySend (GetTxAvailable ());
}
if (ack > m_nextTxSequence)
{
m_nextTxSequence = ack; // If advanced
}
if (m_txBuffer.Size () == 0 && m_state != FIN_WAIT_1 && m_state != CLOSING)
{ // No retransmit timer if no data to retransmit
NS_LOG_LOGIC (this << " Cancelled ReTxTimeout event which was set to expire at " <<
(Simulator::Now () + Simulator::GetDelayLeft (m_retxEvent)).GetSeconds ());
m_retxEvent.Cancel ();
}
// Try to send more data
SendPendingData (m_connected);
}
// Retransmit timeout
void
TcpSocketBase::ReTxTimeout ()
{
NS_LOG_FUNCTION (this);
NS_LOG_LOGIC (this << " ReTxTimeout Expired at time " << Simulator::Now ().GetSeconds ());
// If erroneous timeout in closed/timed-wait state, just return
if (m_state == CLOSED || m_state == TIME_WAIT) return;
// If all data are received (non-closing socket and nothing to send), just return
if (m_state <= ESTABLISHED && m_txBuffer.HeadSequence () >= m_highTxMark) return;
Retransmit ();
}
void
TcpSocketBase::DelAckTimeout (void)
{
m_delAckCount = 0;
SendEmptyPacket (TcpHeader::ACK);
}
void
TcpSocketBase::LastAckTimeout (void)
{
NS_LOG_FUNCTION (this);
m_lastAckEvent.Cancel ();
if (m_state == LAST_ACK)
{
CloseAndNotify ();
}
if (!m_closeNotified)
{
m_closeNotified = true;
}
}
// Send 1-byte data to probe for the window size at the receiver when
// the local knowledge tells that the receiver has zero window size
// C.f.: RFC793 p.42, RFC1112 sec.4.2.2.17
void
TcpSocketBase::PersistTimeout ()
{
NS_LOG_LOGIC ("PersistTimeout expired at " << Simulator::Now ().GetSeconds ());
m_persistTimeout = std::min (Seconds (60), Time (2 * m_persistTimeout)); // max persist timeout = 60s
Ptr<Packet> p = m_txBuffer.CopyFromSequence (1, m_nextTxSequence);
TcpHeader tcpHeader;
tcpHeader.SetSequenceNumber (m_nextTxSequence);
tcpHeader.SetAckNumber (m_rxBuffer.NextRxSequence ());
tcpHeader.SetSourcePort (m_endPoint->GetLocalPort ());
tcpHeader.SetDestinationPort (m_endPoint->GetPeerPort ());
tcpHeader.SetWindowSize (AdvertisedWindowSize ());
AddOptions (tcpHeader);
m_tcp->SendPacket (p, tcpHeader, m_endPoint->GetLocalAddress (),
m_endPoint->GetPeerAddress (), m_boundnetdevice);
NS_LOG_LOGIC ("Schedule persist timeout at time "
<< Simulator::Now ().GetSeconds () << " to expire at time "
<< (Simulator::Now () + m_persistTimeout).GetSeconds ());
m_persistEvent = Simulator::Schedule (m_persistTimeout, &TcpSocketBase::PersistTimeout, this);
}
void
TcpSocketBase::Retransmit ()
{
m_nextTxSequence = m_txBuffer.HeadSequence (); // Start from highest Ack
m_rtt->IncreaseMultiplier (); // Double the timeout value for next retx timer
m_dupAckCount = 0;
DoRetransmit (); // Retransmit the packet
}
void
TcpSocketBase::DoRetransmit ()
{
NS_LOG_FUNCTION (this);
// Retransmit SYN packet
if (m_state == SYN_SENT)
{
if (m_cnCount > 0)
{
SendEmptyPacket (TcpHeader::SYN);
}
else
{
NotifyConnectionFailed ();
}
return;
}
// Retransmit non-data packet: Only if in FIN_WAIT_1 or CLOSING state
if (m_txBuffer.Size () == 0)
{
if (m_state == FIN_WAIT_1 || m_state == CLOSING)
{ // Must have lost FIN, re-send
SendEmptyPacket (TcpHeader::FIN);
}
return;
}
// Retransmit a data packet: Call SendDataPacket
NS_LOG_LOGIC ("TcpSocketBase " << this << " retxing seq " << m_txBuffer.HeadSequence ());
SendDataPacket (m_txBuffer.HeadSequence (), m_segmentSize, true);
}
void
TcpSocketBase::CancelAllTimers ()
{
m_retxEvent.Cancel ();
m_persistEvent.Cancel ();
m_delAckEvent.Cancel ();
m_lastAckEvent.Cancel ();
m_timewaitEvent.Cancel ();
}
/** Move TCP to Time_Wait state and schedule a transition to Closed state */
void
TcpSocketBase::TimeWait ()
{
NS_LOG_INFO (TcpStateName[m_state] << " -> TIME_WAIT");
m_state = TIME_WAIT;
CancelAllTimers ();
// Move from TIME_WAIT to CLOSED after 2*MSL. Max segment lifetime is 2 min
// according to RFC793, p.28
m_timewaitEvent = Simulator::Schedule (Seconds (2*m_msl),
&TcpSocketBase::CloseAndNotify, this);
}
/** Below are the attribute get/set functions */
void
TcpSocketBase::SetSndBufSize (uint32_t size)
{
m_txBuffer.SetMaxBufferSize (size);
}
uint32_t
TcpSocketBase::GetSndBufSize (void) const
{
return m_txBuffer.MaxBufferSize ();
}
void
TcpSocketBase::SetRcvBufSize (uint32_t size)
{
m_rxBuffer.SetMaxBufferSize (size);
}
uint32_t
TcpSocketBase::GetRcvBufSize (void) const
{
return m_rxBuffer.MaxBufferSize ();
}
void
TcpSocketBase::SetSegSize (uint32_t size)
{
m_segmentSize = size;
NS_ABORT_MSG_UNLESS (m_state == CLOSED, "Cannot change segment size dynamically.");
}
uint32_t
TcpSocketBase::GetSegSize (void) const
{
return m_segmentSize;
}
void
TcpSocketBase::SetConnTimeout (Time timeout)
{
m_cnTimeout = timeout;
}
Time
TcpSocketBase::GetConnTimeout (void) const
{
return m_cnTimeout;
}
void
TcpSocketBase::SetConnCount (uint32_t count)
{
m_cnRetries = count;
}
uint32_t
TcpSocketBase::GetConnCount (void) const
{
return m_cnRetries;
}
void
TcpSocketBase::SetDelAckTimeout (Time timeout)
{
m_delAckTimeout = timeout;
}
Time
TcpSocketBase::GetDelAckTimeout (void) const
{
return m_delAckTimeout;
}
void
TcpSocketBase::SetDelAckMaxCount (uint32_t count)
{
m_delAckMaxCount = count;
}
uint32_t
TcpSocketBase::GetDelAckMaxCount (void) const
{
return m_delAckMaxCount;
}
void
TcpSocketBase::SetPersistTimeout (Time timeout)
{
m_persistTimeout = timeout;
}
Time
TcpSocketBase::GetPersistTimeout (void) const
{
return m_persistTimeout;
}
bool
TcpSocketBase::SetAllowBroadcast (bool allowBroadcast)
{
// Broadcast is not implemented. Return true only if allowBroadcast==false
return (!allowBroadcast);
}
bool
TcpSocketBase::GetAllowBroadcast (void) const
{
return false;
}
/** Placeholder function for future extension that reads more from the TCP header */
void
TcpSocketBase::ReadOptions (const TcpHeader&)
{
}
/** Placeholder function for future extension that changes the TCP header */
void
TcpSocketBase::AddOptions (TcpHeader&)
{
}
} // namespace ns3