diff --git a/src/network/examples/wscript b/src/network/examples/wscript index 6bd430683..5cf773381 100644 --- a/src/network/examples/wscript +++ b/src/network/examples/wscript @@ -10,3 +10,6 @@ def build(bld): obj = bld.create_ns3_program('main-packet-tag', ['network']) obj.source = 'main-packet-tag.cc' + obj = bld.create_ns3_program('red-tests', ['point-to-point', 'internet', 'applications', 'flow-monitor']) + obj.source = 'red-tests.cc' + diff --git a/src/network/test/examples-to-run.py b/src/network/test/examples-to-run.py index a9647ae79..3861b97dc 100644 --- a/src/network/test/examples-to-run.py +++ b/src/network/test/examples-to-run.py @@ -10,6 +10,7 @@ cpp_examples = [ ("main-packet-header", "True", "True"), ("main-packet-tag", "True", "True"), + ("red-tests", "True", "True"), ] # A list of Python examples to run in order to ensure that they remain diff --git a/src/network/test/red-queue-test-suite.cc b/src/network/test/red-queue-test-suite.cc new file mode 100644 index 000000000..9b71de9f7 --- /dev/null +++ b/src/network/test/red-queue-test-suite.cc @@ -0,0 +1,280 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright © 2011 Marcos Talau + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see . + * + * Author: Marcos Talau (talau@users.sourceforge.net) + * + */ + +#include "ns3/test.h" +#include "ns3/red-queue.h" +#include "ns3/uinteger.h" +#include "ns3/string.h" +#include "ns3/double.h" +#include "ns3/log.h" + +namespace ns3 { + +class RedQueueTestCase : public TestCase +{ +public: + RedQueueTestCase (); + virtual void DoRun (void); +private: + void Enqueue(Ptr queue, uint32_t size, uint32_t nPkt); + void RunRedTest(StringValue mode); +}; + +RedQueueTestCase::RedQueueTestCase () + : TestCase ("Sanity check on the red queue implementation") +{ +} + +void +RedQueueTestCase::RunRedTest(StringValue mode) +{ + uint32_t pktSize = 0; + // 1 for packets; pktSize for bytes + uint32_t modeSize = 1; + double minTh = 2; + double maxTh = 5; + uint32_t qSize = 8; + Ptr queue = CreateObject (); + + // test 1: simple enqueue/dequeue with no drops + 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"); + + if (queue->GetMode () == RedQueue::BYTES) + { + pktSize = 1000; + modeSize = pktSize; + queue->SetTh (minTh * pktSize, maxTh * pktSize); + queue->SetQueueLimit (qSize * pktSize); + } + + Ptr p1, p2, p3, p4, p5, p6, p7, p8; + p1 = Create (pktSize); + p2 = Create (pktSize); + p3 = Create (pktSize); + p4 = Create (pktSize); + p5 = Create (pktSize); + p6 = Create (pktSize); + p7 = Create (pktSize); + p8 = Create (pktSize); + + NS_TEST_EXPECT_MSG_EQ (queue->GetQueueSize (), 0 * modeSize, "There should be no packets in there"); + queue->Enqueue (p1); + NS_TEST_EXPECT_MSG_EQ (queue->GetQueueSize (), 1 * modeSize, "There should be one packet in there"); + queue->Enqueue (p2); + NS_TEST_EXPECT_MSG_EQ (queue->GetQueueSize (), 2 * modeSize, "There should be two packets in there"); + queue->Enqueue (p3); + queue->Enqueue (p4); + queue->Enqueue (p5); + queue->Enqueue (p6); + queue->Enqueue (p7); + queue->Enqueue (p8); + NS_TEST_EXPECT_MSG_EQ (queue->GetQueueSize (), 8 * modeSize, "There should be eight packets in there"); + + Ptr p; + + p = queue->Dequeue (); + NS_TEST_EXPECT_MSG_EQ ((p != 0), true, "I want to remove the first packet"); + NS_TEST_EXPECT_MSG_EQ (queue->GetQueueSize (), 7 * modeSize, "There should be seven packets in there"); + NS_TEST_EXPECT_MSG_EQ (p->GetUid (), p1->GetUid (), "was this the first packet ?"); + + p = queue->Dequeue (); + NS_TEST_EXPECT_MSG_EQ ((p != 0), true, "I want to remove the second packet"); + NS_TEST_EXPECT_MSG_EQ (queue->GetQueueSize (), 6 * modeSize, "There should be six packet in there"); + NS_TEST_EXPECT_MSG_EQ (p->GetUid (), p2->GetUid (), "Was this the second packet ?"); + + p = queue->Dequeue (); + NS_TEST_EXPECT_MSG_EQ ((p != 0), true, "I want to remove the third packet"); + NS_TEST_EXPECT_MSG_EQ (queue->GetQueueSize (), 5 * modeSize, "There should be five packets in there"); + NS_TEST_EXPECT_MSG_EQ (p->GetUid (), p3->GetUid (), "Was this the third packet ?"); + + p = queue->Dequeue (); + p = queue->Dequeue (); + p = queue->Dequeue (); + p = queue->Dequeue (); + p = queue->Dequeue (); + + p = queue->Dequeue (); + NS_TEST_EXPECT_MSG_EQ ((p == 0), true, "There are really no packets in there"); + + + // test 2: more data, but with no drops + queue = CreateObject (); + minTh = 70 * modeSize; + maxTh = 150 * modeSize; + qSize = 300 * 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"); + Enqueue(queue, pktSize, 300); + RedQueue::Stats st = StaticCast (queue)->GetStats (); + NS_TEST_EXPECT_MSG_EQ (st.unforcedDrop, 0, "There should zero dropped packets due probability mark"); + NS_TEST_EXPECT_MSG_EQ (st.forcedDrop, 0, "There should zero dropped packets due hardmark mark"); + NS_TEST_EXPECT_MSG_EQ (st.qLimDrop, 0, "There should zero dropped packets due queue full"); + + // save number of drops from tests + struct d { + uint32_t test3; + uint32_t test4; + uint32_t test5; + uint32_t test6; + uint32_t test7; + } drop; + + + // test 3: more data, now drops due QW change + 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.020)), true, + "Verify that we can actually set the attribute QW"); + Enqueue(queue, pktSize, 300); + st = StaticCast (queue)->GetStats (); + drop.test3 = st.unforcedDrop + st.forcedDrop + st.qLimDrop; + NS_TEST_EXPECT_MSG_NE (drop.test3, 0, "There should be some dropped packets"); + + + // test 4: reduced maxTh, this causes more drops + maxTh = 100 * modeSize; + 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.020)), true, + "Verify that we can actually set the attribute QW"); + Enqueue(queue, pktSize, 300); + st = StaticCast (queue)->GetStats (); + drop.test4 = st.unforcedDrop + st.forcedDrop + st.qLimDrop; + NS_TEST_EXPECT_MSG_GT (drop.test4, drop.test3, "Test 4 should have more drops than test 3"); + + + // test 5: change drop probability to a high value (LInterm) + maxTh = 150 * modeSize; + 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.020)), true, + "Verify that we can actually set the attribute QW"); + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("LInterm", DoubleValue (5)), true, + "Verify that we can actually set the attribute LInterm"); + Enqueue(queue, pktSize, 300); + st = StaticCast (queue)->GetStats (); + drop.test5 = st.unforcedDrop + st.forcedDrop + st.qLimDrop; + NS_TEST_EXPECT_MSG_GT (drop.test5, drop.test3, "Test 5 should have more drops than test 3"); + + + // test 6: disable Gentle param + 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.020)), true, + "Verify that we can actually set the attribute QW"); + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("Gentle", BooleanValue (false)), true, + "Verify that we can actually set the attribute Gentle"); + Enqueue(queue, pktSize, 300); + st = StaticCast (queue)->GetStats (); + drop.test6 = st.unforcedDrop + st.forcedDrop + st.qLimDrop; + NS_TEST_EXPECT_MSG_GT (drop.test6, drop.test3, "Test 6 should have more drops than test 3"); + + + // test 7: disable Wait param + 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.020)), true, + "Verify that we can actually set the attribute QW"); + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("Wait", BooleanValue (false)), true, + "Verify that we can actually set the attribute Wait"); + Enqueue(queue, pktSize, 300); + st = StaticCast (queue)->GetStats (); + drop.test7 = st.unforcedDrop + st.forcedDrop + st.qLimDrop; + NS_TEST_EXPECT_MSG_GT (drop.test7, drop.test3, "Test 7 should have more drops than test 3"); +} + +void +RedQueueTestCase::Enqueue(Ptr queue, uint32_t size, uint32_t nPkt) +{ + for (uint32_t i = 0; i < nPkt; i++) + { + queue->Enqueue (Create (size)); + } +} + +void +RedQueueTestCase::DoRun (void) +{ + RunRedTest (StringValue("Packets")); + RunRedTest (StringValue("Bytes")); +} + +static class RedQueueTestSuite : public TestSuite +{ +public: + RedQueueTestSuite () + : TestSuite ("red-queue", UNIT) + { + AddTestCase (new RedQueueTestCase ()); + } +} g_redQueueTestSuite; + +} // namespace ns3 diff --git a/src/network/utils/red-queue.cc b/src/network/utils/red-queue.cc new file mode 100644 index 000000000..479918fe2 --- /dev/null +++ b/src/network/utils/red-queue.cc @@ -0,0 +1,646 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * This code was ported from NS-2.34, with licence: + * + * Copyright (c) 1990-1997 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Computer Systems + * Engineering Group at Lawrence Berkeley Laboratory. + * 4. Neither the name of the University nor of the Laboratory may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * This port: + * + * Copyright © 2011 Marcos Talau + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see . + * + * Author: Marcos Talau (talau@users.sourceforge.net) + * + * Thanks to: Duy Nguyen by RED efforts in NS3 + * + */ + +/* + * PORT NOTE: Almost all comments have also been ported from NS-2 + */ + +#include "ns3/log.h" +#include "ns3/enum.h" +#include "ns3/uinteger.h" +#include "ns3/double.h" +#include "ns3/simulator.h" +#include "ns3/abort.h" +#include "red-queue.h" + +NS_LOG_COMPONENT_DEFINE ("RedQueue"); + +namespace ns3 { + +NS_OBJECT_ENSURE_REGISTERED (RedQueue); + +TypeId RedQueue::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::RedQueue") + .SetParent () + .AddConstructor () + .AddAttribute ("Mode", + "Bytes or packets", + EnumValue (PACKETS), + MakeEnumAccessor (&RedQueue::SetMode), + MakeEnumChecker (BYTES, "Bytes", + PACKETS, "Packets")) + .AddAttribute ("MeanPktSize", + "Average of packet size", + UintegerValue (500), + MakeUintegerAccessor (&RedQueue::m_meanPktSize), + MakeUintegerChecker ()) + .AddAttribute ("IdlePktSize", + "Average packet size used during idle times. Used when m_cautions = 3", + UintegerValue (0), + MakeUintegerAccessor (&RedQueue::m_idlePktSize), + MakeUintegerChecker ()) + .AddAttribute ("Wait", + "True for waiting between dropped packets", + BooleanValue (true), + MakeBooleanAccessor (&RedQueue::m_isWait), + MakeBooleanChecker ()) + .AddAttribute ("Gentle", + "True to increases dropping probability slowly when average queue exceeds maxthresh", + BooleanValue (true), + MakeBooleanAccessor (&RedQueue::m_isGentle), + MakeBooleanChecker ()) + .AddAttribute ("MinTh", + "Minimum average length threshold in packets/bytes", + DoubleValue (5), + MakeDoubleAccessor (&RedQueue::m_minTh), + MakeDoubleChecker ()) + .AddAttribute ("MaxTh", + "Maximum average length threshold in packets/bytes", + DoubleValue (15), + MakeDoubleAccessor (&RedQueue::m_maxTh), + MakeDoubleChecker ()) + .AddAttribute ("QueueLimit", + "Queue limit in bytes/packets", + UintegerValue (25), + MakeUintegerAccessor (&RedQueue::m_queueLimit), + MakeUintegerChecker ()) + .AddAttribute ("QW", + "Queue weight related to the exponential weighted moving average (EWMA)", + DoubleValue (0.002), + MakeDoubleAccessor (&RedQueue::m_qW), + MakeDoubleChecker ()) + .AddAttribute ("LInterm", + "The maximum probability of dropping a packet", + DoubleValue (50), + MakeDoubleAccessor (&RedQueue::m_lInterm), + MakeDoubleChecker ()) + .AddAttribute ("Ns1Compat", + "NS-1 compatibility", + BooleanValue (false), + MakeBooleanAccessor (&RedQueue::m_isNs1Compat), + MakeBooleanChecker ()) + .AddAttribute ("LinkBandwidth", + "The RED link bandwidth", + DataRateValue (DataRate ("1.5Mbps")), + MakeDataRateAccessor (&RedQueue::m_linkBandwidth), + MakeDataRateChecker ()) + .AddAttribute ("LinkDelay", + "The RED link delay", + TimeValue (MilliSeconds (20)), + MakeTimeAccessor (&RedQueue::m_linkDelay), + MakeTimeChecker ()) + ; + + return tid; +} + +RedQueue::RedQueue () : + Queue (), + m_packets (), + m_bytesInQueue (0), + m_hasRedStarted (false) +{ + NS_LOG_FUNCTION_NOARGS (); +} + +RedQueue::~RedQueue () +{ + NS_LOG_FUNCTION_NOARGS (); +} + +void +RedQueue::SetMode (enum Mode mode) +{ + NS_LOG_FUNCTION (mode); + m_mode = mode; +} + +RedQueue::Mode +RedQueue::GetMode (void) +{ + NS_LOG_FUNCTION_NOARGS (); + return m_mode; +} + +void +RedQueue::SetQueueLimit (uint32_t lim) +{ + NS_LOG_FUNCTION (lim); + m_queueLimit = lim; +} + +void +RedQueue::SetTh (double minTh, double maxTh) +{ + NS_LOG_FUNCTION (this << minTh << maxTh); + NS_ASSERT(minTh <= maxTh); + m_minTh = minTh; + m_maxTh = maxTh; +} + +RedQueue::Stats +RedQueue::GetStats () +{ + return m_stats; +} + +bool +RedQueue::DoEnqueue (Ptr p) +{ + NS_LOG_FUNCTION (this << p); + + if (! m_hasRedStarted ) + { + NS_LOG_INFO ("Initializing RED params."); + InitializeParams (); + m_hasRedStarted = true; + } + + uint32_t nQueued; + + if (GetMode () == BYTES) + { + NS_LOG_DEBUG ("Enqueue in bytes mode"); + nQueued = m_bytesInQueue; + } + else if (GetMode () == PACKETS) + { + NS_LOG_DEBUG ("Enqueue in packets mode"); + nQueued = m_packets.size (); + } + + // simulate number of packets arrival during idle period + uint32_t m = 0; + + if (m_idle == 1) + { + NS_LOG_DEBUG ("RED Queue is idle."); + Time now = Simulator::Now (); + + if (m_cautious == 3) + { + double ptc = m_ptc * m_meanPktSize / m_idlePktSize; + m = uint32_t (ptc * (now - m_idleTime).GetSeconds ()); + } + else + { + m = uint32_t (m_ptc * (now - m_idleTime).GetSeconds ()); + } + + m_idle = 0; + } + + m_qAvg = Estimator (nQueued, m + 1, m_qAvg, m_qW); + + NS_LOG_DEBUG ("\t bytesInQueue " << m_bytesInQueue << "\tQavg " << m_qAvg); + NS_LOG_DEBUG ("\t packetsInQueue " << m_packets.size () << "\tQavg " << m_qAvg); + + m_count++; + m_countBytes += p->GetSize (); + + uint32_t dropType = DTYPE_NONE; + if (m_qAvg >= m_minTh && nQueued > 1) + { + if ((! m_isGentle && m_qAvg >= m_maxTh) || + (m_isGentle && m_qAvg >= 2 * m_maxTh)) + { + NS_LOG_DEBUG ("adding DROP FORCED MARK"); + dropType = DTYPE_FORCED; + } + else if (m_old == 0) + { + /* + * The average queue size has just crossed the + * threshold from below to above "minthresh", or + * from above "minthresh" with an empty queue to + * above "minthresh" with a nonempty queue. + */ + m_count = 1; + m_countBytes = p->GetSize(); + m_old = 1; + } + else if (DropEarly (p, nQueued)) + { + NS_LOG_LOGIC ("DropEarly returns 1"); + dropType = DTYPE_UNFORCED; + } + } + else + { + // No packets are being dropped + m_vProb = 0.0; + m_old = 0; + } + + if (nQueued >= m_queueLimit) + { + NS_LOG_DEBUG ("\t Dropping due to Queue Full " << nQueued); + dropType = DTYPE_FORCED; + m_stats.qLimDrop++; + } + + if (dropType == DTYPE_UNFORCED) + { + NS_LOG_DEBUG ("\t Dropping due to Prob Mark " << m_qAvg); + m_stats.unforcedDrop++; + Drop (p); + return false; + } + else if (dropType == DTYPE_FORCED) + { + NS_LOG_DEBUG ("\t Dropping due to Hard Mark " << m_qAvg); + m_stats.forcedDrop++; + Drop (p); + if (m_isNs1Compat) + { + m_count = 0; + m_countBytes = 0; + } + return false; + } + + m_bytesInQueue += p->GetSize (); + m_packets.push_back (p); + + NS_LOG_LOGIC ("Number packets " << m_packets.size ()); + NS_LOG_LOGIC ("Number bytes " << m_bytesInQueue); + + return true; +} + +/* + * Note: if the link bandwidth changes in the course of the + * simulation, the bandwidth-dependent RED parameters do not change. + * This should be fixed, but it would require some extra parameters, + * and didn't seem worth the trouble... + */ +void +RedQueue::InitializeParams (void) +{ + NS_LOG_FUNCTION_NOARGS (); + + m_stats.forcedDrop = 0; + m_stats.unforcedDrop = 0; + m_stats.qLimDrop = 0; + + m_cautious = 0; + m_ptc = m_linkBandwidth.GetBitRate () / (8.0 * m_meanPktSize); + + m_qAvg = 0.0; + m_count = 0; + m_countBytes = 0; + m_old = 0; + m_idle = 1; + + double th_diff = (m_maxTh - m_minTh); + if (th_diff == 0) + { + th_diff = 1.0; + } + m_vA = 1.0 / th_diff; + m_curMaxP = 1.0 / m_lInterm; + m_vB = - m_minTh / th_diff; + + if (m_isGentle) + { + m_vC = (1.0 - m_curMaxP) / m_maxTh; + m_vD = 2.0 * m_curMaxP - 1.0; + } + m_idleTime = NanoSeconds (0); + +/* + * If m_qW=0, set it to a reasonable value of 1-exp(-1/C) + * This corresponds to choosing m_qW to be of that value for + * which the packet time constant -1/ln(1-m)qW) per default RTT + * of 100ms is an order of magnitude more than the link capacity, C. + * + * If m_qW=-1, then the queue weight is set to be a function of + * the bandwidth and the link propagation delay. In particular, + * the default RTT is assumed to be three times the link delay and + * transmission delay, if this gives a default RTT greater than 100 ms. + * + * If m_qW=-2, set it to a reasonable value of 1-exp(-10/C). + */ + if (m_qW == 0.0) + { + m_qW = 1.0 - exp(-1.0 / m_ptc); + } + else if (m_qW == -1.0) + { + double rtt = 3.0 * (m_linkDelay.GetSeconds () + 1.0 / m_ptc); + + if (rtt < 0.1) + { + rtt = 0.1; + } + m_qW = 1.0 - exp(-1.0 / (10 * rtt * m_ptc)); + } + else if (m_qW == -2.0) + { + m_qW = 1.0 - exp(-10.0 / m_ptc); + } + + // TODO: implement adaptive RED + + NS_LOG_DEBUG ("\tm_delay " << m_linkDelay.GetSeconds () << "; m_isWait " << m_isWait << "; m_qW " << m_qW << "; m_ptc " << m_ptc << "; m_minTh " << m_minTh << "; m_maxTh " << m_maxTh << "; m_isGentle " << m_isGentle << "; th_diff " << th_diff << "; lInterm " << m_lInterm << "; va " << m_vA << "; cur_max_p " << m_curMaxP << "; v_b " << m_vB << "; m_vC " << m_vC << "; m_vD " << m_vD); +} + +// Compute the average queue size +double +RedQueue::Estimator (uint32_t nQueued, uint32_t m, double qAvg, double qW) +{ + NS_LOG_FUNCTION (this << nQueued << m << qAvg << qW); + double newAve; + + newAve = qAvg; + while (--m >= 1) + { + newAve *= 1.0 - qW; + } + newAve *= 1.0 - qW; + newAve += qW * nQueued; + + // TODO: implement adaptive RED + + return newAve; +} + +// Check if packet p needs to be dropped due to probability mark +uint32_t +RedQueue::DropEarly (Ptr p, uint32_t qSize) +{ + NS_LOG_FUNCTION (this << p << qSize); + m_vProb1 = CalculatePNew(m_qAvg, m_maxTh, m_isGentle, m_vA, m_vB, m_vC, m_vD, m_curMaxP); + m_vProb = ModifyP(m_vProb1, m_count, m_countBytes, m_meanPktSize, m_isWait, p->GetSize ()); + + // Drop probability is computed, pick random number and act + if (m_cautious == 1) + { + /* + * Don't drop/mark if the instantaneous queue is much below the average. + * For experimental purposes only. + * pkts: the number of packets arriving in 50 ms + */ + double pkts = m_ptc * 0.05; + double fraction = pow ((1 - m_qW), pkts); + + if ((double) qSize < fraction * m_qAvg) + { + // Queue could have been empty for 0.05 seconds + return 0; + } + } + + UniformVariable uV; + double u = uV.GetValue (); + + if (m_cautious == 2) + { + /* + * Decrease the drop probability if the instantaneous + * queue is much below the average. + * For experimental purposes only. + * pkts: the number of packets arriving in 50 ms + */ + double pkts = m_ptc * 0.05; + double fraction = pow ((1 - m_qW), pkts); + double ratio = qSize / (fraction * m_qAvg); + + if (ratio < 1.0) + { + u *= 1.0 / ratio; + } + } + + if (u <= m_vProb) + { + NS_LOG_LOGIC ("u <= m_vProb; u " << u << "; m_vProb " << m_vProb); + + // DROP or MARK + m_count = 0; + m_countBytes = 0; + // TODO: Implement set bit to mark + + return 1; // drop + } + + return 0; // no drop/mark +} + +// Returns a probability using these function parameters for the DropEarly funtion +double +RedQueue::CalculatePNew (double qAvg, double maxTh, bool isGentle, double vA, + double vB, double vC, double vD, double maxP) +{ + NS_LOG_FUNCTION (this << qAvg << maxTh << isGentle << vA << vB << vC << vD << maxP); + double p; + + if (isGentle && qAvg >= maxTh) + { + // p ranges from maxP to 1 as the average queue + // Size ranges from maxTh to twice maxTh + p = vC * qAvg + vD; + } + else if (!isGentle && qAvg >= maxTh) + { + /* + * OLD: p continues to range linearly above max_p as + * the average queue size ranges above th_max. + * NEW: p is set to 1.0 + */ + p = 1.0; + } + else + { + /* + * p ranges from 0 to max_p as the average queue size ranges from + * th_min to th_max + */ + p = vA * qAvg + vB; + p *= maxP; + } + + if (p > 1.0) + { + p = 1.0; + } + + return p; +} + +// Returns a probability using these function parameters for the DropEarly funtion +double +RedQueue::ModifyP (double p, uint32_t count, uint32_t countBytes, + uint32_t meanPktSize, bool isWait, uint32_t size) +{ + NS_LOG_FUNCTION (this << p << count << countBytes << meanPktSize << isWait << size); + double count1 = (double) count; + + if (GetMode () == BYTES) + { + count1 = (double) (countBytes / meanPktSize); + } + + if (isWait) + { + if (count1 * p < 1.0) + { + p = 0.0; + } + else if (count1 * p < 2.0) + { + p /= (2.0 - count1 * p); + } + else + { + p = 1.0; + } + } + else + { + if (count1 * p < 1.0) + { + p /= (1.0 - count1 * p); + } + else + { + p = 1.0; + } + } + + if ((GetMode () == BYTES) && (p < 1.0)) + { + p = (p * size) / meanPktSize; + } + + if (p > 1.0) + { + p = 1.0; + } + + return p; +} + +uint32_t +RedQueue::GetQueueSize (void) +{ + NS_LOG_FUNCTION_NOARGS (); + if (GetMode () == BYTES) + { + return m_bytesInQueue; + } + else if (GetMode () == PACKETS) + { + return m_packets.size (); + } + else + { + NS_ABORT_MSG ("Unknown RED mode."); + } +} + +Ptr +RedQueue::DoDequeue (void) +{ + NS_LOG_FUNCTION (this); + + if (m_packets.empty ()) + { + NS_LOG_LOGIC ("Queue empty"); + m_idle = 1; + m_idleTime = Simulator::Now (); + + return 0; + } + else + { + m_idle = 0; + Ptr p = m_packets.front (); + m_packets.pop_front (); + m_bytesInQueue -= p->GetSize (); + + NS_LOG_LOGIC ("Popped " << p); + + NS_LOG_LOGIC ("Number packets " << m_packets.size ()); + NS_LOG_LOGIC ("Number bytes " << m_bytesInQueue); + + return p; + } +} + +Ptr +RedQueue::DoPeek (void) const +{ + NS_LOG_FUNCTION (this); + if (m_packets.empty ()) + { + NS_LOG_LOGIC ("Queue empty"); + return 0; + } + + Ptr p = m_packets.front (); + + NS_LOG_LOGIC ("Number packets " << m_packets.size ()); + NS_LOG_LOGIC ("Number bytes " << m_bytesInQueue); + + return p; +} + +} // namespace ns3 diff --git a/src/network/utils/red-queue.h b/src/network/utils/red-queue.h new file mode 100644 index 000000000..ce45848c7 --- /dev/null +++ b/src/network/utils/red-queue.h @@ -0,0 +1,261 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * This code was ported from NS-2.34, with licence: + * + * Copyright (c) 1990-1997 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Computer Systems + * Engineering Group at Lawrence Berkeley Laboratory. + * 4. Neither the name of the University nor of the Laboratory may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * This port: + * + * Copyright © 2011 Marcos Talau + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see . + * + * Author: Marcos Talau (talau@users.sourceforge.net) + * + * Thanks to: Duy Nguyen by RED efforts in NS3 + * + */ + +/* + * PORT NOTE: Almost all comments also been ported from NS-2 + */ + +#ifndef RED_QUEUE_H +#define RED_QUEUE_H + +#include +#include "ns3/packet.h" +#include "ns3/queue.h" +#include "ns3/nstime.h" +#include "ns3/random-variable.h" +#include "ns3/boolean.h" +#include "ns3/data-rate.h" +#include "ns3/nstime.h" + +namespace ns3 { + +class TraceContainer; + +/* + * \ingroup queue + * + * \brief A RED packet queue + */ +class RedQueue : public Queue +{ +public: + static TypeId GetTypeId (void); + /* + * \brief RedQueue Constructor + * + * Create a RED queue + */ + RedQueue (); + + virtual ~RedQueue (); + + typedef struct + { + // Early probability drops + uint32_t unforcedDrop; + // Forced drops, qavg > max threshold + uint32_t forcedDrop; + // Drops due to queue limits + uint32_t qLimDrop; + } Stats; + + // Drop types + enum + { + DTYPE_NONE, // Ok, no drop + DTYPE_FORCED, // A "forced" drop + DTYPE_UNFORCED, // An "unforced" (random) drop + }; + + /* + * Enumeration of the modes supported in the class. + * + */ + enum Mode + { + ILLEGAL, // Mode not set + PACKETS, // Use number of packets for maximum queue size + BYTES, // Use number of bytes for maximum queue size + }; + + /* + * Set the operating mode of this device. + * + * \param mode The operating mode of this device. + */ + void SetMode (RedQueue::Mode mode); + + /* + * Get the encapsulation mode of this device. + * + * \returns The encapsulation mode of this device. + */ + RedQueue::Mode GetMode (void); + + /* + * Get the current value of the queue in bytes or packets. + * + * \returns The queue size in bytes or packets. + */ + uint32_t GetQueueSize (void); + + /* + * Set the limit of the queue. + * + * \param lim The limit in bytes or packets. + */ + void SetQueueLimit(uint32_t lim); + + /* + * Set the thresh limits of RED. + * + * \param min Minimum thresh in bytes or packets. + * \param max Maximum thresh in bytes or packets. + */ + void SetTh(double minTh, double maxTh); + + /* + * Get the RED statistics after running. + * + * \returns The drop statistics. + */ + Stats GetStats(); + +private: + virtual bool DoEnqueue (Ptr p); + virtual Ptr DoDequeue (void); + virtual Ptr DoPeek (void) const; + + // ... + void InitializeParams (void); + // Compute the average queue size + double Estimator (uint32_t nQueued, uint32_t m, double qAvg, double qW); + // Check if packet p needs to be dropped due to probability mark + uint32_t DropEarly (Ptr p, uint32_t qSize); + // Returns a probability using these function parameters for the DropEarly funtion + double CalculatePNew (double qAvg, double maxTh, bool gentle, double vA, + double vB, double vC, double vD, double maxP); + // Returns a probability using these function parameters for the DropEarly funtion + double ModifyP(double p, uint32_t count, uint32_t countBytes, + uint32_t meanPktSize, bool wait, uint32_t size); + + std::list > m_packets; + + uint32_t m_bytesInQueue; + bool m_hasRedStarted; + Stats m_stats; + + // ** Variables supplied by user + // Bytes or packets? + Mode m_mode; + // Avg pkt size + uint32_t m_meanPktSize; + // Avg pkt size used during idle times + uint32_t m_idlePktSize; + // True for waiting between dropped packets + bool m_isWait; + // True to increases dropping prob. slowly when ave queue exceeds maxthresh + bool m_isGentle; + // Min avg length threshold (bytes) + double m_minTh; + // Max avg length threshold (bytes), should be >= 2*minTh + double m_maxTh; + // Queue limit in bytes / packets + uint32_t m_queueLimit; + // Queue weight given to cur q size sample + double m_qW; + // The max probability of dropping a packet + double m_lInterm; + // Ns-1 compatibility + bool m_isNs1Compat; + // Link bandwidth + DataRate m_linkBandwidth; + // Link delay + Time m_linkDelay; + + // ** Variables maintained by RED + // Prob. of packet drop before "count" + double m_vProb1; + // v_prob = v_a * v_ave + v_b + double m_vA; + double m_vB; + // Used for "gentle" mode + double m_vC; + // Used for "gentle" mode + double m_vD; + // Current max_p + double m_curMaxP; + // Prob. of packet drop + double m_vProb; + // # of bytes since last drop + uint32_t m_countBytes; + // 0 when average queue first exceeds thresh + uint32_t m_old; + // 0/1 idle status + uint32_t m_idle; + // packet time constant in packets/second + double m_ptc; + // Average queue length + double m_qAvg; + // number of packets since last random number generation + uint32_t m_count; + /* + * 0 for default RED + * 1 experimental (see red-queue.cc) + * 2 experimental (see red-queue.cc) + * 3 use Idle packet size in the ptc + */ + uint32_t m_cautious; + // Start of current idle period + Time m_idleTime; +}; + +}; // namespace ns3 + +#endif // RED_QUEUE_H diff --git a/src/network/wscript b/src/network/wscript index d36cfe41e..5745b43a7 100644 --- a/src/network/wscript +++ b/src/network/wscript @@ -47,6 +47,7 @@ def build(bld): 'utils/pcap-file-wrapper.cc', 'utils/queue.cc', 'utils/radiotap-header.cc', + 'utils/red-queue.cc', 'utils/simple-channel.cc', 'utils/simple-net-device.cc', 'helper/application-container.cc', @@ -64,6 +65,7 @@ def build(bld): 'test/packet-test-suite.cc', 'test/packet-metadata-test.cc', 'test/pcap-file-test-suite.cc', + 'test/red-queue-test-suite.cc', 'test/sequence-number-test-suite.cc', ] @@ -115,6 +117,7 @@ def build(bld): 'utils/generic-phy.h', 'utils/queue.h', 'utils/radiotap-header.h', + 'utils/red-queue.h', 'utils/sequence-number.h', 'utils/sgi-hashmap.h', 'utils/simple-channel.h',