How to add a random drop queue into NS3?

Please refer to http://csie.nqu.edu.tw/smallko/ns3/ns3-myfifoq.htm first.

 

randomdrop-queue-disc.h (put this file under src/traff-control/model)

/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */

/*

 * Copyright (c) 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:  Stefano Avallone <stavallo@unina.it>

 */

 

#ifndef Randomdrop_QUEUE_DISC_H

#define Randomdrop_QUEUE_DISC_H

 

#include "ns3/queue-disc.h"

 

namespace ns3 {

 

/**

 * \ingroup traffic-control

 *

 * Simple queue disc implementing the FIFO (First-In First-Out) policy.

 *

 */

class RandomdropQueueDisc : public QueueDisc {

public:

  /**

   * \brief Get the type ID.

   * \return the object TypeId

   */

  static TypeId GetTypeId (void);

  /**

   * \brief RandomdropQueueDisc constructor

   *

   * Creates a queue with a depth of 1000 packets by default

   */

  RandomdropQueueDisc ();

 

  virtual ~RandomdropQueueDisc();

 

  // Reasons for dropping packets

  static constexpr const char* LIMIT_EXCEEDED_DROP = "Queue disc limit exceeded";  //!< Packet dropped due to queue disc limit exceeded

  static constexpr const char* RANDOM_DROP = "Dropped by Random";

 

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);

 

  double m_prob;        // random drop probability

};

 

} // namespace ns3

 

#endif /* Randomdrop_QUEUE_DISC_H */

 

 

randomdrop-queue-disc.cc (put this file under src/traff-control/model)

/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */

/*

 * Copyright (c) 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:  Stefano Avallone <stavallo@unina.it>

 */

 

#include "ns3/log.h"

#include "randomdrop-queue-disc.h"

#include "ns3/object-factory.h"

#include "ns3/drop-tail-queue.h"

 

namespace ns3 {

 

NS_LOG_COMPONENT_DEFINE ("RandomdropQueueDisc");

 

NS_OBJECT_ENSURE_REGISTERED (RandomdropQueueDisc);

 

TypeId RandomdropQueueDisc::GetTypeId (void)

{

  static TypeId tid = TypeId ("ns3::RandomdropQueueDisc")

    .SetParent<QueueDisc> ()

    .SetGroupName ("TrafficControl")

    .AddConstructor<RandomdropQueueDisc> ()

    .AddAttribute ("MaxSize",

                   "The max queue size",

                   QueueSizeValue (QueueSize ("1000p")),

                   MakeQueueSizeAccessor (&QueueDisc::SetMaxSize,

                                          &QueueDisc::GetMaxSize),

                   MakeQueueSizeChecker ())

    .AddAttribute ("DropProb",

                   "Random Drop Probability",

                   DoubleValue (0.0),

                   MakeDoubleAccessor (&RandomdropQueueDisc::m_prob),

                   MakeDoubleChecker<double> ())

  ;

  return tid;

}

 

RandomdropQueueDisc::RandomdropQueueDisc ()

