diff --git a/CHANGES.html b/CHANGES.html
index ef8c6e00a..3fde2a9c4 100644
--- a/CHANGES.html
+++ b/CHANGES.html
@@ -58,6 +58,7 @@ us a note on ns-developers mailing list.
Ipv[4,6]AddressGenerator can now check if an address is allocated (Ipv[4,6]AddressGenerator::IsAddressAllocated) or a network has some allocated address (Ipv[4,6]AddressGenerator::IsNetworkAllocated).
LTE UEs can now use IPv6 to send and receive traffic.
UAN module now supports IP stack.
+ Added a FIFO queue disc (FifoQueueDisc) and the Token Bucket Filter (TbfQueueDisc).
Changes to existing API:
diff --git a/examples/traffic-control/tbf-example.cc b/examples/traffic-control/tbf-example.cc
new file mode 100644
index 000000000..a95f40cb5
--- /dev/null
+++ b/examples/traffic-control/tbf-example.cc
@@ -0,0 +1,149 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2015 Universita' degli Studi di Napoli "Federico II"
+ * 2017 Kungliga Tekniska Högskolan
+ *
+ * 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: Pasquale Imputato
+ * Author: Stefano Avallone
+ * Author: Surya Seetharaman - ported from ns-3
+ * RedQueueDisc traffic-control example to accommodate TbfQueueDisc example.
+ */
+
+#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/traffic-control-module.h"
+
+// This simple example shows how to use TrafficControlHelper to install a
+// QueueDisc on a device.
+//
+// Network topology
+//
+// 10.1.1.0
+// n0 -------------- n1
+// point-to-point
+//
+// The output will consist of all the traced changes in
+// the number of tokens in TBF's first and second buckets:
+//
+// FirstBucketTokens 0 to x
+// SecondBucketTokens 0 to x
+// FirstBucketTokens x to 0
+// SecondBucketTokens x to 0
+//
+
+using namespace ns3;
+
+NS_LOG_COMPONENT_DEFINE ("TbfExample");
+
+void
+FirstBucketTokensTrace (uint32_t oldValue, uint32_t newValue)
+{
+ std::cout << "FirstBucketTokens " << oldValue << " to " << newValue << std::endl;
+}
+
+void
+SecondBucketTokensTrace (uint32_t oldValue, uint32_t newValue)
+{
+ std::cout << "SecondBucketTokens " << oldValue << " to " << newValue << std::endl;
+}
+
+int
+main (int argc, char *argv[])
+{
+
+ double simulationTime = 10; //seconds
+ uint32_t burst = 10000;
+ uint32_t mtu = 0;
+ DataRate rate = DataRate ("1Mbps");
+ DataRate peakRate = DataRate ("0bps");
+
+ CommandLine cmd;
+ cmd.AddValue ("burst", "Size of first bucket in bytes", burst);
+ cmd.AddValue ("mtu", "Size of second bucket in bytes", mtu);
+ cmd.AddValue ("rate", "Rate of tokens arriving in first bucket", rate);
+ cmd.AddValue ("peakRate", "Rate of tokens arriving in second bucket", peakRate);
+
+ cmd.Parse (argc, argv);
+
+ NodeContainer nodes;
+ nodes.Create (2);
+
+ PointToPointHelper pointToPoint;
+ pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("2Mb/s"));
+ pointToPoint.SetChannelAttribute ("Delay", StringValue ("0ms"));
+
+ NetDeviceContainer devices;
+ devices = pointToPoint.Install (nodes);
+
+ InternetStackHelper stack;
+ stack.Install (nodes);
+
+ TrafficControlHelper tch;
+ tch.SetRootQueueDisc ("ns3::TbfQueueDisc",
+ "Burst", UintegerValue (burst),
+ "Mtu", UintegerValue (mtu),
+ "Rate", DataRateValue (DataRate (rate)),
+ "PeakRate", DataRateValue (DataRate (peakRate)));
+ QueueDiscContainer qdiscs = tch.Install (devices);
+
+ Ptr q = qdiscs.Get (1);
+ q->TraceConnectWithoutContext ("TokensInFirstBucket", MakeCallback (&FirstBucketTokensTrace));
+ q->TraceConnectWithoutContext ("TokensInSecondBucket", MakeCallback (&SecondBucketTokensTrace));
+
+ Ipv4AddressHelper address;
+ address.SetBase ("10.1.1.0", "255.255.255.0");
+
+ Ipv4InterfaceContainer interfaces = address.Assign (devices);
+
+ //Flow
+ uint16_t port = 7;
+ Address localAddress (InetSocketAddress (Ipv4Address::GetAny (), port));
+ PacketSinkHelper packetSinkHelper ("ns3::TcpSocketFactory", localAddress);
+ ApplicationContainer sinkApp = packetSinkHelper.Install (nodes.Get (0));
+
+ sinkApp.Start (Seconds (0.0));
+ sinkApp.Stop (Seconds (simulationTime + 0.1));
+
+ uint32_t payloadSize = 1448;
+ Config::SetDefault ("ns3::TcpSocket::SegmentSize", UintegerValue (payloadSize));
+
+ OnOffHelper onoff ("ns3::TcpSocketFactory", Ipv4Address::GetAny ());
+ onoff.SetAttribute ("OnTime", StringValue ("ns3::ConstantRandomVariable[Constant=1]"));
+ onoff.SetAttribute ("OffTime", StringValue ("ns3::ConstantRandomVariable[Constant=0.2]"));
+ onoff.SetAttribute ("PacketSize", UintegerValue (payloadSize));
+ onoff.SetAttribute ("DataRate", StringValue ("1.1Mb/s")); //bit/s
+ ApplicationContainer apps;
+
+ InetSocketAddress rmt (interfaces.GetAddress (0), port);
+ rmt.SetTos (0xb8);
+ AddressValue remoteAddress (rmt);
+ onoff.SetAttribute ("Remote", remoteAddress);
+ apps.Add (onoff.Install (nodes.Get (1)));
+ apps.Start (Seconds (1.0));
+ apps.Stop (Seconds (simulationTime + 0.1));
+
+ Simulator::Stop (Seconds (simulationTime + 5));
+ Simulator::Run ();
+
+ Simulator::Destroy ();
+
+ std::cout << std::endl << "*** TC Layer statistics ***" << std::endl;
+ std::cout << q->GetStats () << std::endl;
+ return 0;
+}
diff --git a/examples/traffic-control/wscript b/examples/traffic-control/wscript
index a05df8b21..0808c4b6b 100644
--- a/examples/traffic-control/wscript
+++ b/examples/traffic-control/wscript
@@ -14,3 +14,7 @@ def build(bld):
obj = bld.create_ns3_program('red-vs-nlred', ['point-to-point', 'point-to-point-layout', 'internet', 'applications', 'traffic-control'])
obj.source = 'red-vs-nlred.cc'
+
+ obj = bld.create_ns3_program('tbf-example',
+ ['internet', 'point-to-point', 'applications', 'traffic-control'])
+ obj.source = 'tbf-example.cc'
diff --git a/src/traffic-control/doc/tbf.rst b/src/traffic-control/doc/tbf.rst
new file mode 100644
index 000000000..eb5915baf
--- /dev/null
+++ b/src/traffic-control/doc/tbf.rst
@@ -0,0 +1,142 @@
+.. include:: replace.txt
+.. highlight:: cpp
+
+TBF queue disc
+----------------
+
+This chapter describes the TBF ([Ref1]_) queue disc implementation
+in |ns3|. The TBF model in ns-3 is ported based on Linux kernel code implemented by
+A. Kuznetsov and D. Torokhov.
+
+TBF is a qdisc that allows controlling the bandwidth of the output according
+to a set rate with the possibility of managing burst conditions also. The TBF implementation
+consists of a bucket (buffer) having a limited capacity into which tokens (normally representing a
+unit of bytes or a single packet of predetermined size) are added at a fixed rate 'r' called the
+token rate. Whenever a packet arrives into the tx queue (fifo by default), the bucket is checked
+to see if there are appropriate number of tokens that is equivalent to the length of the packet in
+bytes. If yes, then the tokens are removed and the packet is passed for transmission. If no, then
+packets will have to wait until there are sufficient tokens in the bucket. This data conformance
+can be thus put into three possible scenarios [Ref3]_:
+
+1. Data rate = Token rate : Packets pass without delay
+2. Data rate < Token rate : Few tokens are depleted.
+ * Burst Condition : So the tokens might accumulate and the bucket might become
+ full. Then, the next packets to enter TBF will be transmitted right away without
+ having any limit applied to them, until the bucket is empty. This is called a burst
+ condition and in TBF the burst parameter defines the size of the bucket. In order
+ to overcome this problem and provide better control over the bursts, TBF
+ implements a second bucket which is smaller and generally the same size as the
+ MTU. This second bucket cannot store large amount of tokens, but its
+ replenishing rate will be a lot faster than the one of the big bucket. This second
+ rate is called 'peakRate' and it will determine the maximum rate of a burst.
+3. Data rate > Token rate : This causes the TBF algorithm to throttle itself for a while as
+soon as the bucket gets empty. This is called an 'overlimit situation' [Ref2]_. In this situation,
+some of the packets will be blocked until enough tokens are available at which time a schedule for
+the waking of the queue will be done. If packets keep coming in, at a larger rate, then the
+packets will start to get dropped when the total number of bytes exceeds the QueueLimit.
+
+Model Description
+*****************
+
+The TBF queue disc does not require packet filters, does not admit internal queues
+and uses a single child queue disc. If the user does not provide a child queue disc,
+a Fifo queue disc operating in the same mode (packet or byte) as the TBF queue disc
+and having a size equal to the TBF QueueLimit attribute is created. Otherwise, the
+capacity of the TBF queue disc is determined by the capacity of the child queue disc.
+
+There are two token buckets: first bucket and second bucket. The size of the
+first bucket called 'Burst' should always be greater than the size of the second
+bucket called the Mtu (which is usually the size of a single packet). But the
+'PeakRate' which is the second bucket's token rate should be always greater than
+the 'Rate' which is the first bucket's token rate.
+
+If the PeakRate is zero, then the second bucket does not exist. In order to activate
+the second bucket, both the Mtu and PeakRate values have to be greater than zero. If
+the Mtu value is zero at initialization time, then if a NetDevice exits, the Mtu's
+value will be equal to the Mtu of the NetDevice. But if no NetDevice exists, then
+the QueueDisc will complain thus prompting the user to set the Mtu value.
+
+The source code for the TBF model is located in the directory ``src/traffic-control/model``
+and consists of 2 files `tbf-queue-disc.h` and `tbf-queue-disc.cc` defining a TbfQueueDisc
+class.
+
+* class :cpp:class:`TbfQueueDisc`: This class implements the main TBF algorithm:
+
+ * ``TbfQueueDisc::DoEnqueue ()``: This routine enqueue's the incoming packet if the queue is not full and drops the packet otherwise.
+
+ * ``TbfQueueDisc::DoPeek ()``: This routine peeks for the top item in the queue and if the queue is not empty, it returns the topmost item.
+
+ * ``TbfQueueDisc::DoDequeue ()``: This routine performs the dequeuing of packets according to the following logic:
+
+ * It calls ``TbfQueueDisc::Peek ()`` and calculates the size of the packet to be dequeued in bytes.
+ * Then it calculates the time difference 'delta', which is the time elapsed since the last update of tokens in the buckets.
+ * If the second bucket exists, the number of tokens are updated according to the 'PeakRate' and 'delta'.
+ * From this second bucket a number of tokens equal to the size of the packet to be dequeued is subtracted.
+ * Now the number of tokens in the first bucket are updated according to 'Rate' and 'delta'.
+ * From this first bucket a number of tokens equal to the size of the packet to be dequeued is subtracted.
+ * If after this, both the first and second buckets have tokens greater than zero, then the packet is dequeued.
+ * Else, an event to ``QueueDisc::Run ()`` is scheduled after a time period when enough tokens will be present for the dequeue operation.
+
+References
+==========
+
+.. [Ref1] A. Kuznetsov and D. Torokhov; Linux Cross Reference Source Code; Available online at ``_.
+
+.. [Ref2] J. Vehent; Journey to the Center of the Linux Kernel: Traffic Control, Shaping and QoS; Available online at ``_.
+
+.. [Ref3] Practical IP Network QoS: TBF queuing discipline; Available online at ``_.
+
+Attributes
+==========
+
+The key attributes that the TbfQueueDisc class holds include the following:
+
+* ``MaxSize:`` The maximum number of packets/bytes the queue disc can hold. The default value is 1000 packets.
+* ``Burst:`` Size of the first bucket, in bytes. The default value is 125000 bytes.
+* ``Mtu:`` Size of second bucket defaults to the MTU of the attached NetDevice, if any, or 0 otherwise.
+* ``Rate:`` Rate at which tokens enter the first bucket. The default value is 125KB/s.
+* ``PeakRate:`` Rate at which tokens enter the second bucket. The default value is 0KB/s, which means that there is no second bucket.
+
+TraceSources
+============
+
+The TbfQueueDisc class provides the following trace sources:
+
+* ``TokensInFirstBucket:`` Number of First Bucket Tokens in bytes
+* ``TokensInSecondBucket:`` Number of Second Bucket Tokens in bytes
+
+Examples
+========
+
+The example for TBF is `tbf-example.cc` located in ``examples/traffic-control/``. The command to run the file (the invocation below shows the available command-line options) is:
+
+::
+
+ $ ./waf --run "tbf-example --PrintHelp"
+ $ ./waf --run "tbf-example --burst=125000 --rate=1Mbps --peakRate=1.5Mbps"
+
+The expected output from the previous commands are traced value changes in the number of tokens in the first and second buckets.
+
+Validation
+**********
+
+The TBF model is tested using :cpp:class:`TbfQueueDiscTestSuite` class defined in `src/traffic-control/test/tbf-queue-disc-test-suite.cc`. The suite includes 4 test cases:
+
+* Test 1: Simple Enqueue/Dequeue with verification of attribute setting and subtraction of tokens from the buckets.
+* Test 2: When DataRate == FirstBucketTokenRate; packets should pass smoothly.
+* Test 3: When DataRate >>> FirstBucketTokenRate; some packets should get blocked and waking of queue should get scheduled.
+* Test 4: When DataRate < FirstBucketTokenRate; burst condition, peakRate is set so that bursts are controlled.
+
+The test suite can be run using the following commands:
+
+::
+
+ $ ./waf configure --enable-examples --enable-tests
+ $ ./waf build
+ $ ./test.py -s tbf-queue-disc
+
+or
+
+::
+
+ $ NS_LOG="TbfQueueDisc" ./waf --run "test-runner --suite=tbf-queue-disc"
diff --git a/src/traffic-control/model/tbf-queue-disc.cc b/src/traffic-control/model/tbf-queue-disc.cc
new file mode 100644
index 000000000..e2ad962b7
--- /dev/null
+++ b/src/traffic-control/model/tbf-queue-disc.cc
@@ -0,0 +1,369 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2017 Kungliga Tekniska Högskolan
+ * 2017 Universita' degli Studi di Napoli Federico II
+ *
+ * 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
+ *
+ * TBF, The Token Bucket Filter Queueing discipline
+ *
+ * This implementation is based on linux kernel code by
+ * Authors: Alexey Kuznetsov,
+ * Dmitry Torokhov - allow attaching inner qdiscs -
+ * original idea by Martin Devera
+ *
+ * Implemented in ns-3 by: Surya Seetharaman
+ * Stefano Avallone
+ */
+
+#include "ns3/log.h"
+#include "ns3/enum.h"
+#include "ns3/simulator.h"
+#include "ns3/uinteger.h"
+#include "ns3/attribute.h"
+#include "ns3/object-factory.h"
+#include "ns3/drop-tail-queue.h"
+#include "ns3/net-device-queue-interface.h"
+#include "tbf-queue-disc.h"
+
+namespace ns3 {
+
+NS_LOG_COMPONENT_DEFINE ("TbfQueueDisc");
+
+NS_OBJECT_ENSURE_REGISTERED (TbfQueueDisc);
+
+TypeId TbfQueueDisc::GetTypeId (void)
+{
+ static TypeId tid = TypeId ("ns3::TbfQueueDisc")
+ .SetParent ()
+ .SetGroupName ("TrafficControl")
+ .AddConstructor ()
+ .AddAttribute ("MaxSize",
+ "The max queue size",
+ QueueSizeValue (QueueSize ("1000p")),
+ MakeQueueSizeAccessor (&QueueDisc::SetMaxSize,
+ &QueueDisc::GetMaxSize),
+ MakeQueueSizeChecker ())
+ .AddAttribute ("Burst",
+ "Size of the first bucket in bytes",
+ UintegerValue (125000),
+ MakeUintegerAccessor (&TbfQueueDisc::SetBurst),
+ MakeUintegerChecker ())
+ .AddAttribute ("Mtu",
+ "Size of the second bucket in bytes. If null, it is initialized"
+ " to the MTU of the attached NetDevice (if any)",
+ UintegerValue (0),
+ MakeUintegerAccessor (&TbfQueueDisc::SetMtu),
+ MakeUintegerChecker ())
+ .AddAttribute ("Rate",
+ "Rate at which tokens enter the first bucket in bps or Bps.",
+ DataRateValue (DataRate ("125KB/s")),
+ MakeDataRateAccessor (&TbfQueueDisc::SetRate),
+ MakeDataRateChecker ())
+ .AddAttribute ("PeakRate",
+ "Rate at which tokens enter the second bucket in bps or Bps."
+ "If null, there is no second bucket",
+ DataRateValue (DataRate ("0KB/s")),
+ MakeDataRateAccessor (&TbfQueueDisc::SetPeakRate),
+ MakeDataRateChecker ())
+ .AddTraceSource ("TokensInFirstBucket",
+ "Number of First Bucket Tokens in bytes",
+ MakeTraceSourceAccessor (&TbfQueueDisc::m_btokens),
+ "ns3::TracedValueCallback::Uint32")
+ .AddTraceSource ("TokensInSecondBucket",
+ "Number of Second Bucket Tokens in bytes",
+ MakeTraceSourceAccessor (&TbfQueueDisc::m_ptokens),
+ "ns3::TracedValueCallback::Uint32")
+ ;
+
+ return tid;
+}
+
+TbfQueueDisc::TbfQueueDisc ()
+ : QueueDisc (QueueDiscSizePolicy::SINGLE_CHILD_QUEUE_DISC)
+{
+ NS_LOG_FUNCTION (this);
+}
+
+TbfQueueDisc::~TbfQueueDisc ()
+{
+ NS_LOG_FUNCTION (this);
+}
+
+void
+TbfQueueDisc::DoDispose (void)
+{
+ NS_LOG_FUNCTION (this);
+ QueueDisc::DoDispose ();
+}
+
+void
+TbfQueueDisc::SetBurst (uint32_t burst)
+{
+ NS_LOG_FUNCTION (this << burst);
+ m_burst = burst;
+}
+
+uint32_t
+TbfQueueDisc::GetBurst (void) const
+{
+ NS_LOG_FUNCTION (this);
+ return m_burst;
+}
+
+void
+TbfQueueDisc::SetMtu (uint32_t mtu)
+{
+ NS_LOG_FUNCTION (this << mtu);
+ m_mtu = mtu;
+}
+
+uint32_t
+TbfQueueDisc::GetMtu (void) const
+{
+ NS_LOG_FUNCTION (this);
+ return m_mtu;
+}
+
+void
+TbfQueueDisc::SetRate (DataRate rate)
+{
+ NS_LOG_FUNCTION (this << rate);
+ m_rate = rate;
+}
+
+DataRate
+TbfQueueDisc::GetRate (void) const
+{
+ NS_LOG_FUNCTION (this);
+ return m_rate;
+}
+
+void
+TbfQueueDisc::SetPeakRate (DataRate peakRate)
+{
+ NS_LOG_FUNCTION (this << peakRate);
+ m_peakRate = peakRate;
+}
+
+DataRate
+TbfQueueDisc::GetPeakRate (void) const
+{
+ NS_LOG_FUNCTION (this);
+ return m_peakRate;
+}
+
+uint32_t
+TbfQueueDisc::GetFirstBucketTokens (void) const
+{
+ NS_LOG_FUNCTION (this);
+ return m_btokens;
+}
+
+uint32_t
+TbfQueueDisc::GetSecondBucketTokens (void) const
+{
+ NS_LOG_FUNCTION (this);
+ return m_ptokens;
+}
+
+bool
+TbfQueueDisc::DoEnqueue (Ptr item)
+{
+ NS_LOG_FUNCTION (this << item);
+
+ bool retval = GetQueueDiscClass (0)->GetQueueDisc ()->Enqueue (item);
+
+ // If Queue::Enqueue fails, QueueDisc::Drop is called by the child queue
+ // disc because QueueDisc::AddQueueDiscClass sets the drop callback
+
+ NS_LOG_LOGIC ("Current queue size: " << GetNPackets () << " packets, " << GetNBytes () << " bytes");
+
+ return retval;
+}
+
+Ptr
+TbfQueueDisc::DoPeek ()
+{
+ NS_LOG_FUNCTION (this);
+
+ Ptr item = PeekDequeued ();
+
+ if (!item)
+ {
+ NS_LOG_LOGIC ("No packet returned");
+ return 0;
+ }
+
+ NS_LOG_LOGIC ("Current queue size: " << GetNPackets () << " packets, " << GetNBytes () << " bytes");
+
+ return item;
+}
+
+Ptr
+TbfQueueDisc::DoDequeue (void)
+{
+ NS_LOG_FUNCTION (this);
+ Ptr itemPeek = GetQueueDiscClass (0)->GetQueueDisc ()->Peek ();
+
+ if (itemPeek)
+ {
+ uint32_t pktSize = itemPeek->GetSize ();
+ NS_LOG_LOGIC ("Next packet size " << pktSize);
+
+ int64_t btoks = 0;
+ int64_t ptoks = 0;
+ Time now = Simulator::Now ();
+
+ double delta = (now - m_timeCheckPoint).GetSeconds ();
+ NS_LOG_LOGIC ("Time Difference delta " << delta);
+
+ if (m_peakRate > DataRate ("0bps"))
+ {
+ ptoks = m_ptokens + round (delta * (m_peakRate.GetBitRate () / 8));
+ if (ptoks > m_mtu)
+ {
+ ptoks = m_mtu;
+ }
+ NS_LOG_LOGIC ("Number of ptokens we can consume " << ptoks);
+ NS_LOG_LOGIC ("Required to dequeue next packet " << pktSize);
+ ptoks -= pktSize;
+ }
+
+ btoks = m_btokens + round (delta * (m_rate.GetBitRate () / 8));
+
+ if (btoks > m_burst)
+ {
+ btoks = m_burst;
+ }
+
+ NS_LOG_LOGIC ("Number of btokens we can consume " << btoks);
+ NS_LOG_LOGIC ("Required to dequeue next packet " << pktSize);
+ btoks -= pktSize;
+
+ if ((btoks|ptoks) >= 0) // else packet blocked
+ {
+ Ptr item = GetQueueDiscClass (0)->GetQueueDisc ()->DequeuePeeked ();
+ if (!item)
+ {
+ NS_LOG_DEBUG ("That's odd! Expecting the peeked packet, we got no packet.");
+ return item;
+ }
+
+ m_timeCheckPoint = now;
+ m_btokens = btoks;
+ m_ptokens = ptoks;
+
+ NS_LOG_LOGIC (m_btokens << " btokens and " << m_ptokens << " ptokens after packet dequeue");
+ NS_LOG_LOGIC ("Current queue size: " << GetNPackets () << " packets, " << GetNBytes () << " bytes");
+
+ return item;
+ }
+
+ // the watchdog timer setup.
+ /* A packet gets blocked if the above if condition is not satisfied, i.e.
+ both the ptoks and btoks are less than zero. In that case we have to
+ schedule the waking of queue when enough tokens are available. */
+ if (m_id.IsExpired () == true)
+ {
+ Time requiredDelayTime = std::max (m_rate.CalculateBytesTxTime (-btoks),
+ m_peakRate.CalculateBytesTxTime (-ptoks));
+
+ m_id = Simulator::Schedule (requiredDelayTime, &QueueDisc::Run, this);
+ NS_LOG_LOGIC("Waking Event Scheduled in " << requiredDelayTime);
+ }
+ }
+ return 0;
+}
+
+bool
+TbfQueueDisc::CheckConfig (void)
+{
+ NS_LOG_FUNCTION (this);
+ if (GetNInternalQueues () > 0)
+ {
+ NS_LOG_ERROR ("TbfQueueDisc cannot have internal queues");
+ return false;
+ }
+
+ if (GetNPacketFilters () > 0)
+ {
+ NS_LOG_ERROR ("TbfQueueDisc cannot have packet filters");
+ return false;
+ }
+
+ if (GetNQueueDiscClasses () == 0)
+ {
+ // create a FIFO queue disc
+ ObjectFactory factory;
+ factory.SetTypeId ("ns3::FifoQueueDisc");
+ Ptr qd = factory.Create ();
+
+ if (!qd->SetMaxSize (GetMaxSize ()))
+ {
+ NS_LOG_ERROR ("Cannot set the max size of the child queue disc to that of TbfQueueDisc");
+ return false;
+ }
+ qd->Initialize ();
+
+ Ptr c = CreateObject ();
+ c->SetQueueDisc (qd);
+ AddQueueDiscClass (c);
+ }
+
+ if (GetNQueueDiscClasses () != 1)
+ {
+ NS_LOG_ERROR ("TbfQueueDisc needs 1 child queue disc");
+ return false;
+ }
+
+ if (m_mtu == 0 && GetNetDevice ())
+ {
+ m_mtu = GetNetDevice ()->GetMtu ();
+ }
+
+ if (m_mtu == 0 && m_peakRate > DataRate ("0bps"))
+ {
+ NS_LOG_ERROR ("A non-null peak rate has been set, but the mtu is null. No packet will be dequeued");
+ return false;
+ }
+
+ if (m_burst <= m_mtu)
+ {
+ NS_LOG_WARN ("The size of the first bucket (" << m_burst << ") should be "
+ << "greater than the size of the second bucket (" << m_mtu << ").");
+ }
+
+ if (m_peakRate > DataRate ("0bps") && m_peakRate <= m_rate)
+ {
+ NS_LOG_WARN ("The rate for the second bucket (" << m_peakRate << ") should be "
+ << "greater than the rate for the first bucket (" << m_rate << ").");
+ }
+
+ return true;
+}
+
+void
+TbfQueueDisc::InitializeParams (void)
+{
+ NS_LOG_FUNCTION (this);
+ // Token Buckets are full at the beginning.
+ m_btokens = m_burst;
+ m_ptokens = m_mtu;
+ // Initialising other variables to 0.
+ m_timeCheckPoint = Seconds (0);
+ m_id = EventId ();
+}
+
+} // namespace ns3
diff --git a/src/traffic-control/model/tbf-queue-disc.h b/src/traffic-control/model/tbf-queue-disc.h
new file mode 100644
index 000000000..79baef9c7
--- /dev/null
+++ b/src/traffic-control/model/tbf-queue-disc.h
@@ -0,0 +1,172 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2017 Kungliga Tekniska Högskolan
+ * 2017 Universita' degli Studi di Napoli Federico II
+ *
+ * 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
+ *
+ * TBF, The Token Bucket Filter Queueing discipline
+ *
+ * This implementation is based on linux kernel code by
+ * Authors: Alexey Kuznetsov,
+ * Dmitry Torokhov - allow attaching inner qdiscs -
+ * original idea by Martin Devera
+ *
+ * Implemented in ns-3 by: Surya Seetharaman
+ * Stefano Avallone
+ */
+#ifndef TBF_QUEUE_DISC_H
+#define TBF_QUEUE_DISC_H
+
+#include "ns3/queue-disc.h"
+#include "ns3/nstime.h"
+#include "ns3/boolean.h"
+#include "ns3/data-rate.h"
+#include "ns3/random-variable-stream.h"
+#include "ns3/traced-value.h"
+#include "ns3/trace-source-accessor.h"
+#include "ns3/event-id.h"
+
+namespace ns3 {
+
+/**
+ * \ingroup traffic-control
+ *
+ * \brief A TBF packet queue disc
+ */
+class TbfQueueDisc : public QueueDisc
+{
+public:
+
+ /**
+ * \brief Get the type ID.
+ * \return the object TypeId
+ */
+ static TypeId GetTypeId (void);
+
+ /**
+ * \brief TbfQueueDisc Constructor
+ *
+ * Create a TBF queue disc
+ */
+ TbfQueueDisc ();
+
+ /**
+ * \brief Destructor
+ *
+ * Destructor
+ */
+ virtual ~TbfQueueDisc ();
+
+ /**
+ * \brief Set the size of the first bucket in bytes.
+ *
+ * \param burst The size of first bucket in bytes.
+ */
+ void SetBurst (uint32_t burst);
+
+ /**
+ * \brief Get the size of the first bucket in bytes.
+ *
+ * \returns The size of the first bucket in bytes.
+ */
+ uint32_t GetBurst (void) const;
+
+ /**
+ * \brief Set the size of the second bucket in bytes.
+ *
+ * \param mtu The size of second bucket in bytes.
+ */
+ void SetMtu (uint32_t mtu);
+
+ /**
+ * \brief Get the size of the second bucket in bytes.
+ *
+ * \returns The size of the second bucket in bytes.
+ */
+ uint32_t GetMtu (void) const;
+
+ /**
+ * \brief Set the rate of the tokens entering the first bucket.
+ *
+ * \param rate The rate of first bucket tokens.
+ */
+ void SetRate (DataRate rate);
+
+ /**
+ * \brief Get the rate of the tokens entering the first bucket.
+ *
+ * \returns The rate of first bucket tokens.
+ */
+ DataRate GetRate (void) const;
+
+ /**
+ * \brief Set the rate of the tokens entering the second bucket.
+ *
+ * \param peakRate The rate of second bucket tokens.
+ */
+ void SetPeakRate (DataRate peakRate);
+
+ /**
+ * \brief Get the rate of the tokens entering the second bucket.
+ *
+ * \returns The rate of second bucket tokens.
+ */
+ DataRate GetPeakRate (void) const;
+
+ /**
+ * \brief Get the current number of tokens inside the first bucket in bytes.
+ *
+ * \returns The number of first bucket tokens in bytes.
+ */
+ uint32_t GetFirstBucketTokens (void) const;
+
+ /**
+ * \brief Get the current number of tokens inside the second bucket in bytes.
+ *
+ * \returns The number of second bucket tokens in bytes.
+ */
+ uint32_t GetSecondBucketTokens (void) const;
+
+protected:
+ /**
+ * \brief Dispose of the object
+ */
+ virtual void DoDispose (void);
+
+private:
+
+ virtual bool DoEnqueue (Ptr item);
+ virtual Ptr DoDequeue (void);
+ virtual Ptr DoPeek (void);
+ virtual bool CheckConfig (void);
+ virtual void InitializeParams (void);
+
+ /* parameters for the TBF Queue Disc */
+ uint32_t m_burst; //!< Size of first bucket in bytes
+ uint32_t m_mtu; //!< Size of second bucket in bytes
+ DataRate m_rate; //!< Rate at which tokens enter the first bucket
+ DataRate m_peakRate; //!< Rate at which tokens enter the second bucket
+
+ /* variables stored by TBF Queue Disc */
+ TracedValue m_btokens; //!< Current number of tokens in first bucket
+ TracedValue m_ptokens; //!< Current number of tokens in second bucket
+ Time m_timeCheckPoint; //!< Time check-point
+ EventId m_id; //!< EventId of the scheduled queue waking event when enough tokens are available
+
+};
+
+} // namespace ns3
+
+#endif /* TBF_QUEUE_DISC_H */
diff --git a/src/traffic-control/test/tbf-queue-disc-test-suite.cc b/src/traffic-control/test/tbf-queue-disc-test-suite.cc
new file mode 100644
index 000000000..64ac31ca7
--- /dev/null
+++ b/src/traffic-control/test/tbf-queue-disc-test-suite.cc
@@ -0,0 +1,451 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2017 Kungliga Tekniska Högskolan
+ * 2017 Universita' degli Studi di Napoli Federico II
+ *
+ * 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
+ *
+ * Authors: Surya Seetharaman
+ * Stefano Avallone
+ */
+
+#include "ns3/test.h"
+#include "ns3/tbf-queue-disc.h"
+#include "ns3/packet.h"
+#include "ns3/uinteger.h"
+#include "ns3/string.h"
+#include "ns3/double.h"
+#include "ns3/log.h"
+#include "ns3/simulator.h"
+#include "ns3/node-container.h"
+#include "ns3/simple-net-device.h"
+#include "ns3/simple-channel.h"
+#include "ns3/traffic-control-layer.h"
+#include "ns3/config.h"
+
+using namespace ns3;
+
+/**
+ * \ingroup traffic-control-test
+ * \ingroup tests
+ *
+ * \brief Tbf Queue Disc Test Item
+ */
+class TbfQueueDiscTestItem : public QueueDiscItem {
+public:
+ /**
+ * Constructor
+ *
+ * \param p the packet
+ * \param addr the address
+ */
+ TbfQueueDiscTestItem (Ptr p, const Address & addr);
+ virtual ~TbfQueueDiscTestItem ();
+ virtual void AddHeader (void);
+ virtual bool Mark (void);
+
+private:
+ TbfQueueDiscTestItem ();
+ /**
+ * \brief Copy constructor
+ * Disable default implementation to avoid misuse
+ */
+ TbfQueueDiscTestItem (const TbfQueueDiscTestItem &);
+ /**
+ * \brief Assignment operator
+ * \return this object
+ * Disable default implementation to avoid misuse
+ */
+ TbfQueueDiscTestItem &operator = (const TbfQueueDiscTestItem &);
+};
+
+TbfQueueDiscTestItem::TbfQueueDiscTestItem (Ptr p, const Address & addr)
+ : QueueDiscItem (p, addr, 0)
+{
+}
+
+TbfQueueDiscTestItem::~TbfQueueDiscTestItem ()
+{
+}
+
+void
+TbfQueueDiscTestItem::AddHeader (void)
+{
+}
+
+bool
+TbfQueueDiscTestItem::Mark (void)
+{
+ return false;
+}
+
+/**
+ * \ingroup traffic-control-test
+ * \ingroup tests
+ *
+ * \brief Tbf Queue Disc Test Case
+ */
+class TbfQueueDiscTestCase : public TestCase
+{
+public:
+ TbfQueueDiscTestCase ();
+ virtual void DoRun (void);
+private:
+ /**
+ * Enqueue function
+ * \param queue the queue disc into which enqueue needs to be done
+ * \param dest the destination address
+ * \param size the size of the packet in bytes to be enqueued
+ */
+ void Enqueue (Ptr queue, Address dest, uint32_t size);
+ /**
+ * DequeueAndCheck function to check if a packet is blocked or not after dequeuing and verify against expected result
+ * \param queue the queue disc on which DequeueAndCheck needs to be done
+ * \param flag the boolean value against which the return value of dequeue () has to be compared with
+ * \param printStatement the string to be printed in the NS_TEST_EXPECT_MSG_EQ
+ */
+ void DequeueAndCheck (Ptr queue, bool flag, std::string printStatement);
+ /**
+ * Run TBF test function
+ * \param mode the mode
+ */
+ void RunTbfTest (QueueSizeUnit mode);
+};
+
+TbfQueueDiscTestCase::TbfQueueDiscTestCase ()
+ : TestCase ("Sanity check on the TBF queue implementation")
+{
+}
+
+void
+TbfQueueDiscTestCase::RunTbfTest (QueueSizeUnit mode)
+{
+ uint32_t pktSize = 1500;
+ // 1 for packets; pktSize for bytes
+ uint32_t modeSize = 1;
+ uint32_t qSize = 4;
+ uint32_t burst = 6000;
+ uint32_t mtu = 0;
+ DataRate rate = DataRate ("6KB/s");
+ DataRate peakRate = DataRate ("0KB/s");
+
+ Ptr queue = CreateObject ();
+
+ // test 1: Simple Enqueue/Dequeue with verification of attribute setting
+ /* 1. There is no second bucket since "peakRate" is set to 0.
+ 2. A simple enqueue of five packets, each containing 1500B is followed by
+ the dequeue those five packets.
+ 3. The subtraction of tokens from the first bucket to send out each of the
+ five packets is monitored and verified.
+ Note : The number of tokens in the first bucket is full at the beginning.
+ With the dequeuing of each packet, the number of tokens keeps decreasing.
+ So packets are dequeued as long as there are enough tokens in the bucket. */
+
+ if (mode == QueueSizeUnit::BYTES)
+ {
+ modeSize = pktSize;
+ qSize = qSize * modeSize;
+ }
+
+ NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("MaxSize", QueueSizeValue (QueueSize (mode, qSize))),
+ true, "Verify that we can actually set the attribute MaxSize");
+ NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("Burst", UintegerValue (burst)), true,
+ "Verify that we can actually set the attribute Burst");
+ NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("Mtu", UintegerValue (mtu)), true,
+ "Verify that we can actually set the attribute Mtu");
+ NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("Rate", DataRateValue (rate)), true,
+ "Verify that we can actually set the attribute Rate");
+ NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("PeakRate", DataRateValue (peakRate)), true,
+ "Verify that we can actually set the attribute PeakRate");
+
+ Address dest;
+
+ Ptr p1, p2, p3, p4, p5;
+ p1 = Create (pktSize);
+ p2 = Create (pktSize);
+ p3 = Create (pktSize);
+ p4 = Create (pktSize);
+ p5 = Create (pktSize);
+
+ queue->Initialize ();
+ NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), 0 * modeSize, "There should be no packets in there");
+ queue->Enqueue (Create (p1, dest));
+ NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), 1 * modeSize, "There should be one packet in there");
+ queue->Enqueue (Create (p2, dest));
+ NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), 2 * modeSize, "There should be two packets in there");
+ queue->Enqueue (Create (p3, dest));
+ NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), 3 * modeSize, "There should be three packets in there");
+ queue->Enqueue (Create (p4, dest));
+ NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), 4 * modeSize, "There should be four packets in there");
+ queue->Enqueue (Create (p5, dest));
+ NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), 4 * modeSize,
+ "There should still be four packets in there as this enqueue cannot happen since QueueLimit will be exceeded");
+
+ Ptr item;
+ NS_TEST_EXPECT_MSG_EQ (queue->GetFirstBucketTokens (), burst, "The first token bucket should be full");
+ item = queue->Dequeue ();
+ NS_TEST_EXPECT_MSG_EQ ((item != 0), true, "I want to remove the first packet");
+ NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), 3 * modeSize, "There should be three packets in there");
+ NS_TEST_EXPECT_MSG_EQ (item->GetPacket ()->GetUid (), p1->GetUid (), "was this the first packet ?");
+ NS_TEST_EXPECT_MSG_EQ (queue->GetFirstBucketTokens (), burst - (1 * pktSize),
+ "The number of tokens in the first bucket should be one pktSize lesser");
+
+ item = queue->Dequeue ();
+ NS_TEST_EXPECT_MSG_EQ ((item != 0), true, "I want to remove the second packet");
+ NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), 2 * modeSize, "There should be two packets in there");
+ NS_TEST_EXPECT_MSG_EQ (item->GetPacket ()->GetUid (), p2->GetUid (), "Was this the second packet ?");
+ NS_TEST_EXPECT_MSG_EQ (queue->GetFirstBucketTokens (), burst - (2 * pktSize),
+ "The number of tokens in the first bucket should be two pktSizes lesser");
+
+ item = queue->Dequeue ();
+ NS_TEST_EXPECT_MSG_EQ ((item != 0), true, "I want to remove the third packet");
+ NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), 1 * modeSize, "There should be one packet in there");
+ NS_TEST_EXPECT_MSG_EQ (item->GetPacket ()->GetUid (), p3->GetUid (), "Was this the third packet ?");
+ NS_TEST_EXPECT_MSG_EQ (queue->GetFirstBucketTokens (), burst - (3 * pktSize),
+ "The number of tokens in the first bucket should be three pktSizes lesser");
+
+ item = queue->Dequeue ();
+ NS_TEST_EXPECT_MSG_EQ ((item != 0), true, "I want to remove the fourth packet");
+ NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), 0 * modeSize, "There should be zero packet in there");
+ NS_TEST_EXPECT_MSG_EQ (item->GetPacket ()->GetUid (), p4->GetUid (), "Was this the fourth packet ?");
+ NS_TEST_EXPECT_MSG_EQ (queue->GetFirstBucketTokens (), burst - (4 * pktSize),
+ "The number of tokens in the first bucket should be four pktSizes lesser");
+
+ // test 2 : When DataRate == FirstBucketTokenRate; packets should pass smoothly.
+ queue = CreateObject ();
+ qSize = 10;
+ pktSize = 1000;
+ burst = 10000;
+ mtu = 1000;
+ rate = DataRate ("10KB/s");
+ peakRate = DataRate ("100KB/s");
+ uint32_t nPkt = qSize;
+
+ if (mode == QueueSizeUnit::BYTES)
+ {
+ modeSize = pktSize;
+ qSize = qSize * modeSize;
+ }
+
+ NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("MaxSize", QueueSizeValue (QueueSize (mode, qSize))),
+ true, "Verify that we can actually set the attribute MaxSize");
+ NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("Burst", UintegerValue (burst)), true,
+ "Verify that we can actually set the attribute Burst");
+ NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("Mtu", UintegerValue (mtu)), true,
+ "Verify that we can actually set the attribute Mtu");
+ NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("Rate", DataRateValue (rate)), true,
+ "Verify that we can actually set the attribute Rate");
+ NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("PeakRate", DataRateValue (peakRate)), true,
+ "Verify that we can actually set the attribute PeakRate");
+
+ queue->Initialize ();
+ double delay = 0.09;
+ for (uint32_t i = 1; i <= nPkt; i++)
+ {
+ Simulator::Schedule (Time (Seconds ((i + 1) * delay)), &TbfQueueDiscTestCase::Enqueue, this, queue, dest, pktSize);
+ }
+ delay = 0.1;
+ for (uint32_t i = 1; i <= nPkt; i++)
+ {
+ Simulator::Schedule (Time (Seconds ((i + 1) * delay)), &TbfQueueDiscTestCase::DequeueAndCheck, this,
+ queue, true, "No packet should be blocked");
+ }
+ Simulator::Stop (Seconds (1));
+ Simulator::Run ();
+
+ // test 3 : When DataRate >>> FirstBucketTokenRate; some packets should get blocked and waking of queue should get scheduled.
+ /* 10 packets are enqueued and then dequeued. Since the token rate is less than the data rate, the last packet i.e the 10th
+ packet gets blocked and waking of queue is scheduled after a time when enough tokens will be available. At that time the
+ 10th packet passes through. */
+ queue = CreateObject ();
+
+ Config::SetDefault ("ns3::QueueDisc::Quota", UintegerValue (1));
+ NodeContainer nodesA;
+ nodesA.Create (2);
+ Ptr txDevA = CreateObject ();
+ nodesA.Get (0)->AddDevice (txDevA);
+ Ptr rxDevA = CreateObject ();
+ nodesA.Get (1)->AddDevice (rxDevA);
+ Ptr channelA = CreateObject ();
+ txDevA->SetChannel (channelA);
+ rxDevA->SetChannel (channelA);
+ txDevA->SetNode (nodesA.Get (0));
+ rxDevA->SetNode (nodesA.Get (1));
+
+ dest = txDevA->GetAddress ();
+
+ Ptr tcA = CreateObject ();
+ nodesA.Get (0)->AggregateObject (tcA);
+ queue->SetNetDevice (txDevA);
+ tcA->SetRootQueueDiscOnDevice (txDevA, queue);
+ tcA->Initialize ();
+
+ burst = 5000;
+ mtu = 1000;
+ rate = DataRate ("5KB/s");
+ peakRate = DataRate ("100KB/s");
+
+ if (mode == QueueSizeUnit::BYTES)
+ {
+ modeSize = pktSize;
+ qSize = qSize * modeSize;
+ }
+
+ NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("MaxSize", QueueSizeValue (QueueSize (mode, qSize))),
+ true, "Verify that we can actually set the attribute MaxSize");
+ NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("Burst", UintegerValue (burst)), true,
+ "Verify that we can actually set the attribute Burst");
+ NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("Mtu", UintegerValue (mtu)), true,
+ "Verify that we can actually set the attribute Mtu");
+ NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("Rate", DataRateValue (rate)), true,
+ "Verify that we can actually set the attribute Rate");
+ NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("PeakRate", DataRateValue (peakRate)), true,
+ "Verify that we can actually set the attribute PeakRate");
+
+ delay = 0.09;
+ for (uint32_t i = 1; i <= nPkt; i++)
+ {
+ Simulator::Schedule (Time (Seconds ((i + 1) * delay)), &TbfQueueDiscTestCase::Enqueue, this, queue, dest, pktSize);
+ }
+ delay = 0.1;
+ for (uint32_t i = 1; i <= nPkt; i++)
+ {
+ if (i == 10)
+ {
+ Simulator::Schedule (Time (Seconds ((i + 1) * delay)), &TbfQueueDiscTestCase::DequeueAndCheck, this,
+ queue, false, "10th packet should be blocked");
+ }
+ else
+ {
+ Simulator::Schedule (Time (Seconds ((i + 1) * delay)), &TbfQueueDiscTestCase::DequeueAndCheck, this,
+ queue, true, "This packet should not be blocked");
+ }
+ }
+ Simulator::Stop (Seconds (1.3));
+ Simulator::Run ();
+
+ // test 4 : When DataRate < FirstBucketTokenRate; burst condition, peakRate is set so that bursts are controlled.
+ /* This test checks the burst control ability of TBF. 10 packets each of size 1000 bytes are enqueued followed by
+ their dequeue. The data rate (25 KB/s) is not sufficiently higher than the btokens rate (15 KB/s), so that in
+ the startup phase the first bucket is not empty. Hence when adequate tokens are present in the second bucket,
+ the packets get transmitted, otherwise they are blocked. So basically the transmission of packets falls under the
+ regulation of the second bucket since first bucket will always have excess tokens. TBF does not let all
+ the packets go smoothly without any control just because there are excess tokens in the first bucket. */
+ queue = CreateObject ();
+
+ Config::SetDefault ("ns3::QueueDisc::Quota", UintegerValue (1));
+ NodeContainer nodesB;
+ nodesB.Create (2);
+ Ptr txDevB = CreateObject ();
+ nodesB.Get (0)->AddDevice (txDevB);
+ Ptr rxDevB = CreateObject ();
+ nodesB.Get (1)->AddDevice (rxDevB);
+ Ptr channelB = CreateObject ();
+ txDevB->SetChannel (channelB);
+ rxDevB->SetChannel (channelB);
+ txDevB->SetNode (nodesB.Get (0));
+ rxDevB->SetNode (nodesB.Get (1));
+
+ dest = txDevB->GetAddress ();
+
+ Ptr tcB = CreateObject ();
+ nodesB.Get (0)->AggregateObject (tcB);
+ queue->SetNetDevice (txDevB);
+ tcB->SetRootQueueDiscOnDevice (txDevB, queue);
+ tcB->Initialize ();
+
+ burst = 15000;
+ mtu = 1000;
+ rate = DataRate ("15KB/s");
+ peakRate = DataRate ("20KB/s");
+
+ if (mode == QueueSizeUnit::BYTES)
+ {
+ modeSize = pktSize;
+ qSize = qSize * modeSize;
+ }
+
+ NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("MaxSize", QueueSizeValue (QueueSize (mode, qSize))),
+ true, "Verify that we can actually set the attribute MaxSize");
+ NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("Burst", UintegerValue (burst)), true,
+ "Verify that we can actually set the attribute Burst");
+ NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("Mtu", UintegerValue (mtu)), true,
+ "Verify that we can actually set the attribute Mtu");
+ NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("Rate", DataRateValue (rate)), true,
+ "Verify that we can actually set the attribute Rate");
+ NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("PeakRate", DataRateValue (peakRate)), true,
+ "Verify that we can actually set the attribute PeakRate");
+
+ queue->Initialize ();
+ delay = 0.04;
+ for (uint32_t i = 1; i <= nPkt; i++)
+ {
+ Simulator::Schedule (Time (Seconds ((i + 1) * delay)), &TbfQueueDiscTestCase::Enqueue, this, queue, dest, pktSize);
+ }
+
+ for (uint32_t i = 1; i <= nPkt; i++)
+ {
+ if (i % 2 == 1)
+ {
+ Simulator::Schedule (Time (Seconds ((i + 1) * delay + 0.02)), &TbfQueueDiscTestCase::DequeueAndCheck, this,
+ queue, true, "1st packet should not be blocked");
+ }
+ else
+ {
+ Simulator::Schedule (Time (Seconds ((i + 1) * delay + 0.02)), &TbfQueueDiscTestCase::DequeueAndCheck, this,
+ queue, false, "This packet should be blocked");
+ }
+ }
+ Simulator::Stop (Seconds (0.55));
+ Simulator::Run ();
+
+}
+
+void
+TbfQueueDiscTestCase::Enqueue (Ptr queue, Address dest, uint32_t size)
+{
+ queue->Enqueue (Create (Create (size), dest));
+}
+
+void
+TbfQueueDiscTestCase::DequeueAndCheck (Ptr queue, bool flag, std::string printStatement)
+{
+ Ptr item = queue->Dequeue ();
+ NS_TEST_EXPECT_MSG_EQ ((item != 0), flag, printStatement);
+}
+
+void
+TbfQueueDiscTestCase::DoRun (void)
+{
+ RunTbfTest (QueueSizeUnit::PACKETS);
+ RunTbfTest (QueueSizeUnit::BYTES);
+ Simulator::Destroy ();
+
+}
+
+/**
+ * \ingroup traffic-control-test
+ * \ingroup tests
+ *
+ * \brief Tbf Queue Disc Test Suite
+ */
+static class TbfQueueDiscTestSuite : public TestSuite
+{
+public:
+ TbfQueueDiscTestSuite ()
+ : TestSuite ("tbf-queue-disc", UNIT)
+ {
+ AddTestCase (new TbfQueueDiscTestCase (), TestCase::QUICK);
+ }
+} g_tbfQueueTestSuite; ///< the test suite
diff --git a/src/traffic-control/wscript b/src/traffic-control/wscript
index a4e990fcf..5f84e9064 100644
--- a/src/traffic-control/wscript
+++ b/src/traffic-control/wscript
@@ -19,6 +19,7 @@ def build(bld):
'model/fq-codel-queue-disc.cc',
'model/pie-queue-disc.cc',
'model/mq-queue-disc.cc',
+ 'model/tbf-queue-disc.cc',
'helper/traffic-control-helper.cc',
'helper/queue-disc-container.cc'
]
@@ -30,6 +31,7 @@ def build(bld):
'test/adaptive-red-queue-disc-test-suite.cc',
'test/pie-queue-disc-test-suite.cc',
'test/fifo-queue-disc-test-suite.cc',
+ 'test/tbf-queue-disc-test-suite.cc',
'test/tc-flow-control-test-suite.cc'
]
@@ -46,6 +48,7 @@ def build(bld):
'model/fq-codel-queue-disc.h',
'model/pie-queue-disc.h',
'model/mq-queue-disc.h',
+ 'model/tbf-queue-disc.h',
'helper/traffic-control-helper.h',
'helper/queue-disc-container.h'
]