From 2000b34287d8550f766c912504f5cda9103a416d Mon Sep 17 00:00:00 2001 From: Anh Nguyen Date: Thu, 18 Apr 2013 15:57:07 -0400 Subject: [PATCH] Westwood and Westwood+ --- examples/tcp/examples-to-run.py | 1 + examples/tcp/tcp-variants-comparison.cc | 360 +++++++++++++++++++ examples/tcp/wscript | 8 +- src/internet/model/tcp-westwood.cc | 419 ++++++++++++++++++++++ src/internet/model/tcp-westwood.h | 222 ++++++++++++ src/internet/wscript | 10 + src/test/ns3tcp/ns3tcp-loss-test-suite.cc | 34 +- 7 files changed, 1049 insertions(+), 5 deletions(-) create mode 100644 examples/tcp/tcp-variants-comparison.cc create mode 100644 src/internet/model/tcp-westwood.cc create mode 100644 src/internet/model/tcp-westwood.h diff --git a/examples/tcp/examples-to-run.py b/examples/tcp/examples-to-run.py index 0b06193bf..d22490362 100644 --- a/examples/tcp/examples-to-run.py +++ b/examples/tcp/examples-to-run.py @@ -13,6 +13,7 @@ cpp_examples = [ ("tcp-nsc-lfn", "NSC_ENABLED == True", "False"), ("tcp-nsc-zoo", "NSC_ENABLED == True", "False"), ("tcp-star-server", "True", "True"), + ("tcp-variants-comparison", "True", "True"), ] # A list of Python examples to run in order to ensure that they remain diff --git a/examples/tcp/tcp-variants-comparison.cc b/examples/tcp/tcp-variants-comparison.cc new file mode 100644 index 000000000..36b3d9181 --- /dev/null +++ b/examples/tcp/tcp-variants-comparison.cc @@ -0,0 +1,360 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2013 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 + * + * Authors: Justin P. Rohrer, Truc Anh N. Nguyen , Siddharth Gangadhar + * + * 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. + * + * Work supported in part by NSF FIND (Future Internet Design) Program + * under grant CNS-0626918 (Postmodern Internet Architecture), + * NSF grant CNS-1050226 (Multilayer Network Resilience Analysis and Experimentation on GENI), + * US Department of Defense (DoD), and ITTC at The University of Kansas. + * + * “TCP Westwood(+) Protocol Implementation in ns-3” + * Siddharth Gangadhar, Trúc Anh Ngọc Nguyễn , Greeshma Umapathi, and James P.G. Sterbenz, + * ICST SIMUTools Workshop on ns-3 (WNS3), Cannes, France, March 2013 + */ + +#include +#include +#include + +#include "ns3/core-module.h" +#include "ns3/network-module.h" +#include "ns3/internet-module.h" +#include "ns3/point-to-point-module.h" +#include "ns3/applications-module.h" +#include "ns3/error-model.h" +#include "ns3/tcp-header.h" +#include "ns3/udp-header.h" +#include "ns3/enum.h" +#include "ns3/event-id.h" +#include "ns3/flow-monitor-helper.h" +#include "ns3/ipv4-global-routing-helper.h" + +using namespace ns3; + +NS_LOG_COMPONENT_DEFINE ("TcpVariantsComparison"); + +double old_time = 0.0; +EventId output; +Time current = Time::FromInteger(3, Time::S); //Only record cwnd and ssthresh values every 3 seconds +bool first = true; + +static void +OutputTrace () +{ + // *stream->GetStream() << newtime << " " << newval << std::endl; + // old_time = newval; +} + +static void +CwndTracer (Ptrstream, uint32_t oldval, uint32_t newval) +{ + double new_time = Simulator::Now().GetSeconds(); + if (old_time == 0 && first) + { + double mycurrent = current.GetSeconds(); + *stream->GetStream() << new_time << " " << mycurrent << " " << newval << std::endl; + first = false; + output = Simulator::Schedule(current,&OutputTrace); + } + else + { + if (output.IsExpired()) + { + *stream->GetStream() << new_time << " " << newval << std::endl; + output.Cancel(); + output = Simulator::Schedule(current,&OutputTrace); + } + } +} + +static void +SsThreshTracer (Ptrstream, uint32_t oldval, uint32_t newval) +{ + double new_time = Simulator::Now().GetSeconds(); + if (old_time == 0 && first) + { + double mycurrent = current.GetSeconds(); + *stream->GetStream() << new_time << " " << mycurrent << " " << newval << std::endl; + first = false; + output = Simulator::Schedule(current,&OutputTrace); + } + else + { + if (output.IsExpired()) + { + *stream->GetStream() << new_time << " " << newval << std::endl; + output.Cancel(); + output = Simulator::Schedule(current,&OutputTrace); + } + } +} + +static void +TraceCwnd (std::string cwnd_tr_file_name) +{ + AsciiTraceHelper ascii; + if (cwnd_tr_file_name.compare("") == 0) + { + NS_LOG_DEBUG ("No trace file for cwnd provided"); + return; + } + else + { + Ptr stream = ascii.CreateFileStream(cwnd_tr_file_name.c_str()); + Config::ConnectWithoutContext ("/NodeList/1/$ns3::TcpL4Protocol/SocketList/0/CongestionWindow",MakeBoundCallback (&CwndTracer, stream)); + } +} + +static void +TraceSsThresh(std::string ssthresh_tr_file_name) +{ + AsciiTraceHelper ascii; + if (ssthresh_tr_file_name.compare("") == 0) + { + NS_LOG_DEBUG ("No trace file for ssthresh provided"); + return; + } + else + { + Ptr stream = ascii.CreateFileStream(ssthresh_tr_file_name.c_str()); + Config::ConnectWithoutContext ("/NodeList/1/$ns3::TcpL4Protocol/SocketList/0/SlowStartThreshold",MakeBoundCallback (&SsThreshTracer, stream)); + } +} + +int main (int argc, char *argv[]) +{ + std::string transport_prot = "TcpWestwood"; + double error_p = 0.0; + std::string bandwidth = "2Mbps"; + std::string access_bandwidth = "10Mbps"; + std::string access_delay = "45ms"; + bool tracing = false; + std::string tr_file_name = ""; + std::string cwnd_tr_file_name = ""; + std::string ssthresh_tr_file_name = ""; + double data_mbytes = 0; + uint32_t mtu_bytes = 400; + uint16_t num_flows = 1; + float duration = 100; + uint32_t run = 0; + bool flow_monitor = true; + + + CommandLine cmd; + cmd.AddValue("transport_prot", "Transport protocol to use: TcpTahoe, TcpReno, TcpNewReno, TcpWestwood, TcpWestwoodPlus ", transport_prot); + cmd.AddValue("error_p", "Packet error rate", error_p); + cmd.AddValue("bandwidth", "Bottleneck bandwidth", bandwidth); + cmd.AddValue("access_bandwidth", "Access link bandwidth", access_bandwidth); + cmd.AddValue("delay", "Access link delay", access_delay); + cmd.AddValue("tracing", "Flag to enable/disable tracing", tracing); + cmd.AddValue("tr_name", "Name of output trace file", tr_file_name); + cmd.AddValue("cwnd_tr_name", "Name of output trace file", cwnd_tr_file_name); + cmd.AddValue("ssthresh_tr_name", "Name of output trace file", ssthresh_tr_file_name); + cmd.AddValue("data", "Number of Megabytes of data to transmit", data_mbytes); + cmd.AddValue("mtu", "Size of IP packets to send in bytes", mtu_bytes); + cmd.AddValue("num_flows", "Number of flows", num_flows); + cmd.AddValue("duration", "Time to allow flows to run in seconds", duration); + cmd.AddValue("run", "Run index (for setting repeatable seeds)", run); + cmd.AddValue("flow_monitor", "Enable flow monitor", flow_monitor); + cmd.Parse (argc, argv); + + SeedManager::SetSeed(1); + SeedManager::SetRun(run); + + // User may find it convenient to enable logging + //LogComponentEnable("TcpVariantsComparison", LOG_LEVEL_ALL); + //LogComponentEnable("BulkSendApplication", LOG_LEVEL_INFO); + //LogComponentEnable("DropTailQueue", LOG_LEVEL_ALL); + + // Calculate the ADU size + Header* temp_header = new Ipv4Header(); + uint32_t ip_header = temp_header->GetSerializedSize(); + NS_LOG_LOGIC ("IP Header size is: " << ip_header); + delete temp_header; + temp_header = new TcpHeader(); + uint32_t tcp_header = temp_header->GetSerializedSize(); + NS_LOG_LOGIC ("TCP Header size is: " << tcp_header); + delete temp_header; + uint32_t tcp_adu_size = mtu_bytes - (ip_header + tcp_header); + NS_LOG_LOGIC ("TCP ADU size is: " << tcp_adu_size); + + // Set the simulation start and stop time + float start_time = 0.1; + float stop_time = start_time + duration; + + // Select TCP variant + if (transport_prot.compare("TcpTahoe") == 0) + Config::SetDefault("ns3::TcpL4Protocol::SocketType", TypeIdValue (TcpTahoe::GetTypeId())); + else if (transport_prot.compare("TcpReno") == 0) + Config::SetDefault("ns3::TcpL4Protocol::SocketType", TypeIdValue (TcpReno::GetTypeId())); + else if (transport_prot.compare("TcpNewReno") == 0) + Config::SetDefault("ns3::TcpL4Protocol::SocketType", TypeIdValue (TcpNewReno::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())); + Config::SetDefault("ns3::TcpWestwood::FilterType", EnumValue(TcpWestwood::TUSTIN)); + } + else if (transport_prot.compare("TcpWestwoodPlus") == 0) + { + Config::SetDefault("ns3::TcpL4Protocol::SocketType", TypeIdValue (TcpWestwood::GetTypeId())); + Config::SetDefault("ns3::TcpWestwood::ProtocolType", EnumValue(TcpWestwood::WESTWOODPLUS)); + Config::SetDefault("ns3::TcpWestwood::FilterType", EnumValue(TcpWestwood::TUSTIN)); + } + else + { + NS_LOG_DEBUG ("Invalid TCP version"); + exit (1); + } + + // Create gateways, sources, and sinks + NodeContainer gateways; + gateways.Create (1); + NodeContainer sources; + sources.Create(num_flows); + NodeContainer sinks; + sinks.Create(num_flows); + + // Configure the error model + // Here we use RateErrorModel with packet error rate + Ptr uv = CreateObject(); + uv->SetStream (50); + RateErrorModel error_model; + error_model.SetRandomVariable(uv); + error_model.SetUnit(RateErrorModel::ERROR_UNIT_PACKET); + error_model.SetRate(error_p); + + PointToPointHelper UnReLink; + UnReLink.SetDeviceAttribute ("DataRate", StringValue (bandwidth)); + UnReLink.SetChannelAttribute ("Delay", StringValue ("0.01ms")); + UnReLink.SetDeviceAttribute ("ReceiveErrorModel", PointerValue (&error_model)); + + + InternetStackHelper stack; + stack.InstallAll (); + + Ipv4AddressHelper address; + address.SetBase ("10.0.0.0", "255.255.255.0"); + + // Configure the sources and sinks net devices + // and the channels between the sources/sinks and the gateways + PointToPointHelper LocalLink; + LocalLink.SetDeviceAttribute ("DataRate", StringValue (access_bandwidth)); + LocalLink.SetChannelAttribute ("Delay", StringValue (access_delay)); + Ipv4InterfaceContainer sink_interfaces; + for (int i=0; i ascii_wrap; + if (tr_file_name.compare("") == 0) + { + NS_LOG_DEBUG ("No trace file provided"); + exit (1); + } + else + { + ascii.open (tr_file_name.c_str()); + ascii_wrap = new OutputStreamWrapper(tr_file_name.c_str(), std::ios::out); + } + + stack.EnableAsciiIpv4All (ascii_wrap); + + Simulator::Schedule(Seconds(0.00001), &TraceCwnd, cwnd_tr_file_name); + Simulator::Schedule(Seconds(0.00001), &TraceSsThresh, ssthresh_tr_file_name); + } + + UnReLink.EnablePcapAll("TcpVariantsComparison", true); + LocalLink.EnablePcapAll("TcpVariantsComparison", true); + + // Flow monitor + Ptr flowMonitor; + if (flow_monitor) + { + FlowMonitorHelper flowHelper; + flowMonitor = flowHelper.InstallAll(); + } + + Simulator::Stop (Seconds(stop_time)); + Simulator::Run (); + + if (flow_monitor) + { + flowMonitor->SerializeToXmlFile("TcpVariantsComparison.flowmonitor", true, true); + } + + Simulator::Destroy (); + return 0; +} diff --git a/examples/tcp/wscript b/examples/tcp/wscript index 065209464..590f38311 100644 --- a/examples/tcp/wscript +++ b/examples/tcp/wscript @@ -26,6 +26,12 @@ def build(bld): obj.source = 'tcp-bulk-send.cc' obj = bld.create_ns3_program('tcp-nsc-comparison', - ['point-to-point', 'internet', 'applications', 'flow-monitor']) + ['point-to-point', 'internet', 'applications']) obj.source = 'tcp-nsc-comparison.cc' + + obj = bld.create_ns3_program('tcp-variants-comparison', + ['point-to-point', 'internet', 'applications', 'flow-monitor']) + + obj.source = 'tcp-variants-comparison.cc' + diff --git a/src/internet/model/tcp-westwood.cc b/src/internet/model/tcp-westwood.cc new file mode 100644 index 000000000..6c0d7aa9d --- /dev/null +++ b/src/internet/model/tcp-westwood.cc @@ -0,0 +1,419 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2013 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 + * + * Authors: Siddharth Gangadhar , Truc Anh N. Nguyen , + * and Greeshma Umapathi + * + * 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. + * + * Work supported in part by NSF FIND (Future Internet Design) Program + * under grant CNS-0626918 (Postmodern Internet Architecture), + * NSF grant CNS-1050226 (Multilayer Network Resilience Analysis and Experimentation on GENI), + * US Department of Defense (DoD), and ITTC at The University of Kansas. + */ + +#define NS_LOG_APPEND_CONTEXT \ + if (m_node) { std::clog << Simulator::Now ().GetSeconds () << " [node " << m_node->GetId () << "] "; } + +#include "tcp-westwood.h" +#include "ns3/log.h" +#include "ns3/trace-source-accessor.h" +#include "ns3/simulator.h" +#include "ns3/abort.h" +#include "ns3/node.h" +#include "ns3/sequence-number.h" +#include "rtt-estimator.h" + +NS_LOG_COMPONENT_DEFINE("TcpWestwood"); + +namespace ns3 { + +NS_OBJECT_ENSURE_REGISTERED(TcpWestwood); + +TypeId +TcpWestwood::GetTypeId (void) +{ + static TypeId tid = TypeId("ns3::TcpWestwood") + .SetParent() + .AddConstructor() + .AddTraceSource("CongestionWindow", "The TCP connection's congestion window", + MakeTraceSourceAccessor(&TcpWestwood::m_cWnd)) + .AddAttribute("FilterType", "Use this to choose no filter or Tustin's approximation filter", + EnumValue(TcpWestwood::TUSTIN), MakeEnumAccessor(&TcpWestwood::m_fType), + MakeEnumChecker(TcpWestwood::NONE, "None", TcpWestwood::TUSTIN, "Tustin")) + .AddAttribute("ProtocolType", "Use this to let the code run as Westwood or WestwoodPlus", + EnumValue(TcpWestwood::WESTWOOD), + MakeEnumAccessor(&TcpWestwood::m_pType), + MakeEnumChecker(TcpWestwood::WESTWOOD, "Westwood",TcpWestwood::WESTWOODPLUS, "WestwoodPlus")) + .AddTraceSource("EstimatedBW", "The estimated bandwidth", + MakeTraceSourceAccessor(&TcpWestwood::m_currentBW)); + return tid; +} + +TcpWestwood::TcpWestwood (void) : + m_inFastRec(false), + m_currentBW(0), + m_lastSampleBW(0), + m_lastBW(0), + m_minRtt(0), + m_lastAck(0), + m_prevAckNo(0), + m_accountedFor(0), + m_ackedSegments(0), + m_IsCount(false) +{ + NS_LOG_FUNCTION (this); +} + +TcpWestwood::TcpWestwood (const TcpWestwood& sock) : + TcpSocketBase(sock), + m_cWnd(sock.m_cWnd), + m_ssThresh(sock.m_ssThresh), + m_initialCWnd(sock.m_initialCWnd), + m_inFastRec(false), + m_currentBW(sock.m_currentBW), + m_lastSampleBW(sock.m_lastSampleBW), + m_lastBW(sock.m_lastBW), + m_minRtt(sock.m_minRtt), + m_lastAck(sock.m_lastAck), + m_prevAckNo(sock.m_prevAckNo), + m_accountedFor(sock.m_accountedFor) +{ + NS_LOG_FUNCTION (this); + NS_LOG_LOGIC ("Invoked the copy constructor"); + NS_LOG_INFO ("m_minRtt at copy constructor" << m_minRtt); +} + +TcpWestwood::~TcpWestwood (void) +{ +} + +int +TcpWestwood::Listen (void) +{ + NS_LOG_FUNCTION (this); + InitializeCwnd(); + return TcpSocketBase::Listen(); +} + +int +TcpWestwood::Connect (const Address & address) +{ + NS_LOG_FUNCTION (this << address); + InitializeCwnd(); + return TcpSocketBase::Connect(address); +} + +uint32_t +TcpWestwood::Window (void) +{ + NS_LOG_FUNCTION (this); + return std::min (m_rWnd.Get (), m_cWnd.Get ()); +} + +Ptr +TcpWestwood::Fork (void) +{ + NS_LOG_FUNCTION (this); + return CopyObject(this); +} + +void +TcpWestwood::NewAck (const SequenceNumber32& seq) +{ // Same as Reno + NS_LOG_FUNCTION (this << seq); + NS_LOG_LOGIC ("TcpWestwood receieved ACK for seq " << seq << + " cwnd " << m_cWnd << + " ssthresh " << m_ssThresh); + + // Check for exit condition of fast recovery + if (m_inFastRec) + {// First new ACK after fast recovery, reset cwnd as in Reno + m_cWnd = m_ssThresh; + m_inFastRec = false; + NS_LOG_INFO ("Reset cwnd to " << m_cWnd); + }; + + // Increase of cwnd based on current phase (slow start or congestion avoidance) + if (m_cWnd < m_ssThresh) + { // Slow start mode, add one segSize to cWnd as in Reno + m_cWnd += m_segmentSize; + NS_LOG_INFO ("In SlowStart, updated to cwnd " << m_cWnd << " ssthresh " << m_ssThresh); + } + else + { // Congestion avoidance mode, increase by (segSize*segSize)/cwnd as in Reno + double adder = static_cast (m_segmentSize * m_segmentSize) / m_cWnd.Get(); + adder = std::max(1.0, adder); + m_cWnd += static_cast(adder); + NS_LOG_INFO ("In CongAvoid, updated to cwnd " << m_cWnd << " ssthresh " << m_ssThresh); + } + + // Complete newAck processing + TcpSocketBase::NewAck(seq); +} + +void +TcpWestwood::ReceivedAck (Ptr packet, const TcpHeader& tcpHeader) +{ + NS_LOG_FUNCTION (this); + int acked = 0; + if ((0 != (tcpHeader.GetFlags () & TcpHeader::ACK)) && tcpHeader.GetAckNumber() >= m_prevAckNo) + {// It is a duplicate ACK or a new ACK. Old ACK is ignored. + if (m_pType == TcpWestwood::WESTWOOD) + {// For Westwood, calculate the number of ACKed segments and estimate the BW + acked = CountAck (tcpHeader); + EstimateBW (acked, tcpHeader, Time(0)); + } + else if (m_pType == TcpWestwood::WESTWOODPLUS) + {// For Weswood+, calculate the number of ACKed segments and update m_ackedSegments + if (m_IsCount) + { + acked = CountAck (tcpHeader); + UpdateAckedSegments (acked); + } + } + } + + TcpSocketBase::ReceivedAck (packet, tcpHeader); +} + +void +TcpWestwood::EstimateBW (int acked, const TcpHeader& tcpHeader, Time rtt) +{ + NS_LOG_FUNCTION (this); + if (m_pType == TcpWestwood::WESTWOOD) + { + // Get the time when the current ACK is received + double currentAck = static_cast (Simulator::Now().GetSeconds()); + // Calculate the BW + m_currentBW = acked * m_segmentSize / (currentAck - m_lastAck); + // Update the last ACK time + m_lastAck = currentAck; + } + else if (m_pType == TcpWestwood::WESTWOODPLUS) + { + // Calculate the BW + m_currentBW = m_ackedSegments * m_segmentSize / rtt.GetSeconds(); + // Reset m_ackedSegments and m_IsCount for the next sampling + m_ackedSegments = 0; + m_IsCount = false; + } + + // Filter the BW sample + Filtering(); +} + +int +TcpWestwood::CountAck (const TcpHeader& tcpHeader) +{ + NS_LOG_FUNCTION (this); + + // Calculate the number of acknowledged segments based on the received ACK number + int cumul_ack = (tcpHeader.GetAckNumber() - m_prevAckNo) / m_segmentSize; + + if (cumul_ack == 0) + {// A DUPACK counts for 1 segment delivered successfully + m_accountedFor++; + cumul_ack = 1; + } + if (cumul_ack > 1) + {// A delayed ACK or a cumulative ACK after a retransmission + // Check how much new data it ACKs + if (m_accountedFor >= cumul_ack) + { + m_accountedFor -= cumul_ack; + cumul_ack = 1; + } + else if (m_accountedFor < cumul_ack) + { + cumul_ack -= m_accountedFor; + m_accountedFor = 0; + } + } + + // Update the previous ACK number + m_prevAckNo = tcpHeader.GetAckNumber(); + + return cumul_ack; +} + +void +TcpWestwood::UpdateAckedSegments (int acked) +{ + m_ackedSegments += acked; +} + +void +TcpWestwood::DupAck (const TcpHeader& header, uint32_t count) +{ + NS_LOG_FUNCTION (this << count << m_cWnd); + + if (count == 3 && !m_inFastRec) + {// Triple duplicate ACK triggers fast retransmit + // Adjust cwnd and ssthresh based on the estimated BW + m_ssThresh = m_currentBW * static_cast (m_minRtt.GetSeconds()); + if (m_cWnd > m_ssThresh) + { + m_cWnd = m_ssThresh; + } + m_inFastRec = true; + NS_LOG_INFO ("Triple dupack. Enter fast recovery mode. Reset cwnd to " << m_cWnd <<", ssthresh to " << m_ssThresh); + DoRetransmit (); + } + else if (m_inFastRec) + {// Increase cwnd for every additional DUPACK as in Reno + m_cWnd += m_segmentSize; + NS_LOG_INFO ("Dupack in fast recovery mode. Increase cwnd to " << m_cWnd); + SendPendingData (m_connected); + } +} + +void +TcpWestwood::Retransmit (void) +{ + NS_LOG_FUNCTION (this); + NS_LOG_LOGIC (this << " ReTxTimeout Expired at time " << Simulator::Now ().GetSeconds ()); + m_inFastRec = false; + + // If erroneous timeout in closed/timed-wait state, just return + if (m_state == CLOSED || m_state == TIME_WAIT) + return; + // If all data are received, just return + if (m_txBuffer.HeadSequence() >= m_nextTxSequence) + return; + + // Upon an RTO, adjust cwnd and ssthresh based on the estimated BW + m_ssThresh = std::max (static_cast (2 * m_segmentSize), m_currentBW.Get() * static_cast (m_minRtt.GetSeconds())); + m_cWnd = m_segmentSize; + + // Restart from highest ACK + m_nextTxSequence = m_txBuffer.HeadSequence(); + NS_LOG_INFO ("RTO. Reset cwnd to " << m_cWnd << + ", ssthresh to " << m_ssThresh << ", restart from seqnum " << m_nextTxSequence); + + // Double the next RTO + m_rtt->IncreaseMultiplier(); + + // Retransmit the packet + DoRetransmit(); +} + +void +TcpWestwood::EstimateRtt (const TcpHeader& tcpHeader) +{ + NS_LOG_FUNCTION_NOARGS (); + + // Calculate m_lastRtt + TcpSocketBase::EstimateRtt (tcpHeader); + + // Update minRtt + if (m_minRtt == 0) + { + m_minRtt = m_lastRtt; + } + else + { + if (m_lastRtt < m_minRtt) + { + m_minRtt = m_lastRtt; + } + } + + // For Westwood+, start running a clock on the currently estimated RTT if possible + // to trigger a new BW sampling event + if (m_pType == TcpWestwood::WESTWOODPLUS) + { + if(m_lastRtt != 0 && m_state == ESTABLISHED && !m_IsCount) + { + m_IsCount = true; + m_bwEstimateEvent.Cancel(); + m_bwEstimateEvent = Simulator::Schedule (m_lastRtt, &TcpWestwood::EstimateBW,this,m_ackedSegments,tcpHeader,m_lastRtt); + } + } +} + +void +TcpWestwood::Filtering () +{ + NS_LOG_FUNCTION (this); + + double alpha = 0.9; + + if (m_fType == TcpWestwood::NONE) + { + } + else if (m_fType == TcpWestwood::TUSTIN) + { + double sample_bwe = m_currentBW; + m_currentBW = (alpha * m_lastBW) + ((1 - alpha) * ((sample_bwe + m_lastSampleBW) / 2)); + m_lastSampleBW = sample_bwe; + m_lastBW = m_currentBW; + } +} + +void +TcpWestwood::SetSegSize (uint32_t size) +{ + NS_ABORT_MSG_UNLESS(m_state == CLOSED, "TcpWestwood::SetSegSize() cannot change segment size after connection started."); + m_segmentSize = size; +} + +void +TcpWestwood::SetSSThresh (uint32_t threshold) +{ + NS_LOG_FUNCTION (this); + m_ssThresh = threshold; +} + +uint32_t +TcpWestwood::GetSSThresh (void) const +{ + NS_LOG_FUNCTION (this); + return m_ssThresh; +} + +void +TcpWestwood::SetInitialCwnd (uint32_t cwnd) +{ + NS_ABORT_MSG_UNLESS(m_state == CLOSED, "TcpWestwood::SetInitialCwnd() cannot change initial cwnd after connection started."); + m_initialCWnd = cwnd; +} + +uint32_t +TcpWestwood::GetInitialCwnd (void) const +{ + NS_LOG_FUNCTION (this); + return m_initialCWnd; +} + +void +TcpWestwood::InitializeCwnd(void) +{ + NS_LOG_FUNCTION (this); + /* + * Initialize congestion window, default to 1 MSS (RFC2001, sec.1) and must + * not be larger than 2 MSS (RFC2581, sec.3.1). Both m_initiaCWnd and + * m_segmentSize are set by the attribute system in ns3::TcpSocket. + */ + m_cWnd = m_initialCWnd * m_segmentSize; +} + +} // namespace ns3 diff --git a/src/internet/model/tcp-westwood.h b/src/internet/model/tcp-westwood.h new file mode 100644 index 000000000..39ad54846 --- /dev/null +++ b/src/internet/model/tcp-westwood.h @@ -0,0 +1,222 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2013 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 + * + * Authors: Siddharth Gangadhar , Truc Anh N. Nguyen , + * and Greeshma Umapathi + * + * 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. + * + * Work supported in part by NSF FIND (Future Internet Design) Program + * under grant CNS-0626918 (Postmodern Internet Architecture), + * NSF grant CNS-1050226 (Multilayer Network Resilience Analysis and Experimentation on GENI), + * US Department of Defense (DoD), and ITTC at The University of Kansas. + */ + +#ifndef TCP_WESTWOOD_H +#define TCP_WESTWOOD_H + +#include "tcp-socket-base.h" +#include "ns3/packet.h" + +namespace ns3 { + +/** + * \ingroup socket + * \ingroup tcp + * + * \brief An implementation of a stream socket using TCP. + * + * This class contains the implementation of TCP Westwood and Westwood+. + * + * Westwood and Westwood+ employ the AIAD (Additive Increase/Adaptive Decrease) + * congestion control paradigm. When a congestion episode happens, + * instead of halving the cwnd, these protocols try to estimate the network's + * bandwidth and use the estimated value to adjust the cwnd. + * While Westwood performs the bandwidth sampling every ACK reception, + * Westwood+ samples the bandwidth every RTT. + * + * The two main methods in the implementation are the CountAck (const TCPHeader&) + * and the EstimateBW (int, const, Time). The CountAck method calculates + * the number of acknowledged segments on the receipt of an ACK. + * The EstimateBW estimates the bandwidth based on the value returned by CountAck + * and the sampling interval (last ACK inter-arrival time for Westwood and last RTT for Westwood+). + */ +class TcpWestwood : public TcpSocketBase +{ +public: + static TypeId GetTypeId (void); + + TcpWestwood (void); + TcpWestwood (const TcpWestwood& sock); + virtual ~TcpWestwood (void); + + enum ProtocolType + { + WESTWOOD, + WESTWOODPLUS + }; + + enum FilterType + { + NONE, + TUSTIN + }; + + // From TcpSocketBase + virtual int Connect (const Address &address); + virtual int Listen (void); + +protected: + /** + * Limit the size of outstanding data based on the cwnd and the receiver's advertised window + * + * \return the max. possible number of unacked bytes + */ + virtual uint32_t Window (void); + + /** + * Call CopyObject to clone me + */ + virtual Ptr Fork (void); + + /** + * Process the newly received ACK + * + * \param packet the received ACK packet + * \param tcpHeader the header attached to the ACK packet + */ + virtual void ReceivedAck (Ptr packet, const TcpHeader& tcpHeader); + + /** + * Adjust the cwnd based on the current congestion control phase, + * and then call the TcpSocketBase::NewAck() to complete the processing + * + * \param seq the acknowledgment number + */ + virtual void NewAck (SequenceNumber32 const& seq); + + /** + * Adjust the cwnd using the currently estimated bandwidth, + * retransmit the missing packet, and enter fast recovery if 3 DUPACKs are received + * + * \param header the TCP header of the ACK packet + * \param count the number of DUPACKs + */ + virtual void DupAck (const TcpHeader& header, uint32_t count); + + /** + * Upon an RTO event, adjust the cwnd using the currently estimated bandwidth, + * retransmit the missing packet, and exit fast recovery + */ + virtual void Retransmit (void); + + /** + * Estimate the RTT, record the minimum value, + * and run a clock on the RTT to trigger Westwood+ bandwidth sampling + */ + virtual void EstimateRtt (const TcpHeader& header); + + // Implementing ns3::TcpSocket -- Attribute get/set + /** + * \param size the segment size to be used in a connection + */ + virtual void SetSegSize (uint32_t size); + + /** + * \param the slow-start threshold + */ + virtual void SetSSThresh (uint32_t threshold); + + /** + * \return the slow-start threshold + */ + virtual uint32_t GetSSThresh (void) const; + + /** + * \param cwnd the initial cwnd + */ + virtual void SetInitialCwnd (uint32_t cwnd); + + /** + * \return the initial cwnd + */ + virtual uint32_t GetInitialCwnd (void) const; + +private: + /** + * Initialize cwnd at the beginning of a connection + */ + void InitializeCwnd (void); + + /** + * Calculate the number of acknowledged packets upon the receipt of an ACK packet + * + * \param tcpHeader the header of the received ACK packet + * \return the number of ACKed packets + */ + int CountAck (const TcpHeader& tcpHeader); + + /** + * Update the total number of acknowledged packets during the current RTT + * + * \param acked the number of packets the currently received ACK acknowledges + */ + void UpdateAckedSegments (int acked); + + /** + * Estimate the network's bandwidth + * + * \param acked the number of acknowledged packets returned by CountAck + * \param tcpHeader the header of the packet + * \param rtt the RTT estimation + */ + void EstimateBW (int acked, const TcpHeader& tcpHeader, Time rtt); + + /** + * Tustin filter + */ + void Filtering (void); + +protected: + TracedValue m_cWnd; //< Congestion window + uint32_t m_ssThresh; //< Slow Start Threshold + uint32_t m_initialCWnd; //< Initial cWnd value + bool m_inFastRec; //< Currently in fast recovery if TRUE + + TracedValue m_currentBW; //< Current value of the estimated BW + double m_lastSampleBW; //< Last bandwidth sample + double m_lastBW; //< Last bandwidth sample after being filtered + Time m_minRtt; //< Minimum RTT + double m_lastAck; //< The time last ACK was received + SequenceNumber32 m_prevAckNo; //< Previously received ACK number + int m_accountedFor; //< The number of received DUPACKs + enum ProtocolType m_pType; //< 0 for Westwood, 1 for Westwood+ + enum FilterType m_fType; //< 0 for none, 1 for Tustin + + int m_ackedSegments; //< The number of segments ACKed between RTTs + bool m_IsCount; //< Start keeping track of m_ackedSegments for Westwood+ if TRUE + EventId m_bwEstimateEvent; //< The BW estimation event for Westwood+ + +}; + +} // namespace ns3 + +#endif /* TCP_WESTWOOD_H */ diff --git a/src/internet/wscript b/src/internet/wscript index efe9ad15b..550db6e23 100644 --- a/src/internet/wscript +++ b/src/internet/wscript @@ -135,6 +135,7 @@ def build(bld): 'model/tcp-tahoe.cc', 'model/tcp-reno.cc', 'model/tcp-newreno.cc', + 'model/tcp-westwood.cc', 'model/tcp-rx-buffer.cc', 'model/tcp-tx-buffer.cc', 'model/ipv4-packet-info-tag.cc', @@ -281,6 +282,15 @@ def build(bld): 'helper/ipv6-interface-container.h', 'helper/ipv6-routing-helper.h', 'model/ipv6-address-generator.h', + 'model/tcp-rfc793.h', + 'model/tcp-tahoe.h', + 'model/tcp-reno.h', + 'model/tcp-newreno.h', + 'model/tcp-westwood.h', + 'model/tcp-socket-base.h', + 'model/tcp-tx-buffer.h', + 'model/tcp-rx-buffer.h', + 'model/rtt-estimator.h', ] if bld.env['NSC_ENABLED']: diff --git a/src/test/ns3tcp/ns3tcp-loss-test-suite.cc b/src/test/ns3tcp/ns3tcp-loss-test-suite.cc index 708deecb6..d981ae851 100644 --- a/src/test/ns3tcp/ns3tcp-loss-test-suite.cc +++ b/src/test/ns3tcp/ns3tcp-loss-test-suite.cc @@ -38,12 +38,13 @@ #include "ns3/error-model.h" #include "ns3/pointer.h" #include "ns3tcp-socket-writer.h" +#include "ns3/tcp-westwood.h" using namespace ns3; NS_LOG_COMPONENT_DEFINE ("Ns3TcpLossTest"); -const bool WRITE_VECTORS = false; // set to true to write response vectors +const bool WRITE_VECTORS = false; // set to true to write response vectors const bool WRITE_LOGGING = false; // set to true to write logging const uint32_t PCAP_LINK_TYPE = 1187373557; // Some large random number -- we use to verify data was written by this program const uint32_t PCAP_SNAPLEN = 64; // Don't bother to save much data @@ -95,7 +96,7 @@ Ns3TcpLossTestCase::Ns3TcpLossTestCase () m_writeResults (false), m_writeLogging (WRITE_LOGGING), m_needToClose (true), - m_tcpModel ("ns3::TcpTahoe") + m_tcpModel ("ns3::TcpWestwood") { } @@ -121,7 +122,7 @@ Ns3TcpLossTestCase::DoSetup (void) // std::ostringstream oss; oss << "/response-vectors/ns3tcp-loss-" << m_tcpModel << m_testCase << "-response-vectors.pcap"; - m_pcapFilename = CreateDataDirFilename(oss.str ()); + m_pcapFilename = CreateDataDirFilename(oss.str ()); if (m_writeVectors) { @@ -286,8 +287,19 @@ Ns3TcpLossTestCase::DoRun (void) std::ostringstream tcpModel; tcpModel << "ns3::Tcp" << m_tcpModel; - Config::SetDefault ("ns3::TcpL4Protocol::SocketType", + if (m_tcpModel.compare("WestwoodPlus") == 0) + { + Config::SetDefault("ns3::TcpL4Protocol::SocketType", + TypeIdValue (TcpWestwood::GetTypeId())); + Config::SetDefault("ns3::TcpWestwood::ProtocolType", + EnumValue(TcpWestwood::WESTWOODPLUS)); + } + else + { + Config::SetDefault ("ns3::TcpL4Protocol::SocketType", StringValue (tcpModel.str ())); + } + Config::SetDefault ("ns3::TcpSocket::SegmentSize", UintegerValue (1000)); Config::SetDefault ("ns3::TcpSocket::DelAckCount", UintegerValue (1)); @@ -297,6 +309,7 @@ Ns3TcpLossTestCase::DoRun (void) LogComponentEnable ("TcpLossResponse", LOG_LEVEL_ALL); LogComponentEnable ("ErrorModel", LOG_LEVEL_DEBUG); LogComponentEnable ("TcpLossResponse", LOG_LEVEL_ALL); + LogComponentEnable ("TcpWestwood", LOG_LEVEL_ALL); LogComponentEnable ("TcpNewReno", LOG_LEVEL_INFO); LogComponentEnable ("TcpReno", LOG_LEVEL_INFO); LogComponentEnable ("TcpTahoe", LOG_LEVEL_INFO); @@ -465,6 +478,19 @@ Ns3TcpLossTestSuite::Ns3TcpLossTestSuite () AddTestCase (new Ns3TcpLossTestCase ("NewReno", 3), TestCase::QUICK); AddTestCase (new Ns3TcpLossTestCase ("NewReno", 4), TestCase::QUICK); + AddTestCase (new Ns3TcpLossTestCase ("Westwood", 0), TestCase::QUICK); + AddTestCase (new Ns3TcpLossTestCase ("Westwood", 1), TestCase::QUICK); + AddTestCase (new Ns3TcpLossTestCase ("Westwood", 2), TestCase::QUICK); + AddTestCase (new Ns3TcpLossTestCase ("Westwood", 3), TestCase::QUICK); + AddTestCase (new Ns3TcpLossTestCase ("Westwood", 4), TestCase::QUICK); + + AddTestCase (new Ns3TcpLossTestCase ("WestwoodPlus", 0), TestCase::QUICK); + AddTestCase (new Ns3TcpLossTestCase ("WestwoodPlus", 1), TestCase::QUICK); + AddTestCase (new Ns3TcpLossTestCase ("WestwoodPlus", 2), TestCase::QUICK); + AddTestCase (new Ns3TcpLossTestCase ("WestwoodPlus", 3), TestCase::QUICK); + AddTestCase (new Ns3TcpLossTestCase ("WestwoodPlus", 4), TestCase::QUICK); + } static Ns3TcpLossTestSuite ns3TcpLossTestSuite; +