  : QueueDisc (QueueDiscSizePolicy::SINGLE_INTERNAL_QUEUE)

{

  NS_LOG_FUNCTION (this);

}

 

RandomdropQueueDisc::~RandomdropQueueDisc ()

{

  NS_LOG_FUNCTION (this);

}

 

bool

RandomdropQueueDisc::DoEnqueue (Ptr<QueueDiscItem> item)

{

  NS_LOG_FUNCTION (this << item);

 

  if (GetCurrentSize () + item > GetMaxSize ())

    {

      NS_LOG_LOGIC ("Queue full -- dropping pkt");

      DropBeforeEnqueue (item, LIMIT_EXCEEDED_DROP);

      return false;

    }

   

  double x = (double) rand() / (RAND_MAX + 1.0); 

  //std::cout << "m_prob:" << m_prob << " x:" << x << std::endl;

 

  if (x <= m_prob)

  {

    DropBeforeEnqueue (item, RANDOM_DROP);

    return false;

  }

 

  bool retval = GetInternalQueue (0)->Enqueue (item);

 

  // If Queue::Enqueue fails, QueueDisc::DropBeforeEnqueue is called by the

  // internal queue because QueueDisc::AddInternalQueue sets the trace callback

 

  NS_LOG_LOGIC ("Number packets " << GetInternalQueue (0)->GetNPackets ());

  NS_LOG_LOGIC ("Number bytes " << GetInternalQueue (0)->GetNBytes ());

 

  return retval;

}

 

Ptr<QueueDiscItem>

RandomdropQueueDisc::DoDequeue (void)

{

  NS_LOG_FUNCTION (this);

 

  Ptr<QueueDiscItem> item = GetInternalQueue (0)->Dequeue ();

 

  if (!item)

    {

      NS_LOG_LOGIC ("Queue empty");

      return 0;

    }

 

  return item;

}

 

Ptr<const QueueDiscItem>

RandomdropQueueDisc::DoPeek (void)

{

  NS_LOG_FUNCTION (this);

 

  Ptr<const QueueDiscItem> item = GetInternalQueue (0)->Peek ();

 

  if (!item)

    {

      NS_LOG_LOGIC ("Queue empty");

      return 0;

    }

 

  return item;

}

 

bool

RandomdropQueueDisc::CheckConfig (void)

{

  NS_LOG_FUNCTION (this);

  if (GetNQueueDiscClasses () > 0)

    {

      NS_LOG_ERROR ("RandomdropQueueDisc cannot have classes");

      return false;

    }

 

  if (GetNPacketFilters () > 0)

    {

      NS_LOG_ERROR ("RandomdropQueueDisc needs no packet filter");

      return false;

    }

 

  if (GetNInternalQueues () == 0)

    {

      // add a DropTail queue

      AddInternalQueue (CreateObjectWithAttributes<DropTailQueue<QueueDiscItem> >

                          ("MaxSize", QueueSizeValue (GetMaxSize ())));

    }

 

  if (GetNInternalQueues () != 1)

    {

      NS_LOG_ERROR ("RandomdropQueueDisc needs 1 internal queue");

      return false;

    }

 

  return true;

}

 

void

RandomdropQueueDisc::InitializeParams (void)

{

  NS_LOG_FUNCTION (this);

}

 

} // namespace ns3

 

modify wscript under src/traffic-control

 

 

test-dumbell1.cc (put this file under scratch)

#include <iostream>

 

#include "ns3/core-module.h"

#include "ns3/network-module.h"

#include "ns3/internet-module.h"

#include "ns3/point-to-point-module.h"

#include "ns3/netanim-module.h"

#include "ns3/applications-module.h"

#include "ns3/point-to-point-layout-module.h"

#include "ns3/string.h"

#include <boost/lexical_cast.hpp>

#include "ns3/flow-monitor-module.h"

#include "ns3/traffic-control-module.h"

 

using namespace ns3;

using namespace boost;

 

int main (int argc, char *argv[])

