From 86755f4175e9149fbede22fa20e005101cdb8ed7 Mon Sep 17 00:00:00 2001 From: Vivek Jain Date: Thu, 9 Apr 2020 22:50:37 +0530 Subject: [PATCH] tcp: Added BBR Congestion control --- src/internet/model/tcp-bbr.cc | 674 +++++++++++++++++++++++++ src/internet/model/tcp-bbr.h | 365 +++++++++++++ src/internet/model/tcp-socket-base.cc | 1 + src/internet/model/tcp-socket-state.cc | 5 +- src/internet/model/tcp-socket-state.h | 2 + src/internet/wscript | 2 + 6 files changed, 1048 insertions(+), 1 deletion(-) create mode 100644 src/internet/model/tcp-bbr.cc create mode 100644 src/internet/model/tcp-bbr.h diff --git a/src/internet/model/tcp-bbr.cc b/src/internet/model/tcp-bbr.cc new file mode 100644 index 000000000..ab4a5f7eb --- /dev/null +++ b/src/internet/model/tcp-bbr.cc @@ -0,0 +1,674 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2018 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 + * + * Authors: Vivek Jain + * Viyom Mittal + * Mohit P. Tahiliani + */ + +#include "tcp-bbr.h" +#include "ns3/log.h" +#include "ns3/tcp-socket-base.h" + +namespace ns3 { + +NS_LOG_COMPONENT_DEFINE ("TcpBbr"); +NS_OBJECT_ENSURE_REGISTERED (TcpBbr); + +const double TcpBbr::PACING_GAIN_CYCLE [] = {5.0 / 4, 3.0 / 4, 1, 1, 1, 1, 1, 1}; + +TypeId +TcpBbr::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::TcpBbr") + .SetParent () + .AddConstructor () + .SetGroupName ("Internet") + .AddAttribute ("HighGain", + "Value of high gain", + DoubleValue (2.89), + MakeDoubleAccessor (&TcpBbr::m_highGain), + MakeDoubleChecker ()) + .AddAttribute ("BwWindowLength", + "Length of bandwidth windowed filter", + UintegerValue (10), + MakeUintegerAccessor (&TcpBbr::m_bandwidthWindowLength), + MakeUintegerChecker ()) + .AddAttribute ("RttWindowLength", + "Length of bandwidth windowed filter", + TimeValue (Seconds (10)), + MakeTimeAccessor (&TcpBbr::m_rtPropFilterLen), + MakeTimeChecker ()) + .AddAttribute ("ProbeRttDuration", + "Length of bandwidth windowed filter", + TimeValue (MilliSeconds (200)), + MakeTimeAccessor (&TcpBbr::m_probeRttDuration), + MakeTimeChecker ()) + ; + return tid; +} + +TcpBbr::TcpBbr () + : TcpCongestionOps () +{ + NS_LOG_FUNCTION (this); + m_uv = CreateObject (); +} + +TcpBbr::TcpBbr (const TcpBbr &sock) + : TcpCongestionOps (sock), + m_bandwidthWindowLength (sock.m_bandwidthWindowLength), + m_pacingGain (sock.m_pacingGain), + m_cWndGain (sock.m_cWndGain), + m_highGain (sock.m_highGain), + m_isPipeFilled (sock.m_isPipeFilled), + m_minPipeCwnd (sock.m_minPipeCwnd), + m_roundCount (sock.m_roundCount), + m_roundStart (sock.m_roundStart), + m_nextRoundDelivered (sock.m_nextRoundDelivered), + m_probeRttDuration (sock.m_probeRttDuration), + m_probeRtPropStamp (sock.m_probeRtPropStamp), + m_probeRttDoneStamp (sock.m_probeRttDoneStamp), + m_probeRttRoundDone (sock.m_probeRttRoundDone), + m_packetConservation (sock.m_packetConservation), + m_priorCwnd (sock.m_priorCwnd), + m_idleRestart (sock.m_idleRestart), + m_targetCWnd (sock.m_targetCWnd), + m_fullBandwidth (sock.m_fullBandwidth), + m_fullBandwidthCount (sock.m_fullBandwidthCount), + m_rtProp (Time::Max ()), + m_sendQuantum (sock.m_sendQuantum), + m_cycleStamp (sock.m_cycleStamp), + m_cycleIndex (sock.m_cycleIndex), + m_rtPropExpired (sock.m_rtPropExpired), + m_rtPropFilterLen (sock.m_rtPropFilterLen), + m_rtPropStamp (sock.m_rtPropStamp), + m_isInitialized (sock.m_isInitialized), + m_delivered (sock.m_delivered), + m_appLimited (sock.m_appLimited), // TODO + m_txItemDelivered (sock.m_txItemDelivered) // TODO +{ + NS_LOG_FUNCTION (this); + m_uv = CreateObject (); +} + +int64_t +TcpBbr::AssignStreams (int64_t stream) +{ + NS_LOG_FUNCTION (this << stream); + m_uv->SetStream (stream); + return 1; +} + +void +TcpBbr::InitRoundCounting () +{ + NS_LOG_FUNCTION (this); + m_nextRoundDelivered = 0; + m_roundStart = false; + m_roundCount = 0; +} + +void +TcpBbr::InitFullPipe () +{ + NS_LOG_FUNCTION (this); + m_isPipeFilled = false; + m_fullBandwidth = 0; + m_fullBandwidthCount = 0; +} + +void +TcpBbr::InitPacingRate (Ptr tcb) +{ + NS_LOG_FUNCTION (this << tcb); + + if (!tcb->m_pacing) + { + NS_LOG_WARN ("BBR must use pacing"); + tcb->m_pacing = true; + } + Time rtt = tcb->m_lastRtt != Time::Max () ? tcb->m_lastRtt.Get () : MilliSeconds (1); + DataRate nominalBandwidth (tcb->m_initialCWnd * tcb->m_segmentSize * 8 / rtt.GetSeconds ()); + tcb->m_currentPacingRate = DataRate (m_pacingGain * nominalBandwidth.GetBitRate ()); +} + +void +TcpBbr::EnterStartup () +{ + NS_LOG_FUNCTION (this); + SetBbrState (BbrMode_t::BBR_STARTUP); + m_pacingGain = m_highGain; + m_cWndGain = m_highGain; +} + +void +TcpBbr::HandleRestartFromIdle (Ptr tcb, const TcpRateOps::TcpRateSample &rs) +{ + NS_LOG_FUNCTION (this << tcb << rs); + if (tcb->m_bytesInFlight.Get () == 0U && rs.m_isAppLimited) + { + m_idleRestart = true; + if (m_state == BbrMode_t::BBR_PROBE_BW) + { + SetPacingRate (tcb, 1); + } + } +} + +void +TcpBbr::SetPacingRate (Ptr tcb, double gain) +{ + NS_LOG_FUNCTION (this << tcb << gain); + DataRate rate (gain * m_maxBwFilter.GetBest ().GetBitRate ()); + rate = std::min (rate, tcb->m_maxPacingRate); + if (m_isPipeFilled || rate > tcb->m_currentPacingRate) + { + tcb->m_currentPacingRate = rate; + } +} + +uint32_t +TcpBbr::InFlight (Ptr tcb, double gain) +{ + NS_LOG_FUNCTION (this << tcb << gain); + if (m_rtProp == Time::Max ()) + { + return tcb->m_initialCWnd * tcb->m_segmentSize; + } + double quanta = 3 * m_sendQuantum; + double estimatedBdp = m_maxBwFilter.GetBest () * m_rtProp / 8.0; + return gain * estimatedBdp + quanta; +} + +void +TcpBbr::AdvanceCyclePhase () +{ + NS_LOG_FUNCTION (this); + m_cycleStamp = Simulator::Now (); + m_cycleIndex = (m_cycleIndex + 1) % GAIN_CYCLE_LENGTH; + m_pacingGain = PACING_GAIN_CYCLE [m_cycleIndex]; +} + +bool +TcpBbr::IsNextCyclePhase (Ptr tcb, const TcpRateOps::TcpRateSample &rs) +{ + NS_LOG_FUNCTION (this << tcb << rs); + bool isFullLength = (Simulator::Now () - m_cycleStamp) > m_rtProp; + if (m_pacingGain == 1) + { + return isFullLength; + } + else if (m_pacingGain > 1) + { + return isFullLength && (rs.m_bytesLoss > 0 || rs.m_priorInFlight >= InFlight (tcb, m_pacingGain)); + } + else + { + return isFullLength || rs.m_priorInFlight <= InFlight (tcb, 1); + } +} + +void +TcpBbr::CheckCyclePhase (Ptr tcb, const TcpRateOps::TcpRateSample &rs) +{ + NS_LOG_FUNCTION (this << tcb << rs); + if (m_state == BbrMode_t::BBR_PROBE_BW && IsNextCyclePhase (tcb, rs)) + { + AdvanceCyclePhase (); + } +} + +void +TcpBbr::CheckFullPipe (const TcpRateOps::TcpRateSample &rs) +{ + NS_LOG_FUNCTION (this << rs); + if (m_isPipeFilled || !m_roundStart || rs.m_isAppLimited) + { + return; + } + + /* Check if Bottleneck bandwidth is still growing*/ + if (m_maxBwFilter.GetBest ().GetBitRate () >= m_fullBandwidth.GetBitRate () * 1.25) + { + m_fullBandwidth = m_maxBwFilter.GetBest (); + m_fullBandwidthCount = 0; + return; + } + + m_fullBandwidthCount++; + if (m_fullBandwidthCount >= 3) + { + NS_LOG_DEBUG ("Pipe filled"); + m_isPipeFilled = true; + } +} + +void +TcpBbr::EnterDrain () +{ + NS_LOG_FUNCTION (this); + SetBbrState (BbrMode_t::BBR_DRAIN); + m_pacingGain = 1.0 / m_highGain; + m_cWndGain = m_highGain; +} + +void +TcpBbr::EnterProbeBW () +{ + NS_LOG_FUNCTION (this); + SetBbrState (BbrMode_t::BBR_PROBE_BW); + m_pacingGain = 1; + m_cWndGain = 2; + m_cycleIndex = GAIN_CYCLE_LENGTH - 1 - (int) m_uv->GetValue (0, 6); + AdvanceCyclePhase (); +} + +void +TcpBbr::CheckDrain (Ptr tcb) +{ + NS_LOG_FUNCTION (this << tcb); + if (m_state == BbrMode_t::BBR_STARTUP && m_isPipeFilled) + { + EnterDrain (); + } + + if (m_state == BbrMode_t::BBR_DRAIN && tcb->m_bytesInFlight <= InFlight (tcb, 1)) + { + EnterProbeBW (); + } +} + +void +TcpBbr::UpdateRTprop (Ptr tcb) +{ + NS_LOG_FUNCTION (this << tcb); + m_rtPropExpired = Simulator::Now () > (m_rtPropStamp + m_rtPropFilterLen); + if (tcb->m_lastRtt >= Seconds (0) && (tcb->m_lastRtt <= m_rtProp || m_rtPropExpired)) + { + m_rtProp = tcb->m_lastRtt; + m_rtPropStamp = Simulator::Now (); + } +} + +void +TcpBbr::EnterProbeRTT () +{ + NS_LOG_FUNCTION (this); + SetBbrState (BbrMode_t::BBR_PROBE_RTT); + m_pacingGain = 1; + m_cWndGain = 1; +} + +void +TcpBbr::SaveCwnd (Ptr tcb) +{ + NS_LOG_FUNCTION (this << tcb); + if (tcb->m_congState != TcpSocketState::CA_RECOVERY && m_state != BbrMode_t::BBR_PROBE_RTT) + { + m_priorCwnd = tcb->m_cWnd; + } + else + { + m_priorCwnd = std::max (m_priorCwnd, tcb->m_cWnd.Get ()); + } +} + +void +TcpBbr::RestoreCwnd (Ptr tcb) +{ + NS_LOG_FUNCTION (this << tcb); + tcb->m_cWnd = std::max (m_priorCwnd, tcb->m_cWnd.Get ()); +} + +void +TcpBbr::ExitProbeRTT () +{ + NS_LOG_FUNCTION (this); + if (m_isPipeFilled) + { + EnterProbeBW (); + } + else + { + EnterStartup (); + } +} + +void +TcpBbr::HandleProbeRTT (Ptr tcb) +{ + NS_LOG_FUNCTION (this << tcb); + m_appLimited = (m_delivered + tcb->m_bytesInFlight.Get ()) ? : 1; + + if (m_probeRttDoneStamp == Seconds (0) && tcb->m_bytesInFlight <= m_minPipeCwnd) + { + m_probeRttDoneStamp = Simulator::Now () + m_probeRttDuration; + m_probeRttRoundDone = false; + m_nextRoundDelivered = m_delivered; + } + else if (m_probeRttDoneStamp != Seconds (0)) + { + if (m_roundStart) + { + m_probeRttRoundDone = true; + } + if (m_probeRttRoundDone && Simulator::Now () > m_probeRttDoneStamp) + { + m_rtPropStamp = Simulator::Now (); + RestoreCwnd (tcb); + ExitProbeRTT (); + } + } +} + +void +TcpBbr::CheckProbeRTT (Ptr tcb) +{ + NS_LOG_FUNCTION (this << tcb); + if (m_state != BbrMode_t::BBR_PROBE_RTT && m_rtPropExpired && !m_idleRestart) + { + EnterProbeRTT (); + SaveCwnd (tcb); + m_probeRttDoneStamp = Seconds (0); + } + + if (m_state == BbrMode_t::BBR_PROBE_RTT) + { + HandleProbeRTT (tcb); + } + + m_idleRestart = false; +} + +void +TcpBbr::SetSendQuantum (Ptr tcb) +{ + NS_LOG_FUNCTION (this << tcb); + m_sendQuantum = 1 * tcb->m_segmentSize; +} + +void +TcpBbr::UpdateTargetCwnd (Ptr tcb) +{ + NS_LOG_FUNCTION (this << tcb); + m_targetCWnd = InFlight (tcb, m_cWndGain); +} + +void +TcpBbr::ModulateCwndForRecovery (Ptr tcb, const TcpRateOps::TcpRateSample &rs) +{ + NS_LOG_FUNCTION (this << tcb << rs); + if ( rs.m_bytesLoss > 0) + { + tcb->m_cWnd = std::max ((int) tcb->m_cWnd.Get () - (int) rs.m_bytesLoss, (int) tcb->m_segmentSize); + } + + if (m_packetConservation) + { + tcb->m_cWnd = std::max (tcb->m_cWnd.Get (), tcb->m_bytesInFlight.Get () + rs.m_ackedSacked); + } +} + +void +TcpBbr::ModulateCwndForProbeRTT (Ptr tcb) +{ + NS_LOG_FUNCTION (this << tcb); + if (m_state == BbrMode_t::BBR_PROBE_RTT) + { + tcb->m_cWnd = std::min (tcb->m_cWnd.Get (), m_minPipeCwnd); + } +} + +void +TcpBbr::SetCwnd (Ptr tcb, const TcpRateOps::TcpRateSample &rs) +{ + NS_LOG_FUNCTION (this << tcb << rs); + UpdateTargetCwnd (tcb); + + if (tcb->m_congState == TcpSocketState::CA_RECOVERY) + { + ModulateCwndForRecovery (tcb, rs); + } + + if (!m_packetConservation) + { + if (m_isPipeFilled) + { + tcb->m_cWnd = std::min (tcb->m_cWnd.Get () + (uint32_t) rs.m_ackedSacked, m_targetCWnd); + } + else if (tcb->m_cWnd < m_targetCWnd || m_delivered < tcb->m_initialCWnd * tcb->m_segmentSize) + { + tcb->m_cWnd = tcb->m_cWnd.Get () + rs.m_ackedSacked; + } + tcb->m_cWnd = std::max (tcb->m_cWnd.Get (), m_minPipeCwnd); + } + ModulateCwndForProbeRTT (tcb); + if (tcb->m_congState == TcpSocketState::CA_RECOVERY) + { + m_packetConservation = false; + } +} + +void +TcpBbr::UpdateRound (Ptr tcb, const TcpRateOps::TcpRateSample &rs) +{ + NS_LOG_FUNCTION (this << tcb << rs); + if (m_txItemDelivered >= m_nextRoundDelivered) + { + m_nextRoundDelivered = m_delivered; + m_roundCount++; + m_roundStart = true; + } + else + { + m_roundStart = false; + } +} + +void +TcpBbr::UpdateBtlBw (Ptr tcb, const TcpRateOps::TcpRateSample &rs) +{ + NS_LOG_FUNCTION (this << tcb << rs); + if (rs.m_deliveryRate == 0) + { + return; + } + + UpdateRound (tcb, rs); + + if (rs.m_deliveryRate >= m_maxBwFilter.GetBest () || !rs.m_isAppLimited) + { + m_maxBwFilter.Update (rs.m_deliveryRate, m_roundCount); + } +} + +void +TcpBbr::UpdateModelAndState (Ptr tcb, const TcpRateOps::TcpRateSample &rs) +{ + NS_LOG_FUNCTION (this << tcb << rs); + UpdateBtlBw (tcb, rs); + CheckCyclePhase (tcb, rs); + CheckFullPipe (rs); + CheckDrain (tcb); + UpdateRTprop (tcb); + CheckProbeRTT (tcb); +} + +void +TcpBbr::UpdateControlParameters (Ptr tcb, const TcpRateOps::TcpRateSample &rs) +{ + NS_LOG_FUNCTION (this << tcb << rs); + SetPacingRate (tcb, m_pacingGain); + SetSendQuantum (tcb); + SetCwnd (tcb, rs); +} + +std::string +TcpBbr::WhichState (BbrMode_t mode) const +{ + switch (mode) + { + case 0: + return "BBR_STARTUP"; + case 1: + return "BBR_DRAIN"; + case 2: + return "BBR_PROBE_BW"; + case 3: + return "BBR_PROBE_RTT"; + } + NS_ASSERT (false); +} + +void +TcpBbr::SetBbrState (BbrMode_t mode) +{ + NS_LOG_FUNCTION (this << mode); + NS_LOG_DEBUG (Simulator::Now () << " Changing from " << WhichState (m_state) << " to " << WhichState (mode)); + m_state = mode; +} + +uint32_t +TcpBbr::GetBbrState () +{ + NS_LOG_FUNCTION (this); + return m_state; +} + +double +TcpBbr::GetCwndGain () +{ + NS_LOG_FUNCTION (this); + return m_cWndGain; +} + +double +TcpBbr::GetPacingGain () +{ + NS_LOG_FUNCTION (this); + return m_pacingGain; +} + +std::string +TcpBbr::GetName () const +{ + return "TcpBbr"; +} + +bool +TcpBbr::HasCongControl () const +{ + NS_LOG_FUNCTION (this); + return true; +} + +void +TcpBbr::CongControl(Ptr tcb, + const TcpRateOps::TcpRateConnection &rc, + const TcpRateOps::TcpRateSample &rs) +{ + NS_LOG_FUNCTION (this << tcb << rs); + m_delivered = rc.m_delivered; + m_txItemDelivered = rc.m_txItemDelivered; + UpdateModelAndState (tcb, rs); + UpdateControlParameters (tcb, rs); +} + +void +TcpBbr::CongestionStateSet (Ptr tcb, + const TcpSocketState::TcpCongState_t newState) +{ + NS_LOG_FUNCTION (this << tcb << newState); + if (newState == TcpSocketState::CA_OPEN && !m_isInitialized) + { + NS_LOG_DEBUG ("CongestionStateSet triggered to CA_OPEN :: " << newState); + m_rtProp = tcb->m_lastRtt.Get () != Time::Max () ? tcb->m_lastRtt.Get () : Time::Max (); + m_rtPropStamp = Simulator::Now (); + m_priorCwnd = tcb->m_initialCWnd * tcb->m_segmentSize; + m_targetCWnd = tcb->m_initialCWnd * tcb->m_segmentSize; + m_minPipeCwnd = 4 * tcb->m_segmentSize; + m_sendQuantum = 1 * tcb->m_segmentSize; + m_maxBwFilter = MaxBandwidthFilter_t (m_bandwidthWindowLength, + DataRate (tcb->m_initialCWnd * tcb->m_segmentSize * 8 / m_rtProp.GetSeconds ()) + , 0); + InitRoundCounting (); + InitFullPipe (); + InitPacingRate (tcb); + EnterStartup (); + m_isInitialized = true; + } + else if (newState == TcpSocketState::CA_LOSS) + { + NS_LOG_DEBUG ("CongestionStateSet triggered to CA_LOSS :: " << newState); + SaveCwnd (tcb); + m_roundStart = true; + } + else if (newState == TcpSocketState::CA_RECOVERY) + { + NS_LOG_DEBUG ("CongestionStateSet triggered to CA_RECOVERY :: " << newState); + SaveCwnd (tcb); + tcb->m_cWnd = tcb->m_bytesInFlight.Get () + std::max (tcb->m_lastAckedSackedBytes, tcb->m_segmentSize); + m_packetConservation = true; + } +} + +void +TcpBbr::CwndEvent (Ptr tcb, + const TcpSocketState::TcpCAEvent_t event) +{ + NS_LOG_FUNCTION (this << tcb << event); + if (event == TcpSocketState::CA_EVENT_COMPLETE_CWR) + { + NS_LOG_DEBUG ("CwndEvent triggered to CA_EVENT_COMPLETE_CWR :: " << event); + m_packetConservation = false; + RestoreCwnd (tcb); + } + else if (event == TcpSocketState::CA_EVENT_TX_START) + { + NS_LOG_DEBUG ("CwndEvent triggered to CA_EVENT_TX_START :: " << event); + if (tcb->m_bytesInFlight.Get () == 0 && m_appLimited) + { + m_idleRestart = true; + if (m_state == BbrMode_t::BBR_PROBE_BW && m_appLimited) + { + SetPacingRate (tcb, 1); + } + } + } +} + +uint32_t +TcpBbr::GetSsThresh (Ptr tcb, uint32_t bytesInFlight) +{ + NS_LOG_FUNCTION (this << tcb << bytesInFlight); + SaveCwnd (tcb); + return tcb->m_initialSsThresh; +} +void +TcpBbr::ReduceCwnd (Ptr tcb) +{ + NS_LOG_FUNCTION (this << tcb); +} + +Ptr +TcpBbr::Fork (void) +{ + return CopyObject (this); +} + +} // namespace ns3 diff --git a/src/internet/model/tcp-bbr.h b/src/internet/model/tcp-bbr.h new file mode 100644 index 000000000..d6225c30f --- /dev/null +++ b/src/internet/model/tcp-bbr.h @@ -0,0 +1,365 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2018 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 + * + * Authors: Vivek Jain + * Viyom Mittal + * Mohit P. Tahiliani + */ + +#ifndef TCPBBR_H +#define TCPBBR_H + +#include "ns3/tcp-congestion-ops.h" +#include "ns3/traced-value.h" +#include "ns3/data-rate.h" +#include "ns3/random-variable-stream.h" +#include "ns3/windowed-filter.h" + +class TcpBbrCheckGainValuesTest; + +namespace ns3 { + +class TcpBbr : public TcpCongestionOps +{ +public: + /** + * \brief The number of phases in the BBR ProbeBW gain cycle. + */ + static const uint8_t GAIN_CYCLE_LENGTH = 8; + + /** + * \brief BBR uses an eight-phase cycle with the given pacing_gain value + * in the BBR ProbeBW gain cycle. + */ + const static double PACING_GAIN_CYCLE []; + /** + * \brief Get the type ID. + * \return the object TypeId + */ + static TypeId GetTypeId (void); + + /** + * \brief Constructor + */ + TcpBbr (); + + /** + * Copy constructor. + * \param sock The socket to copy from. + */ + TcpBbr (const TcpBbr &sock); + + /* BBR has the following modes for deciding how fast to send: */ + typedef enum + { + BBR_STARTUP, /* ramp up sending rate rapidly to fill pipe */ + BBR_DRAIN, /* drain any queue created during startup */ + BBR_PROBE_BW, /* discover, share bw: pace around estimated bw */ + BBR_PROBE_RTT, /* cut inflight to min to probe min_rtt */ + } BbrMode_t; + + typedef WindowedFilter, + uint32_t, + uint32_t> + MaxBandwidthFilter_t; + + /** + * Assign a fixed random variable stream number to the random variables + * used by this model. Return the number of streams (possibly zero) that + * have been assigned. + * + * \param stream first stream index to use + * \return the number of stream indices assigned by this model + */ + virtual int64_t AssignStreams (int64_t stream); + + virtual std::string GetName () const; + virtual bool HasCongControl () const; + virtual void CongControl (Ptr tcb, + const TcpRateOps::TcpRateConnection &rc, + const TcpRateOps::TcpRateSample &rs); + virtual void CongestionStateSet (Ptr tcb, + const TcpSocketState::TcpCongState_t newState); + virtual void CwndEvent (Ptr tcb, + const TcpSocketState::TcpCAEvent_t event); + virtual uint32_t GetSsThresh (Ptr tcb, + uint32_t bytesInFlight); + virtual void ReduceCwnd (Ptr tcb); + virtual Ptr Fork (); + +protected: + /** + * \brief TcpBbrCheckGainValuesTest friend class (for tests). + * \relates TcpBbrCheckGainValuesTest + */ + friend class TcpBbrCheckGainValuesTest; + + /** + * \brief Advances pacing gain using cycle gain algorithm, while in BBR_PROBE_BW state + */ + void AdvanceCyclePhase (); + + /** + * \brief Checks whether to advance pacing gain in BBR_PROBE_BW state, + * and if allowed calls AdvanceCyclePhase () + * \param tcb the socket state. + * \param rs rate sample + */ + void CheckCyclePhase (Ptr tcb, const TcpRateOps::TcpRateSample &rs); + + /** + * \brief Checks whether its time to enter BBR_DRAIN or BBR_PROBE_BW state + * \param tcb the socket state. + */ + void CheckDrain (Ptr tcb); + + /** + * \brief Identifies whether pipe or BDP is already full + * \param rs rate sample + */ + void CheckFullPipe (const TcpRateOps::TcpRateSample &rs); + + /** + * \brief This method handles the steps related to the ProbeRTT state + * \param tcb the socket state. + */ + void CheckProbeRTT (Ptr tcb); + + /** + * \brief Updates variables specific to BBR_DRAIN state + */ + void EnterDrain (); + + /** + * \brief Updates variables specific to BBR_PROBE_BW state + */ + void EnterProbeBW (); + + /** + * \brief Updates variables specific to BBR_PROBE_RTT state + */ + void EnterProbeRTT (); + + /** + * \brief Updates variables specific to BBR_STARTUP state + */ + void EnterStartup (); + + /** + * \brief Called on exiting from BBR_PROBE_RTT state, it eithers invoke EnterProbeBW () or EnterStartup () + */ + void ExitProbeRTT (); + + /** + * \brief Gets BBR state. + * \return returns BBR state. + */ + uint32_t GetBbrState (); + + /** + * \brief Gets current pacing gain. + * \return returns current pacing gain. + */ + double GetPacingGain (); + + /** + * \brief Gets current cwnd gain. + * \return returns current cwnd gain. + */ + double GetCwndGain (); + + /** + * \brief Handles the steps for BBR_PROBE_RTT state. + * \param tcb the socket state. + */ + void HandleProbeRTT (Ptr tcb); + + /** + * \brief Updates pacing rate if socket is restarting from idle state. + * \param tcb the socket state. + * \param rs rate sample + */ + void HandleRestartFromIdle (Ptr tcb, const TcpRateOps::TcpRateSample &rs); + + /** + * \brief Estimates the target value for congestion window + * \param tcb the socket state. + * \param gain cwnd gain + */ + uint32_t InFlight (Ptr tcb, double gain); + + /** + * \brief Intializes the full pipe estimator. + */ + void InitFullPipe (); + + /** + * \brief Intializes the pacing rate. + * \param tcb the socket state. + */ + void InitPacingRate (Ptr tcb); + + /** + * \brief Intializes the round counting related variables. + */ + void InitRoundCounting (); + + /** + * \brief Checks whether to move to next value of pacing gain while in BBR_PROBE_BW. + * \param tcb the socket state. + * \param rs rate sample + * \returns true if want to move to next value otherwise false. + */ + bool IsNextCyclePhase (Ptr tcb, const TcpRateOps::TcpRateSample &rs); + + /** + * \brief Modulates congestion window in BBR_PROBE_RTT. + * \param tcb the socket state + */ + void ModulateCwndForProbeRTT (Ptr tcb); + + /** + * \brief Modulates congestion window in CA_RECOVERY. + * \param tcb the socket state. + * \param rs rate sample + */ + void ModulateCwndForRecovery (Ptr tcb, const TcpRateOps::TcpRateSample &rs); + + /** + * \brief Helper to restore the last-known good congestion window + * \param tcb the socket state. + */ + void RestoreCwnd (Ptr tcb); + + /** + * \brief Helper to remember the last-known good congestion window or + * the latest congestion window unmodulated by loss recovery or ProbeRTT. + * \param tcb the socket state. + */ + void SaveCwnd (Ptr tcb); + + /** + * \brief Updates congestion window based on the network model. + * \param tcb the socket state. + * \param rs rate sample + */ + void SetCwnd (Ptr tcb, const TcpRateOps::TcpRateSample &rs); + + /** + * \brief Updates pacing rate based on network model. + * \param tcb the socket state. + * \param gain pacing gain + */ + void SetPacingRate (Ptr tcb, double gain); + + /** + * \brief Updates send quantum based on the network model. + * \param tcb the socket state. + */ + void SetSendQuantum (Ptr tcb); + + /** + * \brief Updates maximum bottleneck. + * \param tcb the socket state. + * \param rs rate sample + */ + void UpdateBtlBw (Ptr tcb, const TcpRateOps::TcpRateSample &rs); + + /** + * \brief Updates control parameters congestion windowm, pacing rate, send quantum. + * \param tcb the socket state. + * \param rs rate sample + */ + void UpdateControlParameters (Ptr tcb, const TcpRateOps::TcpRateSample &rs); + + /** + * \brief Updates BBR network model (Maximum bandwidth and minimum RTT). + * \param tcb the socket state. + * \param rs rate sample + */ + void UpdateModelAndState (Ptr tcb, const TcpRateOps::TcpRateSample &rs); + + /** + * \brief Updates round counting related variables. + * \param tcb the socket state. + * \param rs rate sample + */ + void UpdateRound (Ptr tcb, const TcpRateOps::TcpRateSample &rs); + + /** + * \brief Updates minimum RTT. + * \param tcb the socket state. + */ + void UpdateRTprop (Ptr tcb); + + /** + * \brief Updates target congestion window. + * \param tcb the socket state. + */ + void UpdateTargetCwnd (Ptr tcb); + + /** + * \brief Sets BBR state. + * \param state BBR state. + */ + void SetBbrState (BbrMode_t state); + + /** + * \brief Maps mode into string. + * \return string translation of mode value. + */ + std::string WhichState (BbrMode_t state) const; + +private: + BbrMode_t m_state {BbrMode_t::BBR_STARTUP}; //!< Current state of BBR state machine + MaxBandwidthFilter_t m_maxBwFilter; //!< Maximum bandwidth filter + uint32_t m_bandwidthWindowLength {0}; //!< A constant specifying the length of the BBR.BtlBw max filter window, default 10 packet-timed round trips. + double m_pacingGain {0}; //!< The dynamic pacing gain factor + double m_cWndGain {0}; //!< The dynamic congestion window gain factor + double m_highGain {0}; //!< A constant specifying highest gain factor, default is 2.89 + bool m_isPipeFilled {false}; //!< A boolean that records whether BBR has filled the pipe + uint32_t m_minPipeCwnd {0}; //!< The minimal congestion window value BBR tries to target, default 4 Segment size + uint32_t m_roundCount {0}; //!< Count of packet-timed round trips + bool m_roundStart {false}; //!< A boolean that BBR sets to true once per packet-timed round trip + uint32_t m_nextRoundDelivered {0}; //!< Denotes the end of a packet-timed round trip + Time m_probeRttDuration {MilliSeconds (200)};//!< A constant specifying the minimum duration for which ProbeRTT state, default 200 millisecs + Time m_probeRtPropStamp {Seconds (0)}; //!< The wall clock time at which the current BBR.RTProp sample was obtained. + Time m_probeRttDoneStamp {Seconds (0)}; //!< Time to exit from BBR_PROBE_RTT state + bool m_probeRttRoundDone {false}; //!< True when it is time to exit BBR_PROBE_RTT + bool m_packetConservation {false}; //!< + uint32_t m_priorCwnd {0}; //!< The last-known good congestion window + bool m_idleRestart {false}; //!< When restarting from idle, set it true + uint32_t m_targetCWnd {0}; //!< Target value for congestion window, adapted to the estimated BDP + DataRate m_fullBandwidth {0}; //!< Value of full bandwidth recorded + uint32_t m_fullBandwidthCount {0}; //!< Count of full bandwidth recorded consistently + Time m_rtProp {Time::Max ()}; //!< Estimated two-way round-trip propagation delay of the path, estimated from the windowed minimum recent round-trip delay sample. + uint32_t m_sendQuantum {0}; //!< The maximum size of a data aggregate scheduled and transmitted together + Time m_cycleStamp {Seconds (0)}; //!< Last time gain cycle updated + uint32_t m_cycleIndex {0}; //!< Current index of gain cycle + bool m_rtPropExpired {false}; //!< A boolean recording whether the BBR.RTprop has expired + Time m_rtPropFilterLen {Seconds (10)}; //!< A constant specifying the length of the RTProp min filter window, default 10 secs. + Time m_rtPropStamp {Seconds (0)}; //!< The wall clock time at which the current BBR.RTProp sample was obtained + bool m_isInitialized {false}; //!< Set to true after first time initializtion variables + Ptr m_uv {nullptr}; //!< Uniform Random Variable + uint64_t m_delivered {0}; //!< The total amount of data in bytes delivered so far + uint32_t m_appLimited {0}; //!< The index of the last transmitted packet marked as application-limited + uint32_t m_txItemDelivered {0}; +}; + +} // namespace ns3 +#endif // TCPBBR_H diff --git a/src/internet/model/tcp-socket-base.cc b/src/internet/model/tcp-socket-base.cc index 925fd4587..4bd83fc6c 100644 --- a/src/internet/model/tcp-socket-base.cc +++ b/src/internet/model/tcp-socket-base.cc @@ -1811,6 +1811,7 @@ TcpSocketBase::ReceivedAck (Ptr packet, const TcpHeader& tcpHeader) m_txBuffer->DiscardUpTo (ackNumber, MakeCallback (&TcpRateOps::SkbDelivered, m_rateOps)); uint32_t currentDelivered = static_cast (m_rateOps->GetConnectionRate ().m_delivered - previousDelivered); + m_tcb->m_lastAckedSackedBytes = currentDelivered; if (m_tcb->m_congState == TcpSocketState::CA_CWR && (ackNumber > m_recover)) { diff --git a/src/internet/model/tcp-socket-state.cc b/src/internet/model/tcp-socket-state.cc index 2102a7f69..6ac3e58cd 100644 --- a/src/internet/model/tcp-socket-state.cc +++ b/src/internet/model/tcp-socket-state.cc @@ -116,7 +116,10 @@ TcpSocketState::TcpSocketState (const TcpSocketState &other) m_bytesInFlight (other.m_bytesInFlight), m_lastRtt (other.m_lastRtt), m_ecnMode (other.m_ecnMode), - m_useEcn (other.m_useEcn) + m_useEcn (other.m_useEcn), + m_ectCodePoint (other.m_ectCodePoint), + m_lastAckedSackedBytes (other.m_lastAckedSackedBytes) + { } diff --git a/src/internet/model/tcp-socket-state.h b/src/internet/model/tcp-socket-state.h index 212d24537..bc07d639b 100644 --- a/src/internet/model/tcp-socket-state.h +++ b/src/internet/model/tcp-socket-state.h @@ -207,6 +207,8 @@ public: EcnCodePoint_t m_ectCodePoint {Ect0}; //!< ECT code point to use + uint32_t m_lastAckedSackedBytes {0}; //!< Last acked and sacked recorded upon receiving last acknowledgment + /** * \brief Get cwnd in segments rather than bytes * diff --git a/src/internet/wscript b/src/internet/wscript index 9945d8bfd..fac9008bf 100644 --- a/src/internet/wscript +++ b/src/internet/wscript @@ -163,6 +163,7 @@ def build(bld): 'model/tcp-htcp.cc', 'model/tcp-lp.cc', 'model/tcp-dctcp.cc', + 'model/tcp-bbr.cc', 'model/tcp-rx-buffer.cc', 'model/tcp-tx-buffer.cc', 'model/tcp-tx-item.cc', @@ -421,6 +422,7 @@ def build(bld): 'model/tcp-lp.h', 'model/tcp-dctcp.h', 'model/windowed-filter.h', + 'model/tcp-bbr.h', 'model/tcp-ledbat.h', 'model/tcp-socket-base.h', 'model/tcp-socket-state.h',