tcp: Add DCTCP code and test-suite
This commit is contained in:
committed by
Tom Henderson
parent
c3265b7d44
commit
48bf4e7c29
@@ -44,7 +44,7 @@ connection setup and close logic. Several congestion control algorithms
|
||||
are supported, with NewReno the default, and Westwood, Hybla, HighSpeed,
|
||||
Vegas, Scalable, Veno, Binary Increase Congestion Control (BIC), Yet Another
|
||||
HighSpeed TCP (YeAH), Illinois, H-TCP, Low Extra Delay Background Transport
|
||||
(LEDBAT) and TCP Low Priority (TCP-LP) also supported. The model also supports
|
||||
(LEDBAT), TCP Low Priority (TCP-LP) and and Data Center TCP (DCTCP) also supported. The model also supports
|
||||
Selective Acknowledgements (SACK), Proportional Rate Reduction (PRR) and
|
||||
Explicit Congestion Notification (ECN). Multipath-TCP is not yet supported in
|
||||
the |ns3| releases.
|
||||
@@ -782,6 +782,87 @@ phase or not
|
||||
|
||||
More information (paper): http://cs.northwestern.edu/~akuzma/rice/doc/TCP-LP.pdf
|
||||
|
||||
Data Center TCP (DCTCP)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
DCTCP is an enhancement to the TCP congestion control algorithm for data center
|
||||
networks and leverages Explicit Congestion Notification (ECN) to provide multi-bit
|
||||
feedback to the end hosts. DCTCP extends the Explicit Congestion Notification
|
||||
to estimate the fraction of bytes that encounter congestion, rather than simply
|
||||
detecting that the congestion has occurred. DCTCP then scales the congestion
|
||||
window based on this estimate. This approach achieves high burst tolerance, low
|
||||
latency, and high throughput with shallow-buffered switches.
|
||||
|
||||
* Receiver functionality: If CE is set in IP header of incoming packet, send congestion notification to the sender by setting ECE in TCP header.
|
||||
|
||||
* Sender functionality: It should maintain an average of fraction of packets marked (α) by using the exponential weighted moving average as shown below:
|
||||
|
||||
::
|
||||
|
||||
α = (1 - g) x α + g x F
|
||||
|
||||
where
|
||||
|
||||
* g is the estimation gain (between 0 and 1)
|
||||
* F is the fraction of packets marked in current RTT.
|
||||
|
||||
On receipt of an ACK with ECE bit set, the sender should respond by reducing the congestion
|
||||
window as follows, once for every window of data:
|
||||
|
||||
::
|
||||
|
||||
cwnd = cwnd * (1 - α / 2)
|
||||
|
||||
Following the recommendation of RFC 8257, the default values of the parameters are:
|
||||
|
||||
::
|
||||
|
||||
g = 0.0625
|
||||
|
||||
alpha (α) = 1
|
||||
|
||||
|
||||
|
||||
To enable DCTCP on all TCP sockets, the following configuration can be used:
|
||||
|
||||
::
|
||||
|
||||
Config::SetDefault ("ns3::TcpL4Protocol::SocketType", TypeIdValue (TcpDctcp::GetTypeId ()));
|
||||
|
||||
To enable DCTCP on a chosen TCP socket, the following configuration can be used:
|
||||
|
||||
::
|
||||
|
||||
Config::Set ("$ns3::NodeListPriv/NodeList/1/$ns3::TcpL4Protocol/SocketType", TypeIdValue (TcpDctcp::GetTypeId ()));
|
||||
|
||||
DCTCP requires ECN to be enabled:
|
||||
|
||||
::
|
||||
|
||||
Config::SetDefault ("ns3::TcpSocketBase::EcnMode", StringValue ("ClassicEcn"));
|
||||
|
||||
DCTCP depends on a simple queue management algorithm in routers / switches to
|
||||
mark packets. The current implementation of DCTCP in ns-3 uses RED with a simple
|
||||
configuration to achieve the behavior of desired queue management algorithm.
|
||||
|
||||
To configure RED router for DCTCP:
|
||||
|
||||
::
|
||||
|
||||
Config::SetDefault ("ns3::RedQueueDisc::UseEcn", BooleanValue (true));
|
||||
Config::SetDefault ("ns3::RedQueueDisc::QW", DoubleValue (1.0));
|
||||
|
||||
The following unit tests have been written to validate the implementation of DCTCP:
|
||||
|
||||
* ECT flags should be set for SYN, SYN+ACK, ACK and data packets for DCTCP traffic
|
||||
* ECT flags should not be set for SYN, SYN+ACK and pure ACK packets, but should be set on data packets for ECN enabled traditional TCP flows
|
||||
* ECE should be set only when CE flags are received at receiver and even if sender doesn’t send CWR, receiver should not send ECE if it doesn’t receive packets with CE flags
|
||||
* Test to validate cwnd increment in DCTCP
|
||||
* Test to validate cwnd decrement in DCTCP
|
||||
|
||||
More information about DCTCP is available in the RFC 8257:
|
||||
https://tools.ietf.org/html/rfc8257
|
||||
|
||||
Support for Explicit Congestion Notification (ECN)
|
||||
++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
|
||||
245
src/internet/model/tcp-dctcp.cc
Normal file
245
src/internet/model/tcp-dctcp.cc
Normal file
@@ -0,0 +1,245 @@
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
||||
/*
|
||||
* Copyright (c) 2017 NITK Surathkal
|
||||
*
|
||||
* 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: Shravya K.S. <shravya.ks0@gmail.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tcp-dctcp.h"
|
||||
#include "ns3/log.h"
|
||||
#include "math.h"
|
||||
#include "ns3/tcp-socket-state.h"
|
||||
|
||||
namespace ns3 {
|
||||
|
||||
NS_LOG_COMPONENT_DEFINE ("TcpDctcp");
|
||||
|
||||
NS_OBJECT_ENSURE_REGISTERED (TcpDctcp);
|
||||
|
||||
TypeId TcpDctcp::GetTypeId (void)
|
||||
{
|
||||
static TypeId tid = TypeId ("ns3::TcpDctcp")
|
||||
.SetParent<TcpNewReno> ()
|
||||
.AddConstructor<TcpDctcp> ()
|
||||
.SetGroupName ("Internet")
|
||||
.AddAttribute ("DctcpShiftG",
|
||||
"Parameter G for updating dctcp_alpha",
|
||||
DoubleValue (0.0625),
|
||||
MakeDoubleAccessor (&TcpDctcp::m_g),
|
||||
MakeDoubleChecker<double> (0))
|
||||
.AddAttribute ("DctcpAlphaOnInit",
|
||||
"Initial alpha value",
|
||||
DoubleValue (1.0),
|
||||
MakeDoubleAccessor (&TcpDctcp::SetDctcpAlpha),
|
||||
MakeDoubleChecker<double> (0))
|
||||
;
|
||||
return tid;
|
||||
}
|
||||
|
||||
std::string TcpDctcp::GetName () const
|
||||
{
|
||||
return "TcpDctcp";
|
||||
}
|
||||
|
||||
TcpDctcp::TcpDctcp ()
|
||||
: TcpNewReno ()
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
m_delayedAckReserved = false;
|
||||
m_ceState = false;
|
||||
m_ackedBytesEcn = 0;
|
||||
m_ackedBytesTotal = 0;
|
||||
m_priorRcvNxtFlag = false;
|
||||
m_nextSeqFlag = false;
|
||||
}
|
||||
|
||||
TcpDctcp::TcpDctcp (const TcpDctcp& sock)
|
||||
: TcpNewReno (sock)
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
m_delayedAckReserved = (sock.m_delayedAckReserved);
|
||||
m_ceState = (sock.m_ceState);
|
||||
}
|
||||
|
||||
TcpDctcp::~TcpDctcp (void)
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
}
|
||||
|
||||
Ptr<TcpCongestionOps> TcpDctcp::Fork (void)
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
return CopyObject<TcpDctcp> (this);
|
||||
}
|
||||
|
||||
void
|
||||
TcpDctcp::ReduceCwnd (Ptr<TcpSocketState> tcb)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << tcb);
|
||||
uint32_t val = (int)((1 - m_alpha / 2.0) * tcb->m_cWnd);
|
||||
tcb->m_cWnd = std::max (val, 2 * tcb->m_segmentSize);
|
||||
}
|
||||
|
||||
void
|
||||
TcpDctcp::PktsAcked (Ptr<TcpSocketState> tcb, uint32_t segmentsAcked, const Time &rtt)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << tcb << segmentsAcked << rtt);
|
||||
m_ackedBytesTotal += segmentsAcked * tcb->m_segmentSize;
|
||||
if (tcb->m_ecnState == TcpSocketState::ECN_ECE_RCVD)
|
||||
{
|
||||
m_ackedBytesEcn += segmentsAcked * tcb->m_segmentSize;
|
||||
}
|
||||
if (m_nextSeqFlag == false)
|
||||
{
|
||||
m_nextSeq = tcb->m_nextTxSequence;
|
||||
m_nextSeqFlag = true;
|
||||
}
|
||||
if (tcb->m_lastAckedSeq >= m_nextSeq)
|
||||
{
|
||||
double bytesEcn;
|
||||
if (m_ackedBytesTotal > 0)
|
||||
{
|
||||
bytesEcn = (double) m_ackedBytesEcn / m_ackedBytesTotal;
|
||||
}
|
||||
else
|
||||
{
|
||||
bytesEcn = 0.0;
|
||||
}
|
||||
m_alpha = (1.0 - m_g) * m_alpha + m_g * bytesEcn;
|
||||
Reset (tcb);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TcpDctcp::SetDctcpAlpha (double alpha)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << alpha);
|
||||
m_alpha = alpha;
|
||||
}
|
||||
|
||||
void
|
||||
TcpDctcp::Reset (Ptr<TcpSocketState> tcb)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << tcb);
|
||||
m_nextSeq = tcb->m_nextTxSequence;
|
||||
m_ackedBytesEcn = 0;
|
||||
m_ackedBytesTotal = 0;
|
||||
}
|
||||
|
||||
void
|
||||
TcpDctcp::CeState0to1 (Ptr<TcpSocketState> tcb)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << tcb);
|
||||
if (!m_ceState && m_delayedAckReserved && m_priorRcvNxtFlag)
|
||||
{
|
||||
SequenceNumber32 tmpRcvNxt;
|
||||
/* Save current NextRxSequence. */
|
||||
tmpRcvNxt = tcb->m_rxBuffer->NextRxSequence ();
|
||||
|
||||
/* Generate previous ACK without ECE */
|
||||
tcb->m_rxBuffer->SetNextRxSequence (m_priorRcvNxt);
|
||||
tcb->m_sendEmptyPacketCallback (TcpHeader::ACK);
|
||||
|
||||
/* Recover current RcvNxt. */
|
||||
tcb->m_rxBuffer->SetNextRxSequence (tmpRcvNxt);
|
||||
}
|
||||
|
||||
if (m_priorRcvNxtFlag == false)
|
||||
{
|
||||
m_priorRcvNxtFlag = true;
|
||||
}
|
||||
m_priorRcvNxt = tcb->m_rxBuffer->NextRxSequence ();
|
||||
m_ceState = true;
|
||||
tcb->m_ecnState = TcpSocketState::ECN_CE_RCVD;
|
||||
}
|
||||
|
||||
void
|
||||
TcpDctcp::CeState1to0 (Ptr<TcpSocketState> tcb)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << tcb);
|
||||
if (m_ceState && m_delayedAckReserved && m_priorRcvNxtFlag)
|
||||
{
|
||||
SequenceNumber32 tmpRcvNxt;
|
||||
/* Save current NextRxSequence. */
|
||||
tmpRcvNxt = tcb->m_rxBuffer->NextRxSequence ();
|
||||
|
||||
/* Generate previous ACK with ECE */
|
||||
tcb->m_rxBuffer->SetNextRxSequence (m_priorRcvNxt);
|
||||
tcb->m_sendEmptyPacketCallback (TcpHeader::ACK | TcpHeader::ECE);
|
||||
|
||||
/* Recover current RcvNxt. */
|
||||
tcb->m_rxBuffer->SetNextRxSequence (tmpRcvNxt);
|
||||
}
|
||||
|
||||
if (m_priorRcvNxtFlag == false)
|
||||
{
|
||||
m_priorRcvNxtFlag = true;
|
||||
}
|
||||
m_priorRcvNxt = tcb->m_rxBuffer->NextRxSequence ();
|
||||
m_ceState = false;
|
||||
tcb->m_ecnState = TcpSocketState::ECN_IDLE;
|
||||
}
|
||||
|
||||
void
|
||||
TcpDctcp::UpdateAckReserved (Ptr<TcpSocketState> tcb,
|
||||
const TcpSocketState::TcpCAEvent_t event)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << tcb << event);
|
||||
switch (event)
|
||||
{
|
||||
case TcpSocketState::CA_EVENT_DELAYED_ACK:
|
||||
if (!m_delayedAckReserved)
|
||||
{
|
||||
m_delayedAckReserved = true;
|
||||
}
|
||||
break;
|
||||
case TcpSocketState::CA_EVENT_NON_DELAYED_ACK:
|
||||
if (m_delayedAckReserved)
|
||||
{
|
||||
m_delayedAckReserved = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* Don't care for the rest. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TcpDctcp::CwndEvent (Ptr<TcpSocketState> tcb,
|
||||
const TcpSocketState::TcpCAEvent_t event)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << tcb << event);
|
||||
switch (event)
|
||||
{
|
||||
case TcpSocketState::CA_EVENT_ECN_IS_CE:
|
||||
CeState0to1 (tcb);
|
||||
break;
|
||||
case TcpSocketState::CA_EVENT_ECN_NO_CE:
|
||||
CeState1to0 (tcb);
|
||||
break;
|
||||
case TcpSocketState::CA_EVENT_DELAYED_ACK:
|
||||
case TcpSocketState::CA_EVENT_NON_DELAYED_ACK:
|
||||
UpdateAckReserved (tcb, event);
|
||||
break;
|
||||
default:
|
||||
/* Don't care for the rest. */
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
148
src/internet/model/tcp-dctcp.h
Normal file
148
src/internet/model/tcp-dctcp.h
Normal file
@@ -0,0 +1,148 @@
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
||||
/*
|
||||
* Copyright (c) 2017 NITK Surathkal
|
||||
*
|
||||
* 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: Shravya K.S. <shravya.ks0@gmail.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TCP_DCTCP_H
|
||||
#define TCP_DCTCP_H
|
||||
|
||||
#include "ns3/tcp-congestion-ops.h"
|
||||
|
||||
namespace ns3 {
|
||||
|
||||
/**
|
||||
* \ingroup congestionOps
|
||||
*
|
||||
* \brief An implementation of DCTCP. This model implements all the functionalities mentioned
|
||||
* in the DCTCP SIGCOMM paper except dynamic buffer allocation in switches
|
||||
*/
|
||||
|
||||
class TcpDctcp : public TcpNewReno
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \brief Get the type ID.
|
||||
* \return the object TypeId
|
||||
*/
|
||||
static TypeId GetTypeId (void);
|
||||
|
||||
/**
|
||||
* Create an unbound tcp socket.
|
||||
*/
|
||||
TcpDctcp ();
|
||||
|
||||
/**
|
||||
* \brief Copy constructor
|
||||
* \param sock the object to copy
|
||||
*/
|
||||
TcpDctcp (const TcpDctcp& sock);
|
||||
|
||||
/**
|
||||
* \brief Destructor
|
||||
*/
|
||||
virtual ~TcpDctcp (void);
|
||||
|
||||
/**
|
||||
* \brief Get the name of the TCP flavour
|
||||
*
|
||||
* \return The name of the TCP
|
||||
*/
|
||||
virtual std::string GetName () const;
|
||||
|
||||
virtual Ptr<TcpCongestionOps> Fork ();
|
||||
|
||||
/**
|
||||
* \brief Reduce congestion window based on DCTCP algorithm
|
||||
*
|
||||
* \param tcb internal congestion state
|
||||
*/
|
||||
virtual void ReduceCwnd (Ptr<TcpSocketState> tcb);
|
||||
|
||||
/**
|
||||
* \brief Get information from the acked packet
|
||||
*
|
||||
* \param tcb internal congestion state
|
||||
* \param segmentsAcked count of segments ACKed
|
||||
* \param rtt The estimated rtt
|
||||
*/
|
||||
virtual void PktsAcked (Ptr<TcpSocketState> tcb, uint32_t segmentsAcked,
|
||||
const Time &rtt);
|
||||
/**
|
||||
* \brief Trigger events/calculations on occurrence of congestion window event
|
||||
*
|
||||
* \param tcb internal state
|
||||
* \param event congestion window event which triggered this function
|
||||
*/
|
||||
virtual void CwndEvent (Ptr<TcpSocketState> tcb,
|
||||
const TcpSocketState::TcpCAEvent_t event);
|
||||
|
||||
private:
|
||||
/**
|
||||
* \brief Changes state of m_ceState to true
|
||||
*
|
||||
* \param tcb internal congestion state
|
||||
*/
|
||||
void CeState0to1 (Ptr<TcpSocketState> tcb);
|
||||
|
||||
/**
|
||||
* \brief Changes state of m_ceState to false
|
||||
*
|
||||
* \param tcb internal congestion state
|
||||
*/
|
||||
void CeState1to0 (Ptr<TcpSocketState> tcb);
|
||||
|
||||
/**
|
||||
* \brief Updates the value of m_delayedAckReserved
|
||||
*
|
||||
* \param tcb internal congestion state
|
||||
* \param event the congestion window event
|
||||
*/
|
||||
void UpdateAckReserved (Ptr<TcpSocketState> tcb,
|
||||
const TcpSocketState::TcpCAEvent_t event);
|
||||
|
||||
/**
|
||||
* \brief Resets the value of m_ackedBytesEcn, m_ackedBytesTotal and m_nextSeq
|
||||
*
|
||||
* \param tcb internal congestion state
|
||||
*/
|
||||
void Reset (Ptr<TcpSocketState> tcb);
|
||||
|
||||
/**
|
||||
* \brief Sets the value of m_alpha
|
||||
*
|
||||
* \param alpha DCTCP alpha parameter
|
||||
*/
|
||||
void SetDctcpAlpha (double alpha);
|
||||
|
||||
uint32_t m_ackedBytesEcn; //!< Number of acked bytes which are marked
|
||||
uint32_t m_ackedBytesTotal; //!< Total number of acked bytes
|
||||
SequenceNumber32 m_priorRcvNxt; //!< Sequence number of the first missing byte in data
|
||||
bool m_priorRcvNxtFlag; //!< Variable used in setting the value of m_priorRcvNxt for first time
|
||||
double m_alpha; //!< Parameter used to estimate the amount of network congestion
|
||||
SequenceNumber32 m_nextSeq; //!< TCP sequence number threshold for beginning a new observation window
|
||||
bool m_nextSeqFlag; //!< Variable used in setting the value of m_nextSeq for first time
|
||||
bool m_ceState; //!< DCTCP Congestion Experienced state
|
||||
bool m_delayedAckReserved; //!< Delayed Ack state
|
||||
double m_g; //!< Estimation gain
|
||||
};
|
||||
|
||||
} // namespace ns3
|
||||
|
||||
#endif /* TCP_DCTCP_H */
|
||||
|
||||
@@ -2088,7 +2088,7 @@ TcpSocketBase::ProcessSynSent (Ptr<Packet> packet, const TcpHeader& tcpHeader)
|
||||
m_state = SYN_RCVD;
|
||||
m_synCount = m_synRetries;
|
||||
m_tcb->m_rxBuffer->SetNextRxSequence (tcpHeader.GetSequenceNumber () + SequenceNumber32 (1));
|
||||
/* Check if we recieved an ECN SYN packet. Change the ECN state of receiver to ECN_IDLE if the traffic is ECN capable and
|
||||
/* Check if we received an ECN SYN packet. Change the ECN state of receiver to ECN_IDLE if the traffic is ECN capable and
|
||||
* sender has sent ECN SYN packet
|
||||
*/
|
||||
if (m_ecnMode == EcnMode_t::ClassicEcn && (tcpflags & (TcpHeader::CWR | TcpHeader::ECE)) == (TcpHeader::CWR | TcpHeader::ECE))
|
||||
@@ -2806,10 +2806,17 @@ TcpSocketBase::AddSocketTags (const Ptr<Packet> &p) const
|
||||
if (GetIpTos ())
|
||||
{
|
||||
SocketIpTosTag ipTosTag;
|
||||
if (m_tcb->m_ecnState != TcpSocketState::ECN_DISABLED && CheckEcnEct0 (GetIpTos ()))
|
||||
if (m_tcb->m_ecnState != TcpSocketState::ECN_DISABLED && !CheckNoEcn (GetIpTos ()))
|
||||
{
|
||||
// Set ECT(0) if ECN is enabled with the last received ipTos
|
||||
ipTosTag.SetTos (MarkEcnEct0 (GetIpTos ()));
|
||||
// Classic traffic have ECT(0) flags whereas L4S have ECT(1) flags set with the last received ipTos
|
||||
if (m_congestionControl->GetName () == "TcpDctcp")
|
||||
{
|
||||
ipTosTag.SetTos (MarkEcnEct1 (GetIpTos ()));
|
||||
}
|
||||
else
|
||||
{
|
||||
ipTosTag.SetTos (MarkEcnEct0 (GetIpTos ()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2822,9 +2829,22 @@ TcpSocketBase::AddSocketTags (const Ptr<Packet> &p) const
|
||||
{
|
||||
if (m_tcb->m_ecnState != TcpSocketState::ECN_DISABLED && p->GetSize () > 0)
|
||||
{
|
||||
// Set ECT(0) if ECN is enabled and ipTos is 0
|
||||
// Classic traffic have ECT0 flags whereas L4S have ECT1 flags set
|
||||
SocketIpTosTag ipTosTag;
|
||||
ipTosTag.SetTos (MarkEcnEct0 (GetIpTos ()));
|
||||
if (m_congestionControl->GetName () == "TcpDctcp")
|
||||
{
|
||||
ipTosTag.SetTos (MarkEcnEct1 (GetIpTos ()));
|
||||
}
|
||||
else
|
||||
{
|
||||
ipTosTag.SetTos (MarkEcnEct0 (GetIpTos ()));
|
||||
}
|
||||
p->AddPacketTag (ipTosTag);
|
||||
}
|
||||
else if (m_congestionControl->GetName () == "TcpDctcp")
|
||||
{
|
||||
SocketIpTosTag ipTosTag;
|
||||
ipTosTag.SetTos (MarkEcnEct1 (GetIpTos ()));
|
||||
p->AddPacketTag (ipTosTag);
|
||||
}
|
||||
}
|
||||
@@ -2832,10 +2852,17 @@ TcpSocketBase::AddSocketTags (const Ptr<Packet> &p) const
|
||||
if (IsManualIpv6Tclass ())
|
||||
{
|
||||
SocketIpv6TclassTag ipTclassTag;
|
||||
if (m_tcb->m_ecnState != TcpSocketState::ECN_DISABLED && CheckEcnEct0 (GetIpv6Tclass ()))
|
||||
if (m_tcb->m_ecnState != TcpSocketState::ECN_DISABLED && !CheckNoEcn (GetIpv6Tclass ()))
|
||||
{
|
||||
// Set ECT(0) if ECN is enabled with the last received ipTos
|
||||
ipTclassTag.SetTclass (MarkEcnEct0 (GetIpv6Tclass ()));
|
||||
//Classic traffic have ECT0 flags whereas L4S have ECT1 flags set
|
||||
if (m_congestionControl->GetName () == "TcpDctcp")
|
||||
{
|
||||
ipTclassTag.SetTclass (MarkEcnEct1 (GetIpv6Tclass ()));
|
||||
}
|
||||
else
|
||||
{
|
||||
ipTclassTag.SetTclass (MarkEcnEct0 (GetIpv6Tclass ()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2848,7 +2875,20 @@ TcpSocketBase::AddSocketTags (const Ptr<Packet> &p) const
|
||||
{
|
||||
if (m_tcb->m_ecnState != TcpSocketState::ECN_DISABLED && p->GetSize () > 0)
|
||||
{
|
||||
// Set ECT(0) if ECN is enabled and ipTos is 0
|
||||
SocketIpv6TclassTag ipTclassTag;
|
||||
// Classic traffic have ECT0 flags whereas L4S have ECT1 flags set
|
||||
if (m_congestionControl->GetName () == "TcpDctcp")
|
||||
{
|
||||
ipTclassTag.SetTclass (MarkEcnEct1 (GetIpv6Tclass ()));
|
||||
}
|
||||
else
|
||||
{
|
||||
ipTclassTag.SetTclass (MarkEcnEct0 (GetIpv6Tclass ()));
|
||||
}
|
||||
p->AddPacketTag (ipTclassTag);
|
||||
}
|
||||
else if (m_congestionControl->GetName () == "TcpDctcp")
|
||||
{
|
||||
SocketIpv6TclassTag ipTclassTag;
|
||||
ipTclassTag.SetTclass (MarkEcnEct0 (GetIpv6Tclass ()));
|
||||
p->AddPacketTag (ipTclassTag);
|
||||
|
||||
690
src/internet/test/tcp-dctcp-test.cc
Normal file
690
src/internet/test/tcp-dctcp-test.cc
Normal file
@@ -0,0 +1,690 @@
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
||||
/*
|
||||
* Copyright (c) 2017 NITK Surathkal
|
||||
*
|
||||
* 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: Shravya K.S. <shravya.ks0@gmail.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ns3/ipv4.h"
|
||||
#include "ns3/ipv6.h"
|
||||
#include "../model/ipv4-end-point.h"
|
||||
#include "../model/ipv6-end-point.h"
|
||||
#include "tcp-general-test.h"
|
||||
#include "ns3/node.h"
|
||||
#include "ns3/log.h"
|
||||
#include "tcp-error-model.h"
|
||||
#include "ns3/tcp-l4-protocol.h"
|
||||
#include "ns3/tcp-dctcp.h"
|
||||
#include "ns3/tcp-tx-buffer.h"
|
||||
#include "ns3/config.h"
|
||||
|
||||
using namespace ns3;
|
||||
|
||||
NS_LOG_COMPONENT_DEFINE ("TcpDctcpTestSuite");
|
||||
|
||||
/**
|
||||
* \ingroup internet-test
|
||||
* \ingroup tests
|
||||
*
|
||||
* \brief Validates the setting of ECT and ECE codepoints for DCTCP enabled traffic
|
||||
*/
|
||||
class TcpDctcpCodePointsTest : public TcpGeneralTest
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \brief Constructor
|
||||
*
|
||||
* \param testCase Test case number
|
||||
* \param desc Description about the test
|
||||
*/
|
||||
TcpDctcpCodePointsTest (uint8_t testCase, const std::string &desc);
|
||||
|
||||
protected:
|
||||
virtual void Tx (const Ptr<const Packet> p, const TcpHeader&h, SocketWho who);
|
||||
virtual void Rx (const Ptr<const Packet> p, const TcpHeader&h, SocketWho who);
|
||||
virtual Ptr<TcpSocketMsgBase> CreateSenderSocket (Ptr<Node> node);
|
||||
virtual Ptr<TcpSocketMsgBase> CreateReceiverSocket (Ptr<Node> node);
|
||||
void ConfigureProperties ();
|
||||
|
||||
private:
|
||||
uint32_t m_senderSent;
|
||||
uint32_t m_receiverSent;
|
||||
uint32_t m_senderReceived;
|
||||
uint8_t m_testCase;
|
||||
};
|
||||
|
||||
TcpDctcpCodePointsTest::TcpDctcpCodePointsTest (uint8_t testCase, const std::string &desc)
|
||||
: TcpGeneralTest (desc),
|
||||
m_senderSent (0),
|
||||
m_receiverSent (0),
|
||||
m_senderReceived (0),
|
||||
m_testCase (testCase)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TcpDctcpCodePointsTest::Tx (const Ptr<const Packet> p, const TcpHeader &h, SocketWho who)
|
||||
{
|
||||
if (who == SENDER && (m_testCase == 1 || m_testCase == 2))
|
||||
{
|
||||
m_senderSent++;
|
||||
SocketIpTosTag ipTosTag;
|
||||
p->PeekPacketTag (ipTosTag);
|
||||
if (m_testCase == 1)
|
||||
{
|
||||
if (m_senderSent == 1)
|
||||
{
|
||||
NS_TEST_ASSERT_MSG_EQ (unsigned (ipTosTag.GetTos ()), 0x1, "IP TOS should have ECT1 for SYN packet for DCTCP traffic");
|
||||
}
|
||||
if (m_senderSent == 3)
|
||||
{
|
||||
NS_TEST_ASSERT_MSG_EQ (unsigned (ipTosTag.GetTos ()), 0x1, "IP TOS should have ECT1 for data packets for DCTCP traffic");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_senderSent == 1)
|
||||
{
|
||||
NS_TEST_ASSERT_MSG_NE (unsigned (ipTosTag.GetTos ()), 0x1, "IP TOS should not have ECT1 for SYN packet for DCTCP traffic");
|
||||
}
|
||||
if (m_senderSent == 3)
|
||||
{
|
||||
NS_TEST_ASSERT_MSG_EQ (unsigned (ipTosTag.GetTos ()), 0x2, "IP TOS should have ECT0 for data packets for non-DCTCP but ECN enabled traffic");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (who == RECEIVER && (m_testCase == 1 || m_testCase == 2))
|
||||
{
|
||||
m_receiverSent++;
|
||||
SocketIpTosTag ipTosTag;
|
||||
p->PeekPacketTag (ipTosTag);
|
||||
if (m_testCase == 1)
|
||||
{
|
||||
if (m_receiverSent == 1)
|
||||
{
|
||||
NS_TEST_ASSERT_MSG_EQ (unsigned (ipTosTag.GetTos ()), 0x1, "IP TOS should have ECT1 for SYN+ACK packet for DCTCP traffic");
|
||||
}
|
||||
if (m_receiverSent == 2)
|
||||
{
|
||||
NS_TEST_ASSERT_MSG_EQ (unsigned (ipTosTag.GetTos ()), 0x1, "IP TOS should have ECT1 for pure ACK packets for DCTCP traffic");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_receiverSent == 1)
|
||||
{
|
||||
NS_TEST_ASSERT_MSG_NE (unsigned (ipTosTag.GetTos ()), 0x1, "IP TOS should not have ECT0 for SYN+ACK packet for non-DCTCP traffic");
|
||||
NS_TEST_ASSERT_MSG_NE (unsigned (ipTosTag.GetTos ()), 0x2, "IP TOS should not have ECT1 for SYN+ACK packet for non-DCTCP traffic");
|
||||
}
|
||||
if (m_receiverSent == 2)
|
||||
{
|
||||
NS_TEST_ASSERT_MSG_NE (unsigned (ipTosTag.GetTos ()), 0x1, "IP TOS should not have ECT1 for pure ACK packets for non-DCTCP traffic but ECN enabled traffic");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TcpDctcpCodePointsTest::Rx (const Ptr<const Packet> p, const TcpHeader &h, SocketWho who)
|
||||
{
|
||||
if (who == SENDER && m_testCase == 3)
|
||||
{
|
||||
m_senderReceived++;
|
||||
if (m_senderReceived == 2 && m_testCase == 3)
|
||||
{
|
||||
NS_TEST_ASSERT_MSG_NE (((h.GetFlags ()) & TcpHeader::ECE), 0, "The flag ECE should be set in TCP header of the packet sent by the receiver when it receives a packet with CE bit set in IP header");
|
||||
}
|
||||
if (m_senderReceived > 2 && m_testCase == 3)
|
||||
{
|
||||
NS_TEST_ASSERT_MSG_EQ (((h.GetFlags ()) & TcpHeader::ECE), 0, "The flag ECE should be not be set in TCP header of the packet sent by the receiver if it receives a packet without CE bit set in IP header inspite of Sender not sending CWR flags to it");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TcpDctcpCodePointsTest::ConfigureProperties ()
|
||||
{
|
||||
TcpGeneralTest::ConfigureProperties ();
|
||||
SetEcn (SENDER, TcpSocketBase::ClassicEcn);
|
||||
SetEcn (RECEIVER, TcpSocketBase::ClassicEcn);
|
||||
}
|
||||
|
||||
/**
|
||||
* \ingroup internet-test
|
||||
* \ingroup tests
|
||||
*
|
||||
* \brief A TCP socket which sends a data packet with CE flags set for test 3.
|
||||
*
|
||||
* The SendDataPacket function of this class sends data packet numbered 1 with CE flags set and also doesn't set
|
||||
* CWR flags on receipt of ECE flags for test 3. This is done to verify that DCTCP receiver sends ECE only if it
|
||||
* receives CE inspite of sender not sending CWR flags for ECE
|
||||
*
|
||||
*/
|
||||
class TcpDctcpCongestedRouter : public TcpSocketMsgBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \brief Get the type ID.
|
||||
* \return the object TypeId
|
||||
*/
|
||||
static TypeId GetTypeId (void);
|
||||
|
||||
uint32_t m_dataPacketSent;
|
||||
uint8_t m_testCase;
|
||||
|
||||
TcpDctcpCongestedRouter ()
|
||||
: TcpSocketMsgBase ()
|
||||
{
|
||||
m_dataPacketSent = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Constructor.
|
||||
* \param other The object to copy from.
|
||||
*/
|
||||
TcpDctcpCongestedRouter (const TcpDctcpCongestedRouter &other)
|
||||
: TcpSocketMsgBase (other)
|
||||
{
|
||||
}
|
||||
|
||||
void SetTestCase (uint8_t testCase);
|
||||
protected:
|
||||
virtual uint32_t SendDataPacket (SequenceNumber32 seq, uint32_t maxSize, bool withAck);
|
||||
virtual void ReTxTimeout ();
|
||||
Ptr<TcpSocketBase> Fork (void);
|
||||
};
|
||||
|
||||
NS_OBJECT_ENSURE_REGISTERED (TcpDctcpCongestedRouter);
|
||||
|
||||
TypeId
|
||||
TcpDctcpCongestedRouter::GetTypeId (void)
|
||||
{
|
||||
static TypeId tid = TypeId ("ns3::TcpDctcpCongestedRouter")
|
||||
.SetParent<TcpSocketMsgBase> ()
|
||||
.SetGroupName ("Internet")
|
||||
.AddConstructor<TcpDctcpCongestedRouter> ()
|
||||
;
|
||||
return tid;
|
||||
}
|
||||
|
||||
void
|
||||
TcpDctcpCongestedRouter::ReTxTimeout ()
|
||||
{
|
||||
TcpSocketBase::ReTxTimeout ();
|
||||
}
|
||||
|
||||
void
|
||||
TcpDctcpCongestedRouter::SetTestCase (uint8_t testCase)
|
||||
{
|
||||
m_testCase = testCase;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
TcpDctcpCongestedRouter::SendDataPacket (SequenceNumber32 seq, uint32_t maxSize, bool withAck)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << seq << maxSize << withAck);
|
||||
m_dataPacketSent++;
|
||||
|
||||
bool isRetransmission = false;
|
||||
if (seq != m_tcb->m_highTxMark)
|
||||
{
|
||||
isRetransmission = true;
|
||||
}
|
||||
|
||||
Ptr<Packet> p = m_txBuffer->CopyFromSequence (maxSize, seq)->GetPacketCopy ();
|
||||
uint32_t sz = p->GetSize (); // Size of packet
|
||||
uint8_t flags = withAck ? TcpHeader::ACK : 0;
|
||||
uint32_t remainingData = m_txBuffer->SizeFromSequence (seq + SequenceNumber32 (sz));
|
||||
|
||||
if (withAck)
|
||||
{
|
||||
m_delAckEvent.Cancel ();
|
||||
m_delAckCount = 0;
|
||||
}
|
||||
|
||||
// For test 3, we don't send CWR flags on receipt of ECE to check if Receiver sends ECE only when there is CE flags
|
||||
if (m_tcb->m_ecnState == TcpSocketState::ECN_ECE_RCVD && m_ecnEchoSeq.Get () > m_ecnCWRSeq.Get () && !isRetransmission && m_testCase != 3)
|
||||
{
|
||||
NS_LOG_INFO ("Backoff mechanism by reducing CWND by half because we've received ECN Echo");
|
||||
m_congestionControl->ReduceCwnd (m_tcb);
|
||||
m_tcb->m_ssThresh = m_tcb->m_cWnd;
|
||||
flags |= TcpHeader::CWR;
|
||||
m_ecnCWRSeq = seq;
|
||||
m_tcb->m_ecnState = TcpSocketState::ECN_CWR_SENT;
|
||||
NS_LOG_DEBUG (TcpSocketState::EcnStateName[m_tcb->m_ecnState] << " -> ECN_CWR_SENT");
|
||||
NS_LOG_INFO ("CWR flags set");
|
||||
NS_LOG_DEBUG (TcpSocketState::TcpCongStateName[m_tcb->m_congState] << " -> CA_CWR");
|
||||
if (m_tcb->m_congState == TcpSocketState::CA_OPEN)
|
||||
{
|
||||
m_congestionControl->CongestionStateSet (m_tcb, TcpSocketState::CA_CWR);
|
||||
m_tcb->m_congState = TcpSocketState::CA_CWR;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Add tags for each socket option.
|
||||
* Note that currently the socket adds both IPv4 tag and IPv6 tag
|
||||
* if both options are set. Once the packet got to layer three, only
|
||||
* the corresponding tags will be read.
|
||||
*/
|
||||
if (GetIpTos ())
|
||||
{
|
||||
SocketIpTosTag ipTosTag;
|
||||
|
||||
NS_LOG_LOGIC (" ECT bits should not be set on retransmitted packets ");
|
||||
if (m_testCase == 3 && m_dataPacketSent == 1 && !isRetransmission)
|
||||
{
|
||||
ipTosTag.SetTos (GetIpTos () | 0x3);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_tcb->m_ecnState != TcpSocketState::ECN_DISABLED && (GetIpTos () & 0x3) == 0 && !isRetransmission)
|
||||
{
|
||||
ipTosTag.SetTos (GetIpTos () | 0x1);
|
||||
}
|
||||
else
|
||||
{
|
||||
ipTosTag.SetTos (GetIpTos ());
|
||||
}
|
||||
}
|
||||
p->AddPacketTag (ipTosTag);
|
||||
}
|
||||
else
|
||||
{
|
||||
SocketIpTosTag ipTosTag;
|
||||
if (m_testCase == 3 && m_dataPacketSent == 1 && !isRetransmission)
|
||||
{
|
||||
ipTosTag.SetTos (0x3);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_tcb->m_ecnState != TcpSocketState::ECN_DISABLED && !isRetransmission)
|
||||
{
|
||||
ipTosTag.SetTos (0x1);
|
||||
}
|
||||
}
|
||||
p->AddPacketTag (ipTosTag);
|
||||
}
|
||||
|
||||
if (IsManualIpv6Tclass ())
|
||||
{
|
||||
SocketIpv6TclassTag ipTclassTag;
|
||||
if (m_testCase == 3 && m_dataPacketSent == 1 && !isRetransmission )
|
||||
{
|
||||
ipTclassTag.SetTclass (GetIpv6Tclass () | 0x3);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_tcb->m_ecnState != TcpSocketState::ECN_DISABLED && (GetIpv6Tclass () & 0x3) == 0 && !isRetransmission)
|
||||
{
|
||||
ipTclassTag.SetTclass (GetIpv6Tclass () | 0x1);
|
||||
}
|
||||
else
|
||||
{
|
||||
ipTclassTag.SetTclass (GetIpv6Tclass ());
|
||||
}
|
||||
}
|
||||
p->AddPacketTag (ipTclassTag);
|
||||
}
|
||||
else
|
||||
{
|
||||
SocketIpv6TclassTag ipTclassTag;
|
||||
if (m_testCase == 3 && m_dataPacketSent == 1 && !isRetransmission)
|
||||
{
|
||||
ipTclassTag.SetTclass (0x3);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_tcb->m_ecnState != TcpSocketState::ECN_DISABLED && !isRetransmission)
|
||||
{
|
||||
ipTclassTag.SetTclass (0x1);
|
||||
}
|
||||
}
|
||||
p->AddPacketTag (ipTclassTag);
|
||||
}
|
||||
|
||||
if (IsManualIpTtl ())
|
||||
{
|
||||
SocketIpTtlTag ipTtlTag;
|
||||
ipTtlTag.SetTtl (GetIpTtl ());
|
||||
p->AddPacketTag (ipTtlTag);
|
||||
}
|
||||
|
||||
if (IsManualIpv6HopLimit ())
|
||||
{
|
||||
SocketIpv6HopLimitTag ipHopLimitTag;
|
||||
ipHopLimitTag.SetHopLimit (GetIpv6HopLimit ());
|
||||
p->AddPacketTag (ipHopLimitTag);
|
||||
}
|
||||
|
||||
uint8_t priority = GetPriority ();
|
||||
if (priority)
|
||||
{
|
||||
SocketPriorityTag priorityTag;
|
||||
priorityTag.SetPriority (priority);
|
||||
p->ReplacePacketTag (priorityTag);
|
||||
}
|
||||
|
||||
if (m_closeOnEmpty && (remainingData == 0))
|
||||
{
|
||||
flags |= TcpHeader::FIN;
|
||||
if (m_state == ESTABLISHED)
|
||||
{ // On active close: I am the first one to send FIN
|
||||
NS_LOG_DEBUG ("ESTABLISHED -> FIN_WAIT_1");
|
||||
m_state = FIN_WAIT_1;
|
||||
}
|
||||
else if (m_state == CLOSE_WAIT)
|
||||
{ // On passive close: Peer sent me FIN already
|
||||
NS_LOG_DEBUG ("CLOSE_WAIT -> LAST_ACK");
|
||||
m_state = LAST_ACK;
|
||||
}
|
||||
}
|
||||
TcpHeader header;
|
||||
header.SetFlags (flags);
|
||||
header.SetSequenceNumber (seq);
|
||||
header.SetAckNumber (m_tcb->m_rxBuffer->NextRxSequence ());
|
||||
if (m_endPoint)
|
||||
{
|
||||
header.SetSourcePort (m_endPoint->GetLocalPort ());
|
||||
header.SetDestinationPort (m_endPoint->GetPeerPort ());
|
||||
}
|
||||
else
|
||||
{
|
||||
header.SetSourcePort (m_endPoint6->GetLocalPort ());
|
||||
header.SetDestinationPort (m_endPoint6->GetPeerPort ());
|
||||
}
|
||||
header.SetWindowSize (AdvertisedWindowSize ());
|
||||
AddOptions (header);
|
||||
|
||||
if (m_retxEvent.IsExpired ())
|
||||
{
|
||||
// Schedules retransmit timeout. m_rto should be already doubled.
|
||||
|
||||
NS_LOG_LOGIC (this << " SendDataPacket Schedule ReTxTimeout at time " <<
|
||||
Simulator::Now ().GetSeconds () << " to expire at time " <<
|
||||
(Simulator::Now () + m_rto.Get ()).GetSeconds () );
|
||||
m_retxEvent = Simulator::Schedule (m_rto, &TcpDctcpCongestedRouter::ReTxTimeout, this);
|
||||
}
|
||||
|
||||
m_txTrace (p, header, this);
|
||||
|
||||
if (m_endPoint)
|
||||
{
|
||||
m_tcp->SendPacket (p, header, m_endPoint->GetLocalAddress (),
|
||||
m_endPoint->GetPeerAddress (), m_boundnetdevice);
|
||||
NS_LOG_DEBUG ("Send segment of size " << sz << " with remaining data " <<
|
||||
remainingData << " via TcpL4Protocol to " << m_endPoint->GetPeerAddress () <<
|
||||
". Header " << header);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_tcp->SendPacket (p, header, m_endPoint6->GetLocalAddress (),
|
||||
m_endPoint6->GetPeerAddress (), m_boundnetdevice);
|
||||
NS_LOG_DEBUG ("Send segment of size " << sz << " with remaining data " <<
|
||||
remainingData << " via TcpL4Protocol to " << m_endPoint6->GetPeerAddress () <<
|
||||
". Header " << header);
|
||||
}
|
||||
|
||||
UpdateRttHistory (seq, sz, isRetransmission);
|
||||
|
||||
// Notify the application of the data being sent unless this is a retransmit
|
||||
if (seq + sz > m_tcb->m_highTxMark)
|
||||
{
|
||||
Simulator::ScheduleNow (&TcpDctcpCongestedRouter::NotifyDataSent, this,
|
||||
(seq + sz - m_tcb->m_highTxMark.Get ()));
|
||||
}
|
||||
// Update highTxMark
|
||||
m_tcb->m_highTxMark = std::max (seq + sz, m_tcb->m_highTxMark.Get ());
|
||||
return sz;
|
||||
}
|
||||
|
||||
Ptr<TcpSocketBase>
|
||||
TcpDctcpCongestedRouter::Fork (void)
|
||||
{
|
||||
return CopyObject<TcpDctcpCongestedRouter> (this);
|
||||
}
|
||||
|
||||
Ptr<TcpSocketMsgBase>
|
||||
TcpDctcpCodePointsTest::CreateSenderSocket (Ptr<Node> node)
|
||||
{
|
||||
if (m_testCase == 2)
|
||||
{
|
||||
return TcpGeneralTest::CreateSenderSocket (node);
|
||||
}
|
||||
else if (m_testCase == 3)
|
||||
{
|
||||
Ptr<TcpDctcpCongestedRouter> socket = DynamicCast<TcpDctcpCongestedRouter> (
|
||||
CreateSocket (node,
|
||||
TcpDctcpCongestedRouter::GetTypeId (),
|
||||
TcpDctcp::GetTypeId ()));
|
||||
socket->SetTestCase (m_testCase);
|
||||
return socket;
|
||||
}
|
||||
else
|
||||
{
|
||||
return TcpGeneralTest::CreateSocket (node, TcpSocketMsgBase::GetTypeId (), TcpDctcp::GetTypeId ());
|
||||
}
|
||||
}
|
||||
|
||||
Ptr<TcpSocketMsgBase>
|
||||
TcpDctcpCodePointsTest::CreateReceiverSocket (Ptr<Node> node)
|
||||
{
|
||||
if (m_testCase == 2)
|
||||
{
|
||||
return TcpGeneralTest::CreateReceiverSocket (node);
|
||||
}
|
||||
else
|
||||
{
|
||||
return TcpGeneralTest::CreateSocket (node, TcpSocketMsgBase::GetTypeId (), TcpDctcp::GetTypeId ());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \ingroup internet-test
|
||||
* \ingroup tests
|
||||
*
|
||||
* \brief DCTCP should be same as NewReno during slow start
|
||||
*/
|
||||
class TcpDctcpToNewReno : public TestCase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \brief Constructor
|
||||
*
|
||||
* \param cWnd congestion window
|
||||
* \param segmentSize segment size
|
||||
* \param ssThresh slow start threshold
|
||||
* \param segmentsAcked segments acked
|
||||
* \param highTxMark high tx mark
|
||||
* \param lastAckedSeq last acked seq
|
||||
* \param rtt RTT
|
||||
* \param name Name of the test
|
||||
*/
|
||||
TcpDctcpToNewReno (uint32_t cWnd, uint32_t segmentSize, uint32_t ssThresh,
|
||||
uint32_t segmentsAcked, SequenceNumber32 highTxMark,
|
||||
SequenceNumber32 lastAckedSeq, Time rtt, const std::string &name);
|
||||
|
||||
private:
|
||||
virtual void DoRun (void);
|
||||
/** \brief Execute the test
|
||||
*/
|
||||
void ExecuteTest (void);
|
||||
|
||||
uint32_t m_cWnd; //!< cWnd
|
||||
uint32_t m_segmentSize; //!< segment size
|
||||
uint32_t m_segmentsAcked; //!< segments acked
|
||||
uint32_t m_ssThresh; //!< ss thresh
|
||||
Time m_rtt; //!< rtt
|
||||
SequenceNumber32 m_highTxMark; //!< high tx mark
|
||||
SequenceNumber32 m_lastAckedSeq; //!< last acked seq
|
||||
Ptr<TcpSocketState> m_state; //!< state
|
||||
};
|
||||
|
||||
TcpDctcpToNewReno::TcpDctcpToNewReno (uint32_t cWnd, uint32_t segmentSize, uint32_t ssThresh,
|
||||
uint32_t segmentsAcked, SequenceNumber32 highTxMark,
|
||||
SequenceNumber32 lastAckedSeq, Time rtt, const std::string &name)
|
||||
: TestCase (name),
|
||||
m_cWnd (cWnd),
|
||||
m_segmentSize (segmentSize),
|
||||
m_segmentsAcked (segmentsAcked),
|
||||
m_ssThresh (ssThresh),
|
||||
m_rtt (rtt),
|
||||
m_highTxMark (highTxMark),
|
||||
m_lastAckedSeq (lastAckedSeq)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TcpDctcpToNewReno::DoRun ()
|
||||
{
|
||||
Simulator::Schedule (Seconds (0.0), &TcpDctcpToNewReno::ExecuteTest, this);
|
||||
Simulator::Run ();
|
||||
Simulator::Destroy ();
|
||||
}
|
||||
|
||||
void
|
||||
TcpDctcpToNewReno::ExecuteTest ()
|
||||
{
|
||||
m_state = CreateObject <TcpSocketState> ();
|
||||
m_state->m_cWnd = m_cWnd;
|
||||
m_state->m_ssThresh = m_ssThresh;
|
||||
m_state->m_segmentSize = m_segmentSize;
|
||||
m_state->m_highTxMark = m_highTxMark;
|
||||
m_state->m_lastAckedSeq = m_lastAckedSeq;
|
||||
|
||||
Ptr<TcpSocketState> state = CreateObject <TcpSocketState> ();
|
||||
state->m_cWnd = m_cWnd;
|
||||
state->m_ssThresh = m_ssThresh;
|
||||
state->m_segmentSize = m_segmentSize;
|
||||
state->m_highTxMark = m_highTxMark;
|
||||
state->m_lastAckedSeq = m_lastAckedSeq;
|
||||
|
||||
Ptr<TcpDctcp> cong = CreateObject <TcpDctcp> ();
|
||||
cong->IncreaseWindow (m_state, m_segmentsAcked);
|
||||
|
||||
Ptr<TcpNewReno> NewRenoCong = CreateObject <TcpNewReno> ();
|
||||
NewRenoCong->IncreaseWindow (state, m_segmentsAcked);
|
||||
|
||||
NS_TEST_ASSERT_MSG_EQ (m_state->m_cWnd.Get (), state->m_cWnd.Get (),
|
||||
"cWnd has not updated correctly");
|
||||
}
|
||||
/**
|
||||
* \ingroup internet-test
|
||||
* \ingroup tests
|
||||
*
|
||||
* \brief Test to validate cWnd decrement DCTCP
|
||||
*/
|
||||
class TcpDctcpDecrementTest : public TestCase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \brief Constructor
|
||||
*
|
||||
* \param cWnd congestion window
|
||||
* \param segmentSize segment size
|
||||
* \param segmentsAcked segments acked
|
||||
* \param highTxMark high tx mark
|
||||
* \param lastAckedSeq last acked seq
|
||||
* \param rtt RTT
|
||||
* \param name Name of the test
|
||||
*/
|
||||
TcpDctcpDecrementTest (uint32_t cWnd, uint32_t segmentSize, uint32_t segmentsAcked, SequenceNumber32 nextTxSequence,
|
||||
SequenceNumber32 lastAckedSeq, Time rtt, const std::string &name);
|
||||
|
||||
private:
|
||||
virtual void DoRun (void);
|
||||
/** \brief Execute the test
|
||||
*/
|
||||
void ExecuteTest (void);
|
||||
|
||||
uint32_t m_cWnd; //!< cWnd
|
||||
uint32_t m_segmentSize; //!< segment size
|
||||
uint32_t m_segmentsAcked; //!< segments acked
|
||||
Time m_rtt; //!< rtt
|
||||
SequenceNumber32 m_nextTxSequence; //!< next seq num to be sent
|
||||
SequenceNumber32 m_lastAckedSeq; //!< last acked seq
|
||||
Ptr<TcpSocketState> m_state; //!< state
|
||||
};
|
||||
|
||||
TcpDctcpDecrementTest::TcpDctcpDecrementTest (uint32_t cWnd, uint32_t segmentSize, uint32_t segmentsAcked, SequenceNumber32 nextTxSequence,
|
||||
SequenceNumber32 lastAckedSeq, Time rtt, const std::string &name)
|
||||
: TestCase (name),
|
||||
m_cWnd (cWnd),
|
||||
m_segmentSize (segmentSize),
|
||||
m_segmentsAcked (segmentsAcked),
|
||||
m_rtt (rtt),
|
||||
m_nextTxSequence (nextTxSequence),
|
||||
m_lastAckedSeq (lastAckedSeq)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TcpDctcpDecrementTest::DoRun ()
|
||||
{
|
||||
Config::SetDefault ("ns3::TcpDctcp::DctcpAlphaOnInit", DoubleValue (0));
|
||||
Simulator::Schedule (Seconds (0.0), &TcpDctcpDecrementTest::ExecuteTest, this);
|
||||
Simulator::Run ();
|
||||
Simulator::Destroy ();
|
||||
}
|
||||
|
||||
void
|
||||
TcpDctcpDecrementTest::ExecuteTest (void)
|
||||
{
|
||||
m_state = CreateObject <TcpSocketState> ();
|
||||
m_state->m_cWnd = m_cWnd;
|
||||
m_state->m_segmentSize = m_segmentSize;
|
||||
m_state->m_nextTxSequence = m_nextTxSequence;
|
||||
m_state->m_lastAckedSeq = m_lastAckedSeq;
|
||||
|
||||
Ptr<TcpDctcp> cong = CreateObject <TcpDctcp> ();
|
||||
m_state->m_ecnState = TcpSocketState::ECN_IDLE;
|
||||
cong->PktsAcked (m_state, m_segmentsAcked, m_rtt);
|
||||
cong->ReduceCwnd (m_state);
|
||||
NS_TEST_ASSERT_MSG_EQ (m_state->m_cWnd.Get (), m_cWnd,
|
||||
"cWnd has updated correctly");
|
||||
|
||||
m_state->m_ecnState = TcpSocketState::ECN_ECE_RCVD;
|
||||
cong->PktsAcked (m_state, m_segmentsAcked, m_rtt);
|
||||
cong->ReduceCwnd (m_state);
|
||||
|
||||
uint32_t val = (uint32_t)(m_cWnd * (1 - 0.0625 / 2.0));
|
||||
NS_TEST_ASSERT_MSG_EQ (m_state->m_cWnd.Get (), val,
|
||||
"cWnd has updated correctly");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* \ingroup internet-test
|
||||
* \ingroup tests
|
||||
*
|
||||
* \brief TCP DCTCP TestSuite
|
||||
*/
|
||||
class TcpDctcpTestSuite : public TestSuite
|
||||
{
|
||||
public:
|
||||
TcpDctcpTestSuite () : TestSuite ("tcp-dctcp-test", UNIT)
|
||||
{
|
||||
AddTestCase (new TcpDctcpToNewReno (2 * 1446, 1446, 4 * 1446, 2, SequenceNumber32 (4753), SequenceNumber32 (3216), MilliSeconds (100), "DCTCP falls to New Reno for slowstart"), TestCase::QUICK);
|
||||
AddTestCase (new TcpDctcpDecrementTest (4 * 1446, 1446, 2, SequenceNumber32 (3216), SequenceNumber32 (4753), MilliSeconds (100), "DCTCP decrement test"), TestCase::QUICK);
|
||||
AddTestCase (new TcpDctcpCodePointsTest (1, "ECT Test : Check if ECT is set on Syn, Syn+Ack, Ack and Data packets for DCTCP packets"),
|
||||
TestCase::QUICK);
|
||||
AddTestCase (new TcpDctcpCodePointsTest (2, "ECT Test : Check if ECT is not set on Syn, Syn+Ack and Ack but set on Data packets for non-DCTCP but ECN enabled traffic"),TestCase::QUICK);
|
||||
AddTestCase (new TcpDctcpCodePointsTest (3, "ECE Functionality Test: ECE should only be sent by reciever when it receives CE flags"),
|
||||
TestCase::QUICK);
|
||||
}
|
||||
};
|
||||
|
||||
static TcpDctcpTestSuite g_tcpdctcpTest; //!< static var for test initialization
|
||||
@@ -160,6 +160,7 @@ def build(bld):
|
||||
'model/tcp-illinois.cc',
|
||||
'model/tcp-htcp.cc',
|
||||
'model/tcp-lp.cc',
|
||||
'model/tcp-dctcp.cc',
|
||||
'model/tcp-rx-buffer.cc',
|
||||
'model/tcp-tx-buffer.cc',
|
||||
'model/tcp-tx-item.cc',
|
||||
@@ -298,6 +299,7 @@ def build(bld):
|
||||
'test/tcp-close-test.cc',
|
||||
'test/icmp-test.cc',
|
||||
'test/ipv4-deduplication-test.cc',
|
||||
'test/tcp-dctcp-test.cc',
|
||||
]
|
||||
privateheaders = bld(features='ns3privateheader')
|
||||
privateheaders.module = 'internet'
|
||||
@@ -402,6 +404,7 @@ def build(bld):
|
||||
'model/tcp-illinois.h',
|
||||
'model/tcp-htcp.h',
|
||||
'model/tcp-lp.h',
|
||||
'model/tcp-dctcp.h',
|
||||
'model/tcp-ledbat.h',
|
||||
'model/tcp-socket-base.h',
|
||||
'model/tcp-socket-state.h',
|
||||
|
||||
Reference in New Issue
Block a user