{

  uint32_t    nLeaf = 2; 

  uint32_t    rngSeed = 1;

  std::string queueDiscType = "fifo";

 

  // Queue parameters

  uint32_t queueDiscLimitPackets = 100;

  double minTh = 15;     // RED parameters

  double maxTh = 60;    // RED parameters

  double dropprob = 0.0;

 

  CommandLine cmd (__FILE__);

  cmd.AddValue ("nLeaf", "Number of leaf nodes for each side", nLeaf);

  cmd.AddValue ("rngSeed", "random number generator seed", rngSeed);

  cmd.AddValue ("queueDiscType", "Set Queue Discipline Type", queueDiscType);

  cmd.AddValue ("DropProb", "Random Drop Probability", dropprob);

 

  cmd.Parse (argc,argv);

  RngSeedManager::SetSeed (rngSeed);

  RngSeedManager::SetRun (1);

 

  NodeContainer router_0;

  router_0.Create (1);

  NodeContainer router_1;

  router_1.Create (1);

  NodeContainer LeafNodes;

  LeafNodes.Create (nLeaf*2);  // left side and right side 

 

  TrafficControlHelper tch;

  if (queueDiscType == "red") {

    tch.SetRootQueueDisc ("ns3::RedQueueDisc");

    Config::SetDefault ("ns3::RedQueueDisc::MaxSize", QueueSizeValue (QueueSize (QueueSizeUnit::PACKETS, queueDiscLimitPackets)));

    Config::SetDefault ("ns3::RedQueueDisc::MinTh", DoubleValue (minTh));

    Config::SetDefault ("ns3::RedQueueDisc::MaxTh", DoubleValue (maxTh));

  } else if (queueDiscType == "fifo") {

    tch.SetRootQueueDisc ("ns3::FifoQueueDisc");

    Config::SetDefault ("ns3::FifoQueueDisc::MaxSize", QueueSizeValue (QueueSize (QueueSizeUnit::PACKETS, queueDiscLimitPackets)));

  } else if (queueDiscType == "randomdrop") {

    tch.SetRootQueueDisc ("ns3::RandomdropQueueDisc");

    Config::SetDefault ("ns3::RandomdropQueueDisc::MaxSize", QueueSizeValue (QueueSize (QueueSizeUnit::PACKETS, queueDiscLimitPackets)));

    Config::SetDefault ("ns3::RandomdropQueueDisc::DropProb", DoubleValue (dropprob));

  }

 

  /* Install the IP stack. */

  InternetStackHelper internetStackH;

  internetStackH.Install (router_0);

  internetStackH.Install (router_1);

  internetStackH.Install (LeafNodes);

 

  // Create the point-to-point link helpers

  PointToPointHelper pointToPointRouter;

  pointToPointRouter.SetDeviceAttribute  ("DataRate", StringValue ("1Mbps"));

  pointToPointRouter.SetChannelAttribute ("Delay", StringValue ("1ms"));

  PointToPointHelper pointToPointLeaf;

  pointToPointLeaf.SetDeviceAttribute    ("DataRate", StringValue ("10Mbps"));

  pointToPointLeaf.SetChannelAttribute   ("Delay", StringValue ("1ms"));

 

  NodeContainer router_nodes;

  router_nodes.Add (router_0);

  router_nodes.Add (router_1);

  NetDeviceContainer bottlelinkDevices = pointToPointRouter.Install (router_nodes);

  QueueDiscContainer queueDiscs = tch.Install (bottlelinkDevices.Get(0));

  Ipv4AddressHelper ipv4;

  ipv4.SetBase ("10.0.0.0", "255.255.255.0");

  Ipv4InterfaceContainer bottlelinkIface = ipv4.Assign (bottlelinkDevices);

  

  NetDeviceContainer leftlinkDevices[nLeaf];

  NetDeviceContainer rightlinkDevices[nLeaf];

  Ipv4InterfaceContainer leftlinkIface[nLeaf];

  Ipv4InterfaceContainer rightlinkIface[nLeaf];

  NodeContainer leftside_nodes[nLeaf];

  NodeContainer rightside_nodes[nLeaf];

  std::string ip;

  for (uint32_t i=0 ; i <nLeaf; i++) {

    leftside_nodes[i].Add (router_0);

    leftside_nodes[i].Add (LeafNodes.Get(i));

    leftlinkDevices[i].Add(pointToPointLeaf.Install (leftside_nodes[i]));

    ip = "10.0." + lexical_cast< std::string >( i + 1 ) +".0";

    //std::cout << "ip:" << ip << std::endl;

    ipv4.SetBase (ns3::Ipv4Address(ip.c_str()), "255.255.255.0");

    leftlinkIface[i].Add(ipv4.Assign (leftlinkDevices[i]));

 

    rightside_nodes[i].Add (router_1);

    rightside_nodes[i].Add (LeafNodes.Get(nLeaf+i));

    rightlinkDevices[i].Add(pointToPointLeaf.Install (rightside_nodes[i]));

    ip = "10.1." + lexical_cast< std::string >( i + 1 ) +".0";

    //std::cout << "ip:" << ip << std::endl;

    ipv4.SetBase (ns3::Ipv4Address(ip.c_str()), "255.255.255.0");

    rightlinkIface[i].Add(ipv4.Assign (rightlinkDevices[i]));

  }

 

  // Set up the actual simulation

  Ipv4GlobalRoutingHelper::PopulateRoutingTables ();

 

  /* Generate Application. */

  ApplicationContainer sinkApp[nLeaf];

  ApplicationContainer sourceApp[nLeaf];

  Ptr<PacketSink> sink[nLeaf];                   /* Pointer to the packet sink application */

  for (uint32_t i=0 ; i <nLeaf; i++) {

    PacketSinkHelper sinkHelper ("ns3::TcpSocketFactory", InetSocketAddress (Ipv4Address::GetAny (), 9+i));

    sinkApp[i] = sinkHelper.Install (LeafNodes.Get(nLeaf+i));

    sink[i] = StaticCast<PacketSink> (sinkApp[i].Get (0));

 

    //std::cout << "destination ip:" << rightlinkIface[i].GetAddress (1) << std::endl;

    BulkSendHelper source ("ns3::TcpSocketFactory", (InetSocketAddress (rightlinkIface[i].GetAddress (1), 9+i)));

    source.SetAttribute ("MaxBytes", UintegerValue (0));

    sourceApp[i] = source.Install (LeafNodes.Get(i));

 

    // To differentiate each applications starting time

    double min = 0.0;

    double max = 1.0;

    Ptr<UniformRandomVariable> x = CreateObject<UniformRandomVariable> ();

    x->SetAttribute ("Min", DoubleValue (min));

    x->SetAttribute ("Max", DoubleValue (max));

    double differ_value = x->GetValue ();

    sinkApp[i].Start (Seconds (0.0));

    sourceApp[i].Start (Seconds (0.1 + differ_value));

  }

 

  FlowMonitorHelper flowmon;

  Ptr<FlowMonitor> monitor = flowmon.InstallAll();

  double single_throughput = 0;

  double total_throughput = 0;

 

  Simulator::Stop (Seconds (20.0));

  Simulator::Run ();

 

  Ptr<Ipv4FlowClassifier> classifier = DynamicCast<Ipv4FlowClassifier> (flowmon.GetClassifier ());

  std::map<FlowId, FlowMonitor::FlowStats> stats = monitor->GetFlowStats ();

  for(std::map<FlowId, FlowMonitor::FlowStats>::const_iterator set = stats.begin(); set != stats.end(); set++)

  {

    single_throughput = set->second.rxBytes * 8.0 / (set->second.timeLastRxPacket.GetSeconds () - set->second.timeFirstRxPacket.GetSeconds ()) / 1000;

   

    Ipv4FlowClassifier::FiveTuple t = classifier->FindFlow(set->first);

 

    Ipv4Address ip1("10.0.0.0");

    Ipv4Address ip2=t.sourceAddress.CombineMask("255.255.0.0");

    if(ip1==ip2)

      total_throughput += single_throughput;

  

    //std::cout << "Flow " << set->first << " (" << t.sourceAddress << " -> " << t.destinationAddress << "), throughput: " << single_throughput << "kbps" << std::endl;

   

  }

  std::cout << "Total throughput (from left to right): " << total_throughput << "Kbps" << std::endl;

  

  Simulator::Destroy ();

  return 0;

}

 

