Splitted congestion control from TcpSocketBase
Added TcpCongestionOps class, in where the congestion control is managed. It is created/added to a socket through TcpL4Protocol. TcpSocketBase and the congestion algorithm classes reworked to be compatible with the new TcpCongestionOps class.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
||||
/*
|
||||
* Copyright (c) 2010 Adrian Sai-wah Tam
|
||||
* Copyright (c) 2015 Natale Patriciello <natale.patriciello@gmail.com>
|
||||
*
|
||||
* 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
|
||||
@@ -15,93 +15,70 @@
|
||||
* 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 "tcp-newreno.h"
|
||||
#include "tcp-congestion-ops.h"
|
||||
#include "tcp-socket-base.h"
|
||||
#include "ns3/log.h"
|
||||
#include "ns3/trace-source-accessor.h"
|
||||
#include "ns3/simulator.h"
|
||||
#include "ns3/abort.h"
|
||||
#include "ns3/node.h"
|
||||
|
||||
namespace ns3 {
|
||||
|
||||
NS_LOG_COMPONENT_DEFINE ("TcpNewReno");
|
||||
|
||||
NS_OBJECT_ENSURE_REGISTERED (TcpCongestionOps);
|
||||
|
||||
TypeId
|
||||
TcpCongestionOps::GetTypeId (void)
|
||||
{
|
||||
static TypeId tid = TypeId ("ns3::TcpCongestionOps")
|
||||
.SetParent<Object> ()
|
||||
.SetGroupName ("Internet")
|
||||
;
|
||||
return tid;
|
||||
}
|
||||
|
||||
TcpCongestionOps::TcpCongestionOps () : Object ()
|
||||
{
|
||||
}
|
||||
|
||||
TcpCongestionOps::TcpCongestionOps (const TcpCongestionOps &other) : Object (other)
|
||||
{
|
||||
}
|
||||
|
||||
TcpCongestionOps::~TcpCongestionOps ()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// RENO
|
||||
|
||||
NS_OBJECT_ENSURE_REGISTERED (TcpNewReno);
|
||||
|
||||
TypeId
|
||||
TcpNewReno::GetTypeId (void)
|
||||
{
|
||||
static TypeId tid = TypeId ("ns3::TcpNewReno")
|
||||
.SetParent<TcpSocketBase> ()
|
||||
.SetParent<TcpCongestionOps> ()
|
||||
.SetGroupName ("Internet")
|
||||
.AddConstructor<TcpNewReno> ()
|
||||
;
|
||||
;
|
||||
return tid;
|
||||
}
|
||||
|
||||
TcpNewReno::TcpNewReno (void)
|
||||
: TcpSocketBase ()
|
||||
TcpNewReno::TcpNewReno (void) : TcpCongestionOps ()
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
}
|
||||
|
||||
TcpNewReno::TcpNewReno (const TcpNewReno& sock)
|
||||
: TcpSocketBase (sock)
|
||||
: TcpCongestionOps (sock)
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
NS_LOG_LOGIC ("Invoked the copy constructor");
|
||||
}
|
||||
|
||||
TcpNewReno::~TcpNewReno (void)
|
||||
{
|
||||
}
|
||||
|
||||
Ptr<TcpSocketBase>
|
||||
TcpNewReno::Fork (void)
|
||||
{
|
||||
return CopyObject<TcpNewReno> (this);
|
||||
}
|
||||
|
||||
/* New ACK (up to seqnum seq) received. Increase cwnd and call TcpSocketBase::NewAck() */
|
||||
void
|
||||
TcpNewReno::NewAck (const SequenceNumber32& seq)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << seq);
|
||||
|
||||
// No cWnd management while recovering
|
||||
if (m_ackState == RECOVERY && seq < m_recover)
|
||||
{
|
||||
TcpSocketBase::NewAck (seq);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t segmentsAcked = (seq - m_txBuffer->HeadSequence ()) / m_tcb->m_segmentSize;
|
||||
|
||||
NS_LOG_LOGIC ("TcpNewReno received ACK for seq " << seq <<
|
||||
" cwnd " << m_tcb->m_cWnd <<
|
||||
" ssthresh " << m_tcb->m_ssThresh <<
|
||||
" acked " << segmentsAcked);
|
||||
|
||||
if (m_tcb->m_cWnd <= m_tcb->m_ssThresh)
|
||||
{
|
||||
segmentsAcked = SlowStart (segmentsAcked);
|
||||
}
|
||||
|
||||
if (m_tcb->m_cWnd > m_tcb->m_ssThresh)
|
||||
{
|
||||
CongestionAvoidance (segmentsAcked);
|
||||
}
|
||||
|
||||
// Complete newAck processing
|
||||
TcpSocketBase::NewAck (seq);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Tcp NewReno slow start algorithm
|
||||
*
|
||||
@@ -140,21 +117,19 @@ u32 tcp_slow_start(struct tcp_sock *tp, u32 acked)
|
||||
* than a segment size, but we keep count of how many segments we have ignored,
|
||||
* and return them.
|
||||
*
|
||||
* \param tcb internal congestion state
|
||||
* \param segmentsAcked count of segments acked
|
||||
* \return the number of segments not considered for increasing the cWnd
|
||||
*/
|
||||
uint32_t
|
||||
TcpNewReno::SlowStart (uint32_t segmentsAcked)
|
||||
TcpNewReno::SlowStart (Ptr<TcpSocketState> tcb, uint32_t segmentsAcked)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << segmentsAcked);
|
||||
NS_LOG_FUNCTION (this << tcb << segmentsAcked);
|
||||
|
||||
if (segmentsAcked >= 1)
|
||||
{
|
||||
m_tcb->m_cWnd = m_tcb->m_cWnd.Get () + m_tcb->m_segmentSize;
|
||||
|
||||
NS_LOG_INFO ("In SlowStart, updated to cwnd " << m_tcb->m_cWnd <<
|
||||
" ssthresh " << m_tcb->m_ssThresh << " MSS " << m_tcb->m_segmentSize);
|
||||
|
||||
tcb->m_cWnd += tcb->m_segmentSize;
|
||||
NS_LOG_INFO ("In SlowStart, updated to cwnd " << tcb->m_cWnd << " ssthresh " << tcb->m_ssThresh);
|
||||
return segmentsAcked - 1;
|
||||
}
|
||||
|
||||
@@ -167,27 +142,78 @@ TcpNewReno::SlowStart (uint32_t segmentsAcked)
|
||||
* During congestion avoidance, cwnd is incremented by roughly 1 full-sized
|
||||
* segment per round-trip time (RTT).
|
||||
*
|
||||
* \param tcb internal congestion state
|
||||
* \param segmentsAcked count of segments acked
|
||||
*/
|
||||
void
|
||||
TcpNewReno::CongestionAvoidance (uint32_t segmentsAcked)
|
||||
TcpNewReno::CongestionAvoidance (Ptr<TcpSocketState> tcb, uint32_t segmentsAcked)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << segmentsAcked);
|
||||
NS_LOG_FUNCTION (this << tcb << segmentsAcked);
|
||||
|
||||
if (segmentsAcked > 0)
|
||||
{
|
||||
double adder = static_cast<double> (m_tcb->m_segmentSize * m_tcb->m_segmentSize) / m_tcb->m_cWnd.Get ();
|
||||
double adder = static_cast<double> (tcb->m_segmentSize * tcb->m_segmentSize) / tcb->m_cWnd.Get ();
|
||||
adder = std::max (1.0, adder);
|
||||
m_tcb->m_cWnd += static_cast<uint32_t> (adder);
|
||||
NS_LOG_INFO ("In CongAvoid, updated to cwnd " << m_tcb->m_cWnd <<
|
||||
" ssthresh " << m_tcb->m_ssThresh);
|
||||
tcb->m_cWnd += static_cast<uint32_t> (adder);
|
||||
NS_LOG_INFO ("In CongAvoid, updated to cwnd " << tcb->m_cWnd <<
|
||||
" ssthresh " << tcb->m_ssThresh);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t
|
||||
TcpNewReno::GetSsThresh ()
|
||||
/**
|
||||
* \brief Try to increase the cWnd following the NewReno specification
|
||||
*
|
||||
* \see SlowStart
|
||||
* \see CongestionAvoidance
|
||||
*
|
||||
* \param tcb internal congestion state
|
||||
* \param segmentsAcked count of segments acked
|
||||
*/
|
||||
void
|
||||
TcpNewReno::IncreaseWindow (Ptr<TcpSocketState> tcb, uint32_t segmentsAcked)
|
||||
{
|
||||
return std::max (2 * m_tcb->m_segmentSize, BytesInFlight () / 2);
|
||||
NS_LOG_FUNCTION (this << tcb << segmentsAcked);
|
||||
|
||||
if (tcb->m_cWnd < tcb->m_ssThresh)
|
||||
{
|
||||
segmentsAcked = SlowStart (tcb, segmentsAcked);
|
||||
}
|
||||
|
||||
if (tcb->m_cWnd >= tcb->m_ssThresh)
|
||||
{
|
||||
CongestionAvoidance (tcb, segmentsAcked);
|
||||
}
|
||||
|
||||
/* At this point, we could have segmentsAcked != 0. This because RFC says
|
||||
* that in slow start, we should increase cWnd by min (N, SMSS); if in
|
||||
* slow start we receive a cumulative ACK, it counts only for 1 SMSS of
|
||||
* increase, wasting the others.
|
||||
*
|
||||
* // Uncorrect assert, I am sorry
|
||||
* NS_ASSERT (segmentsAcked == 0);
|
||||
*/
|
||||
}
|
||||
|
||||
std::string
|
||||
TcpNewReno::GetName () const
|
||||
{
|
||||
return "TcpNewReno";
|
||||
}
|
||||
|
||||
uint32_t
|
||||
TcpNewReno::GetSsThresh (Ptr<const TcpSocketState> state,
|
||||
uint32_t bytesInFlight)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << state << bytesInFlight);
|
||||
|
||||
return std::max (2 * state->m_segmentSize, bytesInFlight / 2);
|
||||
}
|
||||
|
||||
Ptr<TcpCongestionOps>
|
||||
TcpNewReno::Fork ()
|
||||
{
|
||||
return CopyObject<TcpNewReno> (this);
|
||||
}
|
||||
|
||||
} // namespace ns3
|
||||
|
||||
168
src/internet/model/tcp-congestion-ops.h
Normal file
168
src/internet/model/tcp-congestion-ops.h
Normal file
@@ -0,0 +1,168 @@
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
||||
/*
|
||||
* Copyright (c) 2015 Natale Patriciello <natale.patriciello@gmail.com>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#ifndef TCPCONGESTIONOPS_H
|
||||
#define TCPCONGESTIONOPS_H
|
||||
|
||||
#include "ns3/object.h"
|
||||
#include "ns3/timer.h"
|
||||
|
||||
namespace ns3 {
|
||||
|
||||
class TcpSocketState;
|
||||
class TcpSocketBase;
|
||||
|
||||
/**
|
||||
* \brief Congestion control abstract class
|
||||
*
|
||||
* The design is inspired on what Linux v4.0 does (but it has been
|
||||
* in place since years). The congestion control is splitted from the main
|
||||
* socket code, and it is a pluggable component. An interface has been defined;
|
||||
* variables are maintained in the TcpSocketState class, while subclasses of
|
||||
* TcpCongestionOps operate over an instance of that class.
|
||||
*
|
||||
* Only three methods has been utilized right now; however, Linux has many others,
|
||||
* which can be added later in ns-3.
|
||||
*
|
||||
* \see IncreaseWindow
|
||||
* \see PktsAcked
|
||||
*/
|
||||
class TcpCongestionOps : public Object
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \brief Get the type ID.
|
||||
* \return the object TypeId
|
||||
*/
|
||||
static TypeId GetTypeId (void);
|
||||
|
||||
TcpCongestionOps ();
|
||||
TcpCongestionOps (const TcpCongestionOps &other);
|
||||
|
||||
virtual ~TcpCongestionOps ();
|
||||
|
||||
/**
|
||||
* \brief Get the name of the congestion control algorithm
|
||||
*
|
||||
* \return A string identifying the name
|
||||
*/
|
||||
virtual std::string GetName () const = 0;
|
||||
|
||||
/**
|
||||
* \brief Get the slow start threshold after a loss event
|
||||
*
|
||||
* Is guaranteed that the congestion control state (TcpAckState_t) is
|
||||
* changed BEFORE the invocation of this method.
|
||||
* The implementator should return the slow start threshold (and not change
|
||||
* it directly) because, in the future, the TCP implementation may require to
|
||||
* instantly recover from a loss event (e.g. when there is a network with an high
|
||||
* reordering factor).
|
||||
*
|
||||
* \param tcb internal congestion state
|
||||
* \param bytesInFlight total bytes in flight
|
||||
* \return Slow start threshold
|
||||
*/
|
||||
virtual uint32_t GetSsThresh (Ptr<const TcpSocketState> tcb,
|
||||
uint32_t bytesInFlight) = 0;
|
||||
|
||||
/**
|
||||
* \brief Congestion avoidance algorithm implementation
|
||||
*
|
||||
* Mimic the function cong_avoid in Linux. New segments have been ACKed,
|
||||
* and the congestion control duty is to set
|
||||
*
|
||||
* The function is allowed to change directly cWnd and/or ssThresh.
|
||||
*
|
||||
* \param tcb internal congestion state
|
||||
* \param segmentsAcked count of segments acked
|
||||
*/
|
||||
virtual void IncreaseWindow (Ptr<TcpSocketState> tcb, uint32_t segmentsAcked) = 0;
|
||||
|
||||
/**
|
||||
* \brief Timing information on received ACK
|
||||
*
|
||||
* The function is called every time an ACK is received (only one time
|
||||
* also for cumulative ACKs) and contains timing information. It is
|
||||
* optional (congestion controls can not implement it) and the default
|
||||
* implementation does nothing.
|
||||
*
|
||||
* \param tcb internal congestion state
|
||||
* \param segmentsAcked count of segments acked
|
||||
* \param rtt last rtt
|
||||
*/
|
||||
virtual void PktsAcked (Ptr<TcpSocketState> tcb, uint32_t segmentsAcked,
|
||||
const Time& rtt) { }
|
||||
|
||||
// Present in Linux but not in ns-3 yet:
|
||||
/* call before changing ca_state (optional) */
|
||||
// void (*set_state)(struct sock *sk, u8 new_state);
|
||||
/* call when cwnd event occurs (optional) */
|
||||
// void (*cwnd_event)(struct sock *sk, enum tcp_ca_event ev);
|
||||
/* call when ack arrives (optional) */
|
||||
// void (*in_ack_event)(struct sock *sk, u32 flags);
|
||||
/* new value of cwnd after loss (optional) */
|
||||
// u32 (*undo_cwnd)(struct sock *sk);
|
||||
/* hook for packet ack accounting (optional) */
|
||||
// void (*pkts_acked)(struct sock *sk, u32 num_acked, s32 rtt_us);
|
||||
|
||||
/**
|
||||
* \brief Copy the congestion control algorithm across socket
|
||||
*
|
||||
* \return a pointer of the copied object
|
||||
*/
|
||||
virtual Ptr<TcpCongestionOps> Fork () = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief The NewReno implementation
|
||||
*
|
||||
* New Reno introduces partial ACKs inside the well-established Reno algorithm.
|
||||
* This and other modifications are described in RFC 6582.
|
||||
*
|
||||
* \see IncreaseWindow
|
||||
*/
|
||||
class TcpNewReno : public TcpCongestionOps
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \brief Get the type ID.
|
||||
* \return the object TypeId
|
||||
*/
|
||||
static TypeId GetTypeId (void);
|
||||
|
||||
TcpNewReno ();
|
||||
TcpNewReno (const TcpNewReno& sock);
|
||||
|
||||
~TcpNewReno ();
|
||||
|
||||
std::string GetName () const;
|
||||
|
||||
virtual void IncreaseWindow (Ptr<TcpSocketState> tcb, uint32_t segmentsAcked);
|
||||
virtual uint32_t GetSsThresh (Ptr<const TcpSocketState> tcb,
|
||||
uint32_t bytesInFlight);
|
||||
|
||||
virtual Ptr<TcpCongestionOps> Fork ();
|
||||
|
||||
protected:
|
||||
virtual uint32_t SlowStart (Ptr<TcpSocketState> tcb, uint32_t segmentsAcked);
|
||||
virtual void CongestionAvoidance (Ptr<TcpSocketState> tcb, uint32_t segmentsAcked);
|
||||
};
|
||||
|
||||
} // namespace ns3
|
||||
|
||||
#endif // TCPCONGESTIONOPS_H
|
||||
@@ -40,7 +40,7 @@
|
||||
#include "ipv6-l3-protocol.h"
|
||||
#include "ipv6-routing-protocol.h"
|
||||
#include "tcp-socket-factory-impl.h"
|
||||
#include "tcp-newreno.h"
|
||||
#include "tcp-socket-base.h"
|
||||
#include "rtt-estimator.h"
|
||||
|
||||
#include <vector>
|
||||
@@ -77,7 +77,7 @@ TcpL4Protocol::GetTypeId (void)
|
||||
.AddAttribute ("SocketType",
|
||||
"Socket type of TCP objects.",
|
||||
TypeIdValue (TcpNewReno::GetTypeId ()),
|
||||
MakeTypeIdAccessor (&TcpL4Protocol::m_socketTypeId),
|
||||
MakeTypeIdAccessor (&TcpL4Protocol::m_congestionTypeId),
|
||||
MakeTypeIdChecker ())
|
||||
.AddAttribute ("SocketList", "The list of sockets associated to this protocol.",
|
||||
ObjectVectorValue (),
|
||||
@@ -174,18 +174,23 @@ TcpL4Protocol::DoDispose (void)
|
||||
}
|
||||
|
||||
Ptr<Socket>
|
||||
TcpL4Protocol::CreateSocket (TypeId socketTypeId)
|
||||
TcpL4Protocol::CreateSocket (TypeId congestionTypeId)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << socketTypeId);
|
||||
NS_LOG_FUNCTION (this << congestionTypeId.GetName ());
|
||||
ObjectFactory rttFactory;
|
||||
ObjectFactory socketFactory;
|
||||
ObjectFactory congestionAlgorithmFactory;
|
||||
rttFactory.SetTypeId (m_rttTypeId);
|
||||
socketFactory.SetTypeId (socketTypeId);
|
||||
congestionAlgorithmFactory.SetTypeId (congestionTypeId);
|
||||
|
||||
Ptr<RttEstimator> rtt = rttFactory.Create<RttEstimator> ();
|
||||
Ptr<TcpSocketBase> socket = socketFactory.Create<TcpSocketBase> ();
|
||||
Ptr<TcpSocketBase> socket = CreateObject<TcpSocketBase> ();
|
||||
Ptr<TcpCongestionOps> algo = congestionAlgorithmFactory.Create<TcpCongestionOps> ();
|
||||
|
||||
socket->SetNode (m_node);
|
||||
socket->SetTcp (this);
|
||||
socket->SetRtt (rtt);
|
||||
socket->SetCongestionControlAlgorithm (algo);
|
||||
|
||||
m_sockets.push_back (socket);
|
||||
return socket;
|
||||
}
|
||||
@@ -193,7 +198,7 @@ TcpL4Protocol::CreateSocket (TypeId socketTypeId)
|
||||
Ptr<Socket>
|
||||
TcpL4Protocol::CreateSocket (void)
|
||||
{
|
||||
return CreateSocket (m_socketTypeId);
|
||||
return CreateSocket (m_congestionTypeId);
|
||||
}
|
||||
|
||||
Ipv4EndPoint *
|
||||
|
||||
@@ -98,7 +98,7 @@ public:
|
||||
*
|
||||
* \param socketTypeId the socket TypeId
|
||||
*/
|
||||
Ptr<Socket> CreateSocket (TypeId socketTypeId);
|
||||
Ptr<Socket> CreateSocket (TypeId congestionTypeId);
|
||||
|
||||
/**
|
||||
* \brief Allocate an IPv4 Endpoint
|
||||
@@ -282,7 +282,7 @@ private:
|
||||
Ipv4EndPointDemux *m_endPoints; //!< A list of IPv4 end points.
|
||||
Ipv6EndPointDemux *m_endPoints6; //!< A list of IPv6 end points.
|
||||
TypeId m_rttTypeId; //!< The RTT Estimator TypeId
|
||||
TypeId m_socketTypeId; //!< The socket TypeId
|
||||
TypeId m_congestionTypeId; //!< The socket TypeId
|
||||
std::vector<Ptr<TcpSocketBase> > m_sockets; //!< list of sockets
|
||||
IpL4Protocol::DownTargetCallback m_downTarget; //!< Callback to send packets over IPv4
|
||||
IpL4Protocol::DownTargetCallback6 m_downTarget6; //!< Callback to send packets over IPv6
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
||||
/*
|
||||
* 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>
|
||||
*/
|
||||
|
||||
#ifndef TCP_NEWRENO_H
|
||||
#define TCP_NEWRENO_H
|
||||
|
||||
#include "tcp-socket-base.h"
|
||||
|
||||
namespace ns3 {
|
||||
|
||||
/**
|
||||
* \ingroup socket
|
||||
* \ingroup tcp
|
||||
*
|
||||
* \brief An implementation of a stream socket using TCP.
|
||||
*
|
||||
* This class contains the NewReno implementation of TCP, as of \RFC{2582}.
|
||||
*/
|
||||
class TcpNewReno : public TcpSocketBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \brief Get the type ID.
|
||||
* \return the object TypeId
|
||||
*/
|
||||
static TypeId GetTypeId (void);
|
||||
/**
|
||||
* Create an unbound tcp socket.
|
||||
*/
|
||||
TcpNewReno (void);
|
||||
/**
|
||||
* \brief Copy constructor
|
||||
* \param sock the object to copy
|
||||
*/
|
||||
TcpNewReno (const TcpNewReno& sock);
|
||||
virtual ~TcpNewReno (void);
|
||||
|
||||
protected:
|
||||
virtual Ptr<TcpSocketBase> Fork (void); // Call CopyObject<TcpNewReno> to clone me
|
||||
virtual void NewAck (SequenceNumber32 const& seq); // Inc cwnd and call NewAck() of parent
|
||||
virtual uint32_t GetSsThresh ();
|
||||
|
||||
private:
|
||||
uint32_t SlowStart (uint32_t segmentsAcked);
|
||||
void CongestionAvoidance (uint32_t segmentsAcked);
|
||||
};
|
||||
|
||||
} // namespace ns3
|
||||
|
||||
#endif /* TCP_NEWRENO_H */
|
||||
@@ -66,6 +66,7 @@ TcpSocketBase::GetTypeId (void)
|
||||
static TypeId tid = TypeId ("ns3::TcpSocketBase")
|
||||
.SetParent<TcpSocket> ()
|
||||
.SetGroupName ("Internet")
|
||||
.AddConstructor<TcpSocketBase> ()
|
||||
// .AddAttribute ("TcpState", "State in TCP state machine",
|
||||
// TypeId::ATTR_GET,
|
||||
// EnumValue (CLOSED),
|
||||
@@ -98,7 +99,8 @@ TcpSocketBase::GetTypeId (void)
|
||||
MakeBooleanChecker ())
|
||||
.AddAttribute ("MinRto",
|
||||
"Minimum retransmit timeout value",
|
||||
TimeValue (Seconds (1.0)), // RFC 6298 says min RTO=1 sec, but Linux uses 200ms. See http://www.postel.org/pipermail/end2end-interest/2004-November/004402.html
|
||||
TimeValue (Seconds (1.0)), // RFC 6298 says min RTO=1 sec, but Linux uses 200ms.
|
||||
// See http://www.postel.org/pipermail/end2end-interest/2004-November/004402.html
|
||||
MakeTimeAccessor (&TcpSocketBase::SetMinRto,
|
||||
&TcpSocketBase::GetMinRto),
|
||||
MakeTimeChecker ())
|
||||
@@ -148,7 +150,7 @@ TcpSocketBase::GetTypeId (void)
|
||||
"ns3::TcpStatesTracedValueCallback")
|
||||
.AddTraceSource ("AckState",
|
||||
"TCP ACK machine state",
|
||||
MakeTraceSourceAccessor (&TcpSocketBase::m_ackState),
|
||||
MakeTraceSourceAccessor (&TcpSocketBase::m_ackStateTrace),
|
||||
"ns3::TcpAckStatesTracedValueCallback")
|
||||
.AddTraceSource ("RWND",
|
||||
"Remote side's flow control window",
|
||||
@@ -190,24 +192,42 @@ TcpSocketState::GetTypeId (void)
|
||||
"TCP slow start threshold (bytes)",
|
||||
MakeTraceSourceAccessor (&TcpSocketState::m_ssThresh),
|
||||
"ns3::TracedValue::Uint32Callback")
|
||||
.AddTraceSource ("AckState",
|
||||
"TCP ACK machine state",
|
||||
MakeTraceSourceAccessor (&TcpSocketState::m_ackState),
|
||||
"ns3::TracedValue::TcpAckStatesTracedValueCallback")
|
||||
;
|
||||
return tid;
|
||||
}
|
||||
|
||||
TcpSocketState::TcpSocketState (void)
|
||||
: Object ()
|
||||
: Object (),
|
||||
m_cWnd (0),
|
||||
m_ssThresh (0),
|
||||
m_initialCWnd (0),
|
||||
m_initialSsThresh (0),
|
||||
m_segmentSize (0),
|
||||
m_ackState (OPEN)
|
||||
{
|
||||
}
|
||||
|
||||
TcpSocketState::TcpSocketState (const TcpSocketState &other)
|
||||
: m_cWnd (other.m_cWnd),
|
||||
: Object (other),
|
||||
m_cWnd (other.m_cWnd),
|
||||
m_ssThresh (other.m_ssThresh),
|
||||
m_initialCWnd (other.m_initialCWnd),
|
||||
m_initialSsThresh (other.m_initialSsThresh),
|
||||
m_segmentSize (other.m_segmentSize)
|
||||
m_segmentSize (other.m_segmentSize),
|
||||
m_ackState (other.m_ackState)
|
||||
{
|
||||
}
|
||||
|
||||
const char* const
|
||||
TcpSocketState::TcpAckStateName[TcpSocketState::LAST_ACKSTATE] =
|
||||
{
|
||||
"OPEN", "DISORDER", "CWR", "RECOVERY", "LOSS"
|
||||
};
|
||||
|
||||
TcpSocketBase::TcpSocketBase (void)
|
||||
: m_dupAckCount (0),
|
||||
m_delAckCount (0),
|
||||
@@ -236,10 +256,10 @@ TcpSocketBase::TcpSocketBase (void)
|
||||
m_rcvScaleFactor (0),
|
||||
m_timestampEnabled (true),
|
||||
m_timestampToEcho (0),
|
||||
m_ackState (OPEN),
|
||||
m_retxThresh (3),
|
||||
m_limitedTx (false)
|
||||
|
||||
m_limitedTx (false),
|
||||
m_congestionControl (0),
|
||||
m_isFirstPartialAck (true)
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
m_rxBuffer = CreateObject<TcpRxBuffer> ();
|
||||
@@ -255,6 +275,10 @@ TcpSocketBase::TcpSocketBase (void)
|
||||
ok = m_tcb->TraceConnectWithoutContext ("SlowStartThreshold",
|
||||
MakeCallback (&TcpSocketBase::UpdateSsThresh, this));
|
||||
NS_ASSERT (ok == true);
|
||||
|
||||
ok = m_tcb->TraceConnectWithoutContext ("AckState",
|
||||
MakeCallback (&TcpSocketBase::UpdateAckState, this));
|
||||
NS_ASSERT (ok == true);
|
||||
}
|
||||
|
||||
TcpSocketBase::TcpSocketBase (const TcpSocketBase& sock)
|
||||
@@ -292,10 +316,10 @@ TcpSocketBase::TcpSocketBase (const TcpSocketBase& sock)
|
||||
m_rcvScaleFactor (sock.m_rcvScaleFactor),
|
||||
m_timestampEnabled (sock.m_timestampEnabled),
|
||||
m_timestampToEcho (sock.m_timestampToEcho),
|
||||
m_ackState (sock.m_ackState),
|
||||
m_retxThresh (sock.m_retxThresh),
|
||||
m_limitedTx (sock.m_limitedTx),
|
||||
m_tcb (sock.m_tcb)
|
||||
m_tcb (sock.m_tcb),
|
||||
m_isFirstPartialAck (sock.m_isFirstPartialAck)
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
NS_LOG_LOGIC ("Invoked the copy constructor");
|
||||
@@ -314,6 +338,11 @@ TcpSocketBase::TcpSocketBase (const TcpSocketBase& sock)
|
||||
SetRecvCallback (vPS);
|
||||
m_txBuffer = CopyObject (sock.m_txBuffer);
|
||||
m_rxBuffer = CopyObject (sock.m_rxBuffer);
|
||||
m_tcb = CopyObject (sock.m_tcb);
|
||||
if (sock.m_congestionControl)
|
||||
{
|
||||
m_congestionControl = sock.m_congestionControl->Fork ();
|
||||
}
|
||||
|
||||
bool ok;
|
||||
|
||||
@@ -324,6 +353,10 @@ TcpSocketBase::TcpSocketBase (const TcpSocketBase& sock)
|
||||
ok = m_tcb->TraceConnectWithoutContext ("SlowStartThreshold",
|
||||
MakeCallback (&TcpSocketBase::UpdateSsThresh, this));
|
||||
NS_ASSERT (ok == true);
|
||||
|
||||
ok = m_tcb->TraceConnectWithoutContext ("AckState",
|
||||
MakeCallback (&TcpSocketBase::UpdateAckState, this));
|
||||
NS_ASSERT (ok == true);
|
||||
}
|
||||
|
||||
TcpSocketBase::~TcpSocketBase (void)
|
||||
@@ -650,11 +683,12 @@ TcpSocketBase::Close (void)
|
||||
/// \bugid{426} claims we should send reset in this case.
|
||||
if (m_rxBuffer->Size () != 0)
|
||||
{
|
||||
NS_LOG_INFO ("Socket " << this << " << unread rx data during close. Sending reset");
|
||||
NS_LOG_WARN ("Socket " << this << " << unread rx data during close. Sending reset." <<
|
||||
"This is probably due to a bad sink application; check its code");
|
||||
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)
|
||||
@@ -672,7 +706,7 @@ int
|
||||
TcpSocketBase::ShutdownSend (void)
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
|
||||
|
||||
//this prevents data from being added to the buffer
|
||||
m_shutdownSend = true;
|
||||
m_closeOnEmpty = true;
|
||||
@@ -683,7 +717,7 @@ TcpSocketBase::ShutdownSend (void)
|
||||
if (m_state == ESTABLISHED || m_state == CLOSE_WAIT)
|
||||
{
|
||||
NS_LOG_INFO("Emtpy tx buffer, send fin");
|
||||
SendEmptyPacket (TcpHeader::FIN);
|
||||
SendEmptyPacket (TcpHeader::FIN);
|
||||
|
||||
if (m_state == ESTABLISHED)
|
||||
{ // On active close: I am the first one to send FIN
|
||||
@@ -694,10 +728,10 @@ TcpSocketBase::ShutdownSend (void)
|
||||
{ // On passive close: Peer sent me FIN already
|
||||
NS_LOG_DEBUG ("CLOSE_WAIT -> LAST_ACK");
|
||||
m_state = LAST_ACK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -736,7 +770,9 @@ TcpSocketBase::Send (Ptr<Packet> p, uint32_t flags)
|
||||
{ // Try to send the data out
|
||||
if (!m_sendPendingDataEvent.IsRunning ())
|
||||
{
|
||||
m_sendPendingDataEvent = Simulator::Schedule (TimeStep (1), &TcpSocketBase::SendPendingData, this, m_connected);
|
||||
m_sendPendingDataEvent = Simulator::Schedule (TimeStep (1),
|
||||
&TcpSocketBase::SendPendingData,
|
||||
this, m_connected);
|
||||
}
|
||||
}
|
||||
return p->GetSize ();
|
||||
@@ -982,7 +1018,7 @@ TcpSocketBase::CloseAndNotify (void)
|
||||
|
||||
NS_LOG_DEBUG (TcpStateName[m_state] << " -> CLOSED");
|
||||
m_state = CLOSED;
|
||||
DeallocateEndPoint ();
|
||||
DeallocateEndPoint ();
|
||||
}
|
||||
|
||||
|
||||
@@ -1251,6 +1287,21 @@ TcpSocketBase::ReceivedAck (Ptr<Packet> packet, const TcpHeader& tcpHeader)
|
||||
NS_LOG_FUNCTION (this << tcpHeader);
|
||||
|
||||
NS_ASSERT (0 != (tcpHeader.GetFlags () & TcpHeader::ACK));
|
||||
NS_ASSERT (m_tcb->m_segmentSize > 0);
|
||||
|
||||
uint32_t bytesAcked = tcpHeader.GetAckNumber () - m_txBuffer->HeadSequence ();
|
||||
uint32_t segsAcked = bytesAcked / m_tcb->m_segmentSize;
|
||||
m_bytesAckedNotProcessed += bytesAcked % m_tcb->m_segmentSize;
|
||||
|
||||
if (m_bytesAckedNotProcessed >= m_tcb->m_segmentSize)
|
||||
{
|
||||
segsAcked += 1;
|
||||
m_bytesAckedNotProcessed -= m_tcb->m_segmentSize;
|
||||
}
|
||||
|
||||
NS_LOG_LOGIC (" Bytes acked: " << bytesAcked <<
|
||||
" Segments acked: " << segsAcked <<
|
||||
" bytes left: " << m_bytesAckedNotProcessed);
|
||||
|
||||
NS_LOG_DEBUG ("ACK of " << tcpHeader.GetAckNumber () <<
|
||||
" SND.UNA=" << m_txBuffer->HeadSequence () <<
|
||||
@@ -1263,14 +1314,16 @@ TcpSocketBase::ReceivedAck (Ptr<Packet> packet, const TcpHeader& tcpHeader)
|
||||
// There is a DupAck
|
||||
++m_dupAckCount;
|
||||
|
||||
if (m_ackState == OPEN)
|
||||
if (m_tcb->m_ackState == TcpSocketState::OPEN)
|
||||
{
|
||||
// From Open we go Disorder
|
||||
NS_ASSERT (m_dupAckCount == 1);
|
||||
m_ackState = DISORDER;
|
||||
NS_ASSERT_MSG (m_dupAckCount == 1, "From OPEN->DISORDER but with " <<
|
||||
m_dupAckCount << " dup ACKs");
|
||||
m_tcb->m_ackState = TcpSocketState::DISORDER;
|
||||
|
||||
NS_LOG_DEBUG ("OPEN -> DISORDER");
|
||||
}
|
||||
else if (m_ackState == DISORDER)
|
||||
else if (m_tcb->m_ackState == TcpSocketState::DISORDER)
|
||||
{
|
||||
if (m_dupAckCount < m_retxThresh && m_limitedTx)
|
||||
{
|
||||
@@ -1282,18 +1335,19 @@ TcpSocketBase::ReceivedAck (Ptr<Packet> packet, const TcpHeader& tcpHeader)
|
||||
else if (m_dupAckCount == m_retxThresh)
|
||||
{
|
||||
// triple duplicate ack triggers fast retransmit (RFC2582 sec.3 bullet #1)
|
||||
NS_LOG_DEBUG (TcpSocketState::TcpAckStateName[m_tcb->m_ackState] <<
|
||||
" -> RECOVERY");
|
||||
m_recover = m_highTxMark;
|
||||
m_ackState = RECOVERY;
|
||||
m_tcb->m_ackState = TcpSocketState::RECOVERY;
|
||||
|
||||
m_tcb->m_ssThresh = GetSsThresh ();
|
||||
m_tcb->m_ssThresh = m_congestionControl->GetSsThresh (m_tcb,
|
||||
BytesInFlight ());
|
||||
m_tcb->m_cWnd = m_tcb->m_ssThresh + m_dupAckCount * m_tcb->m_segmentSize;
|
||||
|
||||
NS_LOG_INFO (m_dupAckCount << " dupack. Enter fast recovery mode." <<
|
||||
"Reset cwnd to " << m_tcb->m_cWnd << ", ssthresh to " <<
|
||||
m_tcb->m_ssThresh << " at fast recovery seqnum " << m_recover);
|
||||
DoRetransmit ();
|
||||
|
||||
NS_LOG_DEBUG (TcpAckStateName[m_ackState] << " -> RECOVERY");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1301,15 +1355,16 @@ TcpSocketBase::ReceivedAck (Ptr<Packet> packet, const TcpHeader& tcpHeader)
|
||||
"in DISORDER state");
|
||||
}
|
||||
}
|
||||
else if (m_ackState == RECOVERY)
|
||||
else if (m_tcb->m_ackState == TcpSocketState::RECOVERY)
|
||||
{ // Increase cwnd for every additional dupack (RFC2582, sec.3 bullet #3)
|
||||
m_tcb->m_cWnd += m_tcb->m_segmentSize;
|
||||
NS_LOG_INFO ("Dupack in fast recovery mode. Increase cwnd to " << m_tcb->m_cWnd);
|
||||
if (!m_sendPendingDataEvent.IsRunning ())
|
||||
{
|
||||
m_sendPendingDataEvent = Simulator::Schedule (TimeStep (1), &TcpSocketBase::SendPendingData, this, m_connected);
|
||||
}
|
||||
m_tcb->m_cWnd += m_tcb->m_segmentSize;
|
||||
NS_LOG_INFO (m_dupAckCount << " Dupack received in fast recovery mode."
|
||||
"Increase cwnd to " << m_tcb->m_cWnd);
|
||||
SendPendingData (m_connected);
|
||||
}
|
||||
|
||||
// Artificially call PktsAcked. After all, one segment has been ACKed.
|
||||
m_congestionControl->PktsAcked (m_tcb, 1, m_lastRtt);
|
||||
}
|
||||
else if (tcpHeader.GetAckNumber () == m_txBuffer->HeadSequence () &&
|
||||
tcpHeader.GetAckNumber () == m_nextTxSequence)
|
||||
@@ -1318,42 +1373,138 @@ TcpSocketBase::ReceivedAck (Ptr<Packet> packet, const TcpHeader& tcpHeader)
|
||||
}
|
||||
else if (tcpHeader.GetAckNumber () > m_txBuffer->HeadSequence ())
|
||||
{ // Case 3: New ACK, reset m_dupAckCount and update m_txBuffer
|
||||
if (m_ackState == DISORDER)
|
||||
bool callCongestionControl = true;
|
||||
|
||||
/* The following switch is made because m_dupAckCount can be
|
||||
* "inflated" through out-of-order segments (e.g. from retransmission,
|
||||
* while segments have not been lost but are network-reordered). At
|
||||
* least one segment has been acked; in the luckiest case, an amount
|
||||
* equals to segsAcked-m_dupAckCount has not been processed.
|
||||
*
|
||||
* To be clear: segsAcked will be passed to PktsAcked, and it should take
|
||||
* in considerations the times that it has been already called, while newSegsAcked
|
||||
* will be passed to IncreaseCwnd, and it represents the amount of
|
||||
* segments that are allowed to increase the cWnd value.
|
||||
*/
|
||||
uint32_t newSegsAcked = segsAcked;
|
||||
if (segsAcked > m_dupAckCount)
|
||||
{
|
||||
segsAcked -= m_dupAckCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
segsAcked = 1;
|
||||
}
|
||||
|
||||
if (m_tcb->m_ackState == TcpSocketState::DISORDER)
|
||||
{
|
||||
// The network reorder packets. Linux changes the counting lost
|
||||
// packet algorithm from FACK to NewReno. We simply go back in Open.
|
||||
m_ackState = OPEN;
|
||||
m_tcb->m_ackState = TcpSocketState::OPEN;
|
||||
m_congestionControl->PktsAcked (m_tcb, segsAcked, m_lastRtt);
|
||||
m_dupAckCount = 0;
|
||||
|
||||
NS_LOG_DEBUG ("DISORDER -> OPEN");
|
||||
}
|
||||
else if (m_ackState == RECOVERY)
|
||||
else if (m_tcb->m_ackState == TcpSocketState::RECOVERY)
|
||||
{
|
||||
if (tcpHeader.GetAckNumber () < m_recover)
|
||||
{ // Partial ACK, partial window deflation (RFC2582 sec.3 bullet #5 paragraph 3)
|
||||
m_tcb->m_cWnd += m_tcb->m_segmentSize -
|
||||
(tcpHeader.GetAckNumber () - m_txBuffer->HeadSequence ());
|
||||
NS_LOG_INFO ("Partial ACK for seq " << tcpHeader.GetAckNumber () <<
|
||||
" in fast recovery: cwnd set to " << m_tcb->m_cWnd);
|
||||
m_txBuffer->DiscardUpTo(tcpHeader.GetAckNumber ()); //Bug 1850: retransmit before newack
|
||||
{
|
||||
/* Partial ACK.
|
||||
* In case of partial ACK, retransmit the first unacknowledged
|
||||
* segment. Deflate the congestion window by the amount of new
|
||||
* data acknowledged by the Cumulative Acknowledgment field.
|
||||
* If the partial ACK acknowledges at least one SMSS of new data,
|
||||
* then add back SMSS bytes to the congestion window.
|
||||
* This artificially inflates the congestion window in order to
|
||||
* reflect the additional segment that has left the network.
|
||||
* Send a new segment if permitted by the new value of cwnd.
|
||||
* This "partial window deflation" attempts to ensure that, when
|
||||
* fast recovery eventually ends, approximately ssthresh amount
|
||||
* of data will be outstanding in the network. Do not exit the
|
||||
* fast recovery procedure (i.e., if any duplicate ACKs subsequently
|
||||
* arrive, execute step 4 of Section 3.2 of [RFC5681]).
|
||||
*/
|
||||
if (segsAcked >= 1)
|
||||
{
|
||||
m_tcb->m_cWnd += m_tcb->m_segmentSize - bytesAcked;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_tcb->m_cWnd -= bytesAcked;
|
||||
}
|
||||
|
||||
callCongestionControl = false; // No congestion control on cWnd show be invoked
|
||||
m_dupAckCount -= segsAcked; // Update the dupAckCount
|
||||
m_txBuffer->DiscardUpTo (tcpHeader.GetAckNumber ()); //Bug 1850: retransmit before newack
|
||||
DoRetransmit (); // Assume the next seq is lost. Retransmit lost packet
|
||||
|
||||
if (m_isFirstPartialAck)
|
||||
{
|
||||
m_isFirstPartialAck = false;
|
||||
}
|
||||
|
||||
/* This partial ACK acknowledge the fact that one segment has been
|
||||
* previously lost and now successfully received. All others have
|
||||
* been processed when they come under the form of dupACKs
|
||||
*/
|
||||
m_congestionControl->PktsAcked (m_tcb, 1, m_lastRtt);
|
||||
|
||||
NS_LOG_INFO ("Partial ACK for seq " << tcpHeader.GetAckNumber () <<
|
||||
" in fast recovery: cwnd set to " << m_tcb->m_cWnd <<
|
||||
" recover seq: " << m_recover <<
|
||||
" dupAck count: " << m_dupAckCount);
|
||||
}
|
||||
else if (tcpHeader.GetAckNumber () >= m_recover)
|
||||
{// Full ACK (RFC2582 sec.3 bullet #5 paragraph 2, option 1)
|
||||
m_tcb->m_cWnd = std::min ( m_tcb->m_ssThresh.Get (), BytesInFlight () + m_tcb->m_segmentSize);
|
||||
m_ackState = OPEN;
|
||||
m_tcb->m_cWnd = std::min (m_tcb->m_ssThresh.Get (),
|
||||
BytesInFlight () + m_tcb->m_segmentSize);
|
||||
m_isFirstPartialAck = true;
|
||||
m_dupAckCount = 0;
|
||||
|
||||
/* This FULL ACK acknowledge the fact that one segment has been
|
||||
* previously lost and now successfully received. All others have
|
||||
* been processed when they come under the form of dupACKs,
|
||||
* except the (maybe) new ACKs which come from a new window
|
||||
*/
|
||||
m_congestionControl->PktsAcked (m_tcb, segsAcked, m_lastRtt);
|
||||
newSegsAcked = (tcpHeader.GetAckNumber () - m_recover) / m_tcb->m_segmentSize;
|
||||
m_tcb->m_ackState = TcpSocketState::OPEN;
|
||||
|
||||
NS_LOG_INFO ("Received full ACK for seq " << tcpHeader.GetAckNumber () <<
|
||||
". Leaving fast recovery with cwnd set to " << m_tcb->m_cWnd);
|
||||
NS_LOG_DEBUG ("RECOVERY -> OPEN");
|
||||
}
|
||||
}
|
||||
else if (m_ackState == LOSS)
|
||||
else if (m_tcb->m_ackState == TcpSocketState::LOSS)
|
||||
{
|
||||
// Go back in OPEN state
|
||||
m_ackState = OPEN;
|
||||
m_isFirstPartialAck = true;
|
||||
m_congestionControl->PktsAcked (m_tcb, segsAcked, m_lastRtt);
|
||||
m_dupAckCount = 0;
|
||||
m_tcb->m_ackState = TcpSocketState::OPEN;
|
||||
NS_LOG_DEBUG ("LOSS -> OPEN");
|
||||
}
|
||||
|
||||
if (callCongestionControl)
|
||||
{
|
||||
m_congestionControl->IncreaseWindow (m_tcb, newSegsAcked);
|
||||
|
||||
NS_LOG_LOGIC ("Congestion control called: " <<
|
||||
" cWnd: " << m_tcb->m_cWnd <<
|
||||
" ssTh: " << m_tcb->m_ssThresh);
|
||||
}
|
||||
|
||||
NewAck (tcpHeader.GetAckNumber ());
|
||||
m_dupAckCount = 0;
|
||||
|
||||
// Try to send more data
|
||||
if (!m_sendPendingDataEvent.IsRunning ())
|
||||
{
|
||||
m_sendPendingDataEvent = Simulator::Schedule (TimeStep (1),
|
||||
&TcpSocketBase::SendPendingData,
|
||||
this, m_connected);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// If there is any data piggybacked, store it into m_rxBuffer
|
||||
@@ -2243,6 +2394,11 @@ TcpSocketBase::SendPendingData (bool withAck)
|
||||
" highestRxAck " << m_txBuffer->HeadSequence () <<
|
||||
" pd->Size " << m_txBuffer->Size () <<
|
||||
" pd->SFS " << m_txBuffer->SizeFromSequence (m_nextTxSequence));
|
||||
|
||||
NS_LOG_DEBUG ("Window: " << w <<
|
||||
" cWnd: " << m_tcb->m_cWnd <<
|
||||
" unAck: " << UnAckDataCount ());
|
||||
|
||||
uint32_t s = std::min (w, m_tcb->m_segmentSize); // Send no more than window
|
||||
uint32_t sz = SendDataPacket (m_nextTxSequence, s, withAck);
|
||||
nPacketsSent++; // Count sent this loop
|
||||
@@ -2256,38 +2412,39 @@ TcpSocketBase::SendPendingData (bool withAck)
|
||||
}
|
||||
|
||||
uint32_t
|
||||
TcpSocketBase::UnAckDataCount ()
|
||||
TcpSocketBase::UnAckDataCount () const
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
return m_nextTxSequence.Get () - m_txBuffer->HeadSequence ();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
TcpSocketBase::BytesInFlight ()
|
||||
TcpSocketBase::BytesInFlight () const
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
return m_highTxMark.Get () - m_txBuffer->HeadSequence ();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
TcpSocketBase::Window (void)
|
||||
TcpSocketBase::Window (void) const
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
return std::min (m_rWnd.Get (), m_tcb->m_cWnd.Get ());
|
||||
}
|
||||
|
||||
uint32_t
|
||||
TcpSocketBase::AvailableWindow ()
|
||||
TcpSocketBase::AvailableWindow () const
|
||||
{
|
||||
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_DEBUG ("UnAckCount=" << unack << ", Win=" << win);
|
||||
return (win < unack) ? 0 : (win - unack);
|
||||
}
|
||||
|
||||
uint16_t
|
||||
TcpSocketBase::AdvertisedWindowSize ()
|
||||
TcpSocketBase::AdvertisedWindowSize () const
|
||||
{
|
||||
uint32_t w = m_rxBuffer->MaxBufferSize () - m_rxBuffer->Size ();
|
||||
|
||||
@@ -2334,7 +2491,8 @@ TcpSocketBase::ReceivedData (Ptr<Packet> p, const TcpHeader& tcpHeader)
|
||||
{
|
||||
m_delAckEvent = Simulator::Schedule (m_delAckTimeout,
|
||||
&TcpSocketBase::DelAckTimeout, this);
|
||||
NS_LOG_LOGIC (this << " scheduled delayed ACK at " << (Simulator::Now () + Simulator::GetDelayLeft (m_delAckEvent)).GetSeconds ());
|
||||
NS_LOG_LOGIC (this << " scheduled delayed ACK at " <<
|
||||
(Simulator::Now () + Simulator::GetDelayLeft (m_delAckEvent)).GetSeconds ());
|
||||
}
|
||||
}
|
||||
// Notify app to receive if necessary
|
||||
@@ -2462,11 +2620,6 @@ TcpSocketBase::NewAck (SequenceNumber32 const& ack)
|
||||
(Simulator::Now () + Simulator::GetDelayLeft (m_retxEvent)).GetSeconds ());
|
||||
m_retxEvent.Cancel ();
|
||||
}
|
||||
// Try to send more data
|
||||
if (!m_sendPendingDataEvent.IsRunning ())
|
||||
{
|
||||
m_sendPendingDataEvent = Simulator::Schedule (TimeStep (1), &TcpSocketBase::SendPendingData, this, m_connected);
|
||||
}
|
||||
}
|
||||
|
||||
// Retransmit timeout
|
||||
@@ -2576,15 +2729,29 @@ TcpSocketBase::Retransmit ()
|
||||
* retransmission timer and the given segment has already been
|
||||
* retransmitted by way of the retransmission timer at least once, the
|
||||
* value of ssthresh is held constant.
|
||||
*
|
||||
* Conditions to decrement slow - start threshold are as follows:
|
||||
*
|
||||
* *) The TCP state should be less than disorder, which is nothing but open.
|
||||
* If we are entering into the loss state from the open state, we have not yet
|
||||
* reduced the slow - start threshold for the window of data. (Nat: Recovery?)
|
||||
* *) If we have entered the loss state with all the data pointed to by high_seq
|
||||
* acknowledged. Once again it means that in whatever state we are (other than
|
||||
* open state), all the data from the window that got us into the state, prior to
|
||||
* retransmission timer expiry, has been acknowledged. (Nat: How this can happen?)
|
||||
* *) If the above two conditions fail, we still have one more condition that can
|
||||
* demand reducing the slow - start threshold: If we are already in the loss state
|
||||
* and have not yet retransmitted anything. The condition may arise in case we
|
||||
* are not able to retransmit anything because of local congestion.
|
||||
*/
|
||||
|
||||
m_nextTxSequence = m_txBuffer->HeadSequence (); // Restart from highest Ack
|
||||
m_dupAckCount = 0;
|
||||
|
||||
if (m_ackState != LOSS)
|
||||
if (m_tcb->m_ackState != TcpSocketState::LOSS)
|
||||
{
|
||||
m_ackState = LOSS;
|
||||
m_tcb->m_ssThresh = GetSsThresh ();
|
||||
m_tcb->m_ackState = TcpSocketState::LOSS;
|
||||
m_tcb->m_ssThresh = m_congestionControl->GetSsThresh (m_tcb, BytesInFlight ());
|
||||
m_tcb->m_cWnd = m_tcb->m_segmentSize;
|
||||
}
|
||||
|
||||
@@ -2619,12 +2786,13 @@ TcpSocketBase::DoRetransmit ()
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Retransmit a data packet: Call SendDataPacket
|
||||
NS_LOG_LOGIC ("TcpSocketBase " << this << " retxing seq " << m_txBuffer->HeadSequence ());
|
||||
uint32_t sz = SendDataPacket (m_txBuffer->HeadSequence (), m_tcb->m_segmentSize, true);
|
||||
// In case of RTO, advance m_nextTxSequence
|
||||
m_nextTxSequence = std::max (m_nextTxSequence.Get (), m_txBuffer->HeadSequence () + sz);
|
||||
|
||||
NS_LOG_DEBUG ("retxing seq " << m_txBuffer->HeadSequence ());
|
||||
}
|
||||
|
||||
void
|
||||
@@ -2940,9 +3108,9 @@ void TcpSocketBase::UpdateWindowSize (const TcpHeader &header)
|
||||
}
|
||||
|
||||
// Test for conditions that allow updating of the window
|
||||
// 1) segment contains new data (advancing the right edge of the receive
|
||||
// buffer),
|
||||
// 2) segment does not contain new data but the segment acks new data
|
||||
// 1) segment contains new data (advancing the right edge of the receive
|
||||
// buffer),
|
||||
// 2) segment does not contain new data but the segment acks new data
|
||||
// (highest sequence number acked advances), or
|
||||
// 3) the advertised window is larger than the current send window
|
||||
bool update = false;
|
||||
@@ -3006,12 +3174,6 @@ TcpSocketBase::GetRxBuffer (void) const
|
||||
return m_rxBuffer;
|
||||
}
|
||||
|
||||
const char* const
|
||||
TcpSocketBase::TcpAckStateName[TcpSocketBase::LAST_ACKSTATE] =
|
||||
{
|
||||
"OPEN", "DISORDER", "CWR", "RECOVERY", "LOSS"
|
||||
};
|
||||
|
||||
void
|
||||
TcpSocketBase::UpdateCwnd (uint32_t oldValue, uint32_t newValue)
|
||||
{
|
||||
@@ -3024,6 +3186,25 @@ TcpSocketBase::UpdateSsThresh (uint32_t oldValue, uint32_t newValue)
|
||||
m_ssThTrace (oldValue, newValue);
|
||||
}
|
||||
|
||||
void
|
||||
TcpSocketBase::UpdateAckState (TcpSocketState::TcpAckState_t oldValue,
|
||||
TcpSocketState::TcpAckState_t newValue)
|
||||
{
|
||||
m_ackStateTrace (oldValue, newValue);
|
||||
}
|
||||
|
||||
void
|
||||
TcpSocketBase::SetCongestionControlAlgorithm (Ptr<TcpCongestionOps> algo)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << algo);
|
||||
m_congestionControl = algo;
|
||||
}
|
||||
|
||||
Ptr<TcpSocketBase>
|
||||
TcpSocketBase::Fork (void)
|
||||
{
|
||||
return CopyObject<TcpSocketBase> (this);
|
||||
}
|
||||
|
||||
//RttHistory methods
|
||||
RttHistory::RttHistory (SequenceNumber32 s, uint32_t c, Time t)
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "tcp-tx-buffer.h"
|
||||
#include "tcp-rx-buffer.h"
|
||||
#include "rtt-estimator.h"
|
||||
#include "tcp-congestion-ops.h"
|
||||
|
||||
namespace ns3 {
|
||||
|
||||
@@ -45,7 +46,6 @@ class Node;
|
||||
class Packet;
|
||||
class TcpL4Protocol;
|
||||
class TcpHeader;
|
||||
class TcpSocketState;
|
||||
|
||||
/**
|
||||
* \ingroup tcp
|
||||
@@ -76,6 +76,84 @@ public:
|
||||
/// Container for RttHistory objects
|
||||
typedef std::deque<RttHistory> RttHistory_t;
|
||||
|
||||
/**
|
||||
* \brief Data structure that records the congestion state of a connection
|
||||
*
|
||||
* In this data structure, basic informations that should be passed between
|
||||
* socket and the congestion control algorithm are saved. Through the code,
|
||||
* it will be referred as Transmission Control Block (TCB), but there are some
|
||||
* differencies. In the RFCs, the TCB contains all the variables that defines
|
||||
* a connection, while we preferred to maintain in this class only the values
|
||||
* that should be exchanged between socket and other parts, like congestion
|
||||
* control algorithms.
|
||||
*
|
||||
*/
|
||||
class TcpSocketState : public Object
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Get the type ID.
|
||||
* \brief Get the type ID.
|
||||
* \return the object TypeId
|
||||
*/
|
||||
static TypeId GetTypeId (void);
|
||||
|
||||
TcpSocketState ();
|
||||
TcpSocketState (const TcpSocketState &other);
|
||||
|
||||
/**
|
||||
* \brief Definition of the ACK state machine
|
||||
*
|
||||
* The design of this state machine is taken from Linux v4.0, but it has been
|
||||
* maintained in the Linux mainline from ages. It basically avoids to maintain
|
||||
* a lot of boolean variables, and it allows to check the transitions from
|
||||
* different algorithm in a cleaner way.
|
||||
*
|
||||
* These states represent the situation from a congestion control point of view:
|
||||
* in fact, apart the OPEN state, the other states represent a situation in
|
||||
* which there is a congestion, and different actions should be taken,
|
||||
* depending on the case.
|
||||
*
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
OPEN, /**< Normal state, no dubious events */
|
||||
DISORDER, /**< In all the respects it is "Open",
|
||||
* but requires a bit more attention. It is entered when
|
||||
* we see some SACKs or dupacks. It is split of "Open" */
|
||||
CWR, /**< cWnd was reduced due to some Congestion Notification event.
|
||||
* It can be ECN, ICMP source quench, local device congestion.
|
||||
* Not used in NS-3 right now. */
|
||||
RECOVERY, /**< CWND was reduced, we are fast-retransmitting. */
|
||||
LOSS, /**< CWND was reduced due to RTO timeout or SACK reneging. */
|
||||
LAST_ACKSTATE /**< Used only in debug messages */
|
||||
} TcpAckState_t;
|
||||
|
||||
/**
|
||||
* \brief Literal names of TCP states for use in log messages
|
||||
*/
|
||||
static const char* const TcpAckStateName[TcpSocketState::LAST_ACKSTATE];
|
||||
|
||||
// Congestion control
|
||||
TracedValue<uint32_t> m_cWnd; //!< Congestion window
|
||||
TracedValue<uint32_t> m_ssThresh; //!< Slow start threshold
|
||||
uint32_t m_initialCWnd; //!< Initial cWnd value
|
||||
uint32_t m_initialSsThresh; //!< Initial Slow Start Threshold value
|
||||
|
||||
// Segment
|
||||
uint32_t m_segmentSize; //!< Segment size
|
||||
|
||||
// Ack state
|
||||
TracedValue<TcpAckState_t> m_ackState; //!< State in the ACK state machine
|
||||
|
||||
/**
|
||||
* \brief Get cwnd in segments rather than bytes
|
||||
*
|
||||
* \return Congestion window in segments
|
||||
*/
|
||||
uint32_t GetCwndInSegments () const { return m_cWnd / m_segmentSize; }
|
||||
};
|
||||
|
||||
/**
|
||||
* \ingroup socket
|
||||
* \ingroup tcp
|
||||
@@ -104,13 +182,22 @@ typedef std::deque<RttHistory> RttHistory_t;
|
||||
* Another one (CWR) is present but not used. For more information, see
|
||||
* the TcpAckState_t documentation.
|
||||
*
|
||||
*
|
||||
* Congestion control interface
|
||||
* ---------------------------
|
||||
*
|
||||
* The variables needed to congestion control subclasses have been moved inside
|
||||
* the TcpSocketState class. It contains information on the congestion window,
|
||||
* slow start threshold, segment size and the state of the Ack state machine.
|
||||
* Congestion control, unlike older releases of ns-3, has been splitted from
|
||||
* TcpSocketBase. In particular, each congestion control is now a subclass of
|
||||
* the main TcpCongestionOps class. Switching between congestion algorithm is
|
||||
* now a matter of setting a pointer into the TcpSocketBase class.
|
||||
*
|
||||
* The variables needed to congestion control classes to operate correctly have
|
||||
* been moved inside the TcpSocketState class. It contains information on the
|
||||
* congestion window, slow start threshold, segment size and the state of the
|
||||
* Ack state machine.
|
||||
*
|
||||
* To track the trace inside the TcpSocketState class, a "forward" technique is
|
||||
* used, which consists in chaining callbacks from TcpSocketState to TcpSocketBase
|
||||
* (see for example cWnd trace source).
|
||||
*
|
||||
* Fast retransmit
|
||||
* ---------------------------
|
||||
@@ -217,41 +304,6 @@ public:
|
||||
*/
|
||||
Ptr<TcpRxBuffer> GetRxBuffer (void) const;
|
||||
|
||||
/**
|
||||
* \brief Definition of the ACK state machine
|
||||
*
|
||||
* The design of this state machine is taken from Linux v4.0, but it has been
|
||||
* maintained in the Linux mainline from ages. It basically avoids to maintain
|
||||
* a lot of boolean variables, and it allows to check the transitions from
|
||||
* different algorithm in a cleaner way.
|
||||
*
|
||||
* These states represent the situation from a congestion control point of view:
|
||||
* in fact, apart the OPEN state, the other states represent a situation in
|
||||
* which there is a congestion, and different actions should be taken,
|
||||
* depending on the case.
|
||||
*
|
||||
* \see ReceivedAck
|
||||
* \see Retransmit
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
OPEN, /**< Normal state, no dubious events */
|
||||
DISORDER, /**< In all the respects it is "Open",
|
||||
* but requires a bit more attention. It is entered when
|
||||
* we see some SACKs or dupacks. It is split of "Open" */
|
||||
CWR, /**< cWnd was reduced due to some Congestion Notification event.
|
||||
* It can be ECN, ICMP source quench, local device congestion.
|
||||
* Not used in NS-3 right now. */
|
||||
RECOVERY, /**< CWND was reduced, we are fast-retransmitting. */
|
||||
LOSS, /**< CWND was reduced due to RTO timeout or SACK reneging. */
|
||||
LAST_ACKSTATE /**< Used only in debug messages */
|
||||
} TcpAckState_t;
|
||||
|
||||
/**
|
||||
* \brief Literal names of TCP states for use in log messages
|
||||
*/
|
||||
static const char* const TcpAckStateName[TcpSocketBase::LAST_ACKSTATE];
|
||||
|
||||
/**
|
||||
* \brief Callback pointer for cWnd trace chaining
|
||||
*/
|
||||
@@ -262,6 +314,11 @@ public:
|
||||
*/
|
||||
TracedCallback<uint32_t, uint32_t> m_ssThTrace;
|
||||
|
||||
/**
|
||||
* \brief Callback pointer for ack state trace chaining
|
||||
*/
|
||||
TracedCallback<TcpSocketState::TcpAckState_t, TcpSocketState::TcpAckState_t> m_ackStateTrace;
|
||||
|
||||
/**
|
||||
* \brief Callback function to hook to TcpSocketState congestion window
|
||||
* \param oldValue old cWnd value
|
||||
@@ -276,6 +333,21 @@ public:
|
||||
*/
|
||||
void UpdateSsThresh (uint32_t oldValue, uint32_t newValue);
|
||||
|
||||
/**
|
||||
* \brief Callback function to hook to TcpSocketState ack state
|
||||
* \param oldValue old ack state value
|
||||
* \param newValue new ack state value
|
||||
*/
|
||||
void UpdateAckState (TcpSocketState::TcpAckState_t oldValue,
|
||||
TcpSocketState::TcpAckState_t newValue);
|
||||
|
||||
/**
|
||||
* \brief Install a congestion control algorithm on this socket
|
||||
*
|
||||
* \param algo Algorithm to be installed
|
||||
*/
|
||||
void SetCongestionControlAlgorithm (Ptr<TcpCongestionOps> algo);
|
||||
|
||||
// Necessary implementations of null functions from ns3::Socket
|
||||
virtual enum SocketErrno GetErrno (void) const; // returns m_errno
|
||||
virtual enum SocketType GetSocketType (void) const; // returns socket type
|
||||
@@ -611,31 +683,31 @@ protected:
|
||||
* \brief Return count of number of unacked bytes
|
||||
* \returns count of number of unacked bytes
|
||||
*/
|
||||
virtual uint32_t UnAckDataCount (void);
|
||||
virtual uint32_t UnAckDataCount (void) const;
|
||||
|
||||
/**
|
||||
* \brief Return total bytes in flight
|
||||
* \returns total bytes in flight
|
||||
*/
|
||||
virtual uint32_t BytesInFlight (void);
|
||||
virtual uint32_t BytesInFlight (void) const;
|
||||
|
||||
/**
|
||||
* \brief Return the max possible number of unacked bytes
|
||||
* \returns the max possible number of unacked bytes
|
||||
*/
|
||||
virtual uint32_t Window (void);
|
||||
virtual uint32_t Window (void) const;
|
||||
|
||||
/**
|
||||
* \brief Return unfilled portion of window
|
||||
* \return unfilled portion of window
|
||||
*/
|
||||
virtual uint32_t AvailableWindow (void);
|
||||
virtual uint32_t AvailableWindow (void) const;
|
||||
|
||||
/**
|
||||
* \brief The amount of Rx window announced to the peer
|
||||
* \returns size of Rx window announced to the peer
|
||||
*/
|
||||
virtual uint16_t AdvertisedWindowSize (void);
|
||||
virtual uint16_t AdvertisedWindowSize (void) const;
|
||||
|
||||
/**
|
||||
* \brief Update the receiver window (RWND) based on the value of the
|
||||
@@ -658,7 +730,7 @@ protected:
|
||||
* \brief Call CopyObject<> to clone me
|
||||
* \returns a copy of the socket
|
||||
*/
|
||||
virtual Ptr<TcpSocketBase> Fork (void) = 0;
|
||||
virtual Ptr<TcpSocketBase> Fork (void);
|
||||
|
||||
/**
|
||||
* \brief Received an ACK packet
|
||||
@@ -686,11 +758,6 @@ protected:
|
||||
*/
|
||||
virtual void NewAck (SequenceNumber32 const& seq);
|
||||
|
||||
/**
|
||||
* \brief Get the new value of slow start threshold after a loss event
|
||||
*/
|
||||
virtual uint32_t GetSsThresh () = 0;
|
||||
|
||||
/**
|
||||
* \brief Call Retransmit() upon RTO event
|
||||
*/
|
||||
@@ -864,6 +931,7 @@ protected:
|
||||
TracedValue<uint32_t> m_rWnd; //!< Receiver window (RCV.WND in RFC793)
|
||||
TracedValue<SequenceNumber32> m_highRxMark; //!< Highest seqno received
|
||||
TracedValue<SequenceNumber32> m_highRxAckMark; //!< Highest ack received
|
||||
uint32_t m_bytesAckedNotProcessed; //!< Bytes acked, but not processed
|
||||
|
||||
// Options
|
||||
bool m_winScalingEnabled; //!< Window Scale option enabled
|
||||
@@ -875,51 +943,17 @@ protected:
|
||||
|
||||
EventId m_sendPendingDataEvent; //!< micro-delay event to send pending data
|
||||
|
||||
// Ack state
|
||||
TracedValue<TcpAckState_t> m_ackState; //!< State in the ACK state machine
|
||||
|
||||
// Fast Retransmit and Recovery
|
||||
SequenceNumber32 m_recover; //!< Previous highest Tx seqnum for fast recovery
|
||||
uint32_t m_retxThresh; //!< Fast Retransmit threshold
|
||||
bool m_limitedTx; //!< perform limited transmit
|
||||
|
||||
// Transmission Control Block
|
||||
Ptr<TcpSocketState> m_tcb; //!< Congestion control informations
|
||||
};
|
||||
Ptr<TcpSocketState> m_tcb ; //!< Congestion control informations
|
||||
Ptr<TcpCongestionOps> m_congestionControl; //!< Congestion control
|
||||
|
||||
/**
|
||||
* \brief Data structure that records the congestion state of a connection
|
||||
*
|
||||
* In this data structure, basic informations that should be passed between
|
||||
* socket and the congestion control algorithm are saved. Through the code,
|
||||
* it will be referred as Transmission Control Block (TCB), but there are some
|
||||
* differencies. In the RFCs, the TCB contains all the variables that defines
|
||||
* a connection, while we preferred to maintain in this class only the values
|
||||
* that should be exchanged between socket and other parts, like congestion
|
||||
* control algorithms.
|
||||
*
|
||||
*/
|
||||
class TcpSocketState : public Object
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Get the type ID.
|
||||
* \brief Get the type ID.
|
||||
* \return the object TypeId
|
||||
*/
|
||||
static TypeId GetTypeId (void);
|
||||
|
||||
TcpSocketState ();
|
||||
TcpSocketState (const TcpSocketState &other);
|
||||
|
||||
// Congestion control
|
||||
TracedValue<uint32_t> m_cWnd; //!< Congestion window
|
||||
TracedValue<uint32_t> m_ssThresh; //!< Slow start threshold
|
||||
uint32_t m_initialCWnd; //!< Initial cWnd value
|
||||
uint32_t m_initialSsThresh; //!< Initial Slow Start Threshold value
|
||||
|
||||
// Segment
|
||||
uint32_t m_segmentSize; //!< Segment size
|
||||
// Guesses over the other connection end
|
||||
bool m_isFirstPartialAck;//!< First partial ACK during RECOVERY
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -929,8 +963,8 @@ public:
|
||||
* \param [in] oldValue original value of the traced variable
|
||||
* \param [in] newValue new value of the traced variable
|
||||
*/
|
||||
typedef void (* TcpAckStatesTracedValueCallback)(const TcpSocketBase::TcpAckState_t oldValue,
|
||||
const TcpSocketBase::TcpAckState_t newValue);
|
||||
typedef void (* TcpAckStatesTracedValueCallback)(const TcpSocketState::TcpAckState_t oldValue,
|
||||
const TcpSocketState::TcpAckState_t newValue);
|
||||
|
||||
} // namespace ns3
|
||||
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* Authors: Siddharth Gangadhar <siddharth@ittc.ku.edu>, Truc Anh N. Nguyen <annguyen@ittc.ku.edu>,
|
||||
* and Greeshma Umapathi
|
||||
* Authors: Siddharth Gangadhar <siddharth@ittc.ku.edu>,
|
||||
* Truc Anh N. Nguyen <annguyen@ittc.ku.edu>,
|
||||
* Greeshma Umapathi
|
||||
*
|
||||
* James P.G. Sterbenz <jpgs@ittc.ku.edu>, director
|
||||
* ResiliNets Research Group http://wiki.ittc.ku.edu/resilinets
|
||||
@@ -30,250 +31,132 @@
|
||||
* US Department of Defense (DoD), and ITTC at The University of Kansas.
|
||||
*/
|
||||
|
||||
#define NS_LOG_APPEND_CONTEXT \
|
||||
if (m_node) { std::clog << Simulator::Now ().GetSeconds () << " [node " << m_node->GetId () << "] "; }
|
||||
|
||||
#include "tcp-westwood.h"
|
||||
#include "ns3/log.h"
|
||||
#include "ns3/trace-source-accessor.h"
|
||||
#include "ns3/simulator.h"
|
||||
#include "ns3/abort.h"
|
||||
#include "ns3/node.h"
|
||||
#include "ns3/sequence-number.h"
|
||||
#include "rtt-estimator.h"
|
||||
#include "tcp-socket-base.h"
|
||||
|
||||
NS_LOG_COMPONENT_DEFINE ("TcpWestwood");
|
||||
|
||||
namespace ns3 {
|
||||
|
||||
NS_LOG_COMPONENT_DEFINE("TcpWestwood");
|
||||
|
||||
NS_OBJECT_ENSURE_REGISTERED(TcpWestwood);
|
||||
NS_OBJECT_ENSURE_REGISTERED (TcpWestwood);
|
||||
|
||||
TypeId
|
||||
TcpWestwood::GetTypeId (void)
|
||||
{
|
||||
static TypeId tid = TypeId("ns3::TcpWestwood")
|
||||
.SetParent<TcpSocketBase>()
|
||||
.SetGroupName ("Internet")
|
||||
.AddConstructor<TcpWestwood>()
|
||||
.AddAttribute("FilterType", "Use this to choose no filter or Tustin's approximation filter",
|
||||
EnumValue(TcpWestwood::TUSTIN), MakeEnumAccessor(&TcpWestwood::m_fType),
|
||||
MakeEnumChecker(TcpWestwood::NONE, "None", TcpWestwood::TUSTIN, "Tustin"))
|
||||
.AddAttribute("ProtocolType", "Use this to let the code run as Westwood or WestwoodPlus",
|
||||
EnumValue(TcpWestwood::WESTWOOD),
|
||||
MakeEnumAccessor(&TcpWestwood::m_pType),
|
||||
MakeEnumChecker(TcpWestwood::WESTWOOD, "Westwood",TcpWestwood::WESTWOODPLUS, "WestwoodPlus"))
|
||||
.AddTraceSource("EstimatedBW", "The estimated bandwidth",
|
||||
MakeTraceSourceAccessor(&TcpWestwood::m_currentBW),
|
||||
"ns3::TracedValueCallback::Double")
|
||||
.SetParent<TcpNewReno>()
|
||||
.SetGroupName ("Internet")
|
||||
.AddConstructor<TcpWestwood>()
|
||||
.AddAttribute("FilterType", "Use this to choose no filter or Tustin's approximation filter",
|
||||
EnumValue(TcpWestwood::TUSTIN), MakeEnumAccessor(&TcpWestwood::m_fType),
|
||||
MakeEnumChecker(TcpWestwood::NONE, "None", TcpWestwood::TUSTIN, "Tustin"))
|
||||
.AddAttribute("ProtocolType", "Use this to let the code run as Westwood or WestwoodPlus",
|
||||
EnumValue(TcpWestwood::WESTWOOD),
|
||||
MakeEnumAccessor(&TcpWestwood::m_pType),
|
||||
MakeEnumChecker(TcpWestwood::WESTWOOD, "Westwood",TcpWestwood::WESTWOODPLUS, "WestwoodPlus"))
|
||||
.AddTraceSource("EstimatedBW", "The estimated bandwidth",
|
||||
MakeTraceSourceAccessor(&TcpWestwood::m_currentBW),
|
||||
"ns3::TracedValueCallback::Double")
|
||||
;
|
||||
return tid;
|
||||
}
|
||||
|
||||
TcpWestwood::TcpWestwood (void) :
|
||||
m_currentBW(0),
|
||||
m_lastSampleBW(0),
|
||||
m_lastBW(0),
|
||||
m_minRtt(0),
|
||||
m_lastAck(0),
|
||||
m_prevAckNo(0),
|
||||
m_accountedFor(0),
|
||||
m_ackedSegments(0),
|
||||
m_IsCount(false)
|
||||
TcpNewReno (),
|
||||
m_currentBW (0),
|
||||
m_lastSampleBW (0),
|
||||
m_lastBW (0),
|
||||
m_minRtt (Time (0)),
|
||||
m_ackedSegments (0),
|
||||
m_IsCount (false)
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
}
|
||||
|
||||
TcpWestwood::TcpWestwood (const TcpWestwood& sock) :
|
||||
TcpSocketBase(sock),
|
||||
m_currentBW(sock.m_currentBW),
|
||||
m_lastSampleBW(sock.m_lastSampleBW),
|
||||
m_lastBW(sock.m_lastBW),
|
||||
m_minRtt(sock.m_minRtt),
|
||||
m_lastAck(sock.m_lastAck),
|
||||
m_prevAckNo(sock.m_prevAckNo),
|
||||
m_accountedFor(sock.m_accountedFor),
|
||||
m_pType(sock.m_pType),
|
||||
m_fType(sock.m_fType),
|
||||
m_IsCount(sock.m_IsCount)
|
||||
TcpNewReno (sock),
|
||||
m_currentBW (sock.m_currentBW),
|
||||
m_lastSampleBW (sock.m_lastSampleBW),
|
||||
m_lastBW (sock.m_lastBW),
|
||||
m_minRtt (Time (0)),
|
||||
m_pType (sock.m_pType),
|
||||
m_fType (sock.m_fType),
|
||||
m_IsCount (sock.m_IsCount)
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
NS_LOG_LOGIC ("Invoked the copy constructor");
|
||||
NS_LOG_INFO ("m_minRtt at copy constructor" << m_minRtt);
|
||||
}
|
||||
|
||||
TcpWestwood::~TcpWestwood (void)
|
||||
{
|
||||
}
|
||||
|
||||
Ptr<TcpSocketBase>
|
||||
TcpWestwood::Fork (void)
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
return CopyObject<TcpWestwood>(this);
|
||||
}
|
||||
|
||||
void
|
||||
TcpWestwood::NewAck (const SequenceNumber32& seq)
|
||||
{ // Same as Reno
|
||||
NS_LOG_FUNCTION (this << seq);
|
||||
NS_LOG_LOGIC ("TcpWestwood receieved ACK for seq " << seq <<
|
||||
" cwnd " << m_tcb->m_cWnd <<
|
||||
" ssthresh " << m_tcb->m_ssThresh);
|
||||
TcpWestwood::PktsAcked (Ptr<TcpSocketState> tcb, uint32_t packetsAcked,
|
||||
const Time& rtt)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << tcb << packetsAcked << rtt);
|
||||
|
||||
// Increase of cwnd based on current phase (slow start or congestion avoidance)
|
||||
if (m_tcb->m_cWnd < m_tcb->m_ssThresh)
|
||||
{ // Slow start mode, add one segSize to cWnd as in Reno
|
||||
m_tcb->m_cWnd += m_tcb->m_segmentSize;
|
||||
NS_LOG_INFO ("In SlowStart, updated to cwnd " << m_tcb->m_cWnd << " ssthresh " << m_tcb->m_ssThresh);
|
||||
if (rtt.IsZero ())
|
||||
{
|
||||
NS_LOG_WARN ("RTT measured is zero!");
|
||||
return;
|
||||
}
|
||||
|
||||
m_ackedSegments += packetsAcked;
|
||||
|
||||
// Update minRtt
|
||||
if (m_minRtt.IsZero ())
|
||||
{
|
||||
m_minRtt = rtt;
|
||||
}
|
||||
else
|
||||
{ // Congestion avoidance mode, increase by (segSize*segSize)/cwnd as in Reno
|
||||
double adder = static_cast<double> (m_tcb->m_segmentSize * m_tcb->m_segmentSize) / m_tcb->m_cWnd.Get();
|
||||
adder = std::max(1.0, adder);
|
||||
m_tcb->m_cWnd += static_cast<uint32_t>(adder);
|
||||
NS_LOG_INFO ("In CongAvoid, updated to cwnd " << m_tcb->m_cWnd << " ssthresh " << m_tcb->m_ssThresh);
|
||||
}
|
||||
|
||||
// Complete newAck processing
|
||||
TcpSocketBase::NewAck(seq);
|
||||
}
|
||||
|
||||
void
|
||||
TcpWestwood::ReceivedAck (Ptr<Packet> packet, const TcpHeader& tcpHeader)
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
int acked = 0;
|
||||
if ((0 != (tcpHeader.GetFlags () & TcpHeader::ACK)) && tcpHeader.GetAckNumber() >= m_prevAckNo)
|
||||
{// It is a duplicate ACK or a new ACK. Old ACK is ignored.
|
||||
if (m_pType == TcpWestwood::WESTWOOD)
|
||||
{// For Westwood, calculate the number of ACKed segments and estimate the BW
|
||||
acked = CountAck (tcpHeader);
|
||||
EstimateBW (acked, tcpHeader, Time(0));
|
||||
}
|
||||
else if (m_pType == TcpWestwood::WESTWOODPLUS)
|
||||
{// For Weswood+, calculate the number of ACKed segments and update m_ackedSegments
|
||||
if (m_IsCount)
|
||||
{
|
||||
acked = CountAck (tcpHeader);
|
||||
UpdateAckedSegments (acked);
|
||||
}
|
||||
{
|
||||
if (rtt < m_minRtt)
|
||||
{
|
||||
m_minRtt = rtt;
|
||||
}
|
||||
}
|
||||
|
||||
TcpSocketBase::ReceivedAck (packet, tcpHeader);
|
||||
}
|
||||
NS_LOG_LOGIC ("MinRtt: " << m_minRtt.GetMilliSeconds () << "ms");
|
||||
|
||||
void
|
||||
TcpWestwood::EstimateBW (int acked, const TcpHeader& tcpHeader, Time rtt)
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
if (m_pType == TcpWestwood::WESTWOOD)
|
||||
{
|
||||
// Get the time when the current ACK is received
|
||||
double currentAck = static_cast<double> (Simulator::Now().GetSeconds());
|
||||
// Calculate the BW
|
||||
m_currentBW = acked * m_tcb->m_segmentSize / (currentAck - m_lastAck);
|
||||
// Update the last ACK time
|
||||
m_lastAck = currentAck;
|
||||
EstimateBW (rtt, tcb);
|
||||
}
|
||||
else if (m_pType == TcpWestwood::WESTWOODPLUS)
|
||||
{
|
||||
// Calculate the BW
|
||||
m_currentBW = m_ackedSegments * m_tcb->m_segmentSize / rtt.GetSeconds();
|
||||
// Reset m_ackedSegments and m_IsCount for the next sampling
|
||||
m_ackedSegments = 0;
|
||||
if (!(rtt.IsZero () || m_IsCount))
|
||||
{
|
||||
m_IsCount = true;
|
||||
m_bwEstimateEvent.Cancel ();
|
||||
m_bwEstimateEvent = Simulator::Schedule (rtt, &TcpWestwood::EstimateBW,
|
||||
this, rtt, tcb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TcpWestwood::EstimateBW (const Time &rtt, Ptr<TcpSocketState> tcb)
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
|
||||
NS_ASSERT (!rtt.IsZero ());
|
||||
|
||||
m_currentBW = m_ackedSegments * tcb->m_segmentSize / rtt.GetSeconds ();
|
||||
|
||||
if (m_pType == TcpWestwood::WESTWOODPLUS)
|
||||
{
|
||||
m_IsCount = false;
|
||||
}
|
||||
|
||||
m_ackedSegments = 0;
|
||||
NS_LOG_LOGIC ("Estimated BW: " << m_currentBW);
|
||||
|
||||
// Filter the BW sample
|
||||
Filtering();
|
||||
}
|
||||
|
||||
int
|
||||
TcpWestwood::CountAck (const TcpHeader& tcpHeader)
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
|
||||
// Calculate the number of acknowledged segments based on the received ACK number
|
||||
int cumul_ack = (tcpHeader.GetAckNumber() - m_prevAckNo) / m_tcb->m_segmentSize;
|
||||
|
||||
if (cumul_ack == 0)
|
||||
{// A DUPACK counts for 1 segment delivered successfully
|
||||
m_accountedFor++;
|
||||
cumul_ack = 1;
|
||||
}
|
||||
if (cumul_ack > 1)
|
||||
{// A delayed ACK or a cumulative ACK after a retransmission
|
||||
// Check how much new data it ACKs
|
||||
if (m_accountedFor >= cumul_ack)
|
||||
{
|
||||
m_accountedFor -= cumul_ack;
|
||||
cumul_ack = 1;
|
||||
}
|
||||
else if (m_accountedFor < cumul_ack)
|
||||
{
|
||||
cumul_ack -= m_accountedFor;
|
||||
m_accountedFor = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the previous ACK number
|
||||
m_prevAckNo = tcpHeader.GetAckNumber();
|
||||
|
||||
return cumul_ack;
|
||||
}
|
||||
|
||||
void
|
||||
TcpWestwood::UpdateAckedSegments (int acked)
|
||||
{
|
||||
m_ackedSegments += acked;
|
||||
}
|
||||
|
||||
|
||||
uint32_t
|
||||
TcpWestwood::GetSsThresh ()
|
||||
{
|
||||
return uint32_t(m_currentBW * static_cast<double> (m_minRtt.GetSeconds()));
|
||||
}
|
||||
|
||||
void
|
||||
TcpWestwood::EstimateRtt (const TcpHeader& tcpHeader)
|
||||
{
|
||||
NS_LOG_FUNCTION_NOARGS ();
|
||||
|
||||
// Calculate m_lastRtt
|
||||
TcpSocketBase::EstimateRtt (tcpHeader);
|
||||
|
||||
// Update minRtt
|
||||
if (m_minRtt == Time (0))
|
||||
{
|
||||
m_minRtt = m_lastRtt;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_lastRtt < m_minRtt)
|
||||
{
|
||||
m_minRtt = m_lastRtt;
|
||||
}
|
||||
}
|
||||
|
||||
// For Westwood+, start running a clock on the currently estimated RTT if possible
|
||||
// to trigger a new BW sampling event
|
||||
if (m_pType == TcpWestwood::WESTWOODPLUS)
|
||||
{
|
||||
if(m_lastRtt != Time (0) && m_state == ESTABLISHED && !m_IsCount)
|
||||
{
|
||||
m_IsCount = true;
|
||||
m_bwEstimateEvent.Cancel();
|
||||
m_bwEstimateEvent = Simulator::Schedule (m_lastRtt, &TcpWestwood::EstimateBW,this,m_ackedSegments,tcpHeader,m_lastRtt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TcpWestwood::Filtering ()
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
|
||||
double alpha = 0.9;
|
||||
|
||||
@@ -287,6 +170,27 @@ TcpWestwood::Filtering ()
|
||||
m_lastSampleBW = sample_bwe;
|
||||
m_lastBW = m_currentBW;
|
||||
}
|
||||
|
||||
NS_LOG_LOGIC ("Estimated BW after filtering: " << m_currentBW);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
TcpWestwood::GetSsThresh (Ptr<const TcpSocketState> tcb,
|
||||
uint32_t bytesInFlight)
|
||||
{
|
||||
(void) bytesInFlight;
|
||||
NS_LOG_LOGIC ("CurrentBW: " << m_currentBW << " minRtt: " <<
|
||||
m_minRtt << " ssthresh: " <<
|
||||
m_currentBW * static_cast<double> (m_minRtt.GetSeconds ()));
|
||||
|
||||
return std::max (2*tcb->m_segmentSize,
|
||||
uint32_t (m_currentBW * static_cast<double> (m_minRtt.GetSeconds ())));
|
||||
}
|
||||
|
||||
Ptr<TcpCongestionOps>
|
||||
TcpWestwood::Fork ()
|
||||
{
|
||||
return CreateObject<TcpWestwood> (*this);
|
||||
}
|
||||
|
||||
} // namespace ns3
|
||||
|
||||
@@ -33,11 +33,17 @@
|
||||
#ifndef TCP_WESTWOOD_H
|
||||
#define TCP_WESTWOOD_H
|
||||
|
||||
#include "tcp-socket-base.h"
|
||||
#include "ns3/packet.h"
|
||||
#include "tcp-congestion-ops.h"
|
||||
#include "ns3/sequence-number.h"
|
||||
#include "ns3/traced-value.h"
|
||||
|
||||
namespace ns3 {
|
||||
|
||||
class Packet;
|
||||
class TcpHeader;
|
||||
class Time;
|
||||
class EventId;
|
||||
|
||||
/**
|
||||
* \ingroup socket
|
||||
* \ingroup tcp
|
||||
@@ -57,9 +63,9 @@ namespace ns3 {
|
||||
* and the EstimateBW (int, const, Time). The CountAck method calculates
|
||||
* the number of acknowledged segments on the receipt of an ACK.
|
||||
* The EstimateBW estimates the bandwidth based on the value returned by CountAck
|
||||
* and the sampling interval (last ACK inter-arrival time for Westwood and last RTT for Westwood+).
|
||||
* and the sampling interval (last ACK inter-arrival time for Westwood and last RTT for Westwood+).
|
||||
*/
|
||||
class TcpWestwood : public TcpSocketBase
|
||||
class TcpWestwood : public TcpNewReno
|
||||
{
|
||||
public:
|
||||
/**
|
||||
@@ -94,35 +100,15 @@ public:
|
||||
TUSTIN
|
||||
};
|
||||
|
||||
protected:
|
||||
virtual Ptr<TcpSocketBase> Fork (void); // Call CopyObject<TcpTahoe> to clone me
|
||||
virtual void NewAck (SequenceNumber32 const& seq); // Inc cwnd and call NewAck() of parent
|
||||
virtual uint32_t GetSsThresh ();
|
||||
virtual uint32_t GetSsThresh (Ptr<const TcpSocketState> tcb,
|
||||
uint32_t bytesInFlight);
|
||||
|
||||
/**
|
||||
* Process the newly received ACK
|
||||
*
|
||||
* \param packet the received ACK packet
|
||||
* \param tcpHeader the header attached to the ACK packet
|
||||
*/
|
||||
virtual void ReceivedAck (Ptr<Packet> packet, const TcpHeader& tcpHeader);
|
||||
|
||||
/**
|
||||
* Estimate the RTT, record the minimum value,
|
||||
* and run a clock on the RTT to trigger Westwood+ bandwidth sampling
|
||||
* \param header the packet header
|
||||
*/
|
||||
virtual void EstimateRtt (const TcpHeader& header);
|
||||
virtual void PktsAcked (Ptr<TcpSocketState> tcb, uint32_t packetsAcked,
|
||||
const Time& rtt);
|
||||
|
||||
virtual Ptr<TcpCongestionOps> Fork ();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Calculate the number of acknowledged packets upon the receipt of an ACK packet
|
||||
*
|
||||
* \param tcpHeader the header of the received ACK packet
|
||||
* \return the number of ACKed packets
|
||||
*/
|
||||
int CountAck (const TcpHeader& tcpHeader);
|
||||
|
||||
/**
|
||||
* Update the total number of acknowledged packets during the current RTT
|
||||
*
|
||||
@@ -133,25 +119,16 @@ private:
|
||||
/**
|
||||
* Estimate the network's bandwidth
|
||||
*
|
||||
* \param acked the number of acknowledged packets returned by CountAck
|
||||
* \param tcpHeader the header of the packet
|
||||
* \param acked the number of acknowledged packets
|
||||
* \param rtt the RTT estimation
|
||||
*/
|
||||
void EstimateBW (int acked, const TcpHeader& tcpHeader, Time rtt);
|
||||
|
||||
/**
|
||||
* Tustin filter
|
||||
*/
|
||||
void Filtering (void);
|
||||
void EstimateBW (const Time& rtt, Ptr<TcpSocketState> tcb);
|
||||
|
||||
protected:
|
||||
TracedValue<double> m_currentBW; //!< Current value of the estimated BW
|
||||
double m_lastSampleBW; //!< Last bandwidth sample
|
||||
double m_lastBW; //!< Last bandwidth sample after being filtered
|
||||
Time m_minRtt; //!< Minimum RTT
|
||||
double m_lastAck; //!< The time last ACK was received
|
||||
SequenceNumber32 m_prevAckNo; //!< Previously received ACK number
|
||||
int m_accountedFor; //!< The number of received DUPACKs
|
||||
enum ProtocolType m_pType; //!< 0 for Westwood, 1 for Westwood+
|
||||
enum FilterType m_fType; //!< 0 for none, 1 for Tustin
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "ns3/core-module.h"
|
||||
#include "ns3/tcp-header.h"
|
||||
#include "ns3/buffer.h"
|
||||
#include "ns3/private/tcp-option-rfc793.h"
|
||||
#include "../model/tcp-option-rfc793.h"
|
||||
|
||||
namespace ns3 {
|
||||
|
||||
|
||||
@@ -145,7 +145,7 @@ def build(bld):
|
||||
'model/ipv6-option-demux.cc',
|
||||
'model/icmpv6-l4-protocol.cc',
|
||||
'model/tcp-socket-base.cc',
|
||||
'model/tcp-newreno.cc',
|
||||
'model/tcp-congestion-ops.cc',
|
||||
'model/tcp-westwood.cc',
|
||||
'model/tcp-rx-buffer.cc',
|
||||
'model/tcp-tx-buffer.cc',
|
||||
@@ -321,7 +321,7 @@ def build(bld):
|
||||
'helper/ipv6-interface-container.h',
|
||||
'helper/ipv6-routing-helper.h',
|
||||
'model/ipv6-address-generator.h',
|
||||
'model/tcp-newreno.h',
|
||||
'model/tcp-congestion-ops.h',
|
||||
'model/tcp-westwood.h',
|
||||
'model/tcp-socket-base.h',
|
||||
'model/tcp-tx-buffer.h',
|
||||
|
||||
Reference in New Issue
Block a user