diff --git a/CHANGES.html b/CHANGES.html
index b6811e281..9c59cb077 100644
--- a/CHANGES.html
+++ b/CHANGES.html
@@ -55,6 +55,7 @@ us a note on ns-developers mailing list.
New API:
- Added FqCobalt queue disc with L4S features and set associative hash.
+- Added FqPIE queue disc with L4S mode
Changes to existing API:
@@ -127,9 +128,9 @@ in order to support multi-users (MU) transmissions.
- Added Active/Inactive feature to PIE queue disc
- Added netmap and DPDK emulation device variants
- Added capability to configure STL pair and containers as attributes
-- Added CartesianToGeographic coordinate conversion capability
-
- Added LollipopCounter, a sequence number counter type
-
- Added 6 GHz band support for Wi-Fi 802.11ax
+
- Added CartesianToGeographic coordinate conversion capability
+- Added LollipopCounter, a sequence number counter type
+- Added 6 GHz band support for Wi-Fi 802.11ax
Changes to existing API:
diff --git a/RELEASE_NOTES b/RELEASE_NOTES
index f2c73be99..d51d88823 100644
--- a/RELEASE_NOTES
+++ b/RELEASE_NOTES
@@ -32,6 +32,7 @@ New user-visible features
- (wifi) HT Greenfield (HT_GF) preamble support has been removed from the model
- (wifi) Some wifi/src/model files were moved to the relevant subfolders (non-ht, ht, vht, he, and rate-control)
- (traffic-control) Added FqCobalt queue disc with L4S features and set associative hash.
+- (traffic-control) Added FqPIE queue disc with L4S mode.
Bugs fixed
----------
diff --git a/src/test/ns3tc/fq-pie-queue-disc-test-suite.cc b/src/test/ns3tc/fq-pie-queue-disc-test-suite.cc
new file mode 100644
index 000000000..8eaa4bbd9
--- /dev/null
+++ b/src/test/ns3tc/fq-pie-queue-disc-test-suite.cc
@@ -0,0 +1,834 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2020 NITK Surathkal
+ *
+ * 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: Bhaskar Kataria
+ * Tom Henderson
+ * Mohit P. Tahiliani
+ * Vivek Jain
+ * Ankit Deepak
+ *
+*/
+
+#include "ns3/test.h"
+#include "ns3/simulator.h"
+#include "ns3/fq-pie-queue-disc.h"
+#include "ns3/pie-queue-disc.h"
+#include "ns3/ipv4-header.h"
+#include "ns3/ipv4-packet-filter.h"
+#include "ns3/ipv4-queue-disc-item.h"
+#include "ns3/ipv4-address.h"
+#include "ns3/ipv6-header.h"
+#include "ns3/ipv6-packet-filter.h"
+#include "ns3/ipv6-queue-disc-item.h"
+#include "ns3/tcp-header.h"
+#include "ns3/udp-header.h"
+#include "ns3/string.h"
+#include "ns3/pointer.h"
+
+using namespace ns3;
+
+// Variable to assign g_hash to a new packet's flow
+int32_t g_hash;
+
+/**
+ * Simple test packet filter able to classify IPv4 packets
+ *
+ */
+class Ipv4FqPieTestPacketFilter : public Ipv4PacketFilter {
+public:
+ /**
+ * \brief Get the type ID.
+ * \return the object TypeId
+ */
+ static TypeId GetTypeId (void);
+
+ Ipv4FqPieTestPacketFilter ();
+ virtual ~Ipv4FqPieTestPacketFilter ();
+
+private:
+ virtual int32_t DoClassify (Ptr item) const;
+ virtual bool CheckProtocol (Ptr item) const;
+};
+
+TypeId
+Ipv4FqPieTestPacketFilter::GetTypeId (void)
+{
+ static TypeId tid = TypeId ("ns3::Ipv4FqPieTestPacketFilter")
+ .SetParent ()
+ .SetGroupName ("Internet")
+ .AddConstructor ()
+ ;
+ return tid;
+}
+
+Ipv4FqPieTestPacketFilter::Ipv4FqPieTestPacketFilter ()
+{
+}
+
+Ipv4FqPieTestPacketFilter::~Ipv4FqPieTestPacketFilter ()
+{
+}
+
+int32_t
+Ipv4FqPieTestPacketFilter::DoClassify (Ptr item) const
+{
+ return g_hash;
+}
+
+bool
+Ipv4FqPieTestPacketFilter::CheckProtocol (Ptr item) const
+{
+ return true;
+}
+
+/**
+ * This class tests packets for which there is no suitable filter
+ */
+class FqPieQueueDiscNoSuitableFilter : public TestCase
+{
+public:
+ FqPieQueueDiscNoSuitableFilter ();
+ virtual ~FqPieQueueDiscNoSuitableFilter ();
+
+private:
+ virtual void DoRun (void);
+};
+
+FqPieQueueDiscNoSuitableFilter::FqPieQueueDiscNoSuitableFilter ()
+ : TestCase ("Test packets that are not classified by any filter")
+{
+}
+
+FqPieQueueDiscNoSuitableFilter::~FqPieQueueDiscNoSuitableFilter ()
+{
+}
+
+void
+FqPieQueueDiscNoSuitableFilter::DoRun (void)
+{
+ // Packets that cannot be classified by the available filters should be dropped
+ Ptr queueDisc = CreateObjectWithAttributes ("MaxSize", StringValue ("4p"));
+ Ptr filter = CreateObject ();
+ queueDisc->AddPacketFilter (filter);
+
+ g_hash = -1;
+ queueDisc->SetQuantum (1500);
+ queueDisc->Initialize ();
+
+ Ptr p;
+ p = Create ();
+ Ptr item;
+ Ipv6Header ipv6Header;
+ Address dest;
+ item = Create (p, dest, 0, ipv6Header);
+ queueDisc->Enqueue (item);
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetNQueueDiscClasses (), 0, "no flow queue should have been created");
+
+ p = Create (reinterpret_cast ("hello, world"), 12);
+ item = Create (p, dest, 0, ipv6Header);
+ queueDisc->Enqueue (item);
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetNQueueDiscClasses (), 0, "no flow queue should have been created");
+
+ Simulator::Destroy ();
+}
+
+/**
+ * This class tests the IP flows separation and the packet limit
+ */
+class FqPieQueueDiscIPFlowsSeparationAndPacketLimit : public TestCase
+{
+public:
+ FqPieQueueDiscIPFlowsSeparationAndPacketLimit ();
+ virtual ~FqPieQueueDiscIPFlowsSeparationAndPacketLimit ();
+
+private:
+ virtual void DoRun (void);
+ void AddPacket (Ptr queue, Ipv4Header hdr);
+};
+
+FqPieQueueDiscIPFlowsSeparationAndPacketLimit::FqPieQueueDiscIPFlowsSeparationAndPacketLimit ()
+ : TestCase ("Test IP flows separation and packet limit")
+{
+}
+
+FqPieQueueDiscIPFlowsSeparationAndPacketLimit::~FqPieQueueDiscIPFlowsSeparationAndPacketLimit ()
+{
+}
+
+void
+FqPieQueueDiscIPFlowsSeparationAndPacketLimit::AddPacket (Ptr queue, Ipv4Header hdr)
+{
+ Ptr p = Create (100);
+ Address dest;
+ Ptr item = Create (p, dest, 0, hdr);
+ queue->Enqueue (item);
+}
+
+void
+FqPieQueueDiscIPFlowsSeparationAndPacketLimit::DoRun (void)
+{
+ Ptr queueDisc = CreateObjectWithAttributes ("MaxSize", StringValue ("4p"));
+
+ queueDisc->SetQuantum (1500);
+ queueDisc->Initialize ();
+
+ Ipv4Header hdr;
+ hdr.SetPayloadSize (100);
+ hdr.SetSource (Ipv4Address ("10.10.1.1"));
+ hdr.SetDestination (Ipv4Address ("10.10.1.2"));
+ hdr.SetProtocol (7);
+
+ // Add three packets from the first flow
+ AddPacket (queueDisc, hdr);
+ AddPacket (queueDisc, hdr);
+ AddPacket (queueDisc, hdr);
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 3, "unexpected number of packets in the queue disc");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 3, "unexpected number of packets in the flow queue");
+
+ // Add two packets from the second flow
+ hdr.SetDestination (Ipv4Address ("10.10.1.7"));
+ // Add the first packet
+ AddPacket (queueDisc, hdr);
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 4, "unexpected number of packets in the queue disc");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 3, "unexpected number of packets in the flow queue");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the flow queue");
+ // Add the second packet that causes two packets to be dropped from the fat flow (max backlog = 300, threshold = 150)
+ AddPacket (queueDisc, hdr);
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 3, "unexpected number of packets in the queue disc");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the flow queue");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetNPackets (), 2, "unexpected number of packets in the flow queue");
+
+ Simulator::Destroy ();
+}
+
+/**
+ * This class tests the deficit per flow
+ */
+class FqPieQueueDiscDeficit : public TestCase
+{
+public:
+ FqPieQueueDiscDeficit ();
+ virtual ~FqPieQueueDiscDeficit ();
+
+private:
+ virtual void DoRun (void);
+ void AddPacket (Ptr queue, Ipv4Header hdr);
+};
+
+FqPieQueueDiscDeficit::FqPieQueueDiscDeficit ()
+ : TestCase ("Test credits and flows status")
+{
+}
+
+FqPieQueueDiscDeficit::~FqPieQueueDiscDeficit ()
+{
+}
+
+void
+FqPieQueueDiscDeficit::AddPacket (Ptr queue, Ipv4Header hdr)
+{
+ Ptr p = Create (100);
+ Address dest;
+ Ptr item = Create (p, dest, 0, hdr);
+ queue->Enqueue (item);
+}
+
+void
+FqPieQueueDiscDeficit::DoRun (void)
+{
+ Ptr queueDisc = CreateObjectWithAttributes ();
+
+ queueDisc->SetQuantum (90);
+ queueDisc->Initialize ();
+
+ Ipv4Header hdr;
+ hdr.SetPayloadSize (100);
+ hdr.SetSource (Ipv4Address ("10.10.1.1"));
+ hdr.SetDestination (Ipv4Address ("10.10.1.2"));
+ hdr.SetProtocol (7);
+
+ // Add a packet from the first flow
+ AddPacket (queueDisc, hdr);
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 1, "unexpected number of packets in the queue disc");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the first flow queue");
+ Ptr flow1 = StaticCast (queueDisc->GetQueueDiscClass (0));
+ NS_TEST_ASSERT_MSG_EQ (flow1->GetDeficit (), static_cast (queueDisc->GetQuantum ()), "the deficit of the first flow must equal the quantum");
+ NS_TEST_ASSERT_MSG_EQ (flow1->GetStatus (), FqPieFlow::NEW_FLOW, "the first flow must be in the list of new queues");
+ // Dequeue a packet
+ queueDisc->Dequeue ();
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 0, "unexpected number of packets in the queue disc");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 0, "unexpected number of packets in the first flow queue");
+ // the deficit for the first flow becomes 90 - (100+20) = -30
+ NS_TEST_ASSERT_MSG_EQ (flow1->GetDeficit (), -30, "unexpected deficit for the first flow");
+
+ // Add two packets from the first flow
+ AddPacket (queueDisc, hdr);
+ AddPacket (queueDisc, hdr);
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 2, "unexpected number of packets in the queue disc");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 2, "unexpected number of packets in the first flow queue");
+ NS_TEST_ASSERT_MSG_EQ (flow1->GetStatus (), FqPieFlow::NEW_FLOW, "the first flow must still be in the list of new queues");
+
+ // Add two packets from the second flow
+ hdr.SetDestination (Ipv4Address ("10.10.1.10"));
+ AddPacket (queueDisc, hdr);
+ AddPacket (queueDisc, hdr);
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 4, "unexpected number of packets in the queue disc");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 2, "unexpected number of packets in the first flow queue");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetNPackets (), 2, "unexpected number of packets in the second flow queue");
+ Ptr flow2 = StaticCast (queueDisc->GetQueueDiscClass (1));
+ NS_TEST_ASSERT_MSG_EQ (flow2->GetDeficit (), static_cast (queueDisc->GetQuantum ()), "the deficit of the second flow must equal the quantum");
+ NS_TEST_ASSERT_MSG_EQ (flow2->GetStatus (), FqPieFlow::NEW_FLOW, "the second flow must be in the list of new queues");
+
+ // Dequeue a packet (from the second flow, as the first flow has a negative deficit)
+ queueDisc->Dequeue ();
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 3, "unexpected number of packets in the queue disc");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 2, "unexpected number of packets in the first flow queue");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the second flow queue");
+ // the first flow got a quantum of deficit (-30+90=60) and has been moved to the end of the list of old queues
+ NS_TEST_ASSERT_MSG_EQ (flow1->GetDeficit (), 60, "unexpected deficit for the first flow");
+ NS_TEST_ASSERT_MSG_EQ (flow1->GetStatus (), FqPieFlow::OLD_FLOW, "the first flow must be in the list of old queues");
+ // the second flow has a negative deficit (-30) and is still in the list of new queues
+ NS_TEST_ASSERT_MSG_EQ (flow2->GetDeficit (), -30, "unexpected deficit for the second flow");
+ NS_TEST_ASSERT_MSG_EQ (flow2->GetStatus (), FqPieFlow::NEW_FLOW, "the second flow must be in the list of new queues");
+
+ // Dequeue a packet (from the first flow, as the second flow has a negative deficit)
+ queueDisc->Dequeue ();
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 2, "unexpected number of packets in the queue disc");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the first flow queue");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the second flow queue");
+ // the first flow has a negative deficit (60-(100+20)= -60) and stays in the list of old queues
+ NS_TEST_ASSERT_MSG_EQ (flow1->GetDeficit (), -60, "unexpected deficit for the first flow");
+ NS_TEST_ASSERT_MSG_EQ (flow1->GetStatus (), FqPieFlow::OLD_FLOW, "the first flow must be in the list of old queues");
+ // the second flow got a quantum of deficit (-30+90=60) and has been moved to the end of the list of old queues
+ NS_TEST_ASSERT_MSG_EQ (flow2->GetDeficit (), 60, "unexpected deficit for the second flow");
+ NS_TEST_ASSERT_MSG_EQ (flow2->GetStatus (), FqPieFlow::OLD_FLOW, "the second flow must be in the list of new queues");
+
+ // Dequeue a packet (from the second flow, as the first flow has a negative deficit)
+ queueDisc->Dequeue ();
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 1, "unexpected number of packets in the queue disc");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the first flow queue");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetNPackets (), 0, "unexpected number of packets in the second flow queue");
+ // the first flow got a quantum of deficit (-60+90=30) and has been moved to the end of the list of old queues
+ NS_TEST_ASSERT_MSG_EQ (flow1->GetDeficit (), 30, "unexpected deficit for the first flow");
+ NS_TEST_ASSERT_MSG_EQ (flow1->GetStatus (), FqPieFlow::OLD_FLOW, "the first flow must be in the list of old queues");
+ // the second flow has a negative deficit (60-(100+20)= -60)
+ NS_TEST_ASSERT_MSG_EQ (flow2->GetDeficit (), -60, "unexpected deficit for the second flow");
+ NS_TEST_ASSERT_MSG_EQ (flow2->GetStatus (), FqPieFlow::OLD_FLOW, "the second flow must be in the list of new queues");
+
+ // Dequeue a packet (from the first flow, as the second flow has a negative deficit)
+ queueDisc->Dequeue ();
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 0, "unexpected number of packets in the queue disc");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 0, "unexpected number of packets in the first flow queue");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetNPackets (), 0, "unexpected number of packets in the second flow queue");
+ // the first flow has a negative deficit (30-(100+20)= -90)
+ NS_TEST_ASSERT_MSG_EQ (flow1->GetDeficit (), -90, "unexpected deficit for the first flow");
+ NS_TEST_ASSERT_MSG_EQ (flow1->GetStatus (), FqPieFlow::OLD_FLOW, "the first flow must be in the list of old queues");
+ // the second flow got a quantum of deficit (-60+90=30) and has been moved to the end of the list of old queues
+ NS_TEST_ASSERT_MSG_EQ (flow2->GetDeficit (), 30, "unexpected deficit for the second flow");
+ NS_TEST_ASSERT_MSG_EQ (flow2->GetStatus (), FqPieFlow::OLD_FLOW, "the second flow must be in the list of new queues");
+
+ // Dequeue a packet
+ queueDisc->Dequeue ();
+ // the first flow is at the head of the list of old queues but has a negative deficit, thus it gets a quantun
+ // of deficit (-90+90=0) and is moved to the end of the list of old queues. Then, the second flow (which has a
+ // positive deficit) is selected, but the second flow is empty and thus it is set to inactive. The first flow is
+ // reconsidered, but it has a null deficit, hence it gets another quantum of deficit (0+90=90). Then, the first
+ // flow is reconsidered again, now it has a positive deficit and hence it is selected. But, it is empty and
+ // therefore is set to inactive, too.
+ NS_TEST_ASSERT_MSG_EQ (flow1->GetDeficit (), 90, "unexpected deficit for the first flow");
+ NS_TEST_ASSERT_MSG_EQ (flow1->GetStatus (), FqPieFlow::INACTIVE, "the first flow must be inactive");
+ NS_TEST_ASSERT_MSG_EQ (flow2->GetDeficit (), 30, "unexpected deficit for the second flow");
+ NS_TEST_ASSERT_MSG_EQ (flow2->GetStatus (), FqPieFlow::INACTIVE, "the second flow must be inactive");
+
+ Simulator::Destroy ();
+}
+
+/**
+ * This class tests the TCP flows separation
+ */
+class FqPieQueueDiscTCPFlowsSeparation : public TestCase
+{
+public:
+ FqPieQueueDiscTCPFlowsSeparation ();
+ virtual ~FqPieQueueDiscTCPFlowsSeparation ();
+
+private:
+ virtual void DoRun (void);
+ void AddPacket (Ptr queue, Ipv4Header ipHdr, TcpHeader tcpHdr);
+};
+
+FqPieQueueDiscTCPFlowsSeparation::FqPieQueueDiscTCPFlowsSeparation ()
+ : TestCase ("Test TCP flows separation")
+{
+}
+
+FqPieQueueDiscTCPFlowsSeparation::~FqPieQueueDiscTCPFlowsSeparation ()
+{
+}
+
+void
+FqPieQueueDiscTCPFlowsSeparation::AddPacket (Ptr queue, Ipv4Header ipHdr, TcpHeader tcpHdr)
+{
+ Ptr p = Create (100);
+ p->AddHeader (tcpHdr);
+ Address dest;
+ Ptr item = Create (p, dest, 0, ipHdr);
+ queue->Enqueue (item);
+}
+
+void
+FqPieQueueDiscTCPFlowsSeparation::DoRun (void)
+{
+ Ptr queueDisc = CreateObjectWithAttributes ("MaxSize", StringValue ("10p"));
+
+ queueDisc->SetQuantum (1500);
+ queueDisc->Initialize ();
+
+ Ipv4Header hdr;
+ hdr.SetPayloadSize (100);
+ hdr.SetSource (Ipv4Address ("10.10.1.1"));
+ hdr.SetDestination (Ipv4Address ("10.10.1.2"));
+ hdr.SetProtocol (6);
+
+ TcpHeader tcpHdr;
+ tcpHdr.SetSourcePort (7);
+ tcpHdr.SetDestinationPort (27);
+
+ // Add three packets from the first flow
+ AddPacket (queueDisc, hdr, tcpHdr);
+ AddPacket (queueDisc, hdr, tcpHdr);
+ AddPacket (queueDisc, hdr, tcpHdr);
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 3, "unexpected number of packets in the queue disc");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 3, "unexpected number of packets in the first flow queue");
+
+ // Add a packet from the second flow
+ tcpHdr.SetSourcePort (8);
+ AddPacket (queueDisc, hdr, tcpHdr);
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 4, "unexpected number of packets in the queue disc");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 3, "unexpected number of packets in the first flow queue");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the second flow queue");
+
+ // Add a packet from the third flow
+ tcpHdr.SetDestinationPort (28);
+ AddPacket (queueDisc, hdr, tcpHdr);
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 5, "unexpected number of packets in the queue disc");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 3, "unexpected number of packets in the first flow queue");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the second flow queue");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (2)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the third flow queue");
+
+ // Add two packets from the fourth flow
+ tcpHdr.SetSourcePort (7);
+ AddPacket (queueDisc, hdr, tcpHdr);
+ AddPacket (queueDisc, hdr, tcpHdr);
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 7, "unexpected number of packets in the queue disc");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 3, "unexpected number of packets in the first flow queue");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the second flow queue");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (2)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the third flow queue");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (3)->GetQueueDisc ()->GetNPackets (), 2, "unexpected number of packets in the third flow queue");
+
+ Simulator::Destroy ();
+}
+
+/**
+ * This class tests the UDP flows separation
+ */
+class FqPieQueueDiscUDPFlowsSeparation : public TestCase
+{
+public:
+ FqPieQueueDiscUDPFlowsSeparation ();
+ virtual ~FqPieQueueDiscUDPFlowsSeparation ();
+
+private:
+ virtual void DoRun (void);
+ void AddPacket (Ptr queue, Ipv4Header ipHdr, UdpHeader udpHdr);
+};
+
+FqPieQueueDiscUDPFlowsSeparation::FqPieQueueDiscUDPFlowsSeparation ()
+ : TestCase ("Test UDP flows separation")
+{
+}
+
+FqPieQueueDiscUDPFlowsSeparation::~FqPieQueueDiscUDPFlowsSeparation ()
+{
+}
+
+void
+FqPieQueueDiscUDPFlowsSeparation::AddPacket (Ptr queue, Ipv4Header ipHdr, UdpHeader udpHdr)
+{
+ Ptr p = Create (100);
+ p->AddHeader (udpHdr);
+ Address dest;
+ Ptr item = Create (p, dest, 0, ipHdr);
+ queue->Enqueue (item);
+}
+
+void
+FqPieQueueDiscUDPFlowsSeparation::DoRun (void)
+{
+ Ptr queueDisc = CreateObjectWithAttributes ("MaxSize", StringValue ("10p"));
+
+ queueDisc->SetQuantum (1500);
+ queueDisc->Initialize ();
+
+ Ipv4Header hdr;
+ hdr.SetPayloadSize (100);
+ hdr.SetSource (Ipv4Address ("10.10.1.1"));
+ hdr.SetDestination (Ipv4Address ("10.10.1.2"));
+ hdr.SetProtocol (17);
+
+ UdpHeader udpHdr;
+ udpHdr.SetSourcePort (7);
+ udpHdr.SetDestinationPort (27);
+
+ // Add three packets from the first flow
+ AddPacket (queueDisc, hdr, udpHdr);
+ AddPacket (queueDisc, hdr, udpHdr);
+ AddPacket (queueDisc, hdr, udpHdr);
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 3, "unexpected number of packets in the queue disc");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 3, "unexpected number of packets in the first flow queue");
+
+ // Add a packet from the second flow
+ udpHdr.SetSourcePort (8);
+ AddPacket (queueDisc, hdr, udpHdr);
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 4, "unexpected number of packets in the queue disc");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 3, "unexpected number of packets in the first flow queue");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the second flow queue");
+
+ // Add a packet from the third flow
+ udpHdr.SetDestinationPort (28);
+ AddPacket (queueDisc, hdr, udpHdr);
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 5, "unexpected number of packets in the queue disc");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 3, "unexpected number of packets in the first flow queue");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the second flow queue");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (2)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the third flow queue");
+
+ // Add two packets from the fourth flow
+ udpHdr.SetSourcePort (7);
+ AddPacket (queueDisc, hdr, udpHdr);
+ AddPacket (queueDisc, hdr, udpHdr);
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 7, "unexpected number of packets in the queue disc");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 3, "unexpected number of packets in the first flow queue");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the second flow queue");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (2)->GetQueueDisc ()->GetNPackets (), 1, "unexpected number of packets in the third flow queue");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (3)->GetQueueDisc ()->GetNPackets (), 2, "unexpected number of packets in the third flow queue");
+
+ Simulator::Destroy ();
+}
+
+
+/*
+ * This class tests linear probing, collision response, and set
+ * creation capability of set associative hashing in FqPIE.
+ * We modified DoClassify () and CheckProtocol () so that we could control
+ * the hash returned for each packet. In the beginning, we use flow hashes
+ * ranging from 0 to 7. These must go into different queues in the same set.
+ * The set number for these is obtained using outerhash, which is 0.
+ * When a new packet arrives with flow hash 1024, outerhash = 0 is obtained
+ * and the first set is iteratively searched.
+ * The packet is eventually added to queue 0 since the tags of queues
+ * in the set do not match with the hash of the flow. The tag of queue 0 is
+ * updated as 1024. When a packet with hash 1025 arrives, outerhash = 0
+ * is obtained and the first set is iteratively searched.
+ * Since there is no match, it is added to queue 0 and the tag of queue 0 is
+ * updated to 1025.
+ *
+ * The variable outerhash stores the nearest multiple of 8 that is lesser than
+ * the hash. When a flow hash of 20 arrives, the value of outerhash
+ * is 16. Since m_flowIndices[16] wasn’t previously allotted, a new flow
+ * is created, and the tag corresponding to this queue is set to 20.
+*/
+
+class FqPieQueueDiscSetLinearProbing : public TestCase
+{
+public:
+ FqPieQueueDiscSetLinearProbing ();
+ virtual ~FqPieQueueDiscSetLinearProbing ();
+private:
+ virtual void DoRun (void);
+ void AddPacket (Ptr queue, Ipv4Header hdr);
+};
+
+FqPieQueueDiscSetLinearProbing::FqPieQueueDiscSetLinearProbing ()
+ : TestCase ("Test credits and flows status")
+{
+}
+
+FqPieQueueDiscSetLinearProbing::~FqPieQueueDiscSetLinearProbing ()
+{
+}
+
+void
+FqPieQueueDiscSetLinearProbing::AddPacket (Ptr queue, Ipv4Header hdr)
+{
+ Ptr p = Create (100);
+ Address dest;
+ Ptr item = Create (p, dest, 0, hdr);
+ queue->Enqueue (item);
+}
+
+void
+FqPieQueueDiscSetLinearProbing::DoRun (void)
+{
+ Ptr queueDisc = CreateObjectWithAttributes ("EnableSetAssociativeHash", BooleanValue (true));
+ queueDisc->SetQuantum (90);
+ queueDisc->Initialize ();
+
+ Ptr filter = CreateObject ();
+ queueDisc->AddPacketFilter (filter);
+
+ Ipv4Header hdr;
+ hdr.SetPayloadSize (100);
+ hdr.SetSource (Ipv4Address ("10.10.1.1"));
+ hdr.SetDestination (Ipv4Address ("10.10.1.2"));
+ hdr.SetProtocol (7);
+
+ g_hash = 0;
+ AddPacket (queueDisc, hdr);
+ g_hash = 1;
+ AddPacket (queueDisc, hdr);
+ AddPacket (queueDisc, hdr);
+ g_hash = 2;
+ AddPacket (queueDisc, hdr);
+ g_hash = 3;
+ AddPacket (queueDisc, hdr);
+ g_hash = 4;
+ AddPacket (queueDisc, hdr);
+ AddPacket (queueDisc, hdr);
+ g_hash = 5;
+ AddPacket (queueDisc, hdr);
+ g_hash = 6;
+ AddPacket (queueDisc, hdr);
+ g_hash = 7;
+ AddPacket (queueDisc, hdr);
+ g_hash = 1024;
+ AddPacket (queueDisc, hdr);
+
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->QueueDisc::GetNPackets (), 11,
+ "unexpected number of packets in the queue disc");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 2,
+ "unexpected number of packets in the first flow queue of set one");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetNPackets (), 2,
+ "unexpected number of packets in the second flow queue of set one");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (2)->GetQueueDisc ()->GetNPackets (), 1,
+ "unexpected number of packets in the third flow queue of set one");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (3)->GetQueueDisc ()->GetNPackets (), 1,
+ "unexpected number of packets in the fourth flow queue of set one");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (4)->GetQueueDisc ()->GetNPackets (), 2,
+ "unexpected number of packets in the fifth flow queue of set one");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (5)->GetQueueDisc ()->GetNPackets (), 1,
+ "unexpected number of packets in the sixth flow queue of set one");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (6)->GetQueueDisc ()->GetNPackets (), 1,
+ "unexpected number of packets in the seventh flow queue of set one");
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (7)->GetQueueDisc ()->GetNPackets (), 1,
+ "unexpected number of packets in the eighth flow queue of set one");
+ g_hash = 1025;
+ AddPacket (queueDisc, hdr);
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetNPackets (), 3,
+ "unexpected number of packets in the first flow of set one");
+ g_hash = 10;
+ AddPacket (queueDisc, hdr);
+ NS_TEST_ASSERT_MSG_EQ (queueDisc->GetQueueDiscClass (8)->GetQueueDisc ()->GetNPackets (), 1,
+ "unexpected number of packets in the first flow of set two");
+ Simulator::Destroy ();
+}
+
+
+/**
+ * This class tests L4S mode. This test is divided to sub test one without hash collisions and so ECT0 and ECT1 flows are
+ * classified into different flows.
+ * Sub Test 1
+ * 70 packets are enqueued into both the flows with the delay of 0.5ms between two enqueues, and dequeued with the delay of
+ * 1ms between two dequeues.
+ * Sub Test 2
+ * 140(70 ECT0 + 70 ECT1) packets are enqueued such that ECT1 packets are enqueued at 0.5ms, 1.5ms, 2.5ms and so on, and ECT0 packets are
+ * enqueued are enqueued at 1ms, 2ms, 3ms and so on
+ * Any future classifier options (e.g. SetAssociativehash) should be disabled to prevent a hash collision on this test case.
+ */
+class FqPieQueueDiscL4sMode : public TestCase
+{
+public:
+ FqPieQueueDiscL4sMode ();
+ virtual ~FqPieQueueDiscL4sMode ();
+
+private:
+ virtual void DoRun (void);
+ void AddPacket (Ptr queue, Ipv4Header hdr, u_int32_t nPkt);
+ void AddPacketWithDelay (Ptr queue,Ipv4Header hdr, double delay, uint32_t nPkt);
+ void Dequeue (Ptr queue, uint32_t nPkt);
+ void DequeueWithDelay (Ptr queue, double delay, uint32_t nPkt);
+};
+
+FqPieQueueDiscL4sMode::FqPieQueueDiscL4sMode ()
+ : TestCase ("Test L4S mode")
+{
+}
+
+FqPieQueueDiscL4sMode::~FqPieQueueDiscL4sMode ()
+{
+}
+
+void
+FqPieQueueDiscL4sMode::AddPacket (Ptr queue, Ipv4Header hdr, uint32_t nPkt)
+{
+ Address dest;
+ Ptr p = Create (100);
+ for (uint32_t i = 0; i < nPkt; i++)
+ {
+ Ptr item = Create (p, dest, 0, hdr);
+ queue->Enqueue (item);
+ }
+}
+
+void
+FqPieQueueDiscL4sMode::AddPacketWithDelay (Ptr queue,Ipv4Header hdr, double delay, uint32_t nPkt)
+{
+ for (uint32_t i = 0; i < nPkt; i++)
+ {
+ Simulator::Schedule (Time (Seconds ((i + 1) * delay)), &FqPieQueueDiscL4sMode::AddPacket, this, queue, hdr, 1);
+ }
+}
+
+void
+FqPieQueueDiscL4sMode::Dequeue (Ptr queue, uint32_t nPkt)
+{
+ for (uint32_t i = 0; i < nPkt; i++)
+ {
+ Ptr item = queue->Dequeue ();
+ }
+}
+
+void
+FqPieQueueDiscL4sMode::DequeueWithDelay (Ptr queue, double delay, uint32_t nPkt)
+{
+ for (uint32_t i = 0; i < nPkt; i++)
+ {
+ Simulator::Schedule (Time (Seconds ((i + 1) * delay)), &FqPieQueueDiscL4sMode::Dequeue, this, queue, 1);
+ }
+}
+
+void
+FqPieQueueDiscL4sMode::DoRun (void)
+{
+ // Test is divided into 2 sub test cases:
+ // 1) Without hash collisions
+ // 2) With hash collisions
+
+ // Test case 1, Without hash collisions
+ Ptr queueDisc = CreateObjectWithAttributes ("MaxSize", StringValue ("10240p"), "UseEcn", BooleanValue (true),
+ "Perturbation", UintegerValue (0), "UseL4s", BooleanValue (true),
+ "CeThreshold", TimeValue (MilliSeconds (2)));
+
+ queueDisc->SetQuantum (1514);
+ queueDisc->Initialize ();
+ Ipv4Header hdr;
+ hdr.SetPayloadSize (100);
+ hdr.SetSource (Ipv4Address ("10.10.1.1"));
+ hdr.SetDestination (Ipv4Address ("10.10.1.2"));
+ hdr.SetProtocol (7);
+ hdr.SetEcn (Ipv4Header::ECN_ECT1);
+
+ // Add 70 ECT1 (ECN capable) packets from the first flow
+ // Set delay = 0.5ms
+ double delay = 0.0005;
+ Simulator::Schedule (Time (Seconds (0)), &FqPieQueueDiscL4sMode::AddPacketWithDelay, this, queueDisc, hdr, delay, 70);
+
+ // Add 70 ECT0 (ECN capable) packets from second flow
+ hdr.SetEcn (Ipv4Header::ECN_ECT0);
+ hdr.SetDestination (Ipv4Address ("10.10.1.10"));
+ Simulator::Schedule (Time (Seconds (0)), &FqPieQueueDiscL4sMode::AddPacketWithDelay, this, queueDisc, hdr, delay, 70);
+
+ //Dequeue 140 packets with delay 1ms
+ delay = 0.001;
+ DequeueWithDelay (queueDisc, delay, 140);
+ Simulator::Stop (Seconds (10.0));
+ Simulator::Run ();
+
+ Ptr q0 = queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetObject ();
+ Ptr q1 = queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetObject ();
+
+ NS_TEST_EXPECT_MSG_EQ (q0->GetStats ().GetNMarkedPackets (PieQueueDisc::CE_THRESHOLD_EXCEEDED_MARK), 66, "There should be 66 marked packets"
+ "4th packet is enqueued at 2ms and dequeued at 4ms hence the delay of 2ms which not greater than CE threshold"
+ "5th packet is enqueued at 2.5ms and dequeued at 5ms hence the delay of 2.5ms and subsequent packet also do have delay"
+ "greater than CE threshold so all the packets after 4th packet are marked");
+ NS_TEST_EXPECT_MSG_EQ (q0->GetStats ().GetNDroppedPackets (PieQueueDisc::UNFORCED_DROP), 0, "Queue delay is less than max burst allowance so"
+ "There should not be any dropped packets");
+ NS_TEST_EXPECT_MSG_EQ (q0->GetStats ().GetNMarkedPackets (PieQueueDisc::UNFORCED_MARK), 0, "There should not be any marked packets");
+ NS_TEST_EXPECT_MSG_EQ (q1->GetStats ().GetNMarkedPackets (PieQueueDisc::UNFORCED_MARK), 0, "There should not be marked packets.");
+ NS_TEST_EXPECT_MSG_EQ (q1->GetStats ().GetNDroppedPackets (PieQueueDisc::UNFORCED_DROP), 0, "There should not be any dropped packets");
+
+ Simulator::Destroy ();
+
+ // Test case 2, With hash collisions
+ queueDisc = CreateObjectWithAttributes ("MaxSize", StringValue ("10240p"), "UseEcn", BooleanValue (true),
+ "Perturbation", UintegerValue (0), "UseL4s", BooleanValue (true),
+ "CeThreshold", TimeValue (MilliSeconds (2)));
+
+ queueDisc->SetQuantum (1514);
+ queueDisc->Initialize ();
+ hdr.SetPayloadSize (100);
+ hdr.SetSource (Ipv4Address ("10.10.1.1"));
+ hdr.SetDestination (Ipv4Address ("10.10.1.2"));
+ hdr.SetProtocol (7);
+ hdr.SetEcn (Ipv4Header::ECN_ECT1);
+
+ // Add 70 ECT1 (ECN capable) packets from the first flow
+ // Set delay = 1ms
+ delay = 0.001;
+ Simulator::Schedule (Time (Seconds (0.0005)), &FqPieQueueDiscL4sMode::AddPacket, this, queueDisc, hdr, 1);
+ Simulator::Schedule (Time (Seconds (0.0005)), &FqPieQueueDiscL4sMode::AddPacketWithDelay, this, queueDisc, hdr, delay, 69);
+
+ // Add 70 ECT0 (ECN capable) packets from first flow
+ hdr.SetEcn (Ipv4Header::ECN_ECT0);
+ Simulator::Schedule (Time (Seconds (0)), &FqPieQueueDiscL4sMode::AddPacketWithDelay, this, queueDisc, hdr, delay, 70);
+
+ //Dequeue 140 packets with delay 1ms
+ DequeueWithDelay (queueDisc, delay, 140);
+ Simulator::Stop (Seconds (1.0));
+ Simulator::Run ();
+ q0 = queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetObject ();
+ q0 = queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetObject ();
+
+ NS_TEST_EXPECT_MSG_EQ (q0->GetStats ().GetNMarkedPackets (PieQueueDisc::CE_THRESHOLD_EXCEEDED_MARK), 68, "There should be 68 marked packets"
+ "2nd ECT1 packet is enqueued at 1.5ms and dequeued at 3ms hence the delay of 1.5ms which not greater than CE threshold"
+ "3rd packet is enqueued at 2.5ms and dequeued at 5ms hence the delay of 2.5ms and subsequent packet also do have delay"
+ "greater than CE threshold so all the packets after 2nd packet are marked");
+ NS_TEST_EXPECT_MSG_EQ (q0->GetStats ().GetNDroppedPackets (PieQueueDisc::UNFORCED_DROP), 0, "Queue delay is less than max burst allowance so"
+ "There should not be any dropped packets");
+ NS_TEST_EXPECT_MSG_EQ (q0->GetStats ().GetNMarkedPackets (PieQueueDisc::UNFORCED_MARK), 0, "There should not be any marked packets");
+
+ Simulator::Destroy ();
+}
+
+class FqPieQueueDiscTestSuite : public TestSuite
+{
+public:
+ FqPieQueueDiscTestSuite ();
+};
+
+FqPieQueueDiscTestSuite::FqPieQueueDiscTestSuite ()
+ : TestSuite ("fq-pie-queue-disc", UNIT)
+{
+ AddTestCase (new FqPieQueueDiscNoSuitableFilter, TestCase::QUICK);
+ AddTestCase (new FqPieQueueDiscIPFlowsSeparationAndPacketLimit, TestCase::QUICK);
+ AddTestCase (new FqPieQueueDiscDeficit, TestCase::QUICK);
+ AddTestCase (new FqPieQueueDiscTCPFlowsSeparation, TestCase::QUICK);
+ AddTestCase (new FqPieQueueDiscUDPFlowsSeparation, TestCase::QUICK);
+ AddTestCase (new FqPieQueueDiscSetLinearProbing, TestCase::QUICK);
+ AddTestCase (new FqPieQueueDiscL4sMode, TestCase::QUICK);
+}
+
+static FqPieQueueDiscTestSuite FqPieQueueDiscTestSuite;
diff --git a/src/test/wscript b/src/test/wscript
index 1bbbb1a74..c491a637d 100644
--- a/src/test/wscript
+++ b/src/test/wscript
@@ -32,6 +32,7 @@ def build(bld):
'csma-system-test-suite.cc',
'ns3tc/fq-codel-queue-disc-test-suite.cc',
'ns3tc/fq-cobalt-queue-disc-test-suite.cc',
+ 'ns3tc/fq-pie-queue-disc-test-suite.cc',
'ns3tc/pfifo-fast-queue-disc-test-suite.cc',
'ns3tcp/ns3tcp-cwnd-test-suite.cc',
'ns3tcp/ns3tcp-interop-test-suite.cc',
diff --git a/src/traffic-control/doc/fq-pie.rst b/src/traffic-control/doc/fq-pie.rst
new file mode 100644
index 000000000..0397e2401
--- /dev/null
+++ b/src/traffic-control/doc/fq-pie.rst
@@ -0,0 +1,83 @@
+.. include:: replace.txt
+.. highlight:: cpp
+.. highlight:: bash
+
+FqPIE queue disc
+------------------
+
+This chapter describes the FqPIE ([Hoe16]_) queue disc implementation in |ns3|.
+
+The FlowQueue-PIE (FQ-PIE) algorithm is similar to the FlowQueue part of FlowQueue-CoDel (FQ-CoDel) algorithm available in ns-3-dev/src/traffic-control/doc/fq-codel.rst.
+The documentation for PIE is available in ns-3-dev/src/traffic-control/doc/pie.rst.
+
+
+Model Description
+*****************
+
+The source code for the FqPIE queue disc is located in the directory
+``src/traffic-control/model`` and consists of 2 files `fq-pie-queue-disc.h`
+and `fq-pie-queue-disc.cc` defining a FqPieQueueDisc class and a helper
+FqPieFlow class. The code was ported to |ns3| based on Linux kernel code
+implemented by Mohit P. Tahiliani.
+
+The Model Description is similar to the FqCoDel documentation mentioned above.
+
+
+References
+==========
+
+.. [CAK16] https://github.com/torvalds/linux/blob/master/net/sched/sch_fq_pie.c , Implementation of FqPIE in Linux
+.. [PIE] https://ieeexplore.ieee.org/document/9000684
+
+Attributes
+==========
+
+The key attributes that the FqPieQueue class holds include the following:
+
+Most of the key attributes are similar to the FqCoDel implementation mentioned above.
+Some differences are:
+Absence of ``MinBytes``, ``Interval`` and ``Target`` parameter.
+Some extra parameters are
+* ``MarkEcnThreshold:`` ECN marking threshold (RFC 8033 suggests 0.1 (i.e., 10%) default).
+* ``MeanPktSize:`` Average of packet size.
+* ``A:`` Decrement value of drop probability. Default value is 1./4096 .
+* ``B:`` The Threshold after which Blue is enabled. Default value is 400ms.
+* ``Tupdate:`` Time period to calculate drop probability
+* ``Supdate:`` Start time of the update timer
+* ``DequeueThreshold:`` Minimum queue size in bytes before dequeue rate is measured
+* ``QueueDelayReference:`` Desired queue delay
+* ``MaxBurstAllowance:`` Current max burst allowance before random drop
+* ``UseDequeueRateEstimator:`` Enable/Disable usage of Dequeue Rate Estimator
+* ``UseCapDropAdjustment:`` Enable/Disable Cap Drop Adjustment feature mentioned in RFC 8033
+* ``UseDerandomization:`` Enable/Disable Derandomization feature mentioned in RFC 8033
+
+
+Examples
+========
+
+A typical usage pattern is to create a traffic control helper and to configure type
+and attributes of queue disc and filters from the helper. For example, FqPIE
+can be configured as follows:
+
+.. sourcecode:: cpp
+
+ TrafficControlHelper tch;
+ tch.SetRootQueueDisc ("ns3::FqPieQueueDisc", "DropBatchSize", UintegerValue (1)
+ "Perturbation", UintegerValue (256));
+ QueueDiscContainer qdiscs = tch.Install (devices);
+
+Validation
+**********
+
+The FqPie model is tested using :cpp:class:`FqPieQueueDiscTestSuite` class defined in `src/test/ns3tc/fq-pie-queue-test-suite.cc`.
+
+The tests are similar to the ones for FqCoDel queue disc mentioned in first section of this document.
+The test suite can be run using the following commands::
+
+ $ ./waf configure --enable-examples --enable-tests
+ $ ./waf build
+ $ ./test.py -s fq-pie-queue-disc
+
+or::
+
+ $ NS_LOG="FqPieQueueDisc" ./waf --run "test-runner --suite=fq-pie-queue-disc"
\ No newline at end of file
diff --git a/src/traffic-control/model/fq-pie-queue-disc.cc b/src/traffic-control/model/fq-pie-queue-disc.cc
new file mode 100755
index 000000000..872c28830
--- /dev/null
+++ b/src/traffic-control/model/fq-pie-queue-disc.cc
@@ -0,0 +1,561 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2018 NITK Surathkal
+ *
+ * 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: Sumukha PK
+ * Prajval M <26prajval98@gmail.com>
+ * Ishaan R D
+ * Mohit P. Tahiliani
+ */
+
+
+#include "ns3/log.h"
+#include "ns3/string.h"
+#include "ns3/queue.h"
+#include "fq-pie-queue-disc.h"
+#include "pie-queue-disc.h"
+#include "ns3/net-device-queue-interface.h"
+
+namespace ns3 {
+
+NS_LOG_COMPONENT_DEFINE ("FqPieQueueDisc");
+
+NS_OBJECT_ENSURE_REGISTERED (FqPieFlow);
+
+TypeId FqPieFlow::GetTypeId (void)
+{
+ static TypeId tid = TypeId ("ns3::FqPieFlow")
+ .SetParent ()
+ .SetGroupName ("TrafficControl")
+ .AddConstructor ()
+ ;
+ return tid;
+}
+
+FqPieFlow::FqPieFlow ()
+ : m_deficit (0),
+ m_status (INACTIVE),
+ m_index (0)
+{
+ NS_LOG_FUNCTION (this);
+}
+
+FqPieFlow::~FqPieFlow ()
+{
+ NS_LOG_FUNCTION (this);
+}
+
+void
+FqPieFlow::SetDeficit (uint32_t deficit)
+{
+ NS_LOG_FUNCTION (this << deficit);
+ m_deficit = deficit;
+}
+
+int32_t
+FqPieFlow::GetDeficit (void) const
+{
+ NS_LOG_FUNCTION (this);
+ return m_deficit;
+}
+
+void
+FqPieFlow::IncreaseDeficit (int32_t deficit)
+{
+ NS_LOG_FUNCTION (this << deficit);
+ m_deficit += deficit;
+}
+
+void
+FqPieFlow::SetStatus (FlowStatus status)
+{
+ NS_LOG_FUNCTION (this);
+ m_status = status;
+}
+
+FqPieFlow::FlowStatus
+FqPieFlow::GetStatus (void) const
+{
+ NS_LOG_FUNCTION (this);
+ return m_status;
+}
+
+void
+FqPieFlow::SetIndex (uint32_t index)
+{
+ NS_LOG_FUNCTION (this);
+ m_index = index;
+}
+
+uint32_t
+FqPieFlow::GetIndex (void) const
+{
+ return m_index;
+}
+
+
+NS_OBJECT_ENSURE_REGISTERED (FqPieQueueDisc);
+
+TypeId FqPieQueueDisc::GetTypeId (void)
+{
+ static TypeId tid = TypeId ("ns3::FqPieQueueDisc")
+ .SetParent ()
+ .SetGroupName ("TrafficControl")
+ .AddConstructor ()
+ .AddAttribute ("UseEcn",
+ "True to use ECN (packets are marked instead of being dropped)",
+ BooleanValue (true),
+ MakeBooleanAccessor (&FqPieQueueDisc::m_useEcn),
+ MakeBooleanChecker ())
+ .AddAttribute ("MarkEcnThreshold",
+ "ECN marking threshold (RFC 8033 suggests 0.1 (i.e., 10%) default)",
+ DoubleValue (0.1),
+ MakeDoubleAccessor (&FqPieQueueDisc::m_markEcnTh),
+ MakeDoubleChecker (0, 1))
+ .AddAttribute ("CeThreshold",
+ "The FqPie CE threshold for marking packets",
+ TimeValue (Time::Max ()),
+ MakeTimeAccessor (&FqPieQueueDisc::m_ceThreshold),
+ MakeTimeChecker ())
+ .AddAttribute ("UseL4s",
+ "True to use L4S (only ECT1 packets are marked at CE threshold)",
+ BooleanValue (false),
+ MakeBooleanAccessor (&FqPieQueueDisc::m_useL4s),
+ MakeBooleanChecker ())
+ .AddAttribute ("MeanPktSize",
+ "Average of packet size",
+ UintegerValue (1000),
+ MakeUintegerAccessor (&FqPieQueueDisc::m_meanPktSize),
+ MakeUintegerChecker ())
+ .AddAttribute ("A",
+ "Value of alpha",
+ DoubleValue (0.125),
+ MakeDoubleAccessor (&FqPieQueueDisc::m_a),
+ MakeDoubleChecker ())
+ .AddAttribute ("B",
+ "Value of beta",
+ DoubleValue (1.25),
+ MakeDoubleAccessor (&FqPieQueueDisc::m_b),
+ MakeDoubleChecker ())
+ .AddAttribute ("Tupdate",
+ "Time period to calculate drop probability",
+ TimeValue (Seconds (0.015)),
+ MakeTimeAccessor (&FqPieQueueDisc::m_tUpdate),
+ MakeTimeChecker ())
+ .AddAttribute ("Supdate",
+ "Start time of the update timer",
+ TimeValue (Seconds (0)),
+ MakeTimeAccessor (&FqPieQueueDisc::m_sUpdate),
+ MakeTimeChecker ())
+ .AddAttribute ("MaxSize",
+ "The maximum number of packets accepted by this queue disc",
+ QueueSizeValue (QueueSize ("10240p")),
+ MakeQueueSizeAccessor (&QueueDisc::SetMaxSize,
+ &QueueDisc::GetMaxSize),
+ MakeQueueSizeChecker ())
+ .AddAttribute ("DequeueThreshold",
+ "Minimum queue size in bytes before dequeue rate is measured",
+ UintegerValue (16384),
+ MakeUintegerAccessor (&FqPieQueueDisc::m_dqThreshold),
+ MakeUintegerChecker ())
+ .AddAttribute ("QueueDelayReference",
+ "Desired queue delay",
+ TimeValue (Seconds (0.015)),
+ MakeTimeAccessor (&FqPieQueueDisc::m_qDelayRef),
+ MakeTimeChecker ())
+ .AddAttribute ("MaxBurstAllowance",
+ "Current max burst allowance before random drop",
+ TimeValue (Seconds (0.15)),
+ MakeTimeAccessor (&FqPieQueueDisc::m_maxBurst),
+ MakeTimeChecker ())
+ .AddAttribute ("UseDequeueRateEstimator",
+ "Enable/Disable usage of Dequeue Rate Estimator",
+ BooleanValue (false),
+ MakeBooleanAccessor (&FqPieQueueDisc::m_useDqRateEstimator),
+ MakeBooleanChecker ())
+ .AddAttribute ("UseCapDropAdjustment",
+ "Enable/Disable Cap Drop Adjustment feature mentioned in RFC 8033",
+ BooleanValue (true),
+ MakeBooleanAccessor (&FqPieQueueDisc::m_isCapDropAdjustment),
+ MakeBooleanChecker ())
+ .AddAttribute ("UseDerandomization",
+ "Enable/Disable Derandomization feature mentioned in RFC 8033",
+ BooleanValue (false),
+ MakeBooleanAccessor (&FqPieQueueDisc::m_useDerandomization),
+ MakeBooleanChecker ())
+ .AddAttribute ("Flows",
+ "The number of queues into which the incoming packets are classified",
+ UintegerValue (1024),
+ MakeUintegerAccessor (&FqPieQueueDisc::m_flows),
+ MakeUintegerChecker ())
+ .AddAttribute ("DropBatchSize",
+ "The maximum number of packets dropped from the fat flow",
+ UintegerValue (64),
+ MakeUintegerAccessor (&FqPieQueueDisc::m_dropBatchSize),
+ MakeUintegerChecker ())
+ .AddAttribute ("Perturbation",
+ "The salt used as an additional input to the hash function used to classify packets",
+ UintegerValue (0),
+ MakeUintegerAccessor (&FqPieQueueDisc::m_perturbation),
+ MakeUintegerChecker ())
+ .AddAttribute ("EnableSetAssociativeHash",
+ "Enable/Disable Set Associative Hash",
+ BooleanValue (false),
+ MakeBooleanAccessor (&FqPieQueueDisc::m_enableSetAssociativeHash),
+ MakeBooleanChecker ())
+ .AddAttribute ("SetWays",
+ "The size of a set of queues (used by set associative hash)",
+ UintegerValue (8),
+ MakeUintegerAccessor (&FqPieQueueDisc::m_setWays),
+ MakeUintegerChecker ())
+ ;
+ return tid;
+}
+
+FqPieQueueDisc::FqPieQueueDisc ()
+ : QueueDisc (QueueDiscSizePolicy::MULTIPLE_QUEUES, QueueSizeUnit::PACKETS),
+ m_quantum (0)
+{
+ NS_LOG_FUNCTION (this);
+}
+
+FqPieQueueDisc::~FqPieQueueDisc ()
+{
+ NS_LOG_FUNCTION (this);
+}
+
+void
+FqPieQueueDisc::SetQuantum (uint32_t quantum)
+{
+ NS_LOG_FUNCTION (this << quantum);
+ m_quantum = quantum;
+}
+
+uint32_t
+FqPieQueueDisc::GetQuantum (void) const
+{
+ return m_quantum;
+}
+
+uint32_t
+FqPieQueueDisc::SetAssociativeHash (uint32_t flowHash)
+{
+ NS_LOG_FUNCTION (this << flowHash);
+
+ uint32_t h = (flowHash % m_flows);
+ uint32_t innerHash = h % m_setWays;
+ uint32_t outerHash = h - innerHash;
+
+ for (uint32_t i = outerHash; i < outerHash + m_setWays; i++)
+ {
+ auto it = m_flowsIndices.find (i);
+
+ if (it == m_flowsIndices.end ()
+ || (m_tags.find (i) != m_tags.end () && m_tags[i] == flowHash)
+ || StaticCast (GetQueueDiscClass (it->second))->GetStatus () == FqPieFlow::INACTIVE)
+ {
+ // this queue has not been created yet or is associated with this flow
+ // or is inactive, hence we can use it
+ m_tags[i] = flowHash;
+ return i;
+ }
+ }
+
+ // all the queues of the set are used. Use the first queue of the set
+ m_tags[outerHash] = flowHash;
+ return outerHash;
+}
+
+bool
+FqPieQueueDisc::DoEnqueue (Ptr item)
+{
+ NS_LOG_FUNCTION (this << item);
+
+ uint32_t flowHash, h;
+
+ if (GetNPacketFilters () == 0)
+ {
+ flowHash = item->Hash (m_perturbation);
+ }
+ else
+ {
+ int32_t ret = Classify (item);
+
+ if (ret != PacketFilter::PF_NO_MATCH)
+ {
+ flowHash = static_cast (ret);
+ }
+ else
+ {
+ NS_LOG_ERROR ("No filter has been able to classify this packet, drop it.");
+ DropBeforeEnqueue (item, UNCLASSIFIED_DROP);
+ return false;
+ }
+ }
+
+ if (m_enableSetAssociativeHash)
+ {
+ h = SetAssociativeHash (flowHash);
+ }
+ else
+ {
+ h = flowHash % m_flows;
+ }
+
+ Ptr flow;
+ if (m_flowsIndices.find (h) == m_flowsIndices.end ())
+ {
+ NS_LOG_DEBUG ("Creating a new flow queue with index " << h);
+ flow = m_flowFactory.Create ();
+ Ptr qd = m_queueDiscFactory.Create ();
+ // If Pie, Set values of PieQueueDisc to match this QueueDisc
+ Ptr pie = qd->GetObject ();
+ if (pie)
+ {
+ pie->SetAttribute ("UseEcn", BooleanValue (m_useEcn));
+ pie->SetAttribute ("CeThreshold", TimeValue (m_ceThreshold));
+ pie->SetAttribute ("UseL4s", BooleanValue (m_useL4s));
+ }
+ qd->Initialize ();
+ flow->SetQueueDisc (qd);
+ flow->SetIndex (h);
+ AddQueueDiscClass (flow);
+
+ m_flowsIndices[h] = GetNQueueDiscClasses () - 1;
+ }
+ else
+ {
+ flow = StaticCast (GetQueueDiscClass (m_flowsIndices[h]));
+ }
+
+ if (flow->GetStatus () == FqPieFlow::INACTIVE)
+ {
+ flow->SetStatus (FqPieFlow::NEW_FLOW);
+ flow->SetDeficit (m_quantum);
+ m_newFlows.push_back (flow);
+ }
+
+ flow->GetQueueDisc ()->Enqueue (item);
+
+ NS_LOG_DEBUG ("Packet enqueued into flow " << h << "; flow index " << m_flowsIndices[h]);
+
+ if (GetCurrentSize () > GetMaxSize ())
+ {
+ NS_LOG_DEBUG ("Overload; enter FqPieDrop ()");
+ FqPieDrop ();
+ }
+
+ return true;
+}
+
+Ptr
+FqPieQueueDisc::DoDequeue (void)
+{
+ NS_LOG_FUNCTION (this);
+
+ Ptr flow;
+ Ptr item;
+
+ do
+ {
+ bool found = false;
+
+ while (!found && !m_newFlows.empty ())
+ {
+ flow = m_newFlows.front ();
+
+ if (flow->GetDeficit () <= 0)
+ {
+ NS_LOG_DEBUG ("Increase deficit for new flow index " << flow->GetIndex ());
+ flow->IncreaseDeficit (m_quantum);
+ flow->SetStatus (FqPieFlow::OLD_FLOW);
+ m_oldFlows.push_back (flow);
+ m_newFlows.pop_front ();
+ }
+ else
+ {
+ NS_LOG_DEBUG ("Found a new flow " << flow->GetIndex () << " with positive deficit");
+ found = true;
+ }
+ }
+
+ while (!found && !m_oldFlows.empty ())
+ {
+ flow = m_oldFlows.front ();
+
+ if (flow->GetDeficit () <= 0)
+ {
+ NS_LOG_DEBUG ("Increase deficit for old flow index " << flow->GetIndex ());
+ flow->IncreaseDeficit (m_quantum);
+ m_oldFlows.push_back (flow);
+ m_oldFlows.pop_front ();
+ }
+ else
+ {
+ NS_LOG_DEBUG ("Found an old flow " << flow->GetIndex () << " with positive deficit");
+ found = true;
+ }
+ }
+
+ if (!found)
+ {
+ NS_LOG_DEBUG ("No flow found to dequeue a packet");
+ return 0;
+ }
+
+ item = flow->GetQueueDisc ()->Dequeue ();
+
+ if (!item)
+ {
+ NS_LOG_DEBUG ("Could not get a packet from the selected flow queue");
+ if (!m_newFlows.empty ())
+ {
+ flow->SetStatus (FqPieFlow::OLD_FLOW);
+ m_oldFlows.push_back (flow);
+ m_newFlows.pop_front ();
+ }
+ else
+ {
+ flow->SetStatus (FqPieFlow::INACTIVE);
+ m_oldFlows.pop_front ();
+ }
+ }
+ else
+ {
+ NS_LOG_DEBUG ("Dequeued packet " << item->GetPacket ());
+ }
+ } while (item == 0);
+
+ flow->IncreaseDeficit (item->GetSize () * -1);
+
+ return item;
+}
+
+bool
+FqPieQueueDisc::CheckConfig (void)
+{
+ NS_LOG_FUNCTION (this);
+ if (GetNQueueDiscClasses () > 0)
+ {
+ NS_LOG_ERROR ("FqPieQueueDisc cannot have classes");
+ return false;
+ }
+
+ if (GetNInternalQueues () > 0)
+ {
+ NS_LOG_ERROR ("FqPieQueueDisc cannot have internal queues");
+ return false;
+ }
+ // we are at initialization time. If the user has not set a quantum value,
+ // set the quantum to the MTU of the device (if any)
+ if (!m_quantum)
+ {
+ Ptr ndqi = GetNetDeviceQueueInterface ();
+ Ptr dev;
+ // if the NetDeviceQueueInterface object is aggregated to a
+ // NetDevice, get the MTU of such NetDevice
+ if (ndqi && (dev = ndqi->GetObject ()))
+ {
+ m_quantum = dev->GetMtu ();
+ NS_LOG_DEBUG ("Setting the quantum to the MTU of the device: " << m_quantum);
+ }
+
+ if (!m_quantum)
+ {
+ NS_LOG_ERROR ("The quantum parameter cannot be null");
+ return false;
+ }
+ }
+
+ if (m_enableSetAssociativeHash && (m_flows % m_setWays != 0))
+ {
+ NS_LOG_ERROR ("The number of queues must be an integer multiple of the size "
+ "of the set of queues used by set associative hash");
+ return false;
+ }
+
+ // If UseL4S attribute is enabled then CE threshold must be set.
+ if (m_useL4s)
+ {
+ NS_ABORT_MSG_IF (m_ceThreshold == Time::Max(), "CE threshold not set");
+ if (m_useEcn == false)
+ {
+ NS_LOG_WARN ("Enabling ECN as L4S mode is enabled");
+ }
+ }
+ return true;
+}
+
+void
+FqPieQueueDisc::InitializeParams (void)
+{
+ NS_LOG_FUNCTION (this);
+
+ m_flowFactory.SetTypeId ("ns3::FqPieFlow");
+
+ m_queueDiscFactory.SetTypeId ("ns3::PieQueueDisc");
+ m_queueDiscFactory.Set ("MaxSize", QueueSizeValue (GetMaxSize ()));
+ m_queueDiscFactory.Set ("MeanPktSize", UintegerValue (1000));
+ m_queueDiscFactory.Set ("A", DoubleValue (0.125));
+ m_queueDiscFactory.Set ("B", DoubleValue (1.25));
+ m_queueDiscFactory.Set ("Tupdate", TimeValue (Seconds (0.015)));
+ m_queueDiscFactory.Set ("Supdate", TimeValue (Seconds (0)));
+ m_queueDiscFactory.Set ("DequeueThreshold", UintegerValue (16384));
+ m_queueDiscFactory.Set ("QueueDelayReference", TimeValue (Seconds (0.015)));
+ m_queueDiscFactory.Set ("MaxBurstAllowance", TimeValue (Seconds (0.15)));
+ m_queueDiscFactory.Set ("UseDequeueRateEstimator", BooleanValue (false));
+ m_queueDiscFactory.Set ("UseCapDropAdjustment", BooleanValue (true));
+ m_queueDiscFactory.Set ("UseDerandomization", BooleanValue (false));
+}
+
+uint32_t
+FqPieQueueDisc::FqPieDrop (void)
+{
+ NS_LOG_FUNCTION (this);
+
+ uint32_t maxBacklog = 0, index = 0;
+ Ptr qd;
+
+ /* Queue is full! Find the fat flow and drop packet(s) from it */
+ for (uint32_t i = 0; i < GetNQueueDiscClasses (); i++)
+ {
+ qd = GetQueueDiscClass (i)->GetQueueDisc ();
+ uint32_t bytes = qd->GetNBytes ();
+ if (bytes > maxBacklog)
+ {
+ maxBacklog = bytes;
+ index = i;
+ }
+ }
+
+ /* Our goal is to drop half of this fat flow backlog */
+ uint32_t len = 0, count = 0, threshold = maxBacklog >> 1;
+ qd = GetQueueDiscClass (index)->GetQueueDisc ();
+ Ptr item;
+
+ do
+ {
+ NS_LOG_DEBUG ("Drop packet (overflow); count: " << count << " len: " << len << " threshold: " << threshold);
+ item = qd->GetInternalQueue (0)->Dequeue ();
+ DropAfterDequeue (item, OVERLIMIT_DROP);
+ len += item->GetSize ();
+ } while (++count < m_dropBatchSize && len < threshold);
+
+ return index;
+}
+
+} // namespace ns3
+
diff --git a/src/traffic-control/model/fq-pie-queue-disc.h b/src/traffic-control/model/fq-pie-queue-disc.h
new file mode 100755
index 000000000..0f9b49e8b
--- /dev/null
+++ b/src/traffic-control/model/fq-pie-queue-disc.h
@@ -0,0 +1,204 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2018 NITK Surathkal
+ *
+ * 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: Sumukha PK
+ * Prajval M <26prajval98@gmail.com>
+ * Ishaan R D
+ * Mohit P. Tahiliani
+ */
+
+#ifndef FQ_PIE_QUEUE_DISC
+#define FQ_PIE_QUEUE_DISC
+
+#include "ns3/queue-disc.h"
+#include "ns3/object-factory.h"
+#include
+#include