diff --git a/examples/traffic-control/red-vs-fengadaptive.cc b/examples/traffic-control/red-vs-fengadaptive.cc new file mode 100644 index 000000000..818266315 --- /dev/null +++ b/examples/traffic-control/red-vs-fengadaptive.cc @@ -0,0 +1,190 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2016 NITK Surathkal + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Sourabh Jain + * Mohit P. Tahiliani + */ + +#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/point-to-point-layout-module.h" +#include "ns3/traffic-control-module.h" + +#include +#include +#include + +using namespace ns3; + +int main (int argc, char *argv[]) +{ + uint32_t nLeaf = 10; + uint32_t maxPackets = 100; + bool modeBytes = false; + uint32_t queueDiscLimitPackets = 1000; + double minTh = 5; + double maxTh = 15; + uint32_t pktSize = 512; + std::string appDataRate = "10Mbps"; + std::string queueDiscType = "RED"; + uint16_t port = 5001; + std::string bottleNeckLinkBw = "1Mbps"; + std::string bottleNeckLinkDelay = "50ms"; + + CommandLine cmd; + cmd.AddValue ("nLeaf", "Number of left and right side leaf nodes", nLeaf); + cmd.AddValue ("maxPackets","Max Packets allowed in the device queue", maxPackets); + cmd.AddValue ("queueDiscLimitPackets","Max Packets allowed in the queue disc", queueDiscLimitPackets); + cmd.AddValue ("queueDiscType", "Set Queue disc type to RED or FengAdaptive", queueDiscType); + cmd.AddValue ("appPktSize", "Set OnOff App Packet Size", pktSize); + cmd.AddValue ("appDataRate", "Set OnOff App DataRate", appDataRate); + cmd.AddValue ("modeBytes", "Set Queue disc mode to Packets or bytes ", modeBytes); + + cmd.AddValue ("redMinTh", "RED queue minimum threshold", minTh); + cmd.AddValue ("redMaxTh", "RED queue maximum threshold", maxTh); + cmd.Parse (argc,argv); + + if ((queueDiscType != "RED") && (queueDiscType != "FengAdaptive")) + { + std::cout << "Invalid queue disc type: Use --queueDiscType=RED or --queueDiscType=FengAdaptive" << std::endl; + exit (1); + } + + Config::SetDefault ("ns3::OnOffApplication::PacketSize", UintegerValue (pktSize)); + Config::SetDefault ("ns3::OnOffApplication::DataRate", StringValue (appDataRate)); + + Config::SetDefault ("ns3::QueueBase::Mode", StringValue ("QUEUE_MODE_PACKETS")); + Config::SetDefault ("ns3::QueueBase::MaxPackets", UintegerValue (maxPackets)); + + if (!modeBytes) + { + Config::SetDefault ("ns3::RedQueueDisc::Mode", StringValue ("QUEUE_DISC_MODE_PACKETS")); + Config::SetDefault ("ns3::RedQueueDisc::QueueLimit", UintegerValue (queueDiscLimitPackets)); + } + else + { + Config::SetDefault ("ns3::RedQueueDisc::Mode", StringValue ("QUEUE_DISC_MODE_BYTES")); + Config::SetDefault ("ns3::RedQueueDisc::QueueLimit", UintegerValue (queueDiscLimitPackets * pktSize)); + minTh *= pktSize; + maxTh *= pktSize; + } + + Config::SetDefault ("ns3::RedQueueDisc::MinTh", DoubleValue (minTh)); + Config::SetDefault ("ns3::RedQueueDisc::MaxTh", DoubleValue (maxTh)); + Config::SetDefault ("ns3::RedQueueDisc::LinkBandwidth", StringValue (bottleNeckLinkBw)); + Config::SetDefault ("ns3::RedQueueDisc::LinkDelay", StringValue (bottleNeckLinkDelay)); + Config::SetDefault ("ns3::RedQueueDisc::MeanPktSize", UintegerValue (pktSize)); + + if (queueDiscType == "FengAdaptive") + { + // Turn on Feng's Adaptive RED + Config::SetDefault ("ns3::RedQueueDisc::FengAdaptive", BooleanValue (true)); + } + + // Create the point-to-point link helpers + PointToPointHelper bottleNeckLink; + bottleNeckLink.SetDeviceAttribute ("DataRate", StringValue (bottleNeckLinkBw)); + bottleNeckLink.SetChannelAttribute ("Delay", StringValue (bottleNeckLinkDelay)); + + PointToPointHelper pointToPointLeaf; + pointToPointLeaf.SetDeviceAttribute ("DataRate", StringValue ("10Mbps")); + pointToPointLeaf.SetChannelAttribute ("Delay", StringValue ("1ms")); + + PointToPointDumbbellHelper d (nLeaf, pointToPointLeaf, + nLeaf, pointToPointLeaf, + bottleNeckLink); + + // Install Stack + InternetStackHelper stack; + for (uint32_t i = 0; i < d.LeftCount (); ++i) + { + stack.Install (d.GetLeft (i)); + } + for (uint32_t i = 0; i < d.RightCount (); ++i) + { + stack.Install (d.GetRight (i)); + } + + stack.Install (d.GetLeft ()); + stack.Install (d.GetRight ()); + TrafficControlHelper tchBottleneck; + QueueDiscContainer queueDiscs; + tchBottleneck.SetRootQueueDisc ("ns3::RedQueueDisc"); + tchBottleneck.Install (d.GetLeft ()->GetDevice (0)); + queueDiscs = tchBottleneck.Install (d.GetRight ()->GetDevice (0)); + + // Assign IP Addresses + d.AssignIpv4Addresses (Ipv4AddressHelper ("10.1.1.0", "255.255.255.0"), + Ipv4AddressHelper ("10.2.1.0", "255.255.255.0"), + Ipv4AddressHelper ("10.3.1.0", "255.255.255.0")); + + // Install on/off app on all right side nodes + OnOffHelper clientHelper ("ns3::TcpSocketFactory", Address ()); + clientHelper.SetAttribute ("OnTime", StringValue ("ns3::UniformRandomVariable[Min=0.|Max=1.]")); + clientHelper.SetAttribute ("OffTime", StringValue ("ns3::UniformRandomVariable[Min=0.|Max=1.]")); + Address sinkLocalAddress (InetSocketAddress (Ipv4Address::GetAny (), port)); + PacketSinkHelper packetSinkHelper ("ns3::TcpSocketFactory", sinkLocalAddress); + ApplicationContainer sinkApps; + for (uint32_t i = 0; i < d.LeftCount (); ++i) + { + sinkApps.Add (packetSinkHelper.Install (d.GetLeft (i))); + } + sinkApps.Start (Seconds (0.0)); + sinkApps.Stop (Seconds (30.0)); + + ApplicationContainer clientApps; + for (uint32_t i = 0; i < d.RightCount (); ++i) + { + // Create an on/off app sending packets to the left side + AddressValue remoteAddress (InetSocketAddress (d.GetLeftIpv4Address (i), port)); + clientHelper.SetAttribute ("Remote", remoteAddress); + clientApps.Add (clientHelper.Install (d.GetRight (i))); + } + clientApps.Start (Seconds (1.0)); // Start 1 second after sink + clientApps.Stop (Seconds (15.0)); // Stop before the sink + + Ipv4GlobalRoutingHelper::PopulateRoutingTables (); + + std::cout << "Running the simulation" << std::endl; + Simulator::Run (); + + RedQueueDisc::Stats st = StaticCast (queueDiscs.Get (0))->GetStats (); + + if (st.unforcedDrop == 0) + { + std::cout << "There should be some unforced drops" << std::endl; + exit (1); + } + + if (st.qLimDrop != 0) + { + std::cout << "There should be zero drops due to queue full" << std::endl; + exit (1); + } + + std::cout << "*** Stats from the bottleneck queue disc ***" << std::endl; + std::cout << "\t " << st.unforcedDrop << " drops due to prob mark" << std::endl; + std::cout << "\t " << st.forcedDrop << " drops due to hard mark" << std::endl; + std::cout << "\t " << st.qLimDrop << " drops due to queue full" << std::endl; + std::cout << "Destroying the simulation" << std::endl; + + Simulator::Destroy (); + return 0; +} diff --git a/examples/traffic-control/wscript b/examples/traffic-control/wscript index b943e56a7..1204d8b63 100644 --- a/examples/traffic-control/wscript +++ b/examples/traffic-control/wscript @@ -8,3 +8,6 @@ def build(bld): obj = bld.create_ns3_program('queue-discs-benchmark', ['internet', 'point-to-point', 'applications', 'internet-apps', 'traffic-control', 'flow-monitor']) obj.source = 'queue-discs-benchmark.cc' + + obj = bld.create_ns3_program('red-vs-fengadaptive', ['point-to-point', 'point-to-point-layout', 'internet', 'applications', 'traffic-control']) + obj.source = 'red-vs-fengadaptive.cc' diff --git a/src/traffic-control/doc/red.rst b/src/traffic-control/doc/red.rst index 53253def1..cd3359467 100644 --- a/src/traffic-control/doc/red.rst +++ b/src/traffic-control/doc/red.rst @@ -35,6 +35,12 @@ in ns-3 contains implementation of both the features, and is a port of Sally Floyd's ns-2 ARED model. Note that the user is allowed to choose and explicitly configure the simulation by selecting feature (i) or feature (ii), or both. +Feng's Adaptive RED +=================== +Feng's Adaptive RED is a variant of RED that adapts the maximum drop +probability. The model in ns-3 contains implementation of this feature, and is a +port of ns-2 Feng's Adaptive RED model. + Explicit Congestion Notification (ECN) ====================================== This RED model supports an ECN mode of operation to notify endpoints of @@ -57,12 +63,16 @@ the ``Mark ()`` method. References ========== -The RED queue aims to be close to the results cited in: +The RED queue disc aims to be close to the results cited in: S.Floyd, K.Fall http://icir.org/floyd/papers/redsims.ps ARED queue implementation is based on the algorithm provided in: S. Floyd et al, http://www.icir.org/floyd/papers/adaptiveRed.pdf +Feng's Adaptive RED queue implementation is based on the algorithm +provided in: +W. C. Feng et al, http://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=752150 + The addition of explicit congestion notification (ECN) to IP: K. K. Ramakrishnan et al, https://tools.ietf.org/html/rfc3168 @@ -99,6 +109,14 @@ In addition to RED attributes, ARED queue requires following attributes: * Beta (decrement parameter for m_curMaxP) * RTT +In addition to RED attributes, Feng's Adaptive RED queue requires following +attributes: + +* FengAdaptive (Boolean attribute, Default: false) +* Status (status of current queue length, Default: Above) +* FengAlpha (increment parameter for m_curMaxP, Default: 3) +* FengBeta (decrement parameter for m_curMaxP, Default: 2) + Consult the ns-3 documentation for explanation of these attributes. Simulating ARED @@ -133,6 +151,16 @@ To configure (ii); AdaptMaxP must be set to true, as done in Config::SetDefault ("ns3::RedQueueDisc::AdaptMaxP", BooleanValue (true)); +Simulating Feng's Adaptive RED +============================== + +To switch on Feng's Adaptive RED algorithm, the attribute FengAdaptive must be +set to true, as done in ``examples/traffic-control/red-vs-fengadaptive.cc``: + +.. sourcecode:: cpp + + Config::SetDefault ("ns3::RedQueueDisc::FengAdaptive", BooleanValue (true)); + Examples ======== @@ -142,6 +170,9 @@ ARED queue examples can be found at: ``src/traffic-control/examples/adaptive-red-tests.cc`` and ``src/traffic-control/examples/red-vs-ared.cc`` +Feng's Adaptive RED example can be found at: +``examples/traffic-control/red-vs-fengadaptive.cc`` + Validation ********** diff --git a/src/traffic-control/model/red-queue-disc.cc b/src/traffic-control/model/red-queue-disc.cc index 98efedc63..fb1854c47 100644 --- a/src/traffic-control/model/red-queue-disc.cc +++ b/src/traffic-control/model/red-queue-disc.cc @@ -114,6 +114,11 @@ TypeId RedQueueDisc::GetTypeId (void) BooleanValue (false), MakeBooleanAccessor (&RedQueueDisc::m_isAdaptMaxP), MakeBooleanChecker ()) + .AddAttribute ("FengAdaptive", + "True to enable Feng's Adaptive RED", + BooleanValue (false), + MakeBooleanAccessor (&RedQueueDisc::m_isFengAdaptive), + MakeBooleanChecker ()) .AddAttribute ("MinTh", "Minimum average length threshold in packets/bytes", DoubleValue (5), @@ -169,6 +174,16 @@ TypeId RedQueueDisc::GetTypeId (void) DoubleValue (0.9), MakeDoubleAccessor (&RedQueueDisc::SetAredBeta), MakeDoubleChecker (0, 1)) + .AddAttribute ("FengAlpha", + "Decrement parameter for m_curMaxP in Feng's Adaptive RED", + DoubleValue (3.0), + MakeDoubleAccessor (&RedQueueDisc::SetFengAdaptiveA), + MakeDoubleChecker ()) + .AddAttribute ("FengBeta", + "Increment parameter for m_curMaxP in Feng's Adaptive RED", + DoubleValue (2.0), + MakeDoubleAccessor (&RedQueueDisc::SetFengAdaptiveB), + MakeDoubleChecker ()) .AddAttribute ("LastSet", "Store the last time m_curMaxP was updated", TimeValue (Seconds (0.0)), @@ -281,6 +296,44 @@ RedQueueDisc::GetAredBeta (void) return m_beta; } +void +RedQueueDisc::SetFengAdaptiveA (double a) +{ + NS_LOG_FUNCTION (this << a); + m_a = a; + + if (m_a != 3) + { + NS_LOG_WARN ("Alpha value does not follow the recommendations!"); + } +} + +double +RedQueueDisc::GetFengAdaptiveA (void) +{ + NS_LOG_FUNCTION (this); + return m_a; +} + +void +RedQueueDisc::SetFengAdaptiveB (double b) +{ + NS_LOG_FUNCTION (this << b); + m_b = b; + + if (m_b != 2) + { + NS_LOG_WARN ("Beta value does not follow the recommendations!"); + } +} + +double +RedQueueDisc::GetFengAdaptiveB (void) +{ + NS_LOG_FUNCTION (this); + return m_b; +} + void RedQueueDisc::SetQueueLimit (uint32_t lim) { @@ -465,6 +518,12 @@ RedQueueDisc::InitializeParams (void) m_isAdaptMaxP = true; } + if (m_isFengAdaptive) + { + // Initialize m_fengStatus + m_fengStatus = Above; + } + if (m_minTh == 0 && m_maxTh == 0) { m_minTh = 5.0; @@ -568,6 +627,30 @@ RedQueueDisc::InitializeParams (void) << m_vC << "; m_vD " << m_vD); } +// Updating m_curMaxP, following the pseudocode +// from: A Self-Configuring RED Gateway, INFOCOMM '99. +// They recommend m_a = 3, and m_b = 2. +void +RedQueueDisc::UpdateMaxPFeng (double newAve) +{ + NS_LOG_FUNCTION (this << newAve); + + if (m_minTh < newAve && newAve < m_maxTh) + { + m_fengStatus = Between; + } + else if (newAve < m_minTh && m_fengStatus != Below) + { + m_fengStatus = Below; + m_curMaxP = m_curMaxP / m_a; + } + else if (newAve > m_maxTh && m_fengStatus != Above) + { + m_fengStatus = Above; + m_curMaxP = m_curMaxP * m_b; + } +} + // Update m_curMaxP to keep the average queue length within the target range. void RedQueueDisc::UpdateMaxP (double newAve) @@ -610,6 +693,10 @@ RedQueueDisc::Estimator (uint32_t nQueued, uint32_t m, double qAvg, double qW) { UpdateMaxP(newAve); } + else if (m_isFengAdaptive) + { + UpdateMaxPFeng (newAve); // Update MaxP in MIMD fashion. + } return newAve; } @@ -883,6 +970,11 @@ RedQueueDisc::CheckConfig (void) return false; } + if ((m_isARED || m_isAdaptMaxP) && m_isFengAdaptive) + { + NS_LOG_ERROR ("m_isAdaptMaxP and m_isFengAdaptive cannot be simultaneously true"); + } + return true; } diff --git a/src/traffic-control/model/red-queue-disc.h b/src/traffic-control/model/red-queue-disc.h index 398bbea54..e147ad374 100644 --- a/src/traffic-control/model/red-queue-disc.h +++ b/src/traffic-control/model/red-queue-disc.h @@ -98,6 +98,16 @@ public: */ virtual ~RedQueueDisc (); + /** + * \brief Used in Feng's Adaptive RED + */ + enum FengStatus + { + Above, //!< When m_qAvg > m_maxTh + Between, //!< When m_maxTh < m_qAvg < m_minTh + Below, //!< When m_qAvg < m_minTh + }; + /** * \brief Stats */ @@ -179,6 +189,34 @@ public: */ double GetAredBeta (void); + /** + * \brief Set the alpha value to adapt m_curMaxP in Feng's Adaptive RED. + * + * \param a The value of alpha to adapt m_curMaxP in Feng's Adaptive RED. + */ + void SetFengAdaptiveA (double a); + + /** + * \brief Get the alpha value to adapt m_curMaxP in Feng's Adaptive RED. + * + * \returns The alpha value to adapt m_curMaxP in Feng's Adaptive RED. + */ + double GetFengAdaptiveA (void); + + /** + * \brief Set the beta value to adapt m_curMaxP in Feng's Adaptive RED. + * + * \param b The value of beta to adapt m_curMaxP in Feng's Adaptive RED. + */ + void SetFengAdaptiveB (double b); + + /** + * \brief Get the beta value to adapt m_curMaxP in Feng's Adaptive RED. + * + * \returns The beta value to adapt m_curMaxP in Feng's Adaptive RED. + */ + double GetFengAdaptiveB (void); + /** * \brief Set the limit of the queue. * @@ -246,6 +284,11 @@ private: * \param newAve new average queue length */ void UpdateMaxP (double newAve); + /** + * \brief Update m_curMaxP based on Feng's Adaptive RED + * \param newAve new average queue length + */ + void UpdateMaxPFeng (double newAve); /** * \brief Check if a packet needs to be dropped due to probability mark * \param item queue item @@ -302,6 +345,9 @@ private: double m_alpha; //!< Increment parameter for m_curMaxP in ARED double m_beta; //!< Decrement parameter for m_curMaxP in ARED Time m_rtt; //!< Rtt to be considered while automatically setting m_bottom in ARED + bool m_isFengAdaptive; //!< True to enable Feng's Adaptive RED + double m_b; //!< Increment parameter for m_curMaxP in Feng's Adaptive RED + double m_a; //!< Decrement parameter for m_curMaxP in Feng's Adaptive RED bool m_isNs1Compat; //!< Ns-1 compatibility DataRate m_linkBandwidth; //!< Link bandwidth Time m_linkDelay; //!< Link delay @@ -323,6 +369,7 @@ private: double m_ptc; //!< packet time constant in packets/second double m_qAvg; //!< Average queue length uint32_t m_count; //!< Number of packets since last random number generation + FengStatus m_fengStatus; //!< For use in Feng's Adaptive RED /** * 0 for default RED * 1 experimental (see red-queue-disc.cc) diff --git a/src/traffic-control/test/red-queue-disc-test-suite.cc b/src/traffic-control/test/red-queue-disc-test-suite.cc index 37f9f335d..49bea1c79 100644 --- a/src/traffic-control/test/red-queue-disc-test-suite.cc +++ b/src/traffic-control/test/red-queue-disc-test-suite.cc @@ -237,6 +237,8 @@ RedQueueDiscTestCase::RunRedTest (StringValue mode) uint32_t test5; uint32_t test6; uint32_t test7; + uint32_t test11; + uint32_t test12; } drop; @@ -421,6 +423,56 @@ RedQueueDiscTestCase::RunRedTest (StringValue mode) // Packets are ECN capable, RED queue disc is ECN enabled; there should be only unforced marks, no unforced drops NS_TEST_EXPECT_MSG_EQ (st.unforcedDrop, 0, "There should be no unforced drops"); NS_TEST_EXPECT_MSG_NE (st.unforcedMark, 0, "There should be some unforced marks"); + + + // test 11: Original RED with default parameter settings and fixed m_curMaxP + queue = CreateObject (); + minTh = 30 * modeSize; + maxTh = 90 * modeSize; + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("Mode", mode), true, + "Verify that we can actually set the attribute Mode"); + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("MinTh", DoubleValue (minTh)), true, + "Verify that we can actually set the attribute MinTh"); + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("MaxTh", DoubleValue (maxTh)), true, + "Verify that we can actually set the attribute MaxTh"); + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("QueueLimit", UintegerValue (qSize)), true, + "Verify that we can actually set the attribute QueueLimit"); + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("QW", DoubleValue (0.002)), true, + "Verify that we can actually set the attribute QW"); + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("LInterm", DoubleValue (2)), true, + "Verify that we can actually set the attribute LInterm"); + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("Gentle", BooleanValue (true)), true, + "Verify that we can actually set the attribute Gentle"); + queue->Initialize (); + Enqueue (queue, pktSize, 300, false); + st = StaticCast (queue)->GetStats (); + drop.test11 = st.unforcedDrop; + NS_TEST_EXPECT_MSG_NE (drop.test11, 0, "There should some dropped packets due to probability mark"); + + + // test 12: Feng's Adaptive RED with default parameter settings and varying m_curMaxP + queue = CreateObject (); + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("Mode", mode), true, + "Verify that we can actually set the attribute Mode"); + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("MinTh", DoubleValue (minTh)), true, + "Verify that we can actually set the attribute MinTh"); + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("MaxTh", DoubleValue (maxTh)), true, + "Verify that we can actually set the attribute MaxTh"); + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("QueueLimit", UintegerValue (qSize)), true, + "Verify that we can actually set the attribute QueueLimit"); + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("QW", DoubleValue (0.002)), true, + "Verify that we can actually set the attribute QW"); + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("LInterm", DoubleValue (2)), true, + "Verify that we can actually set the attribute LInterm"); + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("Gentle", BooleanValue (true)), true, + "Verify that we can actually set the attribute Gentle"); + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("FengAdaptive", BooleanValue (true)), true, + "Verify that we can actually set the attribute FengAdaptive"); + queue->Initialize (); + Enqueue (queue, pktSize, 300, false); + st = StaticCast (queue)->GetStats (); + drop.test12 = st.unforcedDrop; + NS_TEST_EXPECT_MSG_LT (drop.test12, drop.test11, "Test 12 should have less drops due to probability mark than test 11"); } void