From 3d7a42d42eb091e89598cf4defdfe5c612410227 Mon Sep 17 00:00:00 2001
From: Bhaskar Kataria
Date: Thu, 23 Jul 2020 00:13:32 +0530
Subject: [PATCH] traffic-control: (merges !362) Add FqCobalt queue disc
FqCobalt includes COBALT enhancements to CoDel (Blue enhancement) and
set-associative hash, as used in the Linux cake queue disc. An L4S mode is
also included.
---
CHANGES.html | 2 +-
RELEASE_NOTES | 1 +
.../ns3tc/fq-cobalt-queue-disc-test-suite.cc | 1123 +++++++++++++++++
src/test/wscript | 1 +
src/traffic-control/doc/cobalt.rst | 21 +-
src/traffic-control/doc/fq-cobalt.rst | 76 ++
.../model/cobalt-queue-disc.cc | 59 +-
src/traffic-control/model/cobalt-queue-disc.h | 4 +
.../model/fq-cobalt-queue-disc.cc | 523 ++++++++
.../model/fq-cobalt-queue-disc.h | 197 +++
.../test/cobalt-queue-disc-test-suite.cc | 354 +++++-
src/traffic-control/wscript | 2 +
12 files changed, 2326 insertions(+), 37 deletions(-)
create mode 100644 src/test/ns3tc/fq-cobalt-queue-disc-test-suite.cc
create mode 100644 src/traffic-control/doc/fq-cobalt.rst
create mode 100644 src/traffic-control/model/fq-cobalt-queue-disc.cc
create mode 100644 src/traffic-control/model/fq-cobalt-queue-disc.h
diff --git a/CHANGES.html b/CHANGES.html
index 246c49ffe..b6811e281 100644
--- a/CHANGES.html
+++ b/CHANGES.html
@@ -54,7 +54,7 @@ us a note on ns-developers mailing list.
Changes from ns-3.33 to ns-3.34
New API:
-
+- Added FqCobalt queue disc with L4S features and set associative hash.
Changes to existing API:
diff --git a/RELEASE_NOTES b/RELEASE_NOTES
index ad49b9a09..f2c73be99 100644
--- a/RELEASE_NOTES
+++ b/RELEASE_NOTES
@@ -31,6 +31,7 @@ New user-visible features
- (wifi) Holland PHY configuration has been removed from the model
- (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.
Bugs fixed
----------
diff --git a/src/test/ns3tc/fq-cobalt-queue-disc-test-suite.cc b/src/test/ns3tc/fq-cobalt-queue-disc-test-suite.cc
new file mode 100644
index 000000000..627277f89
--- /dev/null
+++ b/src/test/ns3tc/fq-cobalt-queue-disc-test-suite.cc
@@ -0,0 +1,1123 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2016 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: Pasquale Imputato
+ * Stefano Avallone
+*/
+
+#include "ns3/test.h"
+#include "ns3/simulator.h"
+#include "ns3/fq-cobalt-queue-disc.h"
+#include "ns3/cobalt-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 m_hash to a new packet's flow
+int32_t m_hash;
+
+/**
+ * Simple test packet filter able to classify IPv4 packets
+ *
+ */
+class Ipv4FqCobaltTestPacketFilter : public Ipv4PacketFilter {
+public:
+ /**
+ * \brief Get the type ID.
+ * \return the object TypeId
+ */
+ static TypeId GetTypeId (void);
+
+ Ipv4FqCobaltTestPacketFilter ();
+ virtual ~Ipv4FqCobaltTestPacketFilter ();
+
+private:
+ virtual int32_t DoClassify (Ptr item) const;
+ virtual bool CheckProtocol (Ptr item) const;
+};
+
+TypeId
+Ipv4FqCobaltTestPacketFilter::GetTypeId (void)
+{
+ static TypeId tid = TypeId ("ns3::Ipv4FqCobaltTestPacketFilter")
+ .SetParent ()
+ .SetGroupName ("Internet")
+ .AddConstructor ()
+ ;
+ return tid;
+}
+
+Ipv4FqCobaltTestPacketFilter::Ipv4FqCobaltTestPacketFilter ()
+{
+}
+
+Ipv4FqCobaltTestPacketFilter::~Ipv4FqCobaltTestPacketFilter ()
+{
+}
+
+int32_t
+Ipv4FqCobaltTestPacketFilter::DoClassify (Ptr item) const
+{
+ return m_hash;
+}
+
+bool
+Ipv4FqCobaltTestPacketFilter::CheckProtocol (Ptr item) const
+{
+ return true;
+}
+
+/**
+ * This class tests packets for which there is no suitable filter
+ */
+class FqCobaltQueueDiscNoSuitableFilter : public TestCase
+{
+public:
+ FqCobaltQueueDiscNoSuitableFilter ();
+ virtual ~FqCobaltQueueDiscNoSuitableFilter ();
+
+private:
+ virtual void DoRun (void);
+};
+
+FqCobaltQueueDiscNoSuitableFilter::FqCobaltQueueDiscNoSuitableFilter ()
+ : TestCase ("Test packets that are not classified by any filter")
+{
+}
+
+FqCobaltQueueDiscNoSuitableFilter::~FqCobaltQueueDiscNoSuitableFilter ()
+{
+}
+
+void
+FqCobaltQueueDiscNoSuitableFilter::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);
+
+ m_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 FqCobaltQueueDiscIPFlowsSeparationAndPacketLimit : public TestCase
+{
+public:
+ FqCobaltQueueDiscIPFlowsSeparationAndPacketLimit ();
+ virtual ~FqCobaltQueueDiscIPFlowsSeparationAndPacketLimit ();
+
+private:
+ virtual void DoRun (void);
+ void AddPacket (Ptr queue, Ipv4Header hdr);
+};
+
+FqCobaltQueueDiscIPFlowsSeparationAndPacketLimit::FqCobaltQueueDiscIPFlowsSeparationAndPacketLimit ()
+ : TestCase ("Test IP flows separation and packet limit")
+{
+}
+
+FqCobaltQueueDiscIPFlowsSeparationAndPacketLimit::~FqCobaltQueueDiscIPFlowsSeparationAndPacketLimit ()
+{
+}
+
+void
+FqCobaltQueueDiscIPFlowsSeparationAndPacketLimit::AddPacket (Ptr queue, Ipv4Header hdr)
+{
+ Ptr p = Create (100);
+ Address dest;
+ Ptr item = Create (p, dest, 0, hdr);
+ queue->Enqueue (item);
+}
+
+void
+FqCobaltQueueDiscIPFlowsSeparationAndPacketLimit::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 FqCobaltQueueDiscDeficit : public TestCase
+{
+public:
+ FqCobaltQueueDiscDeficit ();
+ virtual ~FqCobaltQueueDiscDeficit ();
+
+private:
+ virtual void DoRun (void);
+ void AddPacket (Ptr queue, Ipv4Header hdr);
+};
+
+FqCobaltQueueDiscDeficit::FqCobaltQueueDiscDeficit ()
+ : TestCase ("Test credits and flows status")
+{
+}
+
+FqCobaltQueueDiscDeficit::~FqCobaltQueueDiscDeficit ()
+{
+}
+
+void
+FqCobaltQueueDiscDeficit::AddPacket (Ptr queue, Ipv4Header hdr)
+{
+ Ptr p = Create (100);
+ Address dest;
+ Ptr item = Create (p, dest, 0, hdr);
+ queue->Enqueue (item);
+}
+
+void
+FqCobaltQueueDiscDeficit::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 (), FqCobaltFlow::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 (), FqCobaltFlow::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 (), FqCobaltFlow::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 (), FqCobaltFlow::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 (), FqCobaltFlow::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 (), FqCobaltFlow::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 (), FqCobaltFlow::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 (), FqCobaltFlow::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 (), FqCobaltFlow::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 (), FqCobaltFlow::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 (), FqCobaltFlow::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 (), FqCobaltFlow::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 (), FqCobaltFlow::INACTIVE, "the second flow must be inactive");
+
+ Simulator::Destroy ();
+}
+
+/**
+ * This class tests the TCP flows separation
+ */
+class FqCobaltQueueDiscTCPFlowsSeparation : public TestCase
+{
+public:
+ FqCobaltQueueDiscTCPFlowsSeparation ();
+ virtual ~FqCobaltQueueDiscTCPFlowsSeparation ();
+
+private:
+ virtual void DoRun (void);
+ void AddPacket (Ptr queue, Ipv4Header ipHdr, TcpHeader tcpHdr);
+};
+
+FqCobaltQueueDiscTCPFlowsSeparation::FqCobaltQueueDiscTCPFlowsSeparation ()
+ : TestCase ("Test TCP flows separation")
+{
+}
+
+FqCobaltQueueDiscTCPFlowsSeparation::~FqCobaltQueueDiscTCPFlowsSeparation ()
+{
+}
+
+void
+FqCobaltQueueDiscTCPFlowsSeparation::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
+FqCobaltQueueDiscTCPFlowsSeparation::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 FqCobaltQueueDiscUDPFlowsSeparation : public TestCase
+{
+public:
+ FqCobaltQueueDiscUDPFlowsSeparation ();
+ virtual ~FqCobaltQueueDiscUDPFlowsSeparation ();
+
+private:
+ virtual void DoRun (void);
+ void AddPacket (Ptr queue, Ipv4Header ipHdr, UdpHeader udpHdr);
+};
+
+FqCobaltQueueDiscUDPFlowsSeparation::FqCobaltQueueDiscUDPFlowsSeparation ()
+ : TestCase ("Test UDP flows separation")
+{
+}
+
+FqCobaltQueueDiscUDPFlowsSeparation::~FqCobaltQueueDiscUDPFlowsSeparation ()
+{
+}
+
+void
+FqCobaltQueueDiscUDPFlowsSeparation::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
+FqCobaltQueueDiscUDPFlowsSeparation::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 ECN marking
+ * The test is divided into 3 sub test cases.
+ * 1) CE threshold disabled
+ * This test enqueues 100 packets in the beginning of the test and dequeues 60 (some packets are dropped too) packets with the
+ * delay of 110ms. This test checks that ECT0 packets are marked and are marked appropriately and NotECT packets are dropped.
+ *
+ * 2) CE threshold enabled.
+ * This test enqueues 100 packets in the beginning of the test and dequeues 60 packets with delay of 1ms. This test checks that
+ * the ECT0 packets are marked appropriately at CE threshold.
+ *
+ * 3) CE threshold enabled with higher queue delay.
+ * This test is similar to the 2nd sub test cases just with higher queue delay and aims to test that the packets are not
+ * marked twice
+ * Any future classifier options (e.g. SetAssociativehash) should be disabled to prevent a hash collision on this test case.
+ */
+class FqCobaltQueueDiscEcnMarking : public TestCase
+{
+public:
+ FqCobaltQueueDiscEcnMarking ();
+ virtual ~FqCobaltQueueDiscEcnMarking ();
+
+private:
+ virtual void DoRun (void);
+ void AddPacket (Ptr queue, Ipv4Header hdr, u_int32_t nPkt, u_int32_t nPktEnqueued, u_int32_t nQueueFlows);
+ void Dequeue (Ptr queue, uint32_t nPkt);
+ void DequeueWithDelay (Ptr queue, double delay, uint32_t nPkt);
+ void DropNextTracer (int64_t oldVal, int64_t newVal);
+ uint32_t m_dropNextCount; ///< count the number of times m_dropNext is recalculated
+};
+
+FqCobaltQueueDiscEcnMarking::FqCobaltQueueDiscEcnMarking ()
+ : TestCase ("Test ECN marking")
+{
+ m_dropNextCount = 0;
+}
+
+FqCobaltQueueDiscEcnMarking::~FqCobaltQueueDiscEcnMarking ()
+{
+}
+
+void
+FqCobaltQueueDiscEcnMarking::AddPacket (Ptr queue, Ipv4Header hdr, u_int32_t nPkt, u_int32_t nPktEnqueued, u_int32_t nQueueFlows)
+{
+ Address dest;
+ Ptr p = Create (100);
+ for (uint32_t i = 0; i < nPkt; i++)
+ {
+ Ptr item = Create (p, dest, 0, hdr);
+ queue->Enqueue (item);
+ }
+ NS_TEST_EXPECT_MSG_EQ (queue->GetNQueueDiscClasses (), nQueueFlows, "unexpected number of flow queues");
+ NS_TEST_EXPECT_MSG_EQ (queue->GetNPackets (), nPktEnqueued, "unexpected number of enqueued packets");
+}
+
+void
+FqCobaltQueueDiscEcnMarking::Dequeue (Ptr queue, uint32_t nPkt)
+{
+ Ptr q3 = queue->GetQueueDiscClass (3)->GetQueueDisc ()->GetObject ();
+
+ // Trace DropNext after the first dequeue as m_dropNext value is set after the first dequeue
+ if (q3->GetNPackets () == 19)
+ {
+ q3->TraceConnectWithoutContext ("DropNext", MakeCallback (&FqCobaltQueueDiscEcnMarking::DropNextTracer, this));
+ }
+
+ for (uint32_t i = 0; i < nPkt; i++)
+ {
+ Ptr item = queue->Dequeue ();
+ }
+}
+
+void
+FqCobaltQueueDiscEcnMarking::DequeueWithDelay (Ptr queue, double delay, uint32_t nPkt)
+{
+ for (uint32_t i = 0; i < nPkt; i++)
+ {
+ Simulator::Schedule (Time (Seconds ((i + 1) * delay)), &FqCobaltQueueDiscEcnMarking::Dequeue, this, queue, 1);
+ }
+}
+
+void
+FqCobaltQueueDiscEcnMarking::DropNextTracer (int64_t oldVal, int64_t newVal)
+{
+ NS_UNUSED (oldVal);
+ NS_UNUSED (newVal);
+ m_dropNextCount++;
+}
+
+void
+FqCobaltQueueDiscEcnMarking::DoRun (void)
+{
+ // Test is divided into 3 sub test cases:
+ // 1) CeThreshold disabled
+ // 2) CeThreshold enabled
+ // 3) Same as 2 but with higher queue delay
+
+ // Test case 1, CeThreshold disabled
+ Ptr queueDisc = CreateObjectWithAttributes ("MaxSize", StringValue ("10240p"), "UseEcn", BooleanValue (true),
+ "Perturbation", UintegerValue (0), "BlueThreshold", TimeValue (Time::Max ()));
+
+ 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_ECT0);
+
+ // Add 20 ECT0 (ECN capable) packets from the first flow
+ Simulator::Schedule (Time (Seconds (0)), &FqCobaltQueueDiscEcnMarking::AddPacket, this, queueDisc, hdr, 20, 20, 1);
+
+ // Add 20 ECT0 (ECN capable) packets from second flow
+ hdr.SetDestination (Ipv4Address ("10.10.1.10"));
+ Simulator::Schedule (Time (Seconds (0)), &FqCobaltQueueDiscEcnMarking::AddPacket, this, queueDisc, hdr, 20, 40, 2);
+
+ // Add 20 ECT0 (ECN capable) packets from third flow
+ hdr.SetDestination (Ipv4Address ("10.10.1.20"));
+ Simulator::Schedule (Time (Seconds (0)), &FqCobaltQueueDiscEcnMarking::AddPacket, this, queueDisc, hdr, 20, 60, 3);
+
+ // Add 20 NotECT packets from fourth flow
+ hdr.SetDestination (Ipv4Address ("10.10.1.30"));
+ hdr.SetEcn (Ipv4Header::ECN_NotECT);
+ Simulator::Schedule (Time (Seconds (0)), &FqCobaltQueueDiscEcnMarking::AddPacket, this, queueDisc, hdr, 20, 80, 4);
+
+ // Add 20 NotECT packets from fifth flow
+ hdr.SetDestination (Ipv4Address ("10.10.1.40"));
+ Simulator::Schedule (Time (Seconds (0)), &FqCobaltQueueDiscEcnMarking::AddPacket, this, queueDisc, hdr, 20, 100, 5);
+
+ //Dequeue 60 packets with delay 110ms to induce packet drops and keep some remaining packets in each queue
+ DequeueWithDelay (queueDisc, 0.11, 60);
+ Simulator::Run ();
+ Simulator::Stop (Seconds (8.0));
+ Ptr q0 = queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetObject ();
+ Ptr q1 = queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetObject ();
+ Ptr q2 = queueDisc->GetQueueDiscClass (2)->GetQueueDisc ()->GetObject ();
+ Ptr q3 = queueDisc->GetQueueDiscClass (3)->GetQueueDisc ()->GetObject ();
+ Ptr q4 = queueDisc->GetQueueDiscClass (4)->GetQueueDisc ()->GetObject ();
+
+ // As packets in flow queues are ECN capable
+ NS_TEST_EXPECT_MSG_EQ (q0->GetStats ().GetNMarkedPackets (CobaltQueueDisc::FORCED_MARK), 19, "There should be 19 marked packets."
+ "As there is no CoDel minBytes parameter so all the packets apart from the first one gets marked. As q3 and q4 have"
+ "NotEct packets and the queue delay is much higher than 5ms so the queue gets empty pretty quickly so more"
+ "packets from q0 can be dequeued.");
+ NS_TEST_EXPECT_MSG_EQ (q0->GetStats ().GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP), 0, "There should not be any dropped packets");
+ NS_TEST_EXPECT_MSG_EQ (q1->GetStats ().GetNMarkedPackets (CobaltQueueDisc::FORCED_MARK), 16, "There should be 16 marked packets"
+ "As there is no CoDel minBytes parameter so all the packets apart from the first one until no more packets are dequeued"
+ "are marked.");
+ NS_TEST_EXPECT_MSG_EQ (q1->GetStats ().GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP), 0, "There should not be any dropped packets");
+ NS_TEST_EXPECT_MSG_EQ (q2->GetStats ().GetNMarkedPackets (CobaltQueueDisc::FORCED_MARK), 12, "There should be 12 marked packets"
+ "Each packet size is 120 bytes and the quantum is 1500 bytes so in the first turn (1514/120 = 12.61) 13 packets are"
+ "dequeued and apart from the first one, all the packets are marked.");
+ NS_TEST_EXPECT_MSG_EQ (q2->GetStats ().GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP), 0, "There should not be any dropped packets");
+
+ // As packets in flow queues are not ECN capable
+ NS_TEST_EXPECT_MSG_EQ (q3->GetStats ().GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP), m_dropNextCount, "The number of drops should"
+ "be equal to the number of times m_dropNext is updated");
+ NS_TEST_EXPECT_MSG_EQ (q3->GetStats ().GetNMarkedPackets (CobaltQueueDisc::FORCED_MARK), 0, "There should not be any marked packets");
+ NS_TEST_EXPECT_MSG_EQ (q4->GetStats ().GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP), m_dropNextCount, "The number of drops should"
+ "be equal to the number of times m_dropNext is updated");
+ NS_TEST_EXPECT_MSG_EQ (q4->GetStats ().GetNMarkedPackets (CobaltQueueDisc::FORCED_MARK), 0, "There should not be any marked packets");
+
+ Simulator::Destroy ();
+
+ // Test case 2, CeThreshold set to 2ms
+ queueDisc = CreateObjectWithAttributes ("MaxSize", StringValue ("10240p"), "UseEcn", BooleanValue (true),
+ "CeThreshold", TimeValue (MilliSeconds (2)));
+ queueDisc->SetQuantum (1514);
+ queueDisc->Initialize ();
+
+ // Add 20 ECT0 (ECN capable) packets from first flow
+ hdr.SetDestination (Ipv4Address ("10.10.1.2"));
+ hdr.SetEcn (Ipv4Header::ECN_ECT0);
+ Simulator::Schedule (Time (Seconds (0)), &FqCobaltQueueDiscEcnMarking::AddPacket, this, queueDisc, hdr, 20, 20, 1);
+
+ // Add 20 ECT0 (ECN capable) packets from second flow
+ hdr.SetDestination (Ipv4Address ("10.10.1.10"));
+ Simulator::Schedule (Time (Seconds (0)), &FqCobaltQueueDiscEcnMarking::AddPacket, this, queueDisc, hdr, 20, 40, 2);
+
+ // Add 20 ECT0 (ECN capable) packets from third flow
+ hdr.SetDestination (Ipv4Address ("10.10.1.20"));
+ Simulator::Schedule (Time (Seconds (0)), &FqCobaltQueueDiscEcnMarking::AddPacket, this, queueDisc, hdr, 20, 60, 3);
+
+ // Add 20 NotECT packets from fourth flow
+ hdr.SetDestination (Ipv4Address ("10.10.1.30"));
+ hdr.SetEcn (Ipv4Header::ECN_NotECT);
+ Simulator::Schedule (Time (Seconds (0)), &FqCobaltQueueDiscEcnMarking::AddPacket, this, queueDisc, hdr, 20, 80, 4);
+
+ // Add 20 NotECT packets from fifth flow
+ hdr.SetDestination (Ipv4Address ("10.10.1.40"));
+ Simulator::Schedule (Time (Seconds (0)), &FqCobaltQueueDiscEcnMarking::AddPacket, this, queueDisc, hdr, 20, 100, 5);
+
+ //Dequeue 60 packets with delay 0.1ms to induce packet drops and keep some remaining packets in each queue
+ DequeueWithDelay (queueDisc, 0.0001, 60);
+ Simulator::Run ();
+ Simulator::Stop (Seconds (8.0));
+ q0 = queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetObject ();
+ q1 = queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetObject ();
+ q2 = queueDisc->GetQueueDiscClass (2)->GetQueueDisc ()->GetObject ();
+ q3 = queueDisc->GetQueueDiscClass (3)->GetQueueDisc ()->GetObject ();
+ q4 = queueDisc->GetQueueDiscClass (4)->GetQueueDisc ()->GetObject ();
+
+ // As packets in flow queues are ECN capable
+ NS_TEST_EXPECT_MSG_EQ (q0->GetStats ().GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP), 0, "There should not be any dropped packets");
+ NS_TEST_EXPECT_MSG_EQ (q0->GetStats ().GetNMarkedPackets (CobaltQueueDisc::CE_THRESHOLD_EXCEEDED_MARK), 0, "There should not be any marked packets"
+ "with quantum of 1514, 13 packets of size 120 bytes can be dequeued. sojourn time of 13th packet is 1.3ms which is"
+ "less than CE threshold");
+ NS_TEST_EXPECT_MSG_EQ (q1->GetStats ().GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP), 0, "There should not be any dropped packets");
+ NS_TEST_EXPECT_MSG_EQ (q1->GetStats ().GetNMarkedPackets (CobaltQueueDisc::CE_THRESHOLD_EXCEEDED_MARK), 6, "There should be 6 marked packets"
+ "with quantum of 1514, 13 packets of size 120 bytes can be dequeued. sojourn time of 8th packet is 2.1ms which is greater"
+ "than CE threshold and subsequent packet also have sojourn time more 8th packet hence remaining packet are marked.");
+ NS_TEST_EXPECT_MSG_EQ (q2->GetStats ().GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP), 0, "There should not be any dropped packets");
+ NS_TEST_EXPECT_MSG_EQ (q2->GetStats ().GetNMarkedPackets (CobaltQueueDisc::CE_THRESHOLD_EXCEEDED_MARK), 13, "There should be 13 marked packets"
+ "with quantum of 1514, 13 packets of size 120 bytes can be dequeued and all of them have sojourn time more than CE threshold");
+
+ // As packets in flow queues are not ECN capable
+ NS_TEST_EXPECT_MSG_EQ (q3->GetStats ().GetNMarkedPackets (CobaltQueueDisc::CE_THRESHOLD_EXCEEDED_MARK), 0, "There should not be any marked packets");
+ NS_TEST_EXPECT_MSG_EQ (q3->GetStats ().GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP), 0, "There should not be any dropped packets");
+ NS_TEST_EXPECT_MSG_EQ (q4->GetStats ().GetNMarkedPackets (CobaltQueueDisc::CE_THRESHOLD_EXCEEDED_MARK), 0, "There should not be any marked packets");
+ NS_TEST_EXPECT_MSG_EQ (q4->GetStats ().GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP), 1, "There should 1 dropped packet. As the queue"
+ "delay for the first dequeue is greater than the target (5ms), Cobalt overloads the m_dropNext field as an activity timeout"
+ "and dropNext is to set to the current Time value so on the next dequeue a packet is dropped.");
+
+ Simulator::Destroy ();
+
+ // Test case 3, CeThreshold set to 2ms with higher queue delay. This test is mainly to check that the packets are not getting marked twice.
+ queueDisc = CreateObjectWithAttributes ("MaxSize", StringValue ("10240p"), "UseEcn", BooleanValue (true),
+ "CeThreshold", TimeValue (MilliSeconds (2)), "BlueThreshold", TimeValue (Time::Max()));
+ queueDisc->SetQuantum (1514);
+ queueDisc->Initialize ();
+
+ // Add 20 ECT0 (ECN capable) packets from first flow
+ hdr.SetDestination (Ipv4Address ("10.10.1.2"));
+ hdr.SetEcn (Ipv4Header::ECN_ECT0);
+ Simulator::Schedule (Time (Seconds (0)), &FqCobaltQueueDiscEcnMarking::AddPacket, this, queueDisc, hdr, 20, 20, 1);
+
+ // Add 20 ECT0 (ECN capable) packets from second flow
+ hdr.SetDestination (Ipv4Address ("10.10.1.10"));
+ Simulator::Schedule (Time (Seconds (0)), &FqCobaltQueueDiscEcnMarking::AddPacket, this, queueDisc, hdr, 20, 40, 2);
+
+ // Add 20 ECT0 (ECN capable) packets from third flow
+ hdr.SetDestination (Ipv4Address ("10.10.1.20"));
+ Simulator::Schedule (Time (Seconds (0)), &FqCobaltQueueDiscEcnMarking::AddPacket, this, queueDisc, hdr, 20, 60, 3);
+
+ // Add 20 NotECT packets from fourth flow
+ hdr.SetDestination (Ipv4Address ("10.10.1.30"));
+ hdr.SetEcn (Ipv4Header::ECN_NotECT);
+ Simulator::Schedule (Time (Seconds (0)), &FqCobaltQueueDiscEcnMarking::AddPacket, this, queueDisc, hdr, 20, 80, 4);
+
+ // Add 20 NotECT packets from fifth flow
+ hdr.SetDestination (Ipv4Address ("10.10.1.40"));
+ Simulator::Schedule (Time (Seconds (0)), &FqCobaltQueueDiscEcnMarking::AddPacket, this, queueDisc, hdr, 20, 100, 5);
+
+ // Reset m_dropNextCount value;
+ m_dropNextCount = 0;
+
+ //Dequeue 60 packets with delay 110ms to induce packet drops and keep some remaining packets in each queue
+ DequeueWithDelay (queueDisc, 0.110, 60);
+ Simulator::Run ();
+ Simulator::Stop (Seconds (8.0));
+ q0 = queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetObject ();
+ q1 = queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetObject ();
+ q2 = queueDisc->GetQueueDiscClass (2)->GetQueueDisc ()->GetObject ();
+ q3 = queueDisc->GetQueueDiscClass (3)->GetQueueDisc ()->GetObject ();
+ q4 = queueDisc->GetQueueDiscClass (4)->GetQueueDisc ()->GetObject ();
+
+ // As packets in flow queues are ECN capable
+ NS_TEST_EXPECT_MSG_EQ (q0->GetStats ().GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP), 0, "There should not be any dropped packets");
+ NS_TEST_EXPECT_MSG_EQ (q0->GetStats ().GetNMarkedPackets (CobaltQueueDisc::CE_THRESHOLD_EXCEEDED_MARK) +
+ q0->GetStats ().GetNMarkedPackets (CobaltQueueDisc::FORCED_MARK), 20 - q0->GetNPackets (), "Number of CE threshold"
+ " exceeded marks plus Number of Target exceeded marks should be equal to total number of packets dequeued");
+ NS_TEST_EXPECT_MSG_EQ (q1->GetStats ().GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP), 0, "There should not be any dropped packets");
+ NS_TEST_EXPECT_MSG_EQ (q1->GetStats ().GetNMarkedPackets (CobaltQueueDisc::CE_THRESHOLD_EXCEEDED_MARK) +
+ q1->GetStats ().GetNMarkedPackets (CobaltQueueDisc::FORCED_MARK), 20 - q1->GetNPackets (), "Number of CE threshold"
+ " exceeded marks plus Number of Target exceeded marks should be equal to total number of packets dequeued");
+ NS_TEST_EXPECT_MSG_EQ (q2->GetStats ().GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP), 0, "There should not be any dropped packets");
+ NS_TEST_EXPECT_MSG_EQ (q2->GetStats ().GetNMarkedPackets (CobaltQueueDisc::CE_THRESHOLD_EXCEEDED_MARK) +
+ q2->GetStats ().GetNMarkedPackets (CobaltQueueDisc::FORCED_MARK), 20 - q2->GetNPackets (), "Number of CE threshold"
+ " exceeded marks plus Number of Target exceeded marks should be equal to total number of packets dequeued");
+
+ // As packets in flow queues are not ECN capable
+ NS_TEST_EXPECT_MSG_EQ (q3->GetStats ().GetNMarkedPackets (CobaltQueueDisc::CE_THRESHOLD_EXCEEDED_MARK), 0, "There should not be any marked packets");
+ NS_TEST_EXPECT_MSG_EQ (q3->GetStats ().GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP), m_dropNextCount, "The number of drops should"
+ "be equal to the number of times m_dropNext is updated");
+ NS_TEST_EXPECT_MSG_EQ (q4->GetStats ().GetNMarkedPackets (CobaltQueueDisc::CE_THRESHOLD_EXCEEDED_MARK), 0, "There should not be any marked packets");
+ NS_TEST_EXPECT_MSG_EQ (q4->GetStats ().GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP), m_dropNextCount, "The number of drops should"
+ "be equal to the number of times m_dropNext is updated");
+
+ Simulator::Destroy ();
+}
+
+/*
+ * This class tests linear probing, collision response, and set
+ * creation capability of set associative hashing in FqCodel.
+ * 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 FqCobaltQueueDiscSetLinearProbing : public TestCase
+{
+public:
+ FqCobaltQueueDiscSetLinearProbing ();
+ virtual ~FqCobaltQueueDiscSetLinearProbing ();
+private:
+ virtual void DoRun (void);
+ void AddPacket (Ptr queue, Ipv4Header hdr);
+};
+
+FqCobaltQueueDiscSetLinearProbing::FqCobaltQueueDiscSetLinearProbing ()
+ : TestCase ("Test credits and flows status")
+{
+}
+
+FqCobaltQueueDiscSetLinearProbing::~FqCobaltQueueDiscSetLinearProbing ()
+{
+}
+
+void
+FqCobaltQueueDiscSetLinearProbing::AddPacket (Ptr queue, Ipv4Header hdr)
+{
+ Ptr p = Create (100);
+ Address dest;
+ Ptr item = Create (p, dest, 0, hdr);
+ queue->Enqueue (item);
+}
+
+void
+FqCobaltQueueDiscSetLinearProbing::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);
+
+ m_hash = 0;
+ AddPacket (queueDisc, hdr);
+ m_hash = 1;
+ AddPacket (queueDisc, hdr);
+ AddPacket (queueDisc, hdr);
+ m_hash = 2;
+ AddPacket (queueDisc, hdr);
+ m_hash = 3;
+ AddPacket (queueDisc, hdr);
+ m_hash = 4;
+ AddPacket (queueDisc, hdr);
+ AddPacket (queueDisc, hdr);
+ m_hash = 5;
+ AddPacket (queueDisc, hdr);
+ m_hash = 6;
+ AddPacket (queueDisc, hdr);
+ m_hash = 7;
+ AddPacket (queueDisc, hdr);
+ m_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");
+ m_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");
+ m_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 FqCobaltQueueDiscL4sMode : public TestCase
+{
+public:
+ FqCobaltQueueDiscL4sMode ();
+ virtual ~FqCobaltQueueDiscL4sMode ();
+
+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);
+};
+
+FqCobaltQueueDiscL4sMode::FqCobaltQueueDiscL4sMode ()
+ : TestCase ("Test L4S mode")
+{
+}
+
+FqCobaltQueueDiscL4sMode::~FqCobaltQueueDiscL4sMode ()
+{
+}
+
+void
+FqCobaltQueueDiscL4sMode::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
+FqCobaltQueueDiscL4sMode::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)), &FqCobaltQueueDiscL4sMode::AddPacket, this, queue, hdr, 1);
+ }
+}
+
+void
+FqCobaltQueueDiscL4sMode::Dequeue (Ptr queue, uint32_t nPkt)
+{
+ for (uint32_t i = 0; i < nPkt; i++)
+ {
+ Ptr item = queue->Dequeue ();
+ }
+}
+
+void
+FqCobaltQueueDiscL4sMode::DequeueWithDelay (Ptr queue, double delay, uint32_t nPkt)
+{
+ for (uint32_t i = 0; i < nPkt; i++)
+ {
+ Simulator::Schedule (Time (Seconds ((i + 1) * delay)), &FqCobaltQueueDiscL4sMode::Dequeue, this, queue, 1);
+ }
+}
+
+void
+FqCobaltQueueDiscL4sMode::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)), &FqCobaltQueueDiscL4sMode::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)), &FqCobaltQueueDiscL4sMode::AddPacketWithDelay, this, queueDisc, hdr, delay, 70);
+
+ //Dequeue 140 packets with delay 1ms
+ delay = 0.001;
+ DequeueWithDelay (queueDisc, delay, 140);
+ Simulator::Run ();
+ Simulator::Stop (Seconds (8.0));
+ Ptr q0 = queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetObject ();
+ Ptr q1 = queueDisc->GetQueueDiscClass (1)->GetQueueDisc ()->GetObject ();
+
+ NS_TEST_EXPECT_MSG_EQ (q0->GetStats ().GetNMarkedPackets (CobaltQueueDisc::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 (CobaltQueueDisc::TARGET_EXCEEDED_DROP), 0, "There should not be any dropped packets");
+ NS_TEST_EXPECT_MSG_EQ (q0->GetStats ().GetNMarkedPackets (CobaltQueueDisc::FORCED_MARK), 0, "There should not be any marked packets");
+ NS_TEST_EXPECT_MSG_EQ (q1->GetStats ().GetNMarkedPackets (CobaltQueueDisc::FORCED_MARK), 2, "There should be 2 marked packets. Packets are dequeued"
+ "from q0 first, which leads to delay greater than 5ms for the first dequeue from q1. Because of inactivity (started with high queue delay)"
+ "Cobalt keeps drop_next as now and the next packet is marked. With second dequeue count increases to 2, drop_next becomes now plus around"
+ "70ms which is less than the running time(140), and as the queue delay is persistantly higher than 5ms, second packet is marked.");
+ NS_TEST_EXPECT_MSG_EQ (q1->GetStats ().GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_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)), &FqCobaltQueueDiscL4sMode::AddPacket, this, queueDisc, hdr, 1);
+ Simulator::Schedule (Time (Seconds (0.0005)), &FqCobaltQueueDiscL4sMode::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)), &FqCobaltQueueDiscL4sMode::AddPacketWithDelay, this, queueDisc, hdr, delay, 70);
+
+ //Dequeue 140 packets with delay 1ms
+ DequeueWithDelay (queueDisc, delay, 140);
+ Simulator::Run ();
+ Simulator::Stop (Seconds (8.0));
+ q0 = queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetObject ();
+ q0 = queueDisc->GetQueueDiscClass (0)->GetQueueDisc ()->GetObject ();
+
+ NS_TEST_EXPECT_MSG_EQ (q0->GetStats ().GetNMarkedPackets (CobaltQueueDisc::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 (CobaltQueueDisc::TARGET_EXCEEDED_DROP), 0, "There should not be any dropped packets");
+ NS_TEST_EXPECT_MSG_EQ (q0->GetStats ().GetNMarkedPackets (CobaltQueueDisc::FORCED_MARK), 1, "There should be 1 marked packets");
+
+ Simulator::Destroy ();
+
+}
+
+class FqCobaltQueueDiscTestSuite : public TestSuite
+{
+public:
+ FqCobaltQueueDiscTestSuite ();
+};
+
+FqCobaltQueueDiscTestSuite::FqCobaltQueueDiscTestSuite ()
+ : TestSuite ("fq-cobalt-queue-disc", UNIT)
+{
+ AddTestCase (new FqCobaltQueueDiscNoSuitableFilter, TestCase::QUICK);
+ AddTestCase (new FqCobaltQueueDiscIPFlowsSeparationAndPacketLimit, TestCase::QUICK);
+ AddTestCase (new FqCobaltQueueDiscDeficit, TestCase::QUICK);
+ AddTestCase (new FqCobaltQueueDiscTCPFlowsSeparation, TestCase::QUICK);
+ AddTestCase (new FqCobaltQueueDiscUDPFlowsSeparation, TestCase::QUICK);
+ AddTestCase (new FqCobaltQueueDiscEcnMarking, TestCase::QUICK);
+ AddTestCase (new FqCobaltQueueDiscSetLinearProbing, TestCase::QUICK);
+ AddTestCase (new FqCobaltQueueDiscL4sMode, TestCase::QUICK);
+}
+
+static FqCobaltQueueDiscTestSuite fqCobaltQueueDiscTestSuite;
diff --git a/src/test/wscript b/src/test/wscript
index cf970b0b5..1bbbb1a74 100644
--- a/src/test/wscript
+++ b/src/test/wscript
@@ -31,6 +31,7 @@ def build(bld):
test_test.source = [
'csma-system-test-suite.cc',
'ns3tc/fq-codel-queue-disc-test-suite.cc',
+ 'ns3tc/fq-cobalt-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/cobalt.rst b/src/traffic-control/doc/cobalt.rst
index d6d4dfefc..ef2c32047 100644
--- a/src/traffic-control/doc/cobalt.rst
+++ b/src/traffic-control/doc/cobalt.rst
@@ -37,10 +37,14 @@ Linux model of COBALT is provided in ([Cobalt19]_).
`m_stats.qLimDrop`.
* ``CobaltQueueDisc::ShouldDrop ()``: This routine is
``CobaltQueueDisc::DoDequeue()``'s helper routine that determines whether a
- packet should be dropped or not based on its sojourn time. If the sojourn
- time goes above `m_target` and remains above continuously for at least
- `m_interval`, the routine returns ``true`` indicating that it is OK
- to drop the packet. ``Otherwise, it returns ``false``. This routine
+ packet should be dropped or not based on its sojourn time. If L4S mode is
+ enabled then if the packet is ECT1 is checked and if delay is greater than
+ CE threshold then the packet is marked and returns ``false``.
+ If the sojourn time goes above `m_target` and remains above continuously
+ for at least `m_interval`, the routine returns ``true`` indicating that it
+ is OK to drop the packet. ``Otherwise, it returns ``false``. If L4S mode
+ is turned off and CE threshold marking is enabled, then if the delay is
+ greater than CE threshold, packet is marked. This routine
decides if a packet should be dropped based on the dropping state of
CoDel and drop probability of BLUE. The idea is to have both algorithms
running in parallel and their effectiveness is decided by their respective
@@ -53,10 +57,6 @@ Linux model of COBALT is provided in ([Cobalt19]_).
Otherwise Cobalt will take the next packet from the queue and calculate
its drop state by running CoDel and BLUE in parallel till there are none
left to drop.
-* class :cpp:class:`CobaltTimestampTag`: This class implements the timestamp
- tagging for a packet. This tag is used to compute the packet's sojourn time
- (the difference between the time the packet is dequeued and the time it is
- pushed into the queue).
@@ -94,6 +94,8 @@ The key attributes that the CobaltQueue Disc class holds include the following:
* ``Pdrop:`` Value of drop probability.
* ``Increment:`` Increment value of drop probability. Default value is 1./256 .
* ``Decrement:`` Decrement value of drop probability. Default value is 1./4096 .
+* ``CeThreshold:`` The CoDel CE threshold for marking packets.
+* ``UseL4s:`` True to use L4S (only ECT1 packets are marked at CE threshold).
* ``Count:`` Cobalt count.
* ``DropState:`` Dropping state of Cobalt. Default value is false.
* ``Sojourn:`` Per packet time spent in the queue.
@@ -120,7 +122,8 @@ The suite includes 2 test cases:
* Test 1: Simple enqueue/dequeue with no drops.
* Test 2: Change of BLUE's drop probability upon queue full
(Activation of Blue).
-* Test 3: Test for ECN marking of packets
+* Test 3: This test verfies ECN marking.
+* Test 4: CE threshold marking test.
The test suite can be run using the following commands:
diff --git a/src/traffic-control/doc/fq-cobalt.rst b/src/traffic-control/doc/fq-cobalt.rst
new file mode 100644
index 000000000..ef6511b51
--- /dev/null
+++ b/src/traffic-control/doc/fq-cobalt.rst
@@ -0,0 +1,76 @@
+.. include:: replace.txt
+.. highlight:: cpp
+.. highlight:: bash
+
+FqCobalt queue disc
+------------------
+
+This chapter describes the FqCobalt ([Hoe16]_) queue disc implementation in |ns3|.
+
+The FlowQueue-Cobalt (FQ-Cobalt) algorithm is similar to FlowQueue-CoDel (FQ-CoDel) algorithm available in ns-3-dev/src/traffic-control/doc/fq-codel.rst.
+The documentation for Cobalt is available in ns-3-dev/src/traffic-control/doc/cobalt.rst.
+
+FqCobalt is one of the crucial piece needed to complete to CAKE module ``
+
+Model Description
+*****************
+
+The source code for the FqCobalt queue disc is located in the directory
+``src/traffic-control/model`` and consists of 2 files `fq-cobalt-queue-disc.h`
+and `fq-cobalt-queue-disc.cc` defining a FqCobaltQueueDisc class and a helper
+FqCobaltFlow class. The code was ported to |ns3| based on Linux kernel code
+implemented by Jonathan Morton.
+
+The Model Description is similar to the FqCoDel documentation mentioned above.
+
+
+References
+==========
+
+.. [CAK16] https://github.com/torvalds/linux/blob/master/net/sched/sch_cake.c , Implementation of CAKE in Linux
+
+Attributes
+==========
+
+The key attributes that the FqCobaltQueue class holds include the following:
+
+Most of the key attributes are similar to the FqCoDel implementation mentioned above.
+Some differences are:
+Absence of ``MinBytes`` parameter.
+Some extra parameters are
+* ``Pdrop:`` Value of drop probability.
+* ``Increment:`` Increment value of drop probability. Default value is 1./256 .
+* ``Decrement:`` Decrement value of drop probability. Default value is 1./4096 .
+* ``BlueThreshold:`` The Threshold after which Blue is enabled. Default value is 400ms.
+
+Note that if the user wants to disable Blue Enhancement then the user can set
+it to a large value for example Time::Max().
+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, FqCobalt
+can be configured as follows:
+
+.. sourcecode:: cpp
+
+ TrafficControlHelper tch;
+ tch.SetRootQueueDisc ("ns3::FqCobaltQueueDisc", "DropBatchSize", UintegerValue (1)
+ "Perturbation", UintegerValue (256));
+ QueueDiscContainer qdiscs = tch.Install (devices);
+
+Validation
+**********
+
+The FqCobalt model is tested using :cpp:class:`FqCobaltQueueDiscTestSuite` class defined in `src/test/ns3tc/codel-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-cobalt-queue-disc
+
+or::
+
+ $ NS_LOG="FqCobaltQueueDisc" ./waf --run "test-runner --suite=fq-cobalt-queue-disc"
\ No newline at end of file
diff --git a/src/traffic-control/model/cobalt-queue-disc.cc b/src/traffic-control/model/cobalt-queue-disc.cc
index 5c3813a57..06a6f14f0 100644
--- a/src/traffic-control/model/cobalt-queue-disc.cc
+++ b/src/traffic-control/model/cobalt-queue-disc.cc
@@ -84,6 +84,21 @@ TypeId CobaltQueueDisc::GetTypeId (void)
DoubleValue (1. / 4096),
MakeDoubleAccessor (&CobaltQueueDisc::m_decrement),
MakeDoubleChecker ())
+ .AddAttribute ("CeThreshold",
+ "The CoDel CE threshold for marking packets",
+ TimeValue (Time::Max ()),
+ MakeTimeAccessor (&CobaltQueueDisc::m_ceThreshold),
+ MakeTimeChecker ())
+ .AddAttribute ("UseL4s",
+ "True to use L4S (only ECT1 packets are marked at CE threshold)",
+ BooleanValue (false),
+ MakeBooleanAccessor (&CobaltQueueDisc::m_useL4s),
+ MakeBooleanChecker ())
+ .AddAttribute ("BlueThreshold",
+ "The Threshold after which Blue is enabled",
+ TimeValue (MilliSeconds (400)),
+ MakeTimeAccessor (&CobaltQueueDisc::m_blueThreshold),
+ MakeTimeChecker ())
.AddTraceSource ("Count",
"Cobalt count",
MakeTraceSourceAccessor (&CobaltQueueDisc::m_count),
@@ -436,6 +451,31 @@ bool CobaltQueueDisc::CobaltShouldDrop (Ptr item, int64_t now)
int64_t schedule = now - m_dropNext;
bool over_target = CoDelTimeAfter (sojournTime, Time2CoDel (m_target));
bool next_due = m_count && schedule >= 0;
+ bool isMarked = false;
+
+ // If L4S mode is enabled then check if the packet is ECT1 or CE and
+ // if sojourn time is greater than CE threshold then the packet is marked.
+ // If packet is marked succesfully then the CoDel steps can be skipped.
+ if (item && m_useL4s)
+ {
+ uint8_t tosByte = 0;
+ if (item->GetUint8Value (QueueItem::IP_DSFIELD, tosByte) && (((tosByte & 0x3) == 1) || (tosByte & 0x3) == 3))
+ {
+ if ((tosByte & 0x3) == 1)
+ {
+ NS_LOG_DEBUG ("ECT1 packet " << static_cast (tosByte & 0x3));
+ }
+ else
+ {
+ NS_LOG_DEBUG ("CE packet " << static_cast (tosByte & 0x3));
+ }
+ if (CoDelTimeAfter (sojournTime, Time2CoDel (m_ceThreshold)) && Mark (item, CE_THRESHOLD_EXCEEDED_MARK))
+ {
+ NS_LOG_LOGIC ("Marking due to CeThreshold " << m_ceThreshold.GetSeconds ());
+ }
+ return false;
+ }
+ }
if (over_target)
{
@@ -458,7 +498,8 @@ bool CobaltQueueDisc::CobaltShouldDrop (Ptr item, int64_t now)
{
/* Check for marking possibility only if BLUE decides NOT to drop. */
/* Check if router and packet, both have ECN enabled. Only if this is true, mark the packet. */
- drop = !(m_useEcn && Mark (item, FORCED_MARK));
+ isMarked = (m_useEcn && Mark (item, FORCED_MARK));
+ drop = !isMarked;
m_count = max (m_count, m_count + 1);
@@ -477,6 +518,22 @@ bool CobaltQueueDisc::CobaltShouldDrop (Ptr item, int64_t now)
next_due = m_count && schedule >= 0;
}
}
+
+ // If CE threshold is enabled then isMarked flag is used to determine whether
+ // packet is marked and if the packet is marked then a second attempt at marking should be suppressed.
+ // If UseL4S attribute is enabled then ECT0 packets should not be marked.
+ if (!isMarked && !m_useL4s && m_useEcn && CoDelTimeAfter (sojournTime, Time2CoDel (m_ceThreshold)) && Mark (item, CE_THRESHOLD_EXCEEDED_MARK))
+ {
+ NS_LOG_LOGIC ("Marking due to CeThreshold " << m_ceThreshold.GetSeconds ());
+ }
+
+ // Enable Blue Enhancement if sojourn time is greater than blueThreshold and its been m_target time until the last time blue was updated
+ if (CoDelTimeAfter (sojournTime, Time2CoDel (m_blueThreshold)) && CoDelTimeAfter ((now - m_lastUpdateTimeBlue), Time2CoDel (m_target)))
+ {
+ m_Pdrop = min (m_Pdrop + m_increment, (double)1.0);
+ m_lastUpdateTimeBlue = now;
+ }
+
/* Simple BLUE implementation. Lack of ECN is deliberate. */
if (m_Pdrop)
{
diff --git a/src/traffic-control/model/cobalt-queue-disc.h b/src/traffic-control/model/cobalt-queue-disc.h
index 6afe6782c..eac450e37 100644
--- a/src/traffic-control/model/cobalt-queue-disc.h
+++ b/src/traffic-control/model/cobalt-queue-disc.h
@@ -103,6 +103,7 @@ public:
static constexpr const char* TARGET_EXCEEDED_DROP = "Target exceeded drop"; //!< Sojourn time above target
static constexpr const char* OVERLIMIT_DROP = "Overlimit drop"; //!< Overlimit dropped packet
static constexpr const char* FORCED_MARK = "forcedMark"; //!< forced marks by Codel on ECN-enabled
+ static constexpr const char* CE_THRESHOLD_EXCEEDED_MARK = "CE threshold exceeded mark"; //!< Sojourn time above CE threshold
/**
* \brief Get the drop probability of Blue
@@ -227,6 +228,9 @@ private:
Time m_interval; //!< 100 ms sliding minimum time window width
Time m_target; //!< 5 ms target queue delay
bool m_useEcn; //!< True if ECN is used (packets are marked instead of being dropped)
+ Time m_ceThreshold; //!< Threshold above which to CE mark
+ bool m_useL4s; //!< True if L4S is used (ECT1 packets are marked at CE threshold)
+ Time m_blueThreshold; //!< Threshold to enable blue enhancement
// Blue parameters
// Maintained by Cobalt
diff --git a/src/traffic-control/model/fq-cobalt-queue-disc.cc b/src/traffic-control/model/fq-cobalt-queue-disc.cc
new file mode 100644
index 000000000..a6db5d47e
--- /dev/null
+++ b/src/traffic-control/model/fq-cobalt-queue-disc.cc
@@ -0,0 +1,523 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2016 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: Pasquale Imputato
+ * Stefano Avallone
+*/
+
+#include "ns3/log.h"
+#include "ns3/string.h"
+#include "ns3/queue.h"
+#include "fq-cobalt-queue-disc.h"
+#include "cobalt-queue-disc.h"
+#include "ns3/net-device-queue-interface.h"
+
+namespace ns3 {
+
+NS_LOG_COMPONENT_DEFINE ("FqCobaltQueueDisc");
+
+NS_OBJECT_ENSURE_REGISTERED (FqCobaltFlow);
+
+TypeId FqCobaltFlow::GetTypeId (void)
+{
+ static TypeId tid = TypeId ("ns3::FqCobaltFlow")
+ .SetParent ()
+ .SetGroupName ("TrafficControl")
+ .AddConstructor ()
+ ;
+ return tid;
+}
+
+FqCobaltFlow::FqCobaltFlow ()
+ : m_deficit (0),
+ m_status (INACTIVE),
+ m_index (0)
+{
+ NS_LOG_FUNCTION (this);
+}
+
+FqCobaltFlow::~FqCobaltFlow ()
+{
+ NS_LOG_FUNCTION (this);
+}
+
+void
+FqCobaltFlow::SetDeficit (uint32_t deficit)
+{
+ NS_LOG_FUNCTION (this << deficit);
+ m_deficit = deficit;
+}
+
+int32_t
+FqCobaltFlow::GetDeficit (void) const
+{
+ NS_LOG_FUNCTION (this);
+ return m_deficit;
+}
+
+void
+FqCobaltFlow::IncreaseDeficit (int32_t deficit)
+{
+ NS_LOG_FUNCTION (this << deficit);
+ m_deficit += deficit;
+}
+
+void
+FqCobaltFlow::SetStatus (FlowStatus status)
+{
+ NS_LOG_FUNCTION (this);
+ m_status = status;
+}
+
+FqCobaltFlow::FlowStatus
+FqCobaltFlow::GetStatus (void) const
+{
+ NS_LOG_FUNCTION (this);
+ return m_status;
+}
+
+void
+FqCobaltFlow::SetIndex (uint32_t index)
+{
+ NS_LOG_FUNCTION (this);
+ m_index = index;
+}
+
+uint32_t
+FqCobaltFlow::GetIndex (void) const
+{
+ return m_index;
+}
+
+
+NS_OBJECT_ENSURE_REGISTERED (FqCobaltQueueDisc);
+
+TypeId FqCobaltQueueDisc::GetTypeId (void)
+{
+ static TypeId tid = TypeId ("ns3::FqCobaltQueueDisc")
+ .SetParent ()
+ .SetGroupName ("TrafficControl")
+ .AddConstructor ()
+ .AddAttribute ("UseEcn",
+ "True to use ECN (packets are marked instead of being dropped)",
+ BooleanValue (true),
+ MakeBooleanAccessor (&FqCobaltQueueDisc::m_useEcn),
+ MakeBooleanChecker ())
+ .AddAttribute ("Interval",
+ "The CoDel algorithm interval for each FqCobalt queue",
+ StringValue ("100ms"),
+ MakeStringAccessor (&FqCobaltQueueDisc::m_interval),
+ MakeStringChecker ())
+ .AddAttribute ("Target",
+ "The CoDel algorithm target queue delay for each FqCobalt queue",
+ StringValue ("5ms"),
+ MakeStringAccessor (&FqCobaltQueueDisc::m_target),
+ MakeStringChecker ())
+ .AddAttribute ("MaxSize",
+ "The maximum number of packets accepted by this queue disc",
+ QueueSizeValue (QueueSize ("10240p")),
+ MakeQueueSizeAccessor (&QueueDisc::SetMaxSize,
+ &QueueDisc::GetMaxSize),
+ MakeQueueSizeChecker ())
+ .AddAttribute ("Flows",
+ "The number of queues into which the incoming packets are classified",
+ UintegerValue (1024),
+ MakeUintegerAccessor (&FqCobaltQueueDisc::m_flows),
+ MakeUintegerChecker ())
+ .AddAttribute ("DropBatchSize",
+ "The maximum number of packets dropped from the fat flow",
+ UintegerValue (64),
+ MakeUintegerAccessor (&FqCobaltQueueDisc::m_dropBatchSize),
+ MakeUintegerChecker ())
+ .AddAttribute ("Perturbation",
+ "The salt used as an additional input to the hash function used to classify packets",
+ UintegerValue (0),
+ MakeUintegerAccessor (&FqCobaltQueueDisc::m_perturbation),
+ MakeUintegerChecker ())
+ .AddAttribute ("CeThreshold",
+ "The FqCobalt CE threshold for marking packets",
+ TimeValue (Time::Max ()),
+ MakeTimeAccessor (&FqCobaltQueueDisc::m_ceThreshold),
+ MakeTimeChecker ())
+ .AddAttribute ("EnableSetAssociativeHash",
+ "Enable/Disable Set Associative Hash",
+ BooleanValue (false),
+ MakeBooleanAccessor (&FqCobaltQueueDisc::m_enableSetAssociativeHash),
+ MakeBooleanChecker ())
+ .AddAttribute ("SetWays",
+ "The size of a set of queues (used by set associative hash)",
+ UintegerValue (8),
+ MakeUintegerAccessor (&FqCobaltQueueDisc::m_setWays),
+ MakeUintegerChecker ())
+ .AddAttribute ("UseL4s",
+ "True to use L4S (only ECT1 packets are marked at CE threshold)",
+ BooleanValue (false),
+ MakeBooleanAccessor (&FqCobaltQueueDisc::m_useL4s),
+ MakeBooleanChecker ())
+ .AddAttribute ("Pdrop",
+ "Marking Probability",
+ DoubleValue (0),
+ MakeDoubleAccessor (&FqCobaltQueueDisc::m_Pdrop),
+ MakeDoubleChecker ())
+ .AddAttribute ("Increment",
+ "Pdrop increment value",
+ DoubleValue (1. / 256),
+ MakeDoubleAccessor (&FqCobaltQueueDisc::m_increment),
+ MakeDoubleChecker ())
+ .AddAttribute ("Decrement",
+ "Pdrop decrement Value",
+ DoubleValue (1. / 4096),
+ MakeDoubleAccessor (&FqCobaltQueueDisc::m_decrement),
+ MakeDoubleChecker ())
+ .AddAttribute ("BlueThreshold",
+ "The Threshold after which Blue is enabled",
+ TimeValue (MilliSeconds (400)),
+ MakeTimeAccessor (&FqCobaltQueueDisc::m_blueThreshold),
+ MakeTimeChecker ())
+ ;
+ return tid;
+}
+
+FqCobaltQueueDisc::FqCobaltQueueDisc ()
+ : QueueDisc (QueueDiscSizePolicy::MULTIPLE_QUEUES, QueueSizeUnit::PACKETS),
+ m_quantum (0)
+{
+ NS_LOG_FUNCTION (this);
+}
+
+FqCobaltQueueDisc::~FqCobaltQueueDisc ()
+{
+ NS_LOG_FUNCTION (this);
+}
+
+void
+FqCobaltQueueDisc::SetQuantum (uint32_t quantum)
+{
+ NS_LOG_FUNCTION (this << quantum);
+ m_quantum = quantum;
+}
+
+uint32_t
+FqCobaltQueueDisc::GetQuantum (void) const
+{
+ return m_quantum;
+}
+
+uint32_t
+FqCobaltQueueDisc::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 () == FqCobaltFlow::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
+FqCobaltQueueDisc::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 Cobalt, Set values of CobaltQueueDisc to match this QueueDisc
+ Ptr cobalt = qd->GetObject ();
+ if (cobalt)
+ {
+ cobalt->SetAttribute ("UseEcn", BooleanValue (m_useEcn));
+ cobalt->SetAttribute ("CeThreshold", TimeValue (m_ceThreshold));
+ cobalt->SetAttribute ("UseL4s", BooleanValue (m_useL4s));
+ cobalt->SetAttribute ("BlueThreshold", TimeValue (m_blueThreshold));
+ }
+ 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 () == FqCobaltFlow::INACTIVE)
+ {
+ flow->SetStatus (FqCobaltFlow::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 FqCobaltDrop ()");
+ FqCobaltDrop ();
+ }
+
+ return true;
+}
+
+Ptr
+FqCobaltQueueDisc::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 (FqCobaltFlow::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 (FqCobaltFlow::OLD_FLOW);
+ m_oldFlows.push_back (flow);
+ m_newFlows.pop_front ();
+ }
+ else
+ {
+ flow->SetStatus (FqCobaltFlow::INACTIVE);
+ m_oldFlows.pop_front ();
+ }
+ }
+ else
+ {
+ NS_LOG_DEBUG ("Dequeued packet " << item->GetPacket ());
+ }
+ } while (item == 0);
+
+ flow->IncreaseDeficit (item->GetSize () * -1);
+
+ return item;
+}
+
+bool
+FqCobaltQueueDisc::CheckConfig (void)
+{
+ NS_LOG_FUNCTION (this);
+ if (GetNQueueDiscClasses () > 0)
+ {
+ NS_LOG_ERROR ("FqCobaltQueueDisc cannot have classes");
+ return false;
+ }
+
+ if (GetNInternalQueues () > 0)
+ {
+ NS_LOG_ERROR ("FqCobaltQueueDisc 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
+FqCobaltQueueDisc::InitializeParams (void)
+{
+ NS_LOG_FUNCTION (this);
+
+ m_flowFactory.SetTypeId ("ns3::FqCobaltFlow");
+
+ m_queueDiscFactory.SetTypeId ("ns3::CobaltQueueDisc");
+ m_queueDiscFactory.Set ("MaxSize", QueueSizeValue (GetMaxSize ()));
+ m_queueDiscFactory.Set ("Interval", StringValue (m_interval));
+ m_queueDiscFactory.Set ("Target", StringValue (m_target));
+ m_queueDiscFactory.Set ("Pdrop", DoubleValue (m_Pdrop));
+ m_queueDiscFactory.Set ("Increment", DoubleValue (m_increment));
+ m_queueDiscFactory.Set ("Decrement", DoubleValue (m_decrement));
+}
+
+uint32_t
+FqCobaltQueueDisc::FqCobaltDrop (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-cobalt-queue-disc.h b/src/traffic-control/model/fq-cobalt-queue-disc.h
new file mode 100644
index 000000000..417858156
--- /dev/null
+++ b/src/traffic-control/model/fq-cobalt-queue-disc.h
@@ -0,0 +1,197 @@
+/* -*- 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
+ */
+
+#ifndef FQ_COBALT_QUEUE_DISC
+#define FQ_COBALT_QUEUE_DISC
+
+#include "ns3/queue-disc.h"
+#include "ns3/object-factory.h"
+#include
+#include