traffic-control: Add token bucket filter
This commit is contained in:
@@ -58,6 +58,7 @@ us a note on ns-developers mailing list.</p>
|
||||
<li> Ipv[4,6]AddressGenerator can now check if an address is allocated (<b>Ipv[4,6]AddressGenerator::IsAddressAllocated</b>) or a network has some allocated address (<b>Ipv[4,6]AddressGenerator::IsNetworkAllocated</b>).</li>
|
||||
<li> LTE UEs can now use IPv6 to send and receive traffic.</li>
|
||||
<li> UAN module now supports IP stack.</li>
|
||||
<li> Added a FIFO queue disc (FifoQueueDisc) and the Token Bucket Filter (TbfQueueDisc).</li>
|
||||
</ul>
|
||||
<h2>Changes to existing API:</h2>
|
||||
<ul>
|
||||
|
||||
149
examples/traffic-control/tbf-example.cc
Normal file
149
examples/traffic-control/tbf-example.cc
Normal file
@@ -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 <p.imputato@gmail.com>
|
||||
* Author: Stefano Avallone <stefano.avallone@unina.it>
|
||||
* Author: Surya Seetharaman <suryaseetharaman.9@gmail.com> - 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<QueueDisc> 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;
|
||||
}
|
||||
@@ -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'
|
||||
|
||||
142
src/traffic-control/doc/tbf.rst
Normal file
142
src/traffic-control/doc/tbf.rst
Normal file
@@ -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 `<http://lxr.free-electrons.com/source/net/sched/sch_tbf.c>`_.
|
||||
|
||||
.. [Ref2] J. Vehent; Journey to the Center of the Linux Kernel: Traffic Control, Shaping and QoS; Available online at `<http://wiki.linuxwall.info/doku.php/en:ressources:dossiers:networking:traffic_control#tbf\_-_token_bucket_filter>`_.
|
||||
|
||||
.. [Ref3] Practical IP Network QoS: TBF queuing discipline; Available online at `<http://web.opalsoft.net/qos/default.php?p=ds-24>`_.
|
||||
|
||||
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"
|
||||
369
src/traffic-control/model/tbf-queue-disc.cc
Normal file
369
src/traffic-control/model/tbf-queue-disc.cc
Normal file
@@ -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, <kuznet@ms2.inr.ac.ru>
|
||||
* Dmitry Torokhov <dtor@mail.ru> - allow attaching inner qdiscs -
|
||||
* original idea by Martin Devera
|
||||
*
|
||||
* Implemented in ns-3 by: Surya Seetharaman <suryaseetharaman.9@gmail.com>
|
||||
* Stefano Avallone <stavallo@unina.it>
|
||||
*/
|
||||
|
||||
#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<QueueDisc> ()
|
||||
.SetGroupName ("TrafficControl")
|
||||
.AddConstructor<TbfQueueDisc> ()
|
||||
.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<uint32_t> ())
|
||||
.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<uint32_t> ())
|
||||
.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<QueueDiscItem> 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<const QueueDiscItem>
|
||||
TbfQueueDisc::DoPeek ()
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
|
||||
Ptr<const QueueDiscItem> 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<QueueDiscItem>
|
||||
TbfQueueDisc::DoDequeue (void)
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
Ptr<const QueueDiscItem> 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<QueueDiscItem> 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<QueueDisc> qd = factory.Create<QueueDisc> ();
|
||||
|
||||
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<QueueDiscClass> c = CreateObject<QueueDiscClass> ();
|
||||
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
|
||||
172
src/traffic-control/model/tbf-queue-disc.h
Normal file
172
src/traffic-control/model/tbf-queue-disc.h
Normal file
@@ -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, <kuznet@ms2.inr.ac.ru>
|
||||
* Dmitry Torokhov <dtor@mail.ru> - allow attaching inner qdiscs -
|
||||
* original idea by Martin Devera
|
||||
*
|
||||
* Implemented in ns-3 by: Surya Seetharaman <suryaseetharaman.9@gmail.com>
|
||||
* Stefano Avallone <stavallo@unina.it>
|
||||
*/
|
||||
#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<QueueDiscItem> item);
|
||||
virtual Ptr<QueueDiscItem> DoDequeue (void);
|
||||
virtual Ptr<const QueueDiscItem> 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<uint32_t> m_btokens; //!< Current number of tokens in first bucket
|
||||
TracedValue<uint32_t> 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 */
|
||||
451
src/traffic-control/test/tbf-queue-disc-test-suite.cc
Normal file
451
src/traffic-control/test/tbf-queue-disc-test-suite.cc
Normal file
@@ -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 <suryaseetharaman.9@gmail.com>
|
||||
* Stefano Avallone <stavallo@unina.it>
|
||||
*/
|
||||
|
||||
#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<Packet> 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<Packet> 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<TbfQueueDisc> 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<TbfQueueDisc> 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<TbfQueueDisc> queue = CreateObject<TbfQueueDisc> ();
|
||||
|
||||
// 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<Packet> p1, p2, p3, p4, p5;
|
||||
p1 = Create<Packet> (pktSize);
|
||||
p2 = Create<Packet> (pktSize);
|
||||
p3 = Create<Packet> (pktSize);
|
||||
p4 = Create<Packet> (pktSize);
|
||||
p5 = Create<Packet> (pktSize);
|
||||
|
||||
queue->Initialize ();
|
||||
NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), 0 * modeSize, "There should be no packets in there");
|
||||
queue->Enqueue (Create<TbfQueueDiscTestItem> (p1, dest));
|
||||
NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), 1 * modeSize, "There should be one packet in there");
|
||||
queue->Enqueue (Create<TbfQueueDiscTestItem> (p2, dest));
|
||||
NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), 2 * modeSize, "There should be two packets in there");
|
||||
queue->Enqueue (Create<TbfQueueDiscTestItem> (p3, dest));
|
||||
NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), 3 * modeSize, "There should be three packets in there");
|
||||
queue->Enqueue (Create<TbfQueueDiscTestItem> (p4, dest));
|
||||
NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), 4 * modeSize, "There should be four packets in there");
|
||||
queue->Enqueue (Create<TbfQueueDiscTestItem> (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<QueueDiscItem> 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<TbfQueueDisc> ();
|
||||
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<TbfQueueDisc> ();
|
||||
|
||||
Config::SetDefault ("ns3::QueueDisc::Quota", UintegerValue (1));
|
||||
NodeContainer nodesA;
|
||||
nodesA.Create (2);
|
||||
Ptr<SimpleNetDevice> txDevA = CreateObject<SimpleNetDevice> ();
|
||||
nodesA.Get (0)->AddDevice (txDevA);
|
||||
Ptr<SimpleNetDevice> rxDevA = CreateObject<SimpleNetDevice> ();
|
||||
nodesA.Get (1)->AddDevice (rxDevA);
|
||||
Ptr<SimpleChannel> channelA = CreateObject<SimpleChannel> ();
|
||||
txDevA->SetChannel (channelA);
|
||||
rxDevA->SetChannel (channelA);
|
||||
txDevA->SetNode (nodesA.Get (0));
|
||||
rxDevA->SetNode (nodesA.Get (1));
|
||||
|
||||
dest = txDevA->GetAddress ();
|
||||
|
||||
Ptr<TrafficControlLayer> tcA = CreateObject<TrafficControlLayer> ();
|
||||
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<TbfQueueDisc> ();
|
||||
|
||||
Config::SetDefault ("ns3::QueueDisc::Quota", UintegerValue (1));
|
||||
NodeContainer nodesB;
|
||||
nodesB.Create (2);
|
||||
Ptr<SimpleNetDevice> txDevB = CreateObject<SimpleNetDevice> ();
|
||||
nodesB.Get (0)->AddDevice (txDevB);
|
||||
Ptr<SimpleNetDevice> rxDevB = CreateObject<SimpleNetDevice> ();
|
||||
nodesB.Get (1)->AddDevice (rxDevB);
|
||||
Ptr<SimpleChannel> channelB = CreateObject<SimpleChannel> ();
|
||||
txDevB->SetChannel (channelB);
|
||||
rxDevB->SetChannel (channelB);
|
||||
txDevB->SetNode (nodesB.Get (0));
|
||||
rxDevB->SetNode (nodesB.Get (1));
|
||||
|
||||
dest = txDevB->GetAddress ();
|
||||
|
||||
Ptr<TrafficControlLayer> tcB = CreateObject<TrafficControlLayer> ();
|
||||
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<TbfQueueDisc> queue, Address dest, uint32_t size)
|
||||
{
|
||||
queue->Enqueue (Create<TbfQueueDiscTestItem> (Create<Packet> (size), dest));
|
||||
}
|
||||
|
||||
void
|
||||
TbfQueueDiscTestCase::DequeueAndCheck (Ptr<TbfQueueDisc> queue, bool flag, std::string printStatement)
|
||||
{
|
||||
Ptr<QueueDiscItem> 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
|
||||
@@ -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'
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user