A Discrete-Event Network Simulator
API
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
red-queue.cc
Go to the documentation of this file.
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 
73 
75 {
76  static TypeId tid = TypeId ("ns3::RedQueue")
77  .SetParent<Queue> ()
78  .AddConstructor<RedQueue> ()
79  .AddAttribute ("Mode",
80  "Determines unit for QueueLimit",
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 
151  Queue (),
152  m_packets (),
153  m_bytesInQueue (0),
154  m_hasRedStarted (false)
155 {
157  m_uv = CreateObject<UniformRandomVariable> ();
158 }
159 
161 {
163 }
164 
165 void
167 {
168  NS_LOG_FUNCTION (mode);
169  m_mode = mode;
170 }
171 
174 {
176  return m_mode;
177 }
178 
179 void
181 {
182  NS_LOG_FUNCTION (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 
197 {
198  return m_stats;
199 }
200 
201 int64_t
202 RedQueue::AssignStreams (int64_t stream)
203 {
204  m_uv->SetStream (stream);
205  return 1;
206 }
207 
208 bool
210 {
211  NS_LOG_FUNCTION (this << p);
212 
213  if (!m_hasRedStarted )
214  {
215  NS_LOG_INFO ("Initializing RED params.");
216  InitializeParams ();
217  m_hasRedStarted = true;
218  }
219 
220  uint32_t nQueued = 0;
221 
222  if (GetMode () == QUEUE_MODE_BYTES)
223  {
224  NS_LOG_DEBUG ("Enqueue in bytes mode");
225  nQueued = m_bytesInQueue;
226  }
227  else if (GetMode () == QUEUE_MODE_PACKETS)
228  {
229  NS_LOG_DEBUG ("Enqueue in packets mode");
230  nQueued = m_packets.size ();
231  }
232 
233  // simulate number of packets arrival during idle period
234  uint32_t m = 0;
235 
236  if (m_idle == 1)
237  {
238  NS_LOG_DEBUG ("RED Queue is idle.");
239  Time now = Simulator::Now ();
240 
241  if (m_cautious == 3)
242  {
243  double ptc = m_ptc * m_meanPktSize / m_idlePktSize;
244  m = uint32_t (ptc * (now - m_idleTime).GetSeconds ());
245  }
246  else
247  {
248  m = uint32_t (m_ptc * (now - m_idleTime).GetSeconds ());
249  }
250 
251  m_idle = 0;
252  }
253 
254  m_qAvg = Estimator (nQueued, m + 1, m_qAvg, m_qW);
255 
256  NS_LOG_DEBUG ("\t bytesInQueue " << m_bytesInQueue << "\tQavg " << m_qAvg);
257  NS_LOG_DEBUG ("\t packetsInQueue " << m_packets.size () << "\tQavg " << m_qAvg);
258 
259  m_count++;
260  m_countBytes += p->GetSize ();
261 
262  uint32_t dropType = DTYPE_NONE;
263  if (m_qAvg >= m_minTh && nQueued > 1)
264  {
265  if ((!m_isGentle && m_qAvg >= m_maxTh) ||
266  (m_isGentle && m_qAvg >= 2 * m_maxTh))
267  {
268  NS_LOG_DEBUG ("adding DROP FORCED MARK");
269  dropType = DTYPE_FORCED;
270  }
271  else if (m_old == 0)
272  {
273  /*
274  * The average queue size has just crossed the
275  * threshold from below to above "minthresh", or
276  * from above "minthresh" with an empty queue to
277  * above "minthresh" with a nonempty queue.
278  */
279  m_count = 1;
280  m_countBytes = p->GetSize ();
281  m_old = 1;
282  }
283  else if (DropEarly (p, nQueued))
284  {
285  NS_LOG_LOGIC ("DropEarly returns 1");
286  dropType = DTYPE_UNFORCED;
287  }
288  }
289  else
290  {
291  // No packets are being dropped
292  m_vProb = 0.0;
293  m_old = 0;
294  }
295 
296  if (nQueued >= m_queueLimit)
297  {
298  NS_LOG_DEBUG ("\t Dropping due to Queue Full " << nQueued);
299  dropType = DTYPE_FORCED;
300  m_stats.qLimDrop++;
301  }
302 
303  if (dropType == DTYPE_UNFORCED)
304  {
305  NS_LOG_DEBUG ("\t Dropping due to Prob Mark " << m_qAvg);
307  Drop (p);
308  return false;
309  }
310  else if (dropType == DTYPE_FORCED)
311  {
312  NS_LOG_DEBUG ("\t Dropping due to Hard Mark " << m_qAvg);
314  Drop (p);
315  if (m_isNs1Compat)
316  {
317  m_count = 0;
318  m_countBytes = 0;
319  }
320  return false;
321  }
322 
323  m_bytesInQueue += p->GetSize ();
324  m_packets.push_back (p);
325 
326  NS_LOG_LOGIC ("Number packets " << m_packets.size ());
327  NS_LOG_LOGIC ("Number bytes " << m_bytesInQueue);
328 
329  return true;
330 }
331 
332 /*
333  * Note: if the link bandwidth changes in the course of the
334  * simulation, the bandwidth-dependent RED parameters do not change.
335  * This should be fixed, but it would require some extra parameters,
336  * and didn't seem worth the trouble...
337  */
338 void
340 {
342 
344  m_stats.forcedDrop = 0;
345  m_stats.unforcedDrop = 0;
346  m_stats.qLimDrop = 0;
347 
348  m_cautious = 0;
350 
351  m_qAvg = 0.0;
352  m_count = 0;
353  m_countBytes = 0;
354  m_old = 0;
355  m_idle = 1;
356 
357  double th_diff = (m_maxTh - m_minTh);
358  if (th_diff == 0)
359  {
360  th_diff = 1.0;
361  }
362  m_vA = 1.0 / th_diff;
363  m_curMaxP = 1.0 / m_lInterm;
364  m_vB = -m_minTh / th_diff;
365 
366  if (m_isGentle)
367  {
368  m_vC = (1.0 - m_curMaxP) / m_maxTh;
369  m_vD = 2.0 * m_curMaxP - 1.0;
370  }
371  m_idleTime = NanoSeconds (0);
372 
373 /*
374  * If m_qW=0, set it to a reasonable value of 1-exp(-1/C)
375  * This corresponds to choosing m_qW to be of that value for
376  * which the packet time constant -1/ln(1-m)qW) per default RTT
377  * of 100ms is an order of magnitude more than the link capacity, C.
378  *
379  * If m_qW=-1, then the queue weight is set to be a function of
380  * the bandwidth and the link propagation delay. In particular,
381  * the default RTT is assumed to be three times the link delay and
382  * transmission delay, if this gives a default RTT greater than 100 ms.
383  *
384  * If m_qW=-2, set it to a reasonable value of 1-exp(-10/C).
385  */
386  if (m_qW == 0.0)
387  {
388  m_qW = 1.0 - exp (-1.0 / m_ptc);
389  }
390  else if (m_qW == -1.0)
391  {
392  double rtt = 3.0 * (m_linkDelay.GetSeconds () + 1.0 / m_ptc);
393 
394  if (rtt < 0.1)
395  {
396  rtt = 0.1;
397  }
398  m_qW = 1.0 - exp (-1.0 / (10 * rtt * m_ptc));
399  }
400  else if (m_qW == -2.0)
401  {
402  m_qW = 1.0 - exp (-10.0 / m_ptc);
403  }
404 
405  // TODO: implement adaptive RED
406 
407  NS_LOG_DEBUG ("\tm_delay " << m_linkDelay.GetSeconds () << "; m_isWait "
408  << m_isWait << "; m_qW " << m_qW << "; m_ptc " << m_ptc
409  << "; m_minTh " << m_minTh << "; m_maxTh " << m_maxTh
410  << "; m_isGentle " << m_isGentle << "; th_diff " << th_diff
411  << "; lInterm " << m_lInterm << "; va " << m_vA << "; cur_max_p "
412  << m_curMaxP << "; v_b " << m_vB << "; m_vC "
413  << m_vC << "; m_vD " << m_vD);
414 }
415 
416 // Compute the average queue size
417 double
418 RedQueue::Estimator (uint32_t nQueued, uint32_t m, double qAvg, double qW)
419 {
420  NS_LOG_FUNCTION (this << nQueued << m << qAvg << qW);
421  double newAve;
422 
423  newAve = qAvg;
424  while (--m >= 1)
425  {
426  newAve *= 1.0 - qW;
427  }
428  newAve *= 1.0 - qW;
429  newAve += qW * nQueued;
430 
431  // TODO: implement adaptive RED
432 
433  return newAve;
434 }
435 
436 // Check if packet p needs to be dropped due to probability mark
437 uint32_t
438 RedQueue::DropEarly (Ptr<Packet> p, uint32_t qSize)
439 {
440  NS_LOG_FUNCTION (this << p << qSize);
443 
444  // Drop probability is computed, pick random number and act
445  if (m_cautious == 1)
446  {
447  /*
448  * Don't drop/mark if the instantaneous queue is much below the average.
449  * For experimental purposes only.
450  * pkts: the number of packets arriving in 50 ms
451  */
452  double pkts = m_ptc * 0.05;
453  double fraction = pow ((1 - m_qW), pkts);
454 
455  if ((double) qSize < fraction * m_qAvg)
456  {
457  // Queue could have been empty for 0.05 seconds
458  return 0;
459  }
460  }
461 
462  double u = m_uv->GetValue ();
463 
464  if (m_cautious == 2)
465  {
466  /*
467  * Decrease the drop probability if the instantaneous
468  * queue is much below the average.
469  * For experimental purposes only.
470  * pkts: the number of packets arriving in 50 ms
471  */
472  double pkts = m_ptc * 0.05;
473  double fraction = pow ((1 - m_qW), pkts);
474  double ratio = qSize / (fraction * m_qAvg);
475 
476  if (ratio < 1.0)
477  {
478  u *= 1.0 / ratio;
479  }
480  }
481 
482  if (u <= m_vProb)
483  {
484  NS_LOG_LOGIC ("u <= m_vProb; u " << u << "; m_vProb " << m_vProb);
485 
486  // DROP or MARK
487  m_count = 0;
488  m_countBytes = 0;
489  // TODO: Implement set bit to mark
490 
491  return 1; // drop
492  }
493 
494  return 0; // no drop/mark
495 }
496 
497 // Returns a probability using these function parameters for the DropEarly funtion
498 double
499 RedQueue::CalculatePNew (double qAvg, double maxTh, bool isGentle, double vA,
500  double vB, double vC, double vD, double maxP)
501 {
502  NS_LOG_FUNCTION (this << qAvg << maxTh << isGentle << vA << vB << vC << vD << maxP);
503  double p;
504 
505  if (isGentle && qAvg >= maxTh)
506  {
507  // p ranges from maxP to 1 as the average queue
508  // Size ranges from maxTh to twice maxTh
509  p = vC * qAvg + vD;
510  }
511  else if (!isGentle && qAvg >= maxTh)
512  {
513  /*
514  * OLD: p continues to range linearly above max_p as
515  * the average queue size ranges above th_max.
516  * NEW: p is set to 1.0
517  */
518  p = 1.0;
519  }
520  else
521  {
522  /*
523  * p ranges from 0 to max_p as the average queue size ranges from
524  * th_min to th_max
525  */
526  p = vA * qAvg + vB;
527  p *= maxP;
528  }
529 
530  if (p > 1.0)
531  {
532  p = 1.0;
533  }
534 
535  return p;
536 }
537 
538 // Returns a probability using these function parameters for the DropEarly funtion
539 double
540 RedQueue::ModifyP (double p, uint32_t count, uint32_t countBytes,
541  uint32_t meanPktSize, bool isWait, uint32_t size)
542 {
543  NS_LOG_FUNCTION (this << p << count << countBytes << meanPktSize << isWait << size);
544  double count1 = (double) count;
545 
546  if (GetMode () == QUEUE_MODE_BYTES)
547  {
548  count1 = (double) (countBytes / meanPktSize);
549  }
550 
551  if (isWait)
552  {
553  if (count1 * p < 1.0)
554  {
555  p = 0.0;
556  }
557  else if (count1 * p < 2.0)
558  {
559  p /= (2.0 - count1 * p);
560  }
561  else
562  {
563  p = 1.0;
564  }
565  }
566  else
567  {
568  if (count1 * p < 1.0)
569  {
570  p /= (1.0 - count1 * p);
571  }
572  else
573  {
574  p = 1.0;
575  }
576  }
577 
578  if ((GetMode () == QUEUE_MODE_BYTES) && (p < 1.0))
579  {
580  p = (p * size) / meanPktSize;
581  }
582 
583  if (p > 1.0)
584  {
585  p = 1.0;
586  }
587 
588  return p;
589 }
590 
591 uint32_t
593 {
595  if (GetMode () == QUEUE_MODE_BYTES)
596  {
597  return m_bytesInQueue;
598  }
599  else if (GetMode () == QUEUE_MODE_PACKETS)
600  {
601  return m_packets.size ();
602  }
603  else
604  {
605  NS_ABORT_MSG ("Unknown RED mode.");
606  }
607 }
608 
611 {
612  NS_LOG_FUNCTION (this);
613 
614  if (m_packets.empty ())
615  {
616  NS_LOG_LOGIC ("Queue empty");
617  m_idle = 1;
619 
620  return 0;
621  }
622  else
623  {
624  m_idle = 0;
625  Ptr<Packet> p = m_packets.front ();
626  m_packets.pop_front ();
627  m_bytesInQueue -= p->GetSize ();
628 
629  NS_LOG_LOGIC ("Popped " << p);
630 
631  NS_LOG_LOGIC ("Number packets " << m_packets.size ());
632  NS_LOG_LOGIC ("Number bytes " << m_bytesInQueue);
633 
634  return p;
635  }
636 }
637 
639 RedQueue::DoPeek (void) const
640 {
641  NS_LOG_FUNCTION (this);
642  if (m_packets.empty ())
643  {
644  NS_LOG_LOGIC ("Queue empty");
645  return 0;
646  }
647 
648  Ptr<Packet> p = m_packets.front ();
649 
650  NS_LOG_LOGIC ("Number packets " << m_packets.size ());
651  NS_LOG_LOGIC ("Number bytes " << m_bytesInQueue);
652 
653  return p;
654 }
655 
656 } // namespace ns3