wifi: Extend MLO test to check BAR after dropped MPDUs

This commit is contained in:
Stefano Avallone
2025-04-01 13:21:45 +02:00
parent 8480c21c6e
commit d8d9a7812e
2 changed files with 276 additions and 0 deletions

View File

@@ -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<ListErrorModel>()),
m_apErrorModel(CreateObject<ListErrorModel>())
{
}
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<WifiMac> 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<const WifiPsdu> psdu) {
std::list<uint64_t> 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<const WifiPsdu> 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<const WifiPsdu> 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<const WifiPsdu> 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<const WifiPsdu> 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<const WifiPsdu> 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<const WifiPsdu> 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

View File

@@ -786,6 +786,73 @@ class StartSeqNoUpdateAfterAddBaTimeoutTest : public MultiLinkOperationsTestBase
Ptr<ListErrorModel> 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<WifiMac> 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<void(Ptr<const WifiPsdu>, const WifiTxVector&, uint8_t)>&& f = {})
: hdrType(type),
func(f)
{
}
WifiMacType hdrType; ///< MAC header type of frame being transmitted
std::function<void(Ptr<const WifiPsdu>, 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<Events> m_events; //!< list of events for a test run
std::size_t m_processedEvents{0}; //!< number of processed events
Ptr<ListErrorModel> m_staErrorModel; //!< error rate model to corrupt frames at the non-AP STA
Ptr<ListErrorModel> m_apErrorModel; //!< error rate model to corrupt frames at the AP MLD
};
/**
* @ingroup wifi-test
* @ingroup tests