/* -*- 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 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: Marcos Talau (talau@users.sourceforge.net) * * Thanks to: Duy Nguyen by RED efforts in NS3 * * * This file incorporates work covered by the following copyright and * permission notice: * * 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. 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. */ /* * PORT NOTE: This code was ported from ns-2 (queue/red.cc). 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", "Determines unit for QueueLimit", EnumValue (QUEUE_MODE_PACKETS), MakeEnumAccessor (&RedQueue::SetMode), MakeEnumChecker (QUEUE_MODE_BYTES, "QUEUE_MODE_BYTES", QUEUE_MODE_PACKETS, "QUEUE_MODE_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 (RedQueue::QueueMode mode) { NS_LOG_FUNCTION (mode); m_mode = mode; } RedQueue::QueueMode 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 = 0; if (GetMode () == QUEUE_MODE_BYTES) { NS_LOG_DEBUG ("Enqueue in bytes mode"); nQueued = m_bytesInQueue; } else if (GetMode () == QUEUE_MODE_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 (); NS_ASSERT (m_minTh <= m_maxTh); 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 () == QUEUE_MODE_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 () == QUEUE_MODE_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 () == QUEUE_MODE_BYTES) { return m_bytesInQueue; } else if (GetMode () == QUEUE_MODE_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