A Discrete-Event Network Simulator
API
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
red-queue.cc
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright © 2011 Marcos Talau
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation;
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  *
18  * Author: Marcos Talau (talau@users.sourceforge.net)
19  *
20  * Thanks to: Duy Nguyen<duy@soe.ucsc.edu> by RED efforts in NS3
21  *
22  *
23  * This file incorporates work covered by the following copyright and
24  * permission notice:
25  *
26  * Copyright (c) 1990-1997 Regents of the University of California.
27  * All rights reserved.
28  *
29  * Redistribution and use in source and binary forms, with or without
30  * modification, are permitted provided that the following conditions
31  * are met:
32  * 1. Redistributions of source code must retain the above copyright
33  * notice, this list of conditions and the following disclaimer.
34  * 2. Redistributions in binary form must reproduce the above copyright
35  * notice, this list of conditions and the following disclaimer in the
36  * documentation and/or other materials provided with the distribution.
37  * 3. Neither the name of the University nor of the Laboratory may be used
38  * to endorse or promote products derived from this software without
39  * specific prior written permission.
40  *
41  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
42  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44  * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
45  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
46  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
47  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
49  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
50  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51  * SUCH DAMAGE.
52  */
53 
54 /*
55  * PORT NOTE: This code was ported from ns-2 (queue/red.cc). Almost all
56  * comments have also been ported from NS-2
57  */
58 
59 #include "ns3/log.h"
60 #include "ns3/enum.h"
61 #include "ns3/uinteger.h"
62 #include "ns3/double.h"
63 #include "ns3/simulator.h"
64 #include "ns3/abort.h"
65 #include "ns3/random-variable-stream.h"
66 #include "red-queue.h"
67 
68 NS_LOG_COMPONENT_DEFINE ("RedQueue");
69 
70 namespace ns3 {
71 
72 NS_OBJECT_ENSURE_REGISTERED (RedQueue);
73 
74 TypeId RedQueue::GetTypeId (void)
75 {
76  static TypeId tid = TypeId ("ns3::RedQueue")
77  .SetParent<Queue> ()
78  .AddConstructor<RedQueue> ()
79  .AddAttribute ("Mode",
80  "Determines unit for QueueLimit",
81  EnumValue (QUEUE_MODE_PACKETS),
82  MakeEnumAccessor (&RedQueue::SetMode),
83  MakeEnumChecker (QUEUE_MODE_BYTES, "QUEUE_MODE_BYTES",
84  QUEUE_MODE_PACKETS, "QUEUE_MODE_PACKETS"))
85  .AddAttribute ("MeanPktSize",
86  "Average of packet size",
87  UintegerValue (500),
88  MakeUintegerAccessor (&RedQueue::m_meanPktSize),
89  MakeUintegerChecker<uint32_t> ())
90  .AddAttribute ("IdlePktSize",
91  "Average packet size used during idle times. Used when m_cautions = 3",
92  UintegerValue (0),
93  MakeUintegerAccessor (&RedQueue::m_idlePktSize),
94  MakeUintegerChecker<uint32_t> ())
95  .AddAttribute ("Wait",
96  "True for waiting between dropped packets",
97  BooleanValue (true),
98  MakeBooleanAccessor (&RedQueue::m_isWait),
99  MakeBooleanChecker ())
100  .AddAttribute ("Gentle",
101  "True to increases dropping probability slowly when average queue exceeds maxthresh",
102  BooleanValue (true),
103  MakeBooleanAccessor (&RedQueue::m_isGentle),
104  MakeBooleanChecker ())
105  .AddAttribute ("MinTh",
106  "Minimum average length threshold in packets/bytes",
107  DoubleValue (5),
108  MakeDoubleAccessor (&RedQueue::m_minTh),
109  MakeDoubleChecker<double> ())
110  .AddAttribute ("MaxTh",
111  "Maximum average length threshold in packets/bytes",
112  DoubleValue (15),
113  MakeDoubleAccessor (&RedQueue::m_maxTh),
114  MakeDoubleChecker<double> ())
115  .AddAttribute ("QueueLimit",
116  "Queue limit in bytes/packets",
117  UintegerValue (25),
118  MakeUintegerAccessor (&RedQueue::m_queueLimit),
119  MakeUintegerChecker<uint32_t> ())
120  .AddAttribute ("QW",
121  "Queue weight related to the exponential weighted moving average (EWMA)",
122  DoubleValue (0.002),
123  MakeDoubleAccessor (&RedQueue::m_qW),
124  MakeDoubleChecker <double> ())
125  .AddAttribute ("LInterm",
126  "The maximum probability of dropping a packet",
127  DoubleValue (50),
128  MakeDoubleAccessor (&RedQueue::m_lInterm),
129  MakeDoubleChecker <double> ())
130  .AddAttribute ("Ns1Compat",
131  "NS-1 compatibility",
132  BooleanValue (false),
133  MakeBooleanAccessor (&RedQueue::m_isNs1Compat),
134  MakeBooleanChecker ())
135  .AddAttribute ("LinkBandwidth",
136  "The RED link bandwidth",
137  DataRateValue (DataRate ("1.5Mbps")),
138  MakeDataRateAccessor (&RedQueue::m_linkBandwidth),
139  MakeDataRateChecker ())
140  .AddAttribute ("LinkDelay",
141  "The RED link delay",
142  TimeValue (MilliSeconds (20)),
143  MakeTimeAccessor (&RedQueue::m_linkDelay),
144  MakeTimeChecker ())
145  ;
146 
147  return tid;
148 }
149 
150 RedQueue::RedQueue () :
151  Queue (),
152  m_packets (),
153  m_bytesInQueue (0),
154  m_hasRedStarted (false)
155 {
156  NS_LOG_FUNCTION (this);
157  m_uv = CreateObject<UniformRandomVariable> ();
158 }
159 
160 RedQueue::~RedQueue ()
161 {
162  NS_LOG_FUNCTION (this);
163 }
164 
165 void
166 RedQueue::SetMode (RedQueue::QueueMode mode)
167 {
168  NS_LOG_FUNCTION (this << mode);
169  m_mode = mode;
170 }
171 
173 RedQueue::GetMode (void)
174 {
175  NS_LOG_FUNCTION (this);
176  return m_mode;
177 }
178 
179 void
180 RedQueue::SetQueueLimit (uint32_t lim)
181 {
182  NS_LOG_FUNCTION (this <<lim);
183  m_queueLimit = lim;
184 }
185 
186 void
187 RedQueue::SetTh (double minTh, double maxTh)
188 {
189  NS_LOG_FUNCTION (this << minTh << maxTh);
190  NS_ASSERT (minTh <= maxTh);
191  m_minTh = minTh;
192  m_maxTh = maxTh;
193 }
194 
195 RedQueue::Stats
196 RedQueue::GetStats ()
197 {
198  NS_LOG_FUNCTION (this);
199  return m_stats;
200 }
201 
202 int64_t
203 RedQueue::AssignStreams (int64_t stream)
204 {
205  NS_LOG_FUNCTION (this << stream);
206  m_uv->SetStream (stream);
207  return 1;
208 }
209 
210 bool
211 RedQueue::DoEnqueue (Ptr<Packet> p)
212 {
213  NS_LOG_FUNCTION (this << p);
214 
215  if (!m_hasRedStarted )
216  {
217  NS_LOG_INFO ("Initializing RED params.");
218  InitializeParams ();
219  m_hasRedStarted = true;
220  }
221 
222  uint32_t nQueued = 0;
223 
224  if (GetMode () == QUEUE_MODE_BYTES)
225  {
226  NS_LOG_DEBUG ("Enqueue in bytes mode");
227  nQueued = m_bytesInQueue;
228  }
229  else if (GetMode () == QUEUE_MODE_PACKETS)
230  {
231  NS_LOG_DEBUG ("Enqueue in packets mode");
232  nQueued = m_packets.size ();
233  }
234 
235  // simulate number of packets arrival during idle period
236  uint32_t m = 0;
237 
238  if (m_idle == 1)
239  {
240  NS_LOG_DEBUG ("RED Queue is idle.");
241  Time now = Simulator::Now ();
242 
243  if (m_cautious == 3)
244  {
245  double ptc = m_ptc * m_meanPktSize / m_idlePktSize;
246  m = uint32_t (ptc * (now - m_idleTime).GetSeconds ());
247  }
248  else
249  {
250  m = uint32_t (m_ptc * (now - m_idleTime).GetSeconds ());
251  }
252 
253  m_idle = 0;
254  }
255 
256  m_qAvg = Estimator (nQueued, m + 1, m_qAvg, m_qW);
257 
258  NS_LOG_DEBUG ("\t bytesInQueue " << m_bytesInQueue << "\tQavg " << m_qAvg);
259  NS_LOG_DEBUG ("\t packetsInQueue " << m_packets.size () << "\tQavg " << m_qAvg);
260 
261  m_count++;
262  m_countBytes += p->GetSize ();
263 
264  uint32_t dropType = DTYPE_NONE;
265  if (m_qAvg >= m_minTh && nQueued > 1)
266  {
267  if ((!m_isGentle && m_qAvg >= m_maxTh) ||
268  (m_isGentle && m_qAvg >= 2 * m_maxTh))
269  {
270  NS_LOG_DEBUG ("adding DROP FORCED MARK");
271  dropType = DTYPE_FORCED;
272  }
273  else if (m_old == 0)
274  {
275  /*
276  * The average queue size has just crossed the
277  * threshold from below to above "minthresh", or
278  * from above "minthresh" with an empty queue to
279  * above "minthresh" with a nonempty queue.
280  */
281  m_count = 1;
282  m_countBytes = p->GetSize ();
283  m_old = 1;
284  }
285  else if (DropEarly (p, nQueued))
286  {
287  NS_LOG_LOGIC ("DropEarly returns 1");
288  dropType = DTYPE_UNFORCED;
289  }
290  }
291  else
292  {
293  // No packets are being dropped
294  m_vProb = 0.0;
295  m_old = 0;
296  }
297 
298  if (nQueued >= m_queueLimit)
299  {
300  NS_LOG_DEBUG ("\t Dropping due to Queue Full " << nQueued);
301  dropType = DTYPE_FORCED;
302  m_stats.qLimDrop++;
303  }
304 
305  if (dropType == DTYPE_UNFORCED)
306  {
307  NS_LOG_DEBUG ("\t Dropping due to Prob Mark " << m_qAvg);
308  m_stats.unforcedDrop++;
309  Drop (p);
310  return false;
311  }
312  else if (dropType == DTYPE_FORCED)
313  {
314  NS_LOG_DEBUG ("\t Dropping due to Hard Mark " << m_qAvg);
315  m_stats.forcedDrop++;
316  Drop (p);
317  if (m_isNs1Compat)
318  {
319  m_count = 0;
320  m_countBytes = 0;
321  }
322  return false;
323  }
324 
325  m_bytesInQueue += p->GetSize ();
326  m_packets.push_back (p);
327 
328  NS_LOG_LOGIC ("Number packets " << m_packets.size ());
329  NS_LOG_LOGIC ("Number bytes " << m_bytesInQueue);
330 
331  return true;
332 }
333 
334 /*
335  * Note: if the link bandwidth changes in the course of the
336  * simulation, the bandwidth-dependent RED parameters do not change.
337  * This should be fixed, but it would require some extra parameters,
338  * and didn't seem worth the trouble...
339  */
340 void
341 RedQueue::InitializeParams (void)
342 {
343  NS_LOG_FUNCTION (this);
344 
345  NS_ASSERT (m_minTh <= m_maxTh);
346  m_stats.forcedDrop = 0;
347  m_stats.unforcedDrop = 0;
348  m_stats.qLimDrop = 0;
349 
350  m_cautious = 0;
351  m_ptc = m_linkBandwidth.GetBitRate () / (8.0 * m_meanPktSize);
352 
353  m_qAvg = 0.0;
354  m_count = 0;
355  m_countBytes = 0;
356  m_old = 0;
357  m_idle = 1;
358 
359  double th_diff = (m_maxTh - m_minTh);
360  if (th_diff == 0)
361  {
362  th_diff = 1.0;
363  }
364  m_vA = 1.0 / th_diff;
365  m_curMaxP = 1.0 / m_lInterm;
366  m_vB = -m_minTh / th_diff;
367 
368  if (m_isGentle)
369  {
370  m_vC = (1.0 - m_curMaxP) / m_maxTh;
371  m_vD = 2.0 * m_curMaxP - 1.0;
372  }
373  m_idleTime = NanoSeconds (0);
374 
375 /*
376  * If m_qW=0, set it to a reasonable value of 1-exp(-1/C)
377  * This corresponds to choosing m_qW to be of that value for
378  * which the packet time constant -1/ln(1-m)qW) per default RTT
379  * of 100ms is an order of magnitude more than the link capacity, C.
380  *
381  * If m_qW=-1, then the queue weight is set to be a function of
382  * the bandwidth and the link propagation delay. In particular,
383  * the default RTT is assumed to be three times the link delay and
384  * transmission delay, if this gives a default RTT greater than 100 ms.
385  *
386  * If m_qW=-2, set it to a reasonable value of 1-exp(-10/C).
387  */
388  if (m_qW == 0.0)
389  {
390  m_qW = 1.0 - std::exp (-1.0 / m_ptc);
391  }
392  else if (m_qW == -1.0)
393  {
394  double rtt = 3.0 * (m_linkDelay.GetSeconds () + 1.0 / m_ptc);
395 
396  if (rtt < 0.1)
397  {
398  rtt = 0.1;
399  }
400  m_qW = 1.0 - std::exp (-1.0 / (10 * rtt * m_ptc));
401  }
402  else if (m_qW == -2.0)
403  {
404  m_qW = 1.0 - std::exp (-10.0 / m_ptc);
405  }
406 
407  // TODO: implement adaptive RED
408 
409  NS_LOG_DEBUG ("\tm_delay " << m_linkDelay.GetSeconds () << "; m_isWait "
410  << m_isWait << "; m_qW " << m_qW << "; m_ptc " << m_ptc
411  << "; m_minTh " << m_minTh << "; m_maxTh " << m_maxTh
412  << "; m_isGentle " << m_isGentle << "; th_diff " << th_diff
413  << "; lInterm " << m_lInterm << "; va " << m_vA << "; cur_max_p "
414  << m_curMaxP << "; v_b " << m_vB << "; m_vC "
415  << m_vC << "; m_vD " << m_vD);
416 }
417 
418 // Compute the average queue size
419 double
420 RedQueue::Estimator (uint32_t nQueued, uint32_t m, double qAvg, double qW)
421 {
422  NS_LOG_FUNCTION (this << nQueued << m << qAvg << qW);
423  double newAve;
424 
425  newAve = qAvg;
426  while (--m >= 1)
427  {
428  newAve *= 1.0 - qW;
429  }
430  newAve *= 1.0 - qW;
431  newAve += qW * nQueued;
432 
433  // TODO: implement adaptive RED
434 
435  return newAve;
436 }
437 
438 // Check if packet p needs to be dropped due to probability mark
439 uint32_t
440 RedQueue::DropEarly (Ptr<Packet> p, uint32_t qSize)
441 {
442  NS_LOG_FUNCTION (this << p << qSize);
443  m_vProb1 = CalculatePNew (m_qAvg, m_maxTh, m_isGentle, m_vA, m_vB, m_vC, m_vD, m_curMaxP);
444  m_vProb = ModifyP (m_vProb1, m_count, m_countBytes, m_meanPktSize, m_isWait, p->GetSize ());
445 
446  // Drop probability is computed, pick random number and act
447  if (m_cautious == 1)
448  {
449  /*
450  * Don't drop/mark if the instantaneous queue is much below the average.
451  * For experimental purposes only.
452  * pkts: the number of packets arriving in 50 ms
453  */
454  double pkts = m_ptc * 0.05;
455  double fraction = std::pow ((1 - m_qW), pkts);
456 
457  if ((double) qSize < fraction * m_qAvg)
458  {
459  // Queue could have been empty for 0.05 seconds
460  return 0;
461  }
462  }
463 
464  double u = m_uv->GetValue ();
465 
466  if (m_cautious == 2)
467  {
468  /*
469  * Decrease the drop probability if the instantaneous
470  * queue is much below the average.
471  * For experimental purposes only.
472  * pkts: the number of packets arriving in 50 ms
473  */
474  double pkts = m_ptc * 0.05;
475  double fraction = std::pow ((1 - m_qW), pkts);
476  double ratio = qSize / (fraction * m_qAvg);
477 
478  if (ratio < 1.0)
479  {
480  u *= 1.0 / ratio;
481  }
482  }
483 
484  if (u <= m_vProb)
485  {
486  NS_LOG_LOGIC ("u <= m_vProb; u " << u << "; m_vProb " << m_vProb);
487 
488  // DROP or MARK
489  m_count = 0;
490  m_countBytes = 0;
491  // TODO: Implement set bit to mark
492 
493  return 1; // drop
494  }
495 
496  return 0; // no drop/mark
497 }
498 
499 // Returns a probability using these function parameters for the DropEarly funtion
500 double
501 RedQueue::CalculatePNew (double qAvg, double maxTh, bool isGentle, double vA,
502  double vB, double vC, double vD, double maxP)
503 {
504  NS_LOG_FUNCTION (this << qAvg << maxTh << isGentle << vA << vB << vC << vD << maxP);
505  double p;
506 
507  if (isGentle && qAvg >= maxTh)
508  {
509  // p ranges from maxP to 1 as the average queue
510  // Size ranges from maxTh to twice maxTh
511  p = vC * qAvg + vD;
512  }
513  else if (!isGentle && qAvg >= maxTh)
514  {
515  /*
516  * OLD: p continues to range linearly above max_p as
517  * the average queue size ranges above th_max.
518  * NEW: p is set to 1.0
519  */
520  p = 1.0;
521  }
522  else
523  {
524  /*
525  * p ranges from 0 to max_p as the average queue size ranges from
526  * th_min to th_max
527  */
528  p = vA * qAvg + vB;
529  p *= maxP;
530  }
531 
532  if (p > 1.0)
533  {
534  p = 1.0;
535  }
536 
537  return p;
538 }
539 
540 // Returns a probability using these function parameters for the DropEarly funtion
541 double
542 RedQueue::ModifyP (double p, uint32_t count, uint32_t countBytes,
543  uint32_t meanPktSize, bool isWait, uint32_t size)
544 {
545  NS_LOG_FUNCTION (this << p << count << countBytes << meanPktSize << isWait << size);
546  double count1 = (double) count;
547 
548  if (GetMode () == QUEUE_MODE_BYTES)
549  {
550  count1 = (double) (countBytes / meanPktSize);
551  }
552 
553  if (isWait)
554  {
555  if (count1 * p < 1.0)
556  {
557  p = 0.0;
558  }
559  else if (count1 * p < 2.0)
560  {
561  p /= (2.0 - count1 * p);
562  }
563  else
564  {
565  p = 1.0;
566  }
567  }
568  else
569  {
570  if (count1 * p < 1.0)
571  {
572  p /= (1.0 - count1 * p);
573  }
574  else
575  {
576  p = 1.0;
577  }
578  }
579 
580  if ((GetMode () == QUEUE_MODE_BYTES) && (p < 1.0))
581  {
582  p = (p * size) / meanPktSize;
583  }
584 
585  if (p > 1.0)
586  {
587  p = 1.0;
588  }
589 
590  return p;
591 }
592 
593 uint32_t
594 RedQueue::GetQueueSize (void)
595 {
596  NS_LOG_FUNCTION (this);
597  if (GetMode () == QUEUE_MODE_BYTES)
598  {
599  return m_bytesInQueue;
600  }
601  else if (GetMode () == QUEUE_MODE_PACKETS)
602  {
603  return m_packets.size ();
604  }
605  else
606  {
607  NS_ABORT_MSG ("Unknown RED mode.");
608  }
609 }
610 
611 Ptr<Packet>
612 RedQueue::DoDequeue (void)
613 {
614  NS_LOG_FUNCTION (this);
615 
616  if (m_packets.empty ())
617  {
618  NS_LOG_LOGIC ("Queue empty");
619  m_idle = 1;
620  m_idleTime = Simulator::Now ();
621 
622  return 0;
623  }
624  else
625  {
626  m_idle = 0;
627  Ptr<Packet> p = m_packets.front ();
628  m_packets.pop_front ();
629  m_bytesInQueue -= p->GetSize ();
630 
631  NS_LOG_LOGIC ("Popped " << p);
632 
633  NS_LOG_LOGIC ("Number packets " << m_packets.size ());
634  NS_LOG_LOGIC ("Number bytes " << m_bytesInQueue);
635 
636  return p;
637  }
638 }
639 
640 Ptr<const Packet>
641 RedQueue::DoPeek (void) const
642 {
643  NS_LOG_FUNCTION (this);
644  if (m_packets.empty ())
645  {
646  NS_LOG_LOGIC ("Queue empty");
647  return 0;
648  }
649 
650  Ptr<Packet> p = m_packets.front ();
651 
652  NS_LOG_LOGIC ("Number packets " << m_packets.size ());
653  NS_LOG_LOGIC ("Number bytes " << m_bytesInQueue);
654 
655  return p;
656 }
657 
658 } // namespace ns3
Time NanoSeconds(uint64_t ns)
create ns3::Time instances in units of nanoseconds.
Definition: nstime.h:629
#define NS_LOG_FUNCTION(parameters)
Definition: log.h:311
void SetStream(int64_t stream)
Specifies the stream number for this RNG stream.
#define NS_ASSERT(condition)
Definition: assert.h:64
#define NS_LOG_COMPONENT_DEFINE(name)
Definition: log.h:122
uint32_t GetSize(void) const
Definition: packet.h:620
#define NS_LOG_INFO(msg)
Definition: log.h:264
double GetSeconds(void) const
Definition: nstime.h:262
int64_t AssignStreams(int64_t stream)
Definition: red-queue.cc:203
#define NS_LOG_LOGIC(msg)
Definition: log.h:334
double GetValue(double min, double max)
Returns a random double from the uniform distribution with the specified range.
uint64_t GetBitRate() const
Definition: data-rate.cc:235
static Time Now(void)
Definition: simulator.cc:179
QueueMode
Enumeration of the modes supported in the class.
Definition: queue.h:122
#define NS_ABORT_MSG(msg)
Abnormal program termination.
Definition: abort.h:43
#define NS_LOG_DEBUG(msg)
Definition: log.h:255
Time MilliSeconds(uint64_t ms)
create ns3::Time instances in units of milliseconds.
Definition: nstime.h:601