From 5a63ddb268585472b158cc1df40235b925fbbaf6 Mon Sep 17 00:00:00 2001 From: Anh Nguyen Date: Thu, 9 Jun 2016 12:18:15 +0200 Subject: [PATCH] tcp: added TCP YeAH --- RELEASE_NOTES | 1 + examples/tcp/tcp-variants-comparison.cc | 9 +- src/internet/doc/tcp.rst | 35 ++- src/internet/model/tcp-yeah.cc | 345 ++++++++++++++++++++++ src/internet/model/tcp-yeah.h | 189 ++++++++++++ src/internet/test/tcp-yeah-test.cc | 367 ++++++++++++++++++++++++ src/internet/wscript | 3 + 7 files changed, 946 insertions(+), 3 deletions(-) create mode 100644 src/internet/model/tcp-yeah.cc create mode 100644 src/internet/model/tcp-yeah.h create mode 100644 src/internet/test/tcp-yeah-test.cc diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 92bb49cfb..da0ef95be 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -33,6 +33,7 @@ New user-visible features - (internet) Added TCP Scalable congestion control algorithm - (internet) Added TCP Veno congestion control algorithm - (internet) Added TCP Bic congestion control algorithm +- (internet) Added TCP YeAH congestion control algorithm - (network) SocketAddressTag has been removed from the codebase. Users can use RecvFrom (for UDP) or GetPeerName (for TCP) instead. diff --git a/examples/tcp/tcp-variants-comparison.cc b/examples/tcp/tcp-variants-comparison.cc index dde289d91..724eede9e 100644 --- a/examples/tcp/tcp-variants-comparison.cc +++ b/examples/tcp/tcp-variants-comparison.cc @@ -179,7 +179,7 @@ int main (int argc, char *argv[]) CommandLine cmd; cmd.AddValue ("transport_prot", "Transport protocol to use: TcpNewReno, " "TcpHybla, TcpHighSpeed, TcpVegas, TcpScalable, TcpVeno, " - "TcpBic, TcpWestwood, TcpWestwoodPlus ", transport_prot); + "TcpBic, TcpYeah, TcpWestwood, TcpWestwoodPlus ", transport_prot); cmd.AddValue ("error_p", "Packet error rate", error_p); cmd.AddValue ("bandwidth", "Bottleneck bandwidth", bandwidth); cmd.AddValue ("delay", "Bottleneck delay", delay); @@ -254,6 +254,10 @@ int main (int argc, char *argv[]) { Config::SetDefault ("ns3::TcpL4Protocol::SocketType", TypeIdValue (TcpBic::GetTypeId ())); } + else if (transport_prot.compare ("TcpYeah") == 0) + { + Config::SetDefault ("ns3::TcpL4Protocol::SocketType", TypeIdValue (TcpYeah::GetTypeId ())); + } else if (transport_prot.compare ("TcpWestwood") == 0) { // the default protocol type in ns3::TcpWestwood is WESTWOOD Config::SetDefault ("ns3::TcpL4Protocol::SocketType", TypeIdValue (TcpWestwood::GetTypeId ())); @@ -373,7 +377,8 @@ int main (int argc, char *argv[]) || transport_prot.compare ("TcpVegas") == 0 || transport_prot.compare ("TcpVeno") == 0 || transport_prot.compare ("TcpBic") == 0 - || transport_prot.compare ("TcpScalable") == 0) + || transport_prot.compare ("TcpScalable") == 0 + || transport_prot.compare ("TcpYeah") == 0) { Config::SetDefault ("ns3::TcpSocket::SegmentSize", UintegerValue (tcp_adu_size)); BulkSendHelper ftp ("ns3::TcpSocketFactory", Address ()); diff --git a/src/internet/doc/tcp.rst b/src/internet/doc/tcp.rst index a52acba27..b70d04107 100644 --- a/src/internet/doc/tcp.rst +++ b/src/internet/doc/tcp.rst @@ -329,6 +329,38 @@ decrease factor Beta) window size can be used as the new minimum. More informations at: http://ieeexplore.ieee.org/xpl/articleDetails.jsp?arnumber=1354672 +YeAH +^^^^ + +YeAH-TCP (Yet Another HighSpeed TCP) is a heuristic designed to balance various +requirements of a state-of-the-art congestion control algorithm: +1) fully exploit the link capacity of high BDP networks while inducing a small +number of congestion events +2) compete friendly with Reno flows +3) achieve intra and RTT fairness +4) robust to random losses +5) achieve high performance regardless of buffer size + +YeAH operates between 2 modes: Fast and Slow mode. In the Fast mode when the queue +occupancy is small and the network congestion level is low, YeAH increments +its congestion window according to the aggressive STCP rule. When the number of packets +in the queue grows beyond a threshold and the network congestion level is high, YeAH enters +its Slow mode, acting as Reno with a decongestion algorithm. YeAH employs Vegas' mechanism +for calculating the backlog as in Equation (1). The estimation of the network congestion +level is shown in Equation (2). + + Q = (RTT - BaseRTT) (cwnd / RTT) (1) + L = (RTT - BaseRTT) / BaseRTT (2) + +To ensure TCP friendliness, YeAH also implements an algorithm to detect the presence of legacy +Reno flows. Upon the receipt of 3 duplicate ACKs, YeAH decreases its slow start threshold +according to Equation (3) if it's not competing with Reno flows. Otherwise, the ssthresh is +halved as in Reno. + + ssthresh = min{max{cwnd/8, Q}, cwnd/2} + +More information: http://www.csc.lsu.edu/~sjpark/cs7601/4-YeAH_TCP.pdf + Validation ++++++++++ @@ -350,7 +382,8 @@ section below on :ref:`Writing-tcp-tests`. * **tcp-vegas-test:** Unit tests on the Vegas congestion control * **tcp-veno-test:** Unit tests on the Veno congestion control * **tcp-scalable-test:** Unit tests on the Scalable congestion control -* **tcp-bic-test:** Unit tests on the Vegas congestion control +* **tcp-bic-test:** Unit tests on the BIC congestion control +* **tcp-yeah-test:** Unit tests on the YeAH congestion control * **tcp-option:** Unit tests on TCP options * **tcp-pkts-acked-test:** Unit test the number of time that PktsAcked is called * **tcp-rto-test:** Unit test behavior after a RTO timeout occurs diff --git a/src/internet/model/tcp-yeah.cc b/src/internet/model/tcp-yeah.cc new file mode 100644 index 000000000..bbba177fc --- /dev/null +++ b/src/internet/model/tcp-yeah.cc @@ -0,0 +1,345 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2016 ResiliNets, ITTC, University of Kansas + * + * 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: Truc Anh N. Nguyen + * + * James P.G. Sterbenz , director + * ResiliNets Research Group http://wiki.ittc.ku.edu/resilinets + * Information and Telecommunication Technology Center (ITTC) + * and Department of Electrical Engineering and Computer Science + * The University of Kansas Lawrence, KS USA. + */ + +#include "tcp-yeah.h" +#include "ns3/tcp-socket-base.h" +#include "ns3/log.h" + +namespace ns3 { + +NS_LOG_COMPONENT_DEFINE ("TcpYeah"); +NS_OBJECT_ENSURE_REGISTERED (TcpYeah); + + +TypeId +TcpYeah::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::TcpYeah") + .SetParent () + .AddConstructor () + .SetGroupName ("Internet") + .AddAttribute ("Alpha", "Maximum backlog allowed at the bottleneck queue", + UintegerValue (80), + MakeUintegerAccessor (&TcpYeah::m_alpha), + MakeUintegerChecker ()) + .AddAttribute ("Gamma", "Fraction of queue to be removed per RTT", + UintegerValue (1), + MakeUintegerAccessor (&TcpYeah::m_gamma), + MakeUintegerChecker ()) + .AddAttribute ("Delta", "Log minimum fraction of cwnd to be removed on loss", + UintegerValue (3), + MakeUintegerAccessor (&TcpYeah::m_delta), + MakeUintegerChecker ()) + .AddAttribute ("Epsilon", "Log maximum fraction to be removed on early decongestion", + UintegerValue (1), + MakeUintegerAccessor (&TcpYeah::m_epsilon), + MakeUintegerChecker ()) + .AddAttribute ("Phy", "Maximum delta from base", + UintegerValue (8), + MakeUintegerAccessor (&TcpYeah::m_phy), + MakeUintegerChecker ()) + .AddAttribute ("Rho", "Minimum # of consecutive RTT to consider competition on loss", + UintegerValue (16), + MakeUintegerAccessor (&TcpYeah::m_rho), + MakeUintegerChecker ()) + .AddAttribute ("Zeta", "Minimum # of state switches to reset m_renoCount", + UintegerValue (50), + MakeUintegerAccessor (&TcpYeah::m_zeta), + MakeUintegerChecker ()) + .AddAttribute ("StcpAiFactor", "STCP additive increase factor", + UintegerValue (100), + MakeUintegerAccessor (&TcpYeah::m_stcpAiFactor), + MakeUintegerChecker ()) + ; + return tid; +} + +TcpYeah::TcpYeah (void) + : TcpNewReno (), + m_alpha (80), + m_gamma (1), + m_delta (3), + m_epsilon (1), + m_phy (8), + m_rho (16), + m_zeta (50), + m_stcpAiFactor (100), + m_stcp (0), + m_baseRtt (Time::Max ()), + m_minRtt (Time::Max ()), + m_cntRtt (0), + m_doingYeahNow (true), + m_begSndNxt (0), + m_lastQ (0), + m_doingRenoNow (0), + m_renoCount (2), + m_fastCount (0) +{ + NS_LOG_FUNCTION (this); + m_stcp = CreateObject (); + m_stcp->SetAttribute ("AIFactor", (UintegerValue) m_stcpAiFactor); +} + +TcpYeah::TcpYeah (const TcpYeah& sock) + : TcpNewReno (sock), + m_alpha (sock.m_alpha), + m_gamma (sock.m_gamma), + m_delta (sock.m_delta), + m_epsilon (sock.m_epsilon), + m_phy (sock.m_phy), + m_rho (sock.m_rho), + m_zeta (sock.m_zeta), + m_stcpAiFactor (sock.m_stcpAiFactor), + m_baseRtt (sock.m_baseRtt), + m_minRtt (sock.m_minRtt), + m_cntRtt (sock.m_cntRtt), + m_doingYeahNow (sock.m_doingYeahNow), + m_begSndNxt (sock.m_begSndNxt), + m_lastQ (sock.m_lastQ), + m_doingRenoNow (sock.m_doingRenoNow), + m_renoCount (sock.m_renoCount), + m_fastCount (sock.m_fastCount) +{ + NS_LOG_FUNCTION (this); + m_stcp = CopyObject (sock.m_stcp); +} + +TcpYeah::~TcpYeah (void) +{ + NS_LOG_FUNCTION (this); +} + +Ptr +TcpYeah::Fork (void) +{ + return CopyObject (this); +} + +void +TcpYeah::PktsAcked (Ptr tcb, uint32_t segmentsAcked, + const Time& rtt) +{ + NS_LOG_FUNCTION (this << tcb << segmentsAcked << rtt); + + if (rtt.IsZero ()) + { + return; + } + + m_minRtt = std::min (m_minRtt, rtt); + NS_LOG_DEBUG ("Updated m_minRtt = " << m_minRtt.GetMilliSeconds () << " ms"); + + m_baseRtt = std::min (m_baseRtt, rtt); + NS_LOG_DEBUG ("Updated m_baseRtt = " << m_baseRtt.GetMilliSeconds () << " ms"); + + // Update RTT counter + m_cntRtt++; + NS_LOG_DEBUG ("Updated m_cntRtt = " << m_cntRtt); +} + +void +TcpYeah::EnableYeah (const SequenceNumber32 &nextTxSequence) +{ + NS_LOG_FUNCTION (this << nextTxSequence); + + m_doingYeahNow = true; + m_begSndNxt = nextTxSequence; + m_cntRtt = 0; + m_minRtt = Time::Max (); +} + +void +TcpYeah::DisableYeah () +{ + NS_LOG_FUNCTION (this); + + m_doingYeahNow = false; +} + +void +TcpYeah::CongestionStateSet (Ptr tcb, + const TcpSocketState::TcpCongState_t newState) +{ + NS_LOG_FUNCTION (this << tcb << newState); + + if (newState == TcpSocketState::CA_OPEN) + { + EnableYeah (tcb->m_nextTxSequence); + } + else + { + DisableYeah (); + } +} + +void +TcpYeah::IncreaseWindow (Ptr tcb, uint32_t segmentsAcked) +{ + NS_LOG_FUNCTION (this << tcb << segmentsAcked); + + if (tcb->m_cWnd < tcb->m_ssThresh) + { + NS_LOG_LOGIC ("In slow start, invoke NewReno slow start."); + segmentsAcked = TcpNewReno::SlowStart (tcb, segmentsAcked); + } + else if (!m_doingRenoNow) + { // Fast mode + NS_LOG_LOGIC ("In Fast mode, increment cwnd according to STCP rule."); + m_stcp->IncreaseWindow (tcb, segmentsAcked); + NS_LOG_INFO ("In Fast mode, updated to cwnd " << tcb->m_cWnd << + " ssthresh " << tcb->m_ssThresh); + } + else + { // Behave like NewReno + TcpNewReno::CongestionAvoidance (tcb, segmentsAcked); + } + + if (tcb->m_lastAckedSeq >= m_begSndNxt) + { // A YeAH cycle has finished, we do YeAH cwnd adjustment every RTT. + + NS_LOG_LOGIC ("A YeAH cycle has finished, check if enough RTT samples."); + /* + * We perform YeAH calculations only if we got enough RTT samples to + * insure that at least 1 of those samples wasn't from a delayed ACK. + */ + if (m_cntRtt > 2) + { + NS_LOG_LOGIC ("Enough RTT samples to perform YeAH calculations"); + /* + * We have enough RTT samples to perform YeAH algorithm. + * Now we need to determine if we should operate in Fast or Slow mode, + * and if we should execute the precautionary decongestion algorithm. + */ + + uint32_t segCwnd = tcb->GetCwndInSegments (); + + // Calculate the extra number of packets in queue + // Naming convention: minRtt is the minimum RTT of this round, + // baseRtt is the minimum RTT of the entire transmission. + NS_ASSERT (m_minRtt >= m_baseRtt); + Time rttQueue = m_minRtt - m_baseRtt; + + // queue = rttQueue * bw = rttQueue * (cwnd/RTTmin) + double bw = segCwnd / m_minRtt.GetSeconds (); + uint32_t queue = bw * rttQueue.GetSeconds (); + NS_LOG_DEBUG ("Queue backlog = " << queue << + " given by cwnd = " << segCwnd << + ", minRtt = " << m_minRtt.GetMilliSeconds () << + " ms, baseRtt = " << m_baseRtt.GetMilliSeconds () << + " ms"); + + double L = rttQueue.GetSeconds () / m_baseRtt.GetSeconds (); + NS_LOG_DEBUG ("Network congestion level = " << L); + + if (queue > m_alpha || L > (1 / m_phy) ) + { // Slow mode + if (queue > m_alpha && segCwnd > m_renoCount) + { // Precautionary decongestion + NS_LOG_LOGIC ("Execute the precautionary decongestion."); + uint32_t reduction = std::min (queue / m_gamma, segCwnd >> m_epsilon); + segCwnd -= reduction; + segCwnd = std::max (segCwnd, m_renoCount); + tcb->m_cWnd = segCwnd * tcb->m_segmentSize; + tcb->m_ssThresh = tcb->m_cWnd; + + NS_LOG_INFO ("In Slow mode, after precautionary decongestion, " + "updated to cwnd " << tcb->m_cWnd << + " ssthresh " << tcb->m_ssThresh); + } + + if (m_renoCount <= 2) + { + m_renoCount = std::max (segCwnd >> 1, (uint32_t) 2); + } + else + { + m_renoCount++; + } + + m_doingRenoNow = m_doingRenoNow + 1; + NS_LOG_DEBUG ("In Slow mode, updated to m_renoCount = " << + m_renoCount << " m_doingRenoNow = " << m_doingRenoNow); + } + else + { // Fast mode + m_fastCount++; + if (m_fastCount > m_zeta) + { // Reset renoCount + m_renoCount = 2; + m_fastCount = 0; + } + m_doingRenoNow = 0; + NS_LOG_DEBUG ("In Fast mode, updated to m_renoCount = " << + m_renoCount << " m_doingRenoNow = " << m_doingRenoNow); + } + m_lastQ = queue; + } + + // Save the current right edge for next Yeah cycle + m_begSndNxt = tcb->m_nextTxSequence; + + // Reset cntRtt & minRtt + m_cntRtt = 0; + m_minRtt = Time::Max (); + } +} + +std::string +TcpYeah::GetName () const +{ + return "TcpYeah"; +} + +uint32_t +TcpYeah::GetSsThresh (Ptr tcb, + uint32_t bytesInFlight) +{ + NS_LOG_FUNCTION (this << tcb << bytesInFlight); + uint32_t reduction; + uint32_t segBytesInFlight = bytesInFlight / tcb->m_segmentSize; + + if (m_doingRenoNow < m_rho) + { // Not competing with Reno flows + NS_LOG_LOGIC ("Not competing with Reno flows upon loss"); + reduction = m_lastQ; + reduction = std::max (reduction, segBytesInFlight >> m_delta); + reduction = std::min (reduction, std::max (segBytesInFlight >> 1, (uint32_t) 2)); + } + else + { // Competing with Reno flows + NS_LOG_LOGIC ("Competing with Reno flows upon loss"); + reduction = std::max (segBytesInFlight >> 1, (uint32_t) 2); + } + + NS_LOG_INFO ("Reduction amount upon loss = " << reduction); + + m_fastCount = 0; + m_renoCount = std::max (m_renoCount >> 1, (uint32_t) 2); + + return (bytesInFlight - (reduction * tcb->m_segmentSize)); +} + +} // namespace ns3 diff --git a/src/internet/model/tcp-yeah.h b/src/internet/model/tcp-yeah.h new file mode 100644 index 000000000..6e654b763 --- /dev/null +++ b/src/internet/model/tcp-yeah.h @@ -0,0 +1,189 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2016 ResiliNets, ITTC, University of Kansas + * + * 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: Truc Anh N. Nguyen + * + * James P.G. Sterbenz , director + * ResiliNets Research Group http://wiki.ittc.ku.edu/resilinets + * Information and Telecommunication Technology Center (ITTC) + * and Department of Electrical Engineering and Computer Science + * The University of Kansas Lawrence, KS USA. + */ + +#ifndef TCPYEAH_H +#define TCPYEAH_H + +#include "ns3/tcp-congestion-ops.h" +#include "ns3/tcp-scalable.h" + +namespace ns3 { + +/** + * \ingroup tcp + * + * \brief An implementation of TCP YeAH + * + * YeAH-TCP (Yet Another HighSpeed TCP) is a heuristic designed to balance various + * requirements of a state-of-the-art congestion control algorithm: + * 1) fully exploit the link capacity of high BDP networks while inducing a small + * number of congestion events + * 2) compete friendly with Reno flows + * 3) achieve intra and RTT fairness + * 4) robust to random losses + * 5) achieve high performance regardless of buffer size + * + * YeAH operates between 2 modes: Fast and Slow mode. In the Fast mode when the + * queue occupancy is small and the network congestion level is low, YeAH + * increments its congestion window according to the aggressive STCP rule. + * When the number of packets in the queue grows beyond a threshold and the + * network congestion level is high, YeAH enters its Slow mode, acting as Reno + * with a decongestion algorithm. YeAH employs Vegas' mechanism for calculating + * the backlog as in Equation (1). The estimation of the network congestion + * level is shown in Equation (2). + * + * Q = (RTT - BaseRTT) (cwnd / RTT) (1) + * L = (RTT - BaseRTT) / BaseRTT (2) + * + * To ensure TCP friendliness, YeAH also implements an algorithm to detect the + * presence of legacy Reno flows. Upon the receipt of 3 duplicate ACKs, + * YeAH decreases its slow start threshold according to Equation (3) if + * it's not competing with Reno flows. Otherwise, the ssthresh is halved + * as in Reno. + * + * ssthresh = min{max{cwnd/8, Q}, cwnd/2} + * + * More information: http://www.csc.lsu.edu/~sjpark/cs7601/4-YeAH_TCP.pdf + */ + +class TcpYeah : public TcpNewReno +{ +public: + /** + * \brief Get the type ID. + * \return the object TypeId + */ + static TypeId GetTypeId (void); + + /** + * Create an unbound tcp socket. + */ + TcpYeah (void); + + /** + * \brief Copy constructor + * \param sock the object to copy + */ + TcpYeah (const TcpYeah& sock); + virtual ~TcpYeah (void); + + virtual std::string GetName () const; + + /** + * \brief Compute RTTs needed to execute YeAH algorithm + * + * The function filters RTT samples from the last RTT to find + * the current smallest propagation delay + queueing delay (minRtt). + * We take the minimum to avoid the effects of delayed ACKs. + * + * The function also min-filters all RTT measurements seen to find the + * propagation delay (baseRtt). + * + * \param tcb internal congestion state + * \param segementsAcked count of segments ACKed + * \param rtt last RTT + * + */ + virtual void PktsAcked (Ptr tcb, uint32_t segmentsAcked, + const Time& rtt); + + /** + * \brief Enable/disable YeAH algorithm depending on the congestion state + * + * We only start a YeAH cycle when we are in normal congestion state (CA_OPEN state). + * + * \param tcb internal congestion state + * \param newState new congestion state to which the TCP is going to switch + */ + virtual void CongestionStateSet (Ptr tcb, + const TcpSocketState::TcpCongState_t newState); + + /** + * \brief Adjust cwnd following YeAH dual-mode algorithm + * + * \param tcb internal congestion state + * \param segmentsAcked count of segments ACKed + */ + virtual void IncreaseWindow (Ptr tcb, uint32_t segmentsAcked); + + /** + * \brief Get slow start threshold upon the receipt of 3 dupACKs + * + * \param tcb internal congestion state + * \param bytesInFlight number of outstanding bytes + * + * \return the slow start threshold value + */ + virtual uint32_t GetSsThresh (Ptr tcb, + uint32_t bytesInFlight); + + virtual Ptr Fork (); + +protected: +private: + /** + * \brief Enable YeAH algorithm to start taking YeAH samples + * + * YeAH algorithm is enabled in the following situations: + * 1. at the establishment of a connection + * 2. after an RTO + * 3. after fast recovery + * 4. when an idle connection is restarted + * + * \param nextTxSequence Sequence to transmit next + */ + void EnableYeah (const SequenceNumber32 &nextTxSequence); + + /** + * \brief Stop taking YeAH samples + */ + void DisableYeah (); + +private: + uint32_t m_alpha; //!< Maximum backlog allowed at the bottleneck queue; Q_max in the paper + uint32_t m_gamma; //!< Fraction of queue to be removed per RTT when precautionary decongestion executed + uint32_t m_delta; //!< Log minimum fraction of cwnd to be removed on loss + uint32_t m_epsilon; //!< Log maximum fraction to be removed on early decongestion + uint32_t m_phy; //!< Maximum delta from base + uint32_t m_rho; //!< Minimum # of consecutive RTT to consider competition with Reno flows on loss + uint32_t m_zeta; //!< Minimum # of state switches to reset m_renoCount + + uint32_t m_stcpAiFactor; //!< STCP additive increase parameter + Ptr m_stcp; //!< TcpScalable object + Time m_baseRtt; //!< Minimum of all YeAH RTT measurements seen during connection + Time m_minRtt; //!< Minimum of all RTTs measured within last RTT + uint32_t m_cntRtt; //!< # of RTT measurements during last RTT + bool m_doingYeahNow; //!< If true, do YeAH for this RTT + SequenceNumber32 m_begSndNxt; //!< Right edge during last RTT + uint32_t m_lastQ; //!< Last # of packets in the bottleneck queue + uint32_t m_doingRenoNow; //!< # of RTTs in "Slow" mode + uint32_t m_renoCount; //!< Estimated cwnd of competing Reno flow + uint32_t m_fastCount; //!< # of RTTs in "Fast" mode +}; + +} // namespace ns3 + +#endif // TCPYEAH_H diff --git a/src/internet/test/tcp-yeah-test.cc b/src/internet/test/tcp-yeah-test.cc new file mode 100644 index 000000000..fd96aea43 --- /dev/null +++ b/src/internet/test/tcp-yeah-test.cc @@ -0,0 +1,367 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2016 ResiliNets, ITTC, University of Kansas + * + * 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: Truc Anh N. Nguyen + * + * James P.G. Sterbenz , director + * ResiliNets Research Group http://wiki.ittc.ku.edu/resilinets + * Information and Telecommunication Technology Center (ITTC) + * and Department of Electrical Engineering and Computer Science + * The University of Kansas Lawrence, KS USA. + */ + +#include "ns3/test.h" +#include "ns3/log.h" +#include "ns3/tcp-congestion-ops.h" +#include "ns3/tcp-socket-base.h" +#include "ns3/tcp-yeah.h" + +namespace ns3 { + +NS_LOG_COMPONENT_DEFINE ("TcpYeahTestSuite"); + +/** + * \brief Testing TcpYeah additive increase algorithm + */ +class TcpYeahIncrementTest : public TestCase +{ +public: + TcpYeahIncrementTest (uint32_t cWnd, + uint32_t ssThresh, + uint32_t segmentSize, + SequenceNumber32 nextTxSeq, + SequenceNumber32 lastAckedSeq, + uint32_t segmentsAcked, + Time minRtt, + const std::string &name); + +private: + virtual void DoRun (); + void IncreaseWindow (Ptr cong); + + uint32_t m_cWnd; + uint32_t m_ssThresh; + uint32_t m_segmentSize; + SequenceNumber32 m_nextTxSeq; + SequenceNumber32 m_lastAckedSeq; + uint32_t m_segmentsAcked; + Time m_baseRtt; + Time m_minRtt; + uint32_t m_doingRenoNow; + uint32_t m_cntRtt; + uint32_t m_renoCount; + uint32_t m_fastCount; +}; + +TcpYeahIncrementTest::TcpYeahIncrementTest (uint32_t cWnd, + uint32_t ssThresh, + uint32_t segmentSize, + SequenceNumber32 nextTxSeq, + SequenceNumber32 lastAckedSeq, + uint32_t segmentsAcked, + Time minRtt, + const std::string &name) + : TestCase (name), + m_cWnd (cWnd), + m_ssThresh (ssThresh), + m_segmentSize (segmentSize), + m_nextTxSeq (nextTxSeq), + m_lastAckedSeq (lastAckedSeq), + m_segmentsAcked (segmentsAcked), + m_baseRtt (MilliSeconds (100)), + m_minRtt (minRtt), + m_doingRenoNow (0), + m_cntRtt (4), + m_renoCount (2), + m_fastCount (0) +{ +} + +void +TcpYeahIncrementTest::DoRun () +{ + Ptr state = CreateObject (); + state->m_cWnd = m_cWnd; + state->m_ssThresh = m_ssThresh; + state->m_segmentSize = m_segmentSize; + state->m_nextTxSequence = m_nextTxSeq; + state->m_lastAckedSeq = m_lastAckedSeq; + + Ptr cong = CreateObject (); + + // Set baseRtt to 100 ms + cong->PktsAcked (state, m_segmentsAcked, m_baseRtt); + + // Reset YeAH to assign a new value of minRtt + cong->CongestionStateSet (state, TcpSocketState::CA_OPEN); + cong->PktsAcked (state, m_segmentsAcked, m_minRtt); + + // 2 more calls to PktsAcked to increment cntRtt beyond 2 + cong->PktsAcked (state, m_segmentsAcked, m_minRtt); + cong->PktsAcked (state, m_segmentsAcked, m_minRtt); + + cong->IncreaseWindow (state, m_segmentsAcked); + IncreaseWindow (cong); + + NS_TEST_ASSERT_MSG_EQ (state->m_cWnd.Get (), m_cWnd, + "CWnd has not updated correctly"); +} + +void +TcpYeahIncrementTest::IncreaseWindow (Ptr cong) +{ + uint32_t segCwnd = m_cWnd / m_segmentSize; + + if (m_cWnd < m_ssThresh) + { // NewReno slow start + if (m_segmentsAcked >= 1) + { + m_cWnd += m_segmentSize; + m_segmentsAcked -= 1; + NS_LOG_INFO ("In SlowStart, updated to cwnd " << m_cWnd << + " ssthresh " << m_ssThresh); + } + } + else if (!m_doingRenoNow) + { // Fast mode, follow STCP increment rule + UintegerValue aiFactor; + cong->GetAttribute ("StcpAiFactor", aiFactor); + uint32_t w = std::min (segCwnd, (uint32_t) aiFactor.Get ()); + uint32_t delta = m_segmentsAcked / w; + m_cWnd += delta * m_segmentSize; + NS_LOG_INFO ("In Fast mode, updated to cwnd " << m_cWnd << + " ssthresh " << m_ssThresh); + } + else + { // NewReno congestion avoidance + if (m_segmentsAcked > 0) + { + double adder = static_cast (m_segmentSize * m_segmentSize) / m_cWnd; + adder = std::max (1.0, adder); + m_cWnd += static_cast (adder); + NS_LOG_INFO ("In CongAvoid, updated to cwnd " << m_cWnd << + " ssthresh " << m_ssThresh); + } + + } + if (m_lastAckedSeq >= m_nextTxSeq) + { + if (m_cntRtt > 2) + { + /* + * Calculate the extra number of packets in queue + */ + Time rttQueue = m_minRtt - m_baseRtt; + double bw = segCwnd / m_minRtt.GetSeconds (); + uint32_t queue = bw * rttQueue.GetSeconds (); + + // Calculate network congestion level + double L = rttQueue.GetSeconds () / m_baseRtt.GetSeconds (); + + UintegerValue alpha; + cong->GetAttribute ("Alpha", alpha); + UintegerValue phy; + cong->GetAttribute ("Phy", phy); + UintegerValue gamma; + cong->GetAttribute ("Gamma", gamma); + UintegerValue epsilon; + cong->GetAttribute ("Epsilon", epsilon); + UintegerValue zeta; + cong->GetAttribute ("Zeta", zeta); + + if (queue > alpha.Get () || L > (1 / phy.Get ()) ) + { // Slow mode + NS_LOG_INFO ("Enter Slow mode"); + if (queue > alpha.Get () && segCwnd > m_renoCount) + { // Precautionary decongestion + uint32_t reduction = std::min (queue / (uint32_t) gamma.Get (), + segCwnd >> (uint32_t) epsilon.Get ()); + segCwnd -= reduction; + segCwnd = std::max (segCwnd, m_renoCount); + m_cWnd = segCwnd * m_segmentSize; + m_ssThresh = m_cWnd; + NS_LOG_INFO ("In Slow mode, after precautionary decongestion, " + "updated to cwnd " << m_cWnd << " ssthresh " << + m_ssThresh); + } + } + } + } +} + +/** + * \brief Testing TcpYeah multiplicative decrease algorithm + */ +class TcpYeahDecrementTest : public TestCase +{ +public: + TcpYeahDecrementTest (uint32_t cWnd, + uint32_t ssThresh, + uint32_t segmentSize, + SequenceNumber32 nextTxSeq, + SequenceNumber32 lastAckedSeq, + Time minRtt, + UintegerValue rho, + const std::string &name); + +private: + virtual void DoRun (void); + uint32_t CalculateSsThresh (Ptr cong); + + uint32_t m_cWnd; + uint32_t m_ssThresh; + uint32_t m_segmentSize; + uint32_t m_doingRenoNow; + SequenceNumber32 m_nextTxSeq; + SequenceNumber32 m_lastAckedSeq; + Time m_minRtt; + Time m_baseRtt; + uint32_t m_segmentsAcked; + UintegerValue m_rho; +}; + +TcpYeahDecrementTest::TcpYeahDecrementTest (uint32_t cWnd, + uint32_t ssThresh, + uint32_t segmentSize, + SequenceNumber32 nextTxSeq, + SequenceNumber32 lastAckedSeq, + Time minRtt, + UintegerValue rho, + const std::string &name) + : TestCase (name), + m_cWnd (cWnd), + m_ssThresh (ssThresh), + m_segmentSize (segmentSize), + m_doingRenoNow (0), + m_nextTxSeq (nextTxSeq), + m_lastAckedSeq (lastAckedSeq), + m_minRtt (minRtt), + m_baseRtt (MilliSeconds (100)), + m_segmentsAcked (2), + m_rho (rho) +{ +} + +void +TcpYeahDecrementTest::DoRun () +{ + Ptr state = CreateObject (); + state->m_cWnd = m_cWnd; + state->m_nextTxSequence = m_nextTxSeq; + state->m_lastAckedSeq = m_lastAckedSeq; + state->m_segmentSize = m_segmentSize; + state->m_ssThresh = m_ssThresh; + + Ptr cong = CreateObject (); + + // Re-set rho to 1 for this unit test + cong->SetAttribute ("Rho", UintegerValue (m_rho)); + + // Set baseRtt to 100 ms + cong->PktsAcked (state, m_segmentsAcked, m_baseRtt); + + // Set minRtt to a different value + cong->CongestionStateSet (state, TcpSocketState::CA_OPEN); + cong->PktsAcked (state, m_segmentsAcked, m_minRtt); + + // 2 more calls to PktsAcked to increment cntRtt beyond 2 + cong->PktsAcked (state, m_segmentsAcked, m_minRtt); + cong->PktsAcked (state, m_segmentsAcked, m_minRtt); + + // Calculate queue backlog + cong->IncreaseWindow (state, m_segmentsAcked); + + // Now get the value of ssThresh + uint32_t ssThresh = cong->GetSsThresh (state, m_cWnd); + + // Our calculation of ssThresh + uint32_t ssThreshVal = CalculateSsThresh (cong); + + NS_TEST_ASSERT_MSG_EQ (ssThresh, ssThreshVal, + "SsThresh has not updated correctly"); +} + +uint32_t +TcpYeahDecrementTest::CalculateSsThresh (Ptr cong) +{ + // Calculate queue backlog + uint32_t segCwnd = m_cWnd / m_segmentSize; + uint32_t reduction; + + UintegerValue delta; + cong->GetAttribute ("Delta", delta); + + Time rttQueue = m_minRtt - m_baseRtt; + + // queue = rttQueue * bw = rttQueue * (cwnd/RTTmin) + double bw = segCwnd / m_minRtt.GetSeconds (); + uint32_t queue = bw * rttQueue.GetSeconds (); + + NS_LOG_LOGIC ("queue backlog" << queue); + + if (m_doingRenoNow < m_rho.Get ()) + { + reduction = std::max (queue, segCwnd >> delta.Get ()); + reduction = std::min (reduction, std::max (segCwnd >> 1, (uint32_t) 2)); + NS_LOG_INFO ("Reduction amount for yeah upon loss = " << reduction); + } + else + { + reduction = std::max (segCwnd >> 1, (uint32_t) 2); + NS_LOG_INFO ("Reduction amount for reno upon loss = " << reduction); + } + + + return (m_cWnd - (reduction * m_segmentSize)); + +} + + +// ------------------------------------------------------------------- +static class TcpYeahTestSuite : public TestSuite +{ +public: + TcpYeahTestSuite () : TestSuite ("tcp-yeah-test", UNIT) + { + AddTestCase (new TcpYeahIncrementTest (20 * 1446, 25 * 1446, 1446, SequenceNumber32 (2893), + SequenceNumber32 (1447), 1, MilliSeconds (105), + "YeAH test on cWnd when in slow start"), + TestCase::QUICK); + AddTestCase (new TcpYeahIncrementTest (30 * 1446, 25 * 1446, 1446, SequenceNumber32 (2893), + SequenceNumber32 (1447), 30, MilliSeconds (105), + "YeAH test on cWnd when in Fast mode"), + TestCase::QUICK); + AddTestCase (new TcpYeahIncrementTest (40 * 356, 30 * 356, 356, SequenceNumber32 (20761), + SequenceNumber32 (21117), 1, MilliSeconds (120), + "YeAH test on cWnd when in slow mode without precautionary decongestion"), + TestCase::QUICK); + AddTestCase (new TcpYeahIncrementTest (100 * 356, 70 * 356, 356, SequenceNumber32 (20761), + SequenceNumber32 (21117), 1, MilliSeconds (600), + "YeAH test on cWnd when in slow mode with precautionary decongestion"), + TestCase::QUICK); + AddTestCase (new TcpYeahDecrementTest (40 * 1446, 30 * 1446, 1446, SequenceNumber32 (2893), + SequenceNumber32 (7230), MilliSeconds (120), UintegerValue (0), + "YeAH test on ssThresh upon loss while competing with Reno flows"), + TestCase::QUICK); + AddTestCase (new TcpYeahDecrementTest (57 * 1446, 42 * 1446, 1446, SequenceNumber32 (2893), + SequenceNumber32 (7230), MilliSeconds (200), UintegerValue (2), + "YeAH test on ssThresh upon loss while not competing with Reno flows"), + TestCase::QUICK); + } +} g_tcpYeahTest; + +} // namespace ns3 diff --git a/src/internet/wscript b/src/internet/wscript index e401625e4..62492bb81 100644 --- a/src/internet/wscript +++ b/src/internet/wscript @@ -153,6 +153,7 @@ def build(bld): 'model/tcp-scalable.cc', 'model/tcp-veno.cc', 'model/tcp-bic.cc', + 'model/tcp-yeah.cc', 'model/tcp-rx-buffer.cc', 'model/tcp-tx-buffer.cc', 'model/tcp-option.cc', @@ -256,6 +257,7 @@ def build(bld): 'test/tcp-scalable-test.cc', 'test/tcp-veno-test.cc', 'test/tcp-bic-test.cc', + 'test/tcp-yeah-test.cc', 'test/tcp-zero-window-test.cc', 'test/tcp-pkts-acked-test.cc', 'test/tcp-rtt-estimation.cc', @@ -364,6 +366,7 @@ def build(bld): 'model/tcp-scalable.h', 'model/tcp-veno.h', 'model/tcp-bic.h', + 'model/tcp-yeah.h', 'model/tcp-socket-base.h', 'model/tcp-tx-buffer.h', 'model/tcp-rx-buffer.h',