From 66dabbca48a98ac51fadbc365933bbe80deb9e29 Mon Sep 17 00:00:00 2001 From: BHASKAR K Date: Fri, 29 May 2020 21:48:23 +0530 Subject: [PATCH] traffic-control: Add ECN tests for COBALT queue disc --- src/traffic-control/doc/cobalt.rst | 1 + .../test/cobalt-queue-disc-test-suite.cc | 317 ++++++++++++++++++ 2 files changed, 318 insertions(+) diff --git a/src/traffic-control/doc/cobalt.rst b/src/traffic-control/doc/cobalt.rst index 2299d63c1c..d6d4dfefc8 100644 --- a/src/traffic-control/doc/cobalt.rst +++ b/src/traffic-control/doc/cobalt.rst @@ -120,6 +120,7 @@ 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 The test suite can be run using the following commands: diff --git a/src/traffic-control/test/cobalt-queue-disc-test-suite.cc b/src/traffic-control/test/cobalt-queue-disc-test-suite.cc index be665cb9b3..540009694a 100644 --- a/src/traffic-control/test/cobalt-queue-disc-test-suite.cc +++ b/src/traffic-control/test/cobalt-queue-disc-test-suite.cc @@ -323,6 +323,320 @@ CobaltQueueDiscDropTest::DoRun (void) Simulator::Destroy (); } +/** + * \ingroup traffic-control-test + * \ingroup tests + * + * \brief Test 3: Cobalt Queue Disc ECN marking Test Item + */ +class CobaltQueueDiscMarkTest : public TestCase +{ +public: + /** + * Constructor + * + * \param mode the mode + */ + CobaltQueueDiscMarkTest (QueueSizeUnit mode); + virtual void DoRun (void); + +private: + /** + * \brief Enqueue function + * \param queue the queue disc + * \param size the size + * \param nPkt the number of packets + * \param ecnCapable ECN capable traffic + */ + void Enqueue (Ptr queue, uint32_t size, uint32_t nPkt, bool ecnCapable); + /** + * \brief Dequeue function + * \param queue the queue disc + * \param modeSize the mode size + * \param testCase the test case number + */ + void Dequeue (Ptr queue, uint32_t modeSize, uint32_t testCase); + /** + * \brief Drop next tracer function + * \param oldVal the old value of m_dropNext + * \param newVal the new value of m_dropNext + */ + void DropNextTracer (int64_t oldVal, int64_t newVal); + QueueSizeUnit m_mode; ///< mode + uint32_t m_dropNextCount; ///< count the number of times m_dropNext is recalculated + uint32_t nPacketsBeforeFirstDrop; ///< Number of packets in the queue before first drop + uint32_t nPacketsBeforeFirstMark; ///< Number of packets in the queue before first mark +}; + +CobaltQueueDiscMarkTest::CobaltQueueDiscMarkTest (QueueSizeUnit mode) + : TestCase ("Basic mark operations") +{ + m_mode = mode; + m_dropNextCount = 0; +} + +void +CobaltQueueDiscMarkTest::DropNextTracer (int64_t oldVal, int64_t newVal) +{ + NS_UNUSED (oldVal); + NS_UNUSED (newVal); + m_dropNextCount++; +} + +void +CobaltQueueDiscMarkTest::DoRun (void) +{ + // Test is divided into 3 sub test cases: + // 1) Packets are not ECN capable. + // 2) Packets are ECN capable. + // 3) Some packets are ECN capable. + + // Test case 1 + Ptr queue = CreateObject (); + uint32_t pktSize = 1000; + uint32_t modeSize = 0; + nPacketsBeforeFirstDrop = 0; + nPacketsBeforeFirstMark = 0; + + if (m_mode == QueueSizeUnit::BYTES) + { + modeSize = pktSize; + } + else if (m_mode == QueueSizeUnit::PACKETS) + { + modeSize = 1; + } + + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("MaxSize", QueueSizeValue (QueueSize (m_mode, modeSize * 500))), + true, "Verify that we can actually set the attribute MaxSize"); + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("UseEcn", BooleanValue (false)), + true, "Verify that we can actually set the attribute UseEcn"); + + queue->Initialize (); + + // Not-ECT traffic to induce packet drop + Enqueue (queue, pktSize, 20, false); + NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), 20 * modeSize, "There should be 20 packets in queue."); + + // Although the first dequeue occurs with a sojourn time above target + // there should not be any dropped packets in this interval + Time waitUntilFirstDequeue = 2 * queue->GetTarget (); + Simulator::Schedule (waitUntilFirstDequeue, &CobaltQueueDiscMarkTest::Dequeue, this, queue, modeSize, 1); + + // This dequeue should cause a packet to be dropped + Time waitUntilSecondDequeue = waitUntilFirstDequeue + 2 * queue->GetInterval (); + Simulator::Schedule (waitUntilSecondDequeue, &CobaltQueueDiscMarkTest::Dequeue, this, queue, modeSize, 1); + + Simulator::Run (); + Simulator::Destroy (); + + // Test case 2, queue with ECN capable traffic for marking of packets instead of dropping + queue = CreateObject (); + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("MaxSize", QueueSizeValue (QueueSize (m_mode, modeSize * 500))), + true, "Verify that we can actually set the attribute MaxSize"); + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("UseEcn", BooleanValue (true)), + true, "Verify that we can actually set the attribute UseEcn"); + + queue->Initialize (); + + // ECN capable traffic + Enqueue (queue, pktSize, 20, true); + NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), 20 * modeSize, "There should be 20 packets in queue."); + + // Although the first dequeue occurs with a sojourn time above target + // there should not be any marked packets in this interval + Simulator::Schedule (waitUntilFirstDequeue, &CobaltQueueDiscMarkTest::Dequeue, this, queue, modeSize, 2); + + // This dequeue should cause a packet to be marked + Simulator::Schedule (waitUntilSecondDequeue, &CobaltQueueDiscMarkTest::Dequeue, this, queue, modeSize, 2); + + // This dequeue should cause a packet to be marked as dropnext is equal to current time + Simulator::Schedule (waitUntilSecondDequeue, &CobaltQueueDiscMarkTest::Dequeue, this, queue, modeSize, 2); + + // In dropping phase and it's time for next packet to be marked + // the dequeue should cause additional packet to be marked + Simulator::Schedule (waitUntilSecondDequeue * 2, &CobaltQueueDiscMarkTest::Dequeue, this, queue, modeSize, 2); + + Simulator::Run (); + Simulator::Destroy (); + + // Test case 3, some packets are ECN capable + queue = CreateObject (); + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("MaxSize", QueueSizeValue (QueueSize (m_mode, modeSize * 500))), + true, "Verify that we can actually set the attribute MaxSize"); + NS_TEST_EXPECT_MSG_EQ (queue->SetAttributeFailSafe ("UseEcn", BooleanValue (true)), + true, "Verify that we can actually set the attribute UseEcn"); + + queue->Initialize (); + + // First 3 packets in the queue are ecnCapable + Enqueue (queue, pktSize, 3, true); + // Rest of the packet are not ecnCapable + Enqueue (queue, pktSize, 17, false); + NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), 20 * modeSize, "There should be 20 packets in queue."); + + // Although the first dequeue occurs with a sojourn time above target + // there should not be any marked packets in this interval + Simulator::Schedule (waitUntilFirstDequeue, &CobaltQueueDiscMarkTest::Dequeue, this, queue, modeSize, 3); + + // This dequeue should cause a packet to be marked + Simulator::Schedule (waitUntilSecondDequeue, &CobaltQueueDiscMarkTest::Dequeue, this, queue, modeSize, 3); + + // This dequeue should cause a packet to be marked as dropnext is equal to current time + Simulator::Schedule (waitUntilSecondDequeue, &CobaltQueueDiscMarkTest::Dequeue, this, queue, modeSize, 3); + + // In dropping phase and it's time for next packet to be dropped as packets are not ECN capable + // the dequeue should cause packet to be dropped + Simulator::Schedule (waitUntilSecondDequeue * 2, &CobaltQueueDiscMarkTest::Dequeue, this, queue, modeSize, 3); + + Simulator::Run (); + Simulator::Destroy (); +} + +void +CobaltQueueDiscMarkTest::Enqueue (Ptr queue, uint32_t size, uint32_t nPkt, bool ecnCapable) +{ + Address dest; + for (uint32_t i = 0; i < nPkt; i++) + { + queue->Enqueue (Create (Create (size), dest, 0, ecnCapable)); + } +} + +void +CobaltQueueDiscMarkTest::Dequeue (Ptr queue, uint32_t modeSize, uint32_t testCase) +{ + uint32_t initialMarkCount = queue->GetStats ().GetNMarkedPackets (CobaltQueueDisc::FORCED_MARK); + uint32_t initialQSize = queue->GetCurrentSize ().GetValue (); + uint32_t initialDropNext = queue->GetDropNext (); + Time currentTime = Simulator::Now (); + uint32_t currentDropCount = 0; + uint32_t currentMarkCount = 0; + + if (initialMarkCount > 0 && currentTime.GetNanoSeconds () > initialDropNext && testCase == 3) + { + queue->TraceConnectWithoutContext ("DropNext", MakeCallback (&CobaltQueueDiscMarkTest::DropNextTracer, this)); + } + + if (initialQSize != 0) + { + Ptr item = queue->Dequeue (); + if (testCase == 1) + { + currentDropCount = queue->GetStats ().GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP); + if (currentDropCount != 0) + { + nPacketsBeforeFirstDrop = initialQSize; + } + } + if (testCase == 2) + { + if (initialMarkCount == 0 && currentTime > queue->GetTarget ()) + { + if (currentTime < queue->GetInterval ()) + { + currentDropCount = queue->GetStats ().GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP); + currentMarkCount = queue->GetStats ().GetNMarkedPackets (CobaltQueueDisc::FORCED_MARK); + NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), initialQSize - modeSize, "There should be 1 packet dequeued."); + NS_TEST_EXPECT_MSG_EQ (currentDropCount, 0, "There should not be any packet drops"); + NS_TEST_ASSERT_MSG_EQ (currentMarkCount, 0, "We are not in dropping state." + "Sojourn time has just gone above target from below." + "Hence, there should be no marked packets"); + } + else if (currentTime >= queue->GetInterval ()) + { + nPacketsBeforeFirstMark = initialQSize; + currentDropCount = queue->GetStats ().GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP); + currentMarkCount = queue->GetStats ().GetNMarkedPackets (CobaltQueueDisc::FORCED_MARK); + NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), initialQSize - modeSize, + "Sojourn time has been above target for at least interval." + "We enter the dropping state and perform initial packet marking" + "So there should be only 1 more packet dequeued."); + NS_TEST_EXPECT_MSG_EQ (currentDropCount, 0, "There should not be any packet drops"); + NS_TEST_EXPECT_MSG_EQ (currentMarkCount, 1, "There should be 1 marked packet"); + } + } + else if (initialMarkCount > 0) + { + if (currentTime.GetNanoSeconds () <= initialDropNext) + { + currentDropCount = queue->GetStats ().GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP); + currentMarkCount = queue->GetStats ().GetNMarkedPackets (CobaltQueueDisc::FORCED_MARK); + NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), initialQSize - modeSize, "We are in dropping state." + "Sojourn is still above target." + "There should be only 1 more packet dequeued"); + NS_TEST_EXPECT_MSG_EQ (currentDropCount, 0, "There should not be any packet drops"); + NS_TEST_EXPECT_MSG_EQ (currentMarkCount, 2, "There should be 2 marked packet as." + "current dropnext is equal to current time."); + } + else if (currentTime.GetNanoSeconds () > initialDropNext) + { + currentDropCount = queue->GetStats ().GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP); + currentMarkCount = queue->GetStats ().GetNMarkedPackets (CobaltQueueDisc::FORCED_MARK); + NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), initialQSize - modeSize, "We are in dropping state." + "It's time for packet to be marked" + "So there should be only 1 more packet dequeued"); + NS_TEST_EXPECT_MSG_EQ (currentDropCount, 0, "There should not be any packet drops"); + NS_TEST_EXPECT_MSG_EQ (currentMarkCount, 3, "There should be 3 marked packet"); + NS_TEST_EXPECT_MSG_EQ (nPacketsBeforeFirstDrop, nPacketsBeforeFirstMark, "Number of packets in the queue before drop should be equal" + "to number of packets in the queue before first mark as the behavior until packet N should be the same."); + } + } + } + else if (testCase == 3) + { + if (initialMarkCount == 0 && currentTime > queue->GetTarget ()) + { + if (currentTime < queue->GetInterval ()) + { + currentDropCount = queue->GetStats ().GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP); + currentMarkCount = queue->GetStats ().GetNMarkedPackets (CobaltQueueDisc::FORCED_MARK); + NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), initialQSize - modeSize, "There should be 1 packet dequeued."); + NS_TEST_EXPECT_MSG_EQ (currentDropCount, 0, "There should not be any packet drops"); + NS_TEST_ASSERT_MSG_EQ (currentMarkCount, 0, "We are not in dropping state." + "Sojourn time has just gone above target from below." + "Hence, there should be no marked packets"); + } + else if (currentTime >= queue->GetInterval ()) + { + currentDropCount = queue->GetStats ().GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP); + currentMarkCount = queue->GetStats ().GetNMarkedPackets (CobaltQueueDisc::FORCED_MARK); + NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), initialQSize - modeSize, + "Sojourn time has been above target for at least interval." + "We enter the dropping state and perform initial packet marking" + "So there should be only 1 more packet dequeued."); + NS_TEST_EXPECT_MSG_EQ (currentDropCount, 0, "There should not be any packet drops"); + NS_TEST_EXPECT_MSG_EQ (currentMarkCount, 1, "There should be 1 marked packet"); + } + } + else if (initialMarkCount > 0) + { + if (currentTime.GetNanoSeconds () <= initialDropNext) + { + currentDropCount = queue->GetStats ().GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP); + currentMarkCount = queue->GetStats ().GetNMarkedPackets (CobaltQueueDisc::FORCED_MARK); + NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), initialQSize - modeSize, "We are in dropping state." + "Sojourn is still above target." + "So there should be only 1 more packet dequeued"); + NS_TEST_EXPECT_MSG_EQ (currentDropCount, 0, "There should not be any packet drops"); + NS_TEST_EXPECT_MSG_EQ (currentMarkCount, 2, "There should be 2 marked packet" + "as dropnext is equal to current time"); + } + else if (currentTime.GetNanoSeconds () > initialDropNext) + { + currentDropCount = queue->GetStats ().GetNDroppedPackets (CobaltQueueDisc::TARGET_EXCEEDED_DROP); + currentMarkCount = queue->GetStats ().GetNMarkedPackets (CobaltQueueDisc::FORCED_MARK); + NS_TEST_EXPECT_MSG_EQ (queue->GetCurrentSize ().GetValue (), initialQSize - (m_dropNextCount + 1) * modeSize, "We are in dropping state." + "It's time for packet to be dropped as packets are not ecnCapable" + "The number of packets dequeued equals to the number of times m_dropNext is updated plus initial dequeue"); + NS_TEST_EXPECT_MSG_EQ (currentDropCount, m_dropNextCount, "The number of drops equals to the number of times m_dropNext is updated"); + NS_TEST_EXPECT_MSG_EQ (currentMarkCount, 2, "There should still be only 2 marked packet"); + } + } + } + } +} + static class CobaltQueueDiscTestSuite : public TestSuite { public: @@ -334,5 +648,8 @@ public: AddTestCase (new CobaltQueueDiscBasicEnqueueDequeue (BYTES), TestCase::QUICK); // Test 2: Drop test AddTestCase (new CobaltQueueDiscDropTest (), TestCase::QUICK); + // Test 3: Mark test + AddTestCase (new CobaltQueueDiscMarkTest (PACKETS), TestCase::QUICK); + AddTestCase (new CobaltQueueDiscMarkTest (BYTES), TestCase::QUICK); } } g_cobaltQueueTestSuite; ///< the test suite -- GitLab