Execution

 

when the nLeaf is set to 10. Fifo queue is adopted. The throughput is 1030.89Kbps

 

when the nLeaf is set to 10. Random Drop queue is adopted. The drop probability is set to 0.0. The throughput is 1030.89Kbps. The same as Fifo queue.

 

when the nLeaf is set to 10. Random Drop queue is adopted. The drop probability is set to 0.01. The throughput is 1029.61Kbps. The throughput is worse than the Fifo queue.

 

when the nLeaf is set to 30. Fifo queue is adopted. The throughput is 1062.75Kbps.

 

when the nLeaf is set to 30. Random Drop queue is adopted. The drop probability is set to 0.01. The throughput is 1073.22Kbps. The throughput is better than the Fifo queue. Because when FIFO queue is full, all the incoming packets of TCP flows will be dropped. Therefore, the senders will decrease the sending rate. Then those TCP flows that have dropped packets will decrease the sending rate at the same time. However, in Random drop queue scenario, the TCP senders won’t lower their rate at the same time. As a consequence, the throughput will be better for Random Drop Queue.

 

Back to NS3 Learning Guide

Last Modified: 2022/2/17 done

 

[Author]

Dr. Chih-Heng Ke

Department of Computer Science and Information Engineering, National Quemoy University, Kinmen, Taiwan

Email: smallko@gmail.com