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:
Natale Patriciello
2015-10-16 10:42:30 -07:00
parent 958b641e2a
commit b87e04a77a
11 changed files with 782 additions and 554 deletions

View File

@@ -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

View 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

View File

@@ -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 *

View File

@@ -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

View File

@@ -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 */

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 {

View File

@@ -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',