diff --git a/src/wifi/test/wifi-mlo-test.cc b/src/wifi/test/wifi-mlo-test.cc index 23a14494d..dde6d3c67 100644 --- a/src/wifi/test/wifi-mlo-test.cc +++ b/src/wifi/test/wifi-mlo-test.cc @@ -3011,6 +3011,213 @@ StartSeqNoUpdateAfterAddBaTimeoutTest::DoRun() Simulator::Destroy(); } +BarAfterDroppedMpduTest::BarAfterDroppedMpduTest(WifiAssocType assocType) + : MultiLinkOperationsTestBase("Check correct reception of the BAR sent after dropping MPDUs", + 1, + BaseParams{{"{1, 0, BAND_6GHZ, 0}"}, + {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"}, + {}, + assocType}), + m_staErrorModel(CreateObject()), + m_apErrorModel(CreateObject()) +{ +} + +void +BarAfterDroppedMpduTest::DoSetup() +{ + // Set the frame retry limit to 1 so that QoS data frames are dropped after the first TX failure + Config::SetDefault("ns3::WifiMac::FrameRetryLimit", UintegerValue(1)); + Config::SetDefault("ns3::WifiRemoteStationManager::IncrementRetryCountUnderBa", + BooleanValue(true)); + + MultiLinkOperationsTestBase::DoSetup(); + + // install post reception error model on all STAs affiliated with non-AP MLD + for (const auto linkId : m_staMacs[0]->GetLinkIds()) + { + m_staMacs[0]->GetWifiPhy(linkId)->SetPostReceptionErrorModel(m_staErrorModel); + } + // install post reception error model on all APs affiliated with the AP MLD + for (const auto linkId : m_apMac->GetLinkIds()) + { + m_apMac->GetWifiPhy(linkId)->SetPostReceptionErrorModel(m_apErrorModel); + } +} + +void +BarAfterDroppedMpduTest::Transmit(Ptr mac, + uint8_t phyId, + WifiConstPsduMap psduMap, + WifiTxVector txVector, + double txPowerW) +{ + MultiLinkOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW); + + const auto psdu = psduMap.cbegin()->second; + const auto& hdr = psdu->GetHeader(0); + + // nothing to do before setup is completed or if this is a Beacon frame + if (!m_setupDone || hdr.IsBeacon()) + { + return; + } + + auto linkId = mac->GetLinkForPhy(phyId); + NS_TEST_ASSERT_MSG_EQ(linkId.has_value(), + true, + "PHY " << +phyId << " is not operating on any link"); + + if (!m_events.empty()) + { + // check that the expected frame is being transmitted + NS_TEST_EXPECT_MSG_EQ(WifiMacHeader(m_events.front().hdrType).GetTypeString(), + std::string(hdr.GetTypeString()), + "Unexpected MAC header type for frame #" << ++m_processedEvents); + // perform actions/checks, if any + if (m_events.front().func) + { + m_events.front().func(psdu, txVector, linkId.value()); + } + + m_events.pop_front(); + } +} + +void +BarAfterDroppedMpduTest::StartTraffic() +{ + m_setupDone = true; + InsertEvents(); + + PacketSocketAddress sockAddr; + sockAddr.SetSingleDevice(m_apMac->GetDevice()->GetIfIndex()); + sockAddr.SetPhysicalAddress(m_staMacs[0]->GetAddress()); + sockAddr.SetProtocol(1); + + // install client application generating 2 packets of 1000 bytes on the AP MLD + m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(sockAddr, 2, 1000)); +} + +void +BarAfterDroppedMpduTest::InsertEvents() +{ + // lambda returning the UIDs of all MPDUs in the given PSDU + auto getUids = [](Ptr psdu) { + std::list uids; + for (const auto& mpdu : *PeekPointer(psdu)) + { + uids.push_back(mpdu->GetPacket()->GetUid()); + } + return uids; + }; + + // BlockAck agreement establishment (AP MLD -> non-AP STA) + m_events.emplace_back(WIFI_MAC_MGT_ACTION); + m_events.emplace_back(WIFI_MAC_CTL_ACK); + m_events.emplace_back(WIFI_MAC_MGT_ACTION); + m_events.emplace_back(WIFI_MAC_CTL_ACK); + + m_events.emplace_back( + WIFI_MAC_QOSDATA, + [=, this](Ptr psdu, const WifiTxVector& txVector, uint8_t linkId) { + NS_TEST_EXPECT_MSG_EQ(psdu->GetAddr2(), + m_apMac->GetFrameExchangeManager(linkId)->GetAddress(), + "Unexpected TA for the data frame sent by the AP"); + NS_TEST_EXPECT_MSG_EQ(psdu->GetNMpdus(), 2, "Expected to transmit an A-MPDU"); + + // corrupt all the MPDUs in the A-MPDU + m_staErrorModel->SetList(getUids(psdu)); + }); + + // QoS data frames are dropped, thus expect a BAR to advance recipient window + m_events.emplace_back( + WIFI_MAC_CTL_BACKREQ, + [=, this](Ptr psdu, const WifiTxVector& txVector, uint8_t linkId) { + NS_TEST_EXPECT_MSG_EQ(psdu->GetAddr2(), + m_apMac->GetFrameExchangeManager(linkId)->GetAddress(), + "Unexpected TA for the BlockAckReq sent by the AP"); + NS_TEST_EXPECT_MSG_EQ(psdu->GetAddr1(), + m_staMacs[0]->GetAddress(), + "Unexpected RA for the BlockAckReq sent by the AP"); + }); + + m_events.emplace_back( + WIFI_MAC_CTL_BACKRESP, + [=, this](Ptr psdu, const WifiTxVector& txVector, uint8_t linkId) { + NS_TEST_EXPECT_MSG_EQ(psdu->GetAddr2(), + m_staMacs[0]->GetAddress(), + "Unexpected TA for the BlockAck sent by the non-AP STA"); + NS_TEST_EXPECT_MSG_EQ(psdu->GetAddr1(), + m_staMacs[0]->GetBssid(linkId), + "Unexpected RA for the BlockAck sent by the non-AP STA"); + + // generate uplink frames + Simulator::Schedule(MilliSeconds(5), [=, this]() { + PacketSocketAddress sockAddr; + sockAddr.SetSingleDevice(m_staMacs[0]->GetDevice()->GetIfIndex()); + sockAddr.SetPhysicalAddress(m_apMac->GetAddress()); + sockAddr.SetProtocol(1); + + // install client application generating 2 packets of 1000 bytes on the non-AP STA + m_staMacs[0]->GetDevice()->GetNode()->AddApplication( + GetApplication(sockAddr, 2, 1000)); + }); + }); + + // BlockAck agreement establishment (non-AP STA -> AP MLD) + m_events.emplace_back(WIFI_MAC_MGT_ACTION); + m_events.emplace_back(WIFI_MAC_CTL_ACK); + m_events.emplace_back(WIFI_MAC_MGT_ACTION); + m_events.emplace_back(WIFI_MAC_CTL_ACK); + + m_events.emplace_back( + WIFI_MAC_QOSDATA, + [=, this](Ptr psdu, const WifiTxVector& txVector, uint8_t linkId) { + NS_TEST_EXPECT_MSG_EQ(psdu->GetAddr2(), + m_staMacs[0]->GetFrameExchangeManager(linkId)->GetAddress(), + "Unexpected TA for the data frame sent by the non-AP STA"); + NS_TEST_EXPECT_MSG_EQ(psdu->GetNMpdus(), 2, "Expected to transmit an A-MPDU"); + + // corrupt all the MPDUs in the A-MPDU + m_apErrorModel->SetList(getUids(psdu)); + }); + + // QoS data frames are dropped, thus expect a BAR to advance recipient window + m_events.emplace_back( + WIFI_MAC_CTL_BACKREQ, + [=, this](Ptr psdu, const WifiTxVector& txVector, uint8_t linkId) { + NS_TEST_EXPECT_MSG_EQ(psdu->GetAddr2(), + m_staMacs[0]->GetAddress(), + "Unexpected TA for the BlockAckReq sent by the non-AP STA"); + NS_TEST_EXPECT_MSG_EQ(psdu->GetAddr1(), + m_staMacs[0]->GetBssid(linkId), + "Unexpected RA for the BlockAckReq sent by the non-AP STA"); + }); + + m_events.emplace_back( + WIFI_MAC_CTL_BACKRESP, + [=, this](Ptr psdu, const WifiTxVector& txVector, uint8_t linkId) { + NS_TEST_EXPECT_MSG_EQ(psdu->GetAddr2(), + m_apMac->GetFrameExchangeManager(linkId)->GetAddress(), + "Unexpected TA for the BlockAck sent by the AP"); + NS_TEST_EXPECT_MSG_EQ(psdu->GetAddr1(), + m_staMacs[0]->GetAddress(), + "Unexpected RA for the BlockAck sent by the AP"); + }); +} + +void +BarAfterDroppedMpduTest::DoRun() +{ + Simulator::Stop(m_duration); + Simulator::Run(); + + NS_TEST_EXPECT_MSG_EQ(m_events.empty(), true, "Not all events took place"); + + Simulator::Destroy(); +} + WifiMultiLinkOperationsTestSuite::WifiMultiLinkOperationsTestSuite() : TestSuite("wifi-mlo", Type::UNIT) { @@ -3279,6 +3486,8 @@ WifiMultiLinkOperationsTestSuite::WifiMultiLinkOperationsTestSuite() AddTestCase(new ReleaseSeqNoAfterCtsTimeoutTest(), TestCase::Duration::QUICK); AddTestCase(new StartSeqNoUpdateAfterAddBaTimeoutTest(), TestCase::Duration::QUICK); + AddTestCase(new BarAfterDroppedMpduTest(WifiAssocType::ML_SETUP), TestCase::Duration::QUICK); + AddTestCase(new BarAfterDroppedMpduTest(WifiAssocType::LEGACY), TestCase::Duration::QUICK); } static WifiMultiLinkOperationsTestSuite g_wifiMultiLinkOperationsTestSuite; ///< the test suite diff --git a/src/wifi/test/wifi-mlo-test.h b/src/wifi/test/wifi-mlo-test.h index 698e3c436..c070fdc6f 100644 --- a/src/wifi/test/wifi-mlo-test.h +++ b/src/wifi/test/wifi-mlo-test.h @@ -786,6 +786,73 @@ class StartSeqNoUpdateAfterAddBaTimeoutTest : public MultiLinkOperationsTestBase Ptr m_staErrorModel; //!< error rate model to corrupt frames at the non-AP MLD }; +/** + * @ingroup wifi-test + * @ingroup tests + * + * @brief Test BlockAckReq frame sent by a Block Ack originator after dropping QoS data frames + * + * In this test, a non-AP STA associates with an AP MLD with 2 links using either legacy association + * or ML setup. A Block Ack agreement is established first in the downlink direction; the AP MLD + * sends 2 data frames, which are both corrupted. When a timeout occurs at the AP MLD, the 2 data + * frames are dropped, thus the AP MLD sends a BlockAckReq to advance the recipient window. It is + * checked that the BlockAckReq has the correct link addresses and that the non-AP STA replies with + * a BlockAck having correct link addresses. Then, a Block Ack agreement is established in the + * uplink direction and the same pattern of actions are repeated (in the inverse direction). + */ +class BarAfterDroppedMpduTest : public MultiLinkOperationsTestBase +{ + public: + /** + * Constructor. + * + * @param assocType the association type + */ + BarAfterDroppedMpduTest(WifiAssocType assocType); + + protected: + void DoSetup() override; + void DoRun() override; + void Transmit(Ptr mac, + uint8_t phyId, + WifiConstPsduMap psduMap, + WifiTxVector txVector, + double txPowerW) override; + + /// Actions and checks to perform upon the transmission of each frame + struct Events + { + /** + * Constructor. + * + * @param type the frame MAC header type + * @param f function to perform actions and checks + */ + Events(WifiMacType type, + std::function, const WifiTxVector&, uint8_t)>&& f = {}) + : hdrType(type), + func(f) + { + } + + WifiMacType hdrType; ///< MAC header type of frame being transmitted + std::function, const WifiTxVector&, uint8_t)> + func; ///< function to perform actions and checks + }; + + /// Insert elements in the list of expected events (transmitted frames) + void InsertEvents(); + + private: + void StartTraffic() override; + + bool m_setupDone{false}; //!< whether association has been completed + std::list m_events; //!< list of events for a test run + std::size_t m_processedEvents{0}; //!< number of processed events + Ptr m_staErrorModel; //!< error rate model to corrupt frames at the non-AP STA + Ptr m_apErrorModel; //!< error rate model to corrupt frames at the AP MLD +}; + /** * @ingroup wifi-test * @ingroup tests