From a69155655ae00e250d4c39ac0b1908a08b799802 Mon Sep 17 00:00:00 2001 From: Stefano Avallone Date: Wed, 6 Nov 2024 19:56:24 +0100 Subject: [PATCH] wifi: Add support for SLDs performing ML setup and MLDs performing legacy association --- src/test/ns3wifi/wifi-mlo-udp-test-suite.cc | 633 ++++++++++++++++++ .../model/eht/eht-frame-exchange-manager.cc | 4 +- src/wifi/model/eht/emlsr-manager.cc | 4 +- src/wifi/model/sta-wifi-mac.cc | 153 +++-- src/wifi/model/wifi-assoc-manager.cc | 2 +- src/wifi/model/wifi-mac.cc | 6 +- src/wifi/test/wifi-mlo-test.cc | 58 +- src/wifi/test/wifi-mlo-test.h | 18 +- 8 files changed, 772 insertions(+), 106 deletions(-) create mode 100644 src/test/ns3wifi/wifi-mlo-udp-test-suite.cc diff --git a/src/test/ns3wifi/wifi-mlo-udp-test-suite.cc b/src/test/ns3wifi/wifi-mlo-udp-test-suite.cc new file mode 100644 index 000000000..0ce223b00 --- /dev/null +++ b/src/test/ns3wifi/wifi-mlo-udp-test-suite.cc @@ -0,0 +1,633 @@ +/* + * Copyright (c) 2024 Universita' degli Studi di Napoli Federico II + * + * SPDX-License-Identifier: GPL-2.0-only + * + * Author: Stefano Avallone + */ + +#include "ns3/arp-header.h" +#include "ns3/arp-l3-protocol.h" +#include "ns3/config.h" +#include "ns3/frame-exchange-manager.h" +#include "ns3/internet-stack-helper.h" +#include "ns3/ipv4-address-helper.h" +#include "ns3/llc-snap-header.h" +#include "ns3/mobility-helper.h" +#include "ns3/pointer.h" +#include "ns3/qos-txop.h" +#include "ns3/rng-seed-manager.h" +#include "ns3/ssid.h" +#include "ns3/string.h" +#include "ns3/test.h" +#include "ns3/udp-client-server-helper.h" +#include "ns3/wifi-mac-queue.h" +#include "ns3/wifi-mac.h" +#include "ns3/wifi-mlo-test.h" +#include "ns3/wifi-net-device.h" + +#include +#include + +using namespace ns3; + +NS_LOG_COMPONENT_DEFINE("WifiMloUdpTest"); + +/** + * @ingroup wifi-test + * @ingroup tests + * + * @brief Test UDP packet transmission between MLDs and SLDs. + * + * This test sets up an AP MLD and two non-AP MLDs having a variable number of links (possibly one). + * The RF channels to set each link to are provided as input parameters. This test aims at veryfing + * the successful transmission and reception of UDP packets in different traffic scenarios (from + * the first station to the AP, from the AP to the first station, from one station to another). + * The number of transmitted ARP Request/Reply frames is verified, as well as the source HW address + * they carry. Specifically: + * + * STA to AP + * --------- + * The source HW address of the ARP Request sent by the STA is: + * - the unique STA address, if the STA is an SLD + * - the non-AP MLD address, if both the STA and the AP are MLDs + * - the address of the link used to associate, if STA is an MLD and AP is an SLD + * The source HW address of the ARP Reply sent by the AP is: + * - the unique AP address, if the AP is an SLD + * - the AP MLD address, if both the STA and the AP are MLDs + * - the address of the link used by STA to associate, if STA is an SLD and AP is an MLD + * + * AP to STA + * --------- + * The source HW address of the ARP Request sent by the AP is: + * - the unique AP address, if the AP is an SLD + * - the AP MLD address, if the AP is an MLD + * The source HW address of the ARP Reply sent by the STA is: + * - the unique STA address, if the STA is an SLD + * - the non-AP MLD address, if both the STA and the AP are MLDs + * - the address of the link used to associate, if STA is an MLD and AP is an SLD + * + * STA 1 to STA 2 + * -------------- + * The source HW address of the ARP Request sent by STA 1 is: + * - the unique STA 1 address, if STA 1 is an SLD + * - the non-AP MLD address, if both STA 1 and the AP are MLDs + * - the address of the link used to associate, if STA 1 is an MLD and AP is an SLD + * The source HW address of the ARP Reply sent by the STA 2 is (STA 1 is unknown to STA 2): + * - the unique STA 2 address, if STA 2 is an SLD + * - the non-AP MLD address, if STA 2 is an MLD + */ +class WifiMloUdpTest : public MultiLinkOperationsTestBase +{ + public: + /** + * Constructor + * + * @param apChannels string specifying channels for AP + * @param firstStaChannels string specifying channels for first STA + * @param secondStaChannels string specifying channels for second STA + * @param trafficPattern the pattern of traffic to generate + * @param amsduAggr whether A-MSDU aggregation is enabled + */ + WifiMloUdpTest(const std::vector& apChannels, + const std::vector& firstStaChannels, + const std::vector& secondStaChannels, + WifiTrafficPattern trafficPattern, + bool amsduAggr); + + protected: + void DoSetup() override; + void Transmit(Ptr mac, + uint8_t phyId, + WifiConstPsduMap psduMap, + WifiTxVector txVector, + double txPowerW) override; + void DoRun() override; + + /** + * Check source and destination hardware addresses in ARP request frames. + * + * @param arp the ARP header + * @param sender the MAC address of the sender (Address 2 field) + * @param linkId the ID of the link on which the ARP frame is transmitted + */ + void CheckArpRequestHwAddresses(const ArpHeader& arp, Mac48Address sender, uint8_t linkId); + + /** + * Check source and destination hardware addresses in ARP reply frames. + * + * @param arp the ARP header + * @param sender the MAC address of the sender (Address 2 field) + * @param linkId the ID of the link on which the ARP frame is transmitted + */ + void CheckArpReplyHwAddresses(const ArpHeader& arp, Mac48Address sender, uint8_t linkId); + + private: + void StartTraffic() override; + + const std::vector m_2ndStaChannels; ///< string specifying channels for second STA + WifiTrafficPattern m_trafficPattern; ///< the pattern of traffic to generate + bool m_amsduAggr; ///< whether A-MSDU aggregation is enabled + const std::size_t m_nPackets{3}; ///< number of application packets to generate + Ipv4InterfaceContainer m_staInterfaces; ///< IP interfaces for non-AP MLDs + Ipv4InterfaceContainer m_apInterface; ///< IP interface for AP MLD + const uint16_t m_udpPort{50000}; ///< UDP port for application servers + Ptr m_sink; ///< server app on the receiving node + std::size_t m_nArpRequest{0}; ///< counts how many ARP Requests are transmitted + std::size_t m_nCheckedArpRequest{0}; ///< counts how many ARP Requests are checked + std::size_t m_nArpReply{0}; ///< counts how many ARP Replies are transmitted + std::size_t m_nCheckedArpReply{0}; ///< counts how many ARP Replies are checked +}; + +WifiMloUdpTest::WifiMloUdpTest(const std::vector& apChannels, + const std::vector& firstStaChannels, + const std::vector& secondStaChannels, + WifiTrafficPattern trafficPattern, + bool amsduAggr) + : MultiLinkOperationsTestBase( + std::string("Check UDP packet transmission between MLDs ") + + " (#AP_links: " + std::to_string(apChannels.size()) + + ", #STA_1_links: " + std::to_string(firstStaChannels.size()) + + ", #STA_2_links: " + std::to_string(secondStaChannels.size()) + + ", Traffic pattern: " + std::to_string(static_cast(trafficPattern)) + + ", A-MSDU aggregation: " + std::to_string(amsduAggr) + ")", + 2, + BaseParams{firstStaChannels, apChannels, {}}), + m_2ndStaChannels(secondStaChannels), + m_trafficPattern(trafficPattern), + m_amsduAggr(amsduAggr) +{ +} + +void +WifiMloUdpTest::DoSetup() +{ + Config::SetDefault("ns3::WifiMac::BE_MaxAmsduSize", UintegerValue(m_amsduAggr ? 4000 : 0)); + + RngSeedManager::SetSeed(1); + RngSeedManager::SetRun(3); + int64_t streamNumber = 30; + + NodeContainer wifiApNode(1); + NodeContainer wifiStaNodes(2); + + WifiHelper wifi; + // WifiHelper::EnableLogComponents(); + wifi.SetStandard(WIFI_STANDARD_80211be); + wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager", + "DataMode", + StringValue("EhtMcs0"), + "ControlMode", + StringValue("HtMcs0")); + + ChannelMap channelMap{{WIFI_SPECTRUM_2_4_GHZ, CreateObject()}, + {WIFI_SPECTRUM_5_GHZ, CreateObject()}, + {WIFI_SPECTRUM_6_GHZ, CreateObject()}}; + + SpectrumWifiPhyHelper staPhyHelper1; + SpectrumWifiPhyHelper staPhyHelper2; + SpectrumWifiPhyHelper apPhyHelper; + SetChannels(staPhyHelper1, m_staChannels, channelMap); + SetChannels(staPhyHelper2, m_2ndStaChannels, channelMap); + SetChannels(apPhyHelper, m_apChannels, channelMap); + + WifiMacHelper mac; + mac.SetType( + "ns3::StaWifiMac", // default SSID + "ActiveProbing", + BooleanValue(false), + "AssocType", + EnumValue(m_staChannels.size() > 1 ? WifiAssocType::ML_SETUP : WifiAssocType::LEGACY)); + + NetDeviceContainer staDevices = wifi.Install(staPhyHelper1, mac, wifiStaNodes.Get(0)); + + mac.SetType( + "ns3::StaWifiMac", // default SSID + "ActiveProbing", + BooleanValue(false), + "AssocType", + EnumValue(m_2ndStaChannels.size() > 1 ? WifiAssocType::ML_SETUP : WifiAssocType::LEGACY)); + + staDevices.Add(wifi.Install(staPhyHelper2, mac, wifiStaNodes.Get(1))); + + mac.SetType("ns3::ApWifiMac", + "Ssid", + SsidValue(Ssid("ns-3-ssid")), + "BeaconGeneration", + BooleanValue(true)); + + NetDeviceContainer apDevices = wifi.Install(apPhyHelper, mac, wifiApNode); + + // Uncomment the lines below to write PCAP files + // apPhyHelper.EnablePcap("wifi-mlo_AP", apDevices); + // staPhyHelper1.EnablePcap("wifi-mlo_STA", staDevices); + + // Assign fixed streams to random variables in use + streamNumber += WifiHelper::AssignStreams(apDevices, streamNumber); + streamNumber += WifiHelper::AssignStreams(staDevices, streamNumber); + + MobilityHelper mobility; + Ptr positionAlloc = CreateObject(); + + positionAlloc->Add(Vector(0.0, 0.0, 0.0)); + positionAlloc->Add(Vector(1.0, 0.0, 0.0)); + mobility.SetPositionAllocator(positionAlloc); + + mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel"); + mobility.Install(wifiApNode); + mobility.Install(wifiStaNodes); + + m_apMac = DynamicCast(DynamicCast(apDevices.Get(0))->GetMac()); + for (uint8_t i = 0; i < m_nStations; i++) + { + m_staMacs[i] = + DynamicCast(DynamicCast(staDevices.Get(i))->GetMac()); + } + + // Trace PSDUs passed to the PHY on all devices + for (uint8_t phyId = 0; phyId < m_apMac->GetDevice()->GetNPhys(); phyId++) + { + Config::ConnectWithoutContext( + "/NodeList/0/DeviceList/*/$ns3::WifiNetDevice/Phys/" + std::to_string(phyId) + + "/PhyTxPsduBegin", + MakeCallback(&WifiMloUdpTest::Transmit, this).Bind(m_apMac, phyId)); + } + for (uint8_t i = 0; i < m_nStations; i++) + { + for (uint8_t phyId = 0; phyId < m_staMacs[i]->GetDevice()->GetNPhys(); phyId++) + { + Config::ConnectWithoutContext( + "/NodeList/" + std::to_string(i + 1) + "/DeviceList/*/$ns3::WifiNetDevice/Phys/" + + std::to_string(phyId) + "/PhyTxPsduBegin", + MakeCallback(&WifiMloUdpTest::Transmit, this).Bind(m_staMacs[i], phyId)); + } + } + + // install Internet stack on all nodes + InternetStackHelper stack; + auto allNodes = NodeContainer::GetGlobal(); + stack.Install(allNodes); + streamNumber += stack.AssignStreams(allNodes, streamNumber); + + Ipv4AddressHelper address; + address.SetBase("10.1.0.0", "255.255.255.0"); + m_apInterface = address.Assign(NetDeviceContainer(m_apMac->GetDevice())); + for (std::size_t i = 0; i < m_nStations; i++) + { + m_staInterfaces.Add(address.Assign(NetDeviceContainer(m_staMacs[i]->GetDevice()))); + } + + // install a UDP server on all nodes + UdpServerHelper serverHelper(m_udpPort); + auto serverApps = serverHelper.Install(allNodes); + serverApps.Start(Seconds(0.0)); + serverApps.Stop(m_duration); + + switch (m_trafficPattern) + { + case WifiTrafficPattern::STA_TO_AP: + m_sink = DynamicCast(serverApps.Get(0)); + break; + case WifiTrafficPattern::AP_TO_STA: + m_sink = DynamicCast(serverApps.Get(1)); + break; + case WifiTrafficPattern::STA_TO_STA: + m_sink = DynamicCast(serverApps.Get(2)); + break; + default: + m_sink = nullptr; // other cases are not supported + } + + // schedule association/ML setup for one station at a time + m_apMac->TraceConnectWithoutContext("AssociatedSta", + MakeCallback(&WifiMloUdpTest::SetSsid, this)); + m_staMacs[0]->SetSsid(Ssid("ns-3-ssid")); +} + +void +WifiMloUdpTest::StartTraffic() +{ + Ptr srcMac; + Ipv4Address destAddr; + + switch (m_trafficPattern) + { + case WifiTrafficPattern::STA_TO_AP: + srcMac = m_staMacs[0]; + destAddr = m_apInterface.GetAddress(0); + break; + case WifiTrafficPattern::AP_TO_STA: + srcMac = m_apMac; + destAddr = m_staInterfaces.GetAddress(0); + break; + case WifiTrafficPattern::STA_TO_STA: + srcMac = m_staMacs[0]; + destAddr = m_staInterfaces.GetAddress(1); + break; + default: + NS_ABORT_MSG("Unsupported scenario " << +static_cast(m_trafficPattern)); + } + + UdpClientHelper clientHelper(InetSocketAddress(destAddr, m_udpPort)); + clientHelper.SetAttribute("MaxPackets", UintegerValue(m_nPackets)); + clientHelper.SetAttribute("Interval", TimeValue(Time{0})); + clientHelper.SetAttribute("PacketSize", UintegerValue(100)); + auto clientApp = clientHelper.Install(srcMac->GetDevice()->GetNode()); + clientApp.Start(Seconds(0.0)); + clientApp.Stop(m_duration); +} + +void +WifiMloUdpTest::Transmit(Ptr mac, + uint8_t phyId, + WifiConstPsduMap psduMap, + WifiTxVector txVector, + double txPowerW) +{ + MultiLinkOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW); + + auto psdu = psduMap.begin()->second; + auto linkId = m_txPsdus.back().linkId; + + if (!psdu->GetHeader(0).IsQosData() || !psdu->GetHeader(0).HasData()) + { + // we are interested in ARP Request/Reply frames, which are carried by QoS data frames + return; + } + + for (const auto& mpdu : *PeekPointer(psdu)) + { + std::list> packets; + + if (mpdu->GetHeader().IsQosAmsdu()) + { + for (const auto& msdu : *PeekPointer(mpdu)) + { + packets.push_back(msdu.first); + } + } + else + { + packets.push_back(mpdu->GetPacket()); + } + + for (auto pkt : packets) + { + LlcSnapHeader llc; + auto packet = pkt->Copy(); + packet->RemoveHeader(llc); + + if (llc.GetType() != ArpL3Protocol::PROT_NUMBER) + { + continue; + } + + ArpHeader arp; + packet->RemoveHeader(arp); + + if (arp.IsRequest()) + { + CheckArpRequestHwAddresses(arp, psdu->GetAddr2(), linkId); + } + if (arp.IsReply()) + { + CheckArpReplyHwAddresses(arp, psdu->GetAddr2(), linkId); + } + } + } +} + +void +WifiMloUdpTest::CheckArpRequestHwAddresses(const ArpHeader& arp, + Mac48Address sender, + uint8_t linkId) +{ + ++m_nArpRequest; + + Mac48Address expectedSrc; + Ptr srcMac; + + switch (m_trafficPattern) + { + case WifiTrafficPattern::STA_TO_AP: + case WifiTrafficPattern::STA_TO_STA: + // if the ARP Request is sent by a STA, the source HW address is: + // - the unique STA address, if the STA is an SLD + // - the non-AP MLD address, if both the STA and the AP are MLDs + // - the address of the link used to associate, if STA is an MLD and AP is an SLD + expectedSrc = (m_staMacs[0]->GetNLinks() > 1 && m_apMac->GetNLinks() == 1) + ? m_staMacs[0]->GetFrameExchangeManager(linkId)->GetAddress() + : m_staMacs[0]->GetAddress(); + srcMac = m_staMacs[0]; + break; + case WifiTrafficPattern::AP_TO_STA: + // if the ARP Request is sent by an AP, the source HW address is: + // - the unique AP address, if the AP is an SLD + // - the AP MLD address, if the AP is an MLD + expectedSrc = m_apMac->GetAddress(); + srcMac = m_apMac; + break; + default: + NS_ABORT_MSG("Unsupported scenario " << +static_cast(m_trafficPattern)); + } + + // source and destination HW addresses cannot be checked for forwarded frames because + // they can be forwarded on different links + if (!srcMac->GetLinkIdByAddress(sender) && sender != srcMac->GetAddress()) + { + // the sender address in not the MLD address nor a link address of the source device + return; + } + + ++m_nCheckedArpRequest; + + NS_TEST_EXPECT_MSG_EQ(Mac48Address::ConvertFrom(arp.GetSourceHardwareAddress()), + expectedSrc, + "Unexpected source HW address"); + NS_TEST_EXPECT_MSG_EQ(Mac48Address::ConvertFrom(arp.GetDestinationHardwareAddress()), + Mac48Address::GetBroadcast(), + "Unexpected destination HW address"); +} + +void +WifiMloUdpTest::CheckArpReplyHwAddresses(const ArpHeader& arp, Mac48Address sender, uint8_t linkId) +{ + ++m_nArpReply; + + Mac48Address expectedSrc; + Ptr srcMac; + + switch (m_trafficPattern) + { + case WifiTrafficPattern::STA_TO_AP: + // the source HW address of the ARP Reply sent by the AP is: + // - the unique AP address, if the AP is an SLD + // - the AP MLD address, if both the STA and the AP are MLDs + // - the address of the link used by STA to associate, if STA is an SLD and AP is an MLD + expectedSrc = (m_staMacs[0]->GetNLinks() == 1 && m_apMac->GetNLinks() > 1) + ? m_apMac->GetFrameExchangeManager(linkId)->GetAddress() + : m_apMac->GetAddress(); + srcMac = m_apMac; + break; + case WifiTrafficPattern::AP_TO_STA: + // the source HW address of the ARP Reply sent by the STA is: + // - the unique STA address, if the STA is an SLD + // - the non-AP MLD address, if both the STA and the AP are MLDs + // - the address of the link used to associate, if STA is an MLD and AP is an SLD + expectedSrc = (m_staMacs[0]->GetNLinks() > 1 && m_apMac->GetNLinks() == 1) + ? m_staMacs[0]->GetFrameExchangeManager(linkId)->GetAddress() + : m_staMacs[0]->GetAddress(); + srcMac = m_staMacs[0]; + break; + case WifiTrafficPattern::STA_TO_STA: + // given that the source HW address of the ARP Request is unknown to STA 2, the source HW + // address of the ARP Reply sent by STA 2 is: + // - the unique STA 2 address, if STA 2 is an SLD + // - the non-AP MLD address, if STA 2 is an MLD + expectedSrc = m_staMacs[1]->GetAddress(); + srcMac = m_staMacs[1]; + break; + default: + NS_ABORT_MSG("Unsupported scenario " << +static_cast(m_trafficPattern)); + } + + // source and destination HW addresses cannot be checked for forwarded frames because + // they can be forwarded on different links + if (!srcMac->GetLinkIdByAddress(sender) && sender != srcMac->GetAddress()) + { + // the sender address in not the MLD address nor a link address of the source device + return; + } + + ++m_nCheckedArpReply; + + NS_TEST_EXPECT_MSG_EQ(Mac48Address::ConvertFrom(arp.GetSourceHardwareAddress()), + expectedSrc, + "Unexpected source HW address"); +} + +void +WifiMloUdpTest::DoRun() +{ + Simulator::Stop(m_duration); + Simulator::Run(); + + NS_TEST_ASSERT_MSG_NE(m_sink, nullptr, "Sink is not set"); + NS_TEST_EXPECT_MSG_EQ(m_sink->GetReceived(), + m_nPackets, + "Unexpected number of received packets"); + + std::size_t expectedNOrigArpRequest{0}; + std::size_t expectedNFwdArpRequest{0}; + + switch (m_trafficPattern) + { + case WifiTrafficPattern::STA_TO_AP: + case WifiTrafficPattern::STA_TO_STA: + // STA transmits ARP Request on one link, AP retransmits it on all of its links + expectedNOrigArpRequest = 1; + expectedNFwdArpRequest = m_apMac->GetNLinks(); + break; + case WifiTrafficPattern::AP_TO_STA: + // AP transmits ARP Request on all of its links + expectedNOrigArpRequest = m_apMac->GetNLinks(); + expectedNFwdArpRequest = 0; + break; + default: + NS_ABORT_MSG("Unsupported scenario " << +static_cast(m_trafficPattern)); + } + + NS_TEST_EXPECT_MSG_EQ(m_nArpRequest, + expectedNOrigArpRequest + expectedNFwdArpRequest, + "Unexpected number of transmitted ARP Request frames"); + NS_TEST_EXPECT_MSG_EQ(m_nCheckedArpRequest, + expectedNOrigArpRequest, + "Unexpected number of checked ARP Request frames"); + + std::size_t expectedNOrigArpReply{0}; + std::size_t expectedNFwdArpReply{0}; + + switch (m_trafficPattern) + { + case WifiTrafficPattern::STA_TO_AP: + // STA transmits only one ARP Request, AP replies with one (unicast) ARP Reply + expectedNOrigArpReply = 1; + expectedNFwdArpReply = 0; + break; + case WifiTrafficPattern::AP_TO_STA: + // ARP Request is broadcast, so it is sent by the AP on all of its links; the STA sends + // an ARP Reply for each received ARP Request + expectedNOrigArpReply = std::min(m_apMac->GetNLinks(), m_staMacs[0]->GetNLinks()); + expectedNFwdArpReply = 0; + break; + case WifiTrafficPattern::STA_TO_STA: + // AP forwards ARP Request on all of its links; STA 2 sends as many ARP Replies as the + // number of received ARP Requests; each such ARP Reply is forwarded by the AP to STA 1 + expectedNOrigArpReply = expectedNFwdArpReply = m_staMacs[1]->GetNLinks(); + break; + default: + NS_ABORT_MSG("Unsupported scenario " << +static_cast(m_trafficPattern)); + } + + NS_TEST_EXPECT_MSG_EQ(m_nArpReply, + expectedNOrigArpReply + expectedNFwdArpReply, + "Unexpected number of transmitted ARP Reply frames"); + NS_TEST_EXPECT_MSG_EQ(m_nCheckedArpReply, + expectedNOrigArpReply, + "Unexpected number of checked ARP Reply frames"); + + Simulator::Destroy(); +} + +/** + * @ingroup wifi-test + * @ingroup tests + * + * @brief Multi-Link Operations with UDP traffic Test Suite + */ +class WifiMloUdpTestSuite : public TestSuite +{ + public: + WifiMloUdpTestSuite(); +}; + +WifiMloUdpTestSuite::WifiMloUdpTestSuite() + : TestSuite("wifi-mlo-udp", Type::SYSTEM) +{ + using ParamsTuple = std::array, 3>; // AP, STA 1, STA 2 channels + + for (const auto& channels : + {// single link AP, non-AP MLD with 3 links, single link non-AP STA + ParamsTuple{ + {{"{7, 80, BAND_6GHZ, 0}"}, + {"{42, 80, BAND_5GHZ, 2}", "{5, 40, BAND_2_4GHZ, 0}", "{7, 80, BAND_6GHZ, 0}"}, + {"{7, 80, BAND_6GHZ, 0}"}}}, + // AP MLD with 3 links, single link non-AP STA, non-AP MLD with 2 links + ParamsTuple{ + {{"{42, 80, BAND_5GHZ, 2}", "{5, 40, BAND_2_4GHZ, 0}", "{7, 80, BAND_6GHZ, 0}"}, + {"{7, 80, BAND_6GHZ, 0}"}, + {"{42, 80, BAND_5GHZ, 2}", "{5, 40, BAND_2_4GHZ, 0}"}}}, + // AP MLD with 3 links, non-AP MLD with 3 links, non-AP MLD with 2 links + ParamsTuple{ + {{"{42, 80, BAND_5GHZ, 2}", "{5, 40, BAND_2_4GHZ, 0}", "{7, 80, BAND_6GHZ, 0}"}, + {"{42, 80, BAND_5GHZ, 2}", "{5, 40, BAND_2_4GHZ, 0}", "{7, 80, BAND_6GHZ, 0}"}, + {"{5, 40, BAND_2_4GHZ, 0}", "{7, 80, BAND_6GHZ, 0}"}}}}) + { + for (const auto& trafficPattern : {WifiTrafficPattern::STA_TO_AP, + WifiTrafficPattern::AP_TO_STA, + WifiTrafficPattern::STA_TO_STA}) + { + for (const auto amsduAggr : {false, true}) + { + AddTestCase(new WifiMloUdpTest(channels[0], + channels[1], + channels[2], + trafficPattern, + amsduAggr), + TestCase::Duration::QUICK); + } + } + } +} + +static WifiMloUdpTestSuite g_wifiMloUdpTestSuite; ///< the test suite diff --git a/src/wifi/model/eht/eht-frame-exchange-manager.cc b/src/wifi/model/eht/eht-frame-exchange-manager.cc index 74d99cbce..48a05cb33 100644 --- a/src/wifi/model/eht/eht-frame-exchange-manager.cc +++ b/src/wifi/model/eht/eht-frame-exchange-manager.cc @@ -111,7 +111,9 @@ EhtFrameExchangeManager::CreateAliasIfNeeded(Ptr mpdu) const NS_LOG_FUNCTION(this << *mpdu); // alias needs only be created for non-broadcast QoS data frames exchanged between two MLDs - if (!mpdu->GetHeader().IsQosData() || m_mac->GetNLinks() == 1 || + if (auto staMac = DynamicCast(m_mac); + !mpdu->GetHeader().IsQosData() || + (staMac ? (staMac->GetAssocType() == WifiAssocType::LEGACY) : (m_mac->GetNLinks() == 1)) || mpdu->GetHeader().GetAddr1().IsGroup() || !GetWifiRemoteStationManager()->GetMldAddress(mpdu->GetHeader().GetAddr1())) { diff --git a/src/wifi/model/eht/emlsr-manager.cc b/src/wifi/model/eht/emlsr-manager.cc index efcb951f4..f4fad4949 100644 --- a/src/wifi/model/eht/emlsr-manager.cc +++ b/src/wifi/model/eht/emlsr-manager.cc @@ -188,6 +188,9 @@ EmlsrManager::SetWifiMac(Ptr mac) "EmlsrManager requires EMLSR to be activated"); NS_ABORT_MSG_IF(m_staMac->GetTypeOfStation() != STA, "EmlsrManager can only be installed on non-AP MLDs"); + NS_ABORT_MSG_IF(m_mainPhyId >= m_staMac->GetDevice()->GetNPhys(), + "Main PHY ID (" << +m_mainPhyId << ") invalid given the number of PHY objects (" + << +m_staMac->GetDevice()->GetNPhys() << ")"); m_staMac->TraceConnectWithoutContext("AckedMpdu", MakeCallback(&EmlsrManager::TxOk, this)); m_staMac->TraceConnectWithoutContext("DroppedMpdu", @@ -380,7 +383,6 @@ EmlsrManager::SetEmlsrLinks(const std::set& linkIds) std::copy(linkIds.cbegin(), linkIds.cend(), std::ostream_iterator(ss, " ")); } NS_LOG_FUNCTION(this << ss.str()); - NS_ABORT_MSG_IF(linkIds.size() == 1, "Cannot enable EMLSR mode on a single link"); if (linkIds != m_emlsrLinks) { diff --git a/src/wifi/model/sta-wifi-mac.cc b/src/wifi/model/sta-wifi-mac.cc index c948f0b7a..e5e717b86 100644 --- a/src/wifi/model/sta-wifi-mac.cc +++ b/src/wifi/model/sta-wifi-mac.cc @@ -1033,13 +1033,30 @@ StaWifiMac::DoGetLocalAddress(const Mac48Address& remoteAddr) const { if (GetStaLink(link).bssid == remoteAddr) { - // the remote address is the address of the (single link) AP we are associated with; + // the remote address is the address of the AP we are associated with; return link->feManager->GetAddress(); } } // the remote address is unknown - return GetAddress(); + + if (!IsAssociated()) + { + return GetAddress(); + } + + // if this device has performed ML setup with an AP MLD, return the MLD address of this device + const auto linkIds = GetSetupLinkIds(); + NS_ASSERT(!linkIds.empty()); + const auto linkId = *linkIds.cbegin(); // a setup link + + if (GetLink(linkId).stationManager->GetMldAddress(GetBssid(linkId))) + { + return GetAddress(); + } + + // return the address of the link used to perform association with the AP + return GetLink(linkId).feManager->GetAddress(); } bool @@ -1411,83 +1428,73 @@ StaWifiMac::ReceiveAssocResp(Ptr mpdu, uint8_t linkId) return; } - // if this is an MLD, check if we can setup (other) links - if (GetNLinks() > 1) + // create a list of all local Link IDs. IDs are removed as we find a corresponding + // Per-STA Profile Subelements indicating successful association. Links with + // remaining IDs are not setup + std::list setupLinks; + for (const auto& [id, link] : GetLinks()) { - // create a list of all local Link IDs. IDs are removed as we find a corresponding - // Per-STA Profile Subelements indicating successful association. Links with - // remaining IDs are not setup - std::list setupLinks; - for (const auto& [id, link] : GetLinks()) - { - setupLinks.push_back(id); - } - if (assocResp.GetStatusCode().IsSuccess()) - { - setupLinks.remove(linkId); - } + setupLinks.push_back(id); + } + if (assocResp.GetStatusCode().IsSuccess()) + { + setupLinks.remove(linkId); + } - // if a Multi-Link Element is present, check its content - if (const auto& mle = assocResp.Get()) + // if a Multi-Link Element is present, this is an ML setup, hence check if we can setup (other) + // links + if (const auto& mle = assocResp.Get()) + { + NS_ABORT_MSG_IF(!GetLink(linkId).bssid.has_value(), + "The link on which the Association Response was received " + "is not a link we requested to setup"); + NS_ABORT_MSG_IF(linkId != mle->GetLinkIdInfo(), + "The link ID of the AP that transmitted the Association " + "Response does not match the stored link ID"); + NS_ABORT_MSG_IF(GetWifiRemoteStationManager(linkId)->GetMldAddress(hdr.GetAddr2()) != + mle->GetMldMacAddress(), + "The AP MLD MAC address in the received Multi-Link Element does not " + "match the address stored in the station manager for link " + << +linkId); + // process the Per-STA Profile Subelements in the Multi-Link Element + for (std::size_t elem = 0; elem < mle->GetNPerStaProfileSubelements(); elem++) { - NS_ABORT_MSG_IF(!GetLink(linkId).bssid.has_value(), - "The link on which the Association Response was received " - "is not a link we requested to setup"); - NS_ABORT_MSG_IF(linkId != mle->GetLinkIdInfo(), - "The link ID of the AP that transmitted the Association " - "Response does not match the stored link ID"); - NS_ABORT_MSG_IF(GetWifiRemoteStationManager(linkId)->GetMldAddress(hdr.GetAddr2()) != - mle->GetMldMacAddress(), + auto& perStaProfile = mle->GetPerStaProfile(elem); + uint8_t apLinkId = perStaProfile.GetLinkId(); + auto it = GetLinks().find(apLinkId); + uint8_t staLinkid = 0; + std::optional bssid; + NS_ABORT_MSG_IF(it == GetLinks().cend() || + !(bssid = GetLink((staLinkid = it->first)).bssid).has_value(), + "Setup for AP link ID " << apLinkId << " was not requested"); + NS_ABORT_MSG_IF(*bssid != perStaProfile.GetStaMacAddress(), + "The BSSID in the Per-STA Profile for link ID " + << +staLinkid << " does not match the stored BSSID"); + NS_ABORT_MSG_IF(GetWifiRemoteStationManager(staLinkid)->GetMldAddress( + perStaProfile.GetStaMacAddress()) != mle->GetMldMacAddress(), "The AP MLD MAC address in the received Multi-Link Element does not " "match the address stored in the station manager for link " - << +linkId); - // process the Per-STA Profile Subelements in the Multi-Link Element - for (std::size_t elem = 0; elem < mle->GetNPerStaProfileSubelements(); elem++) + << +staLinkid); + // process the Association Response contained in this Per-STA Profile + MgtAssocResponseHeader assoc = perStaProfile.GetAssocResponse(); + if (assoc.GetStatusCode().IsSuccess()) { - auto& perStaProfile = mle->GetPerStaProfile(elem); - uint8_t apLinkId = perStaProfile.GetLinkId(); - auto it = GetLinks().find(apLinkId); - uint8_t staLinkid = 0; - std::optional bssid; - NS_ABORT_MSG_IF(it == GetLinks().cend() || - !(bssid = GetLink((staLinkid = it->first)).bssid).has_value(), - "Setup for AP link ID " << apLinkId << " was not requested"); - NS_ABORT_MSG_IF(*bssid != perStaProfile.GetStaMacAddress(), - "The BSSID in the Per-STA Profile for link ID " - << +staLinkid << " does not match the stored BSSID"); - NS_ABORT_MSG_IF( - GetWifiRemoteStationManager(staLinkid)->GetMldAddress( - perStaProfile.GetStaMacAddress()) != mle->GetMldMacAddress(), - "The AP MLD MAC address in the received Multi-Link Element does not " - "match the address stored in the station manager for link " - << +staLinkid); - // process the Association Response contained in this Per-STA Profile - MgtAssocResponseHeader assoc = perStaProfile.GetAssocResponse(); - if (assoc.GetStatusCode().IsSuccess()) + NS_ABORT_MSG_IF(m_aid != 0 && m_aid != assoc.GetAssociationId(), + "AID should be the same for all the links"); + m_aid = assoc.GetAssociationId(); + NS_LOG_DEBUG("Setup on link " << staLinkid << " completed"); + UpdateApInfo(assoc, *bssid, *bssid, staLinkid); + SetBssid(*bssid, staLinkid); + m_setupCompleted(staLinkid, *bssid); + SetState(ASSOCIATED); + apMldAddress = GetWifiRemoteStationManager(staLinkid)->GetMldAddress(*bssid); + if (!m_linkUp.IsNull()) { - NS_ABORT_MSG_IF(m_aid != 0 && m_aid != assoc.GetAssociationId(), - "AID should be the same for all the links"); - m_aid = assoc.GetAssociationId(); - NS_LOG_DEBUG("Setup on link " << staLinkid << " completed"); - UpdateApInfo(assoc, *bssid, *bssid, staLinkid); - SetBssid(*bssid, staLinkid); - m_setupCompleted(staLinkid, *bssid); - SetState(ASSOCIATED); - apMldAddress = GetWifiRemoteStationManager(staLinkid)->GetMldAddress(*bssid); - if (!m_linkUp.IsNull()) - { - m_linkUp(); - } + m_linkUp(); } - // remove the ID of the link we setup - setupLinks.remove(staLinkid); } - } - // remaining links in setupLinks are not setup and hence must be disabled - for (const auto& id : setupLinks) - { - GetLink(id).bssid = std::nullopt; - GetLink(id).phy->SetOffMode(); + // remove the ID of the link we setup + setupLinks.remove(staLinkid); } if (apMldAddress) { @@ -1495,6 +1502,12 @@ StaWifiMac::ReceiveAssocResp(Ptr mpdu, uint8_t linkId) m_assocLogger(*apMldAddress); } } + // remaining links in setupLinks are not setup and hence must be disabled + for (const auto& id : setupLinks) + { + GetLink(id).bssid = std::nullopt; + GetLink(id).phy->SetOffMode(); + } // the station that associated with the AP may have dissociated and then associated again. // In this case, the station may store packets from the previous period in which it was diff --git a/src/wifi/model/wifi-assoc-manager.cc b/src/wifi/model/wifi-assoc-manager.cc index f311e2e66..a911a8173 100644 --- a/src/wifi/model/wifi-assoc-manager.cc +++ b/src/wifi/model/wifi-assoc-manager.cc @@ -250,7 +250,7 @@ WifiAssocManager::CanSetupMultiLink(OptMleConstRef& mle, OptRnrConstRef& rnr) { NS_LOG_FUNCTION(this); - if (m_mac->GetNLinks() == 1 || GetSortedList().empty()) + if (m_mac->GetAssocType() == WifiAssocType::LEGACY || GetSortedList().empty()) { return false; } diff --git a/src/wifi/model/wifi-mac.cc b/src/wifi/model/wifi-mac.cc index 10ddf723a..8cdf1da7c 100644 --- a/src/wifi/model/wifi-mac.cc +++ b/src/wifi/model/wifi-mac.cc @@ -1884,14 +1884,14 @@ WifiMac::GetLocalAddress(const Mac48Address& remoteAddr) const return m_address; } } - // we get here if no ML setup was established between this device and the remote device, - // i.e., they are not both multi-link devices + // we get here if no ML setup was established between this device and the remote device if (GetNLinks() == 1) { // this is a single link device return m_address; } - // this is an MLD (hence the remote device is single link or unknown) + // this is an MLD which performed legacy association with the remote device or the remote device + // is unknown return DoGetLocalAddress(remoteAddr); } diff --git a/src/wifi/test/wifi-mlo-test.cc b/src/wifi/test/wifi-mlo-test.cc index 6b150a887..50723885d 100644 --- a/src/wifi/test/wifi-mlo-test.cc +++ b/src/wifi/test/wifi-mlo-test.cc @@ -332,7 +332,9 @@ AidAssignmentTest::DoSetup() "Ssid", // first non-AP STA/MLD only starts associating SsidValue(Ssid(m_staDevices.GetN() == 0 ? "ns-3-ssid" : "default")), "ActiveProbing", - BooleanValue(false)); + BooleanValue(false), + "AssocType", + EnumValue(links.size() > 1 ? WifiAssocType::ML_SETUP : WifiAssocType::LEGACY)); auto staNode = CreateObject(); auto staDevice = wifi.Install(phyHelper, mac, staNode); @@ -423,6 +425,7 @@ MultiLinkOperationsTestBase::MultiLinkOperationsTestBase(const std::string& name m_staChannels(baseParams.staChannels), m_apChannels(baseParams.apChannels), m_fixedPhyBands(baseParams.fixedPhyBands), + m_assocType(baseParams.assocType), m_staMacs(nStations), m_nStations(nStations), m_lastAid(0), @@ -680,7 +683,9 @@ MultiLinkOperationsTestBase::DoSetup() "MaxMissedBeacons", UintegerValue(1e6), // do not deassociate "ActiveProbing", - BooleanValue(false)); + BooleanValue(false), + "AssocType", + EnumValue(m_assocType)); NetDeviceContainer staDevices = wifi.Install(staPhyHelper, mac, wifiStaNodes); @@ -2714,7 +2719,8 @@ ReleaseSeqNoAfterCtsTimeoutTest::ReleaseSeqNoAfterCtsTimeoutTest() 1, BaseParams{{"{36, 0, BAND_5GHZ, 0}", "{2, 0, BAND_2_4GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"}, {"{36, 0, BAND_5GHZ, 0}", "{2, 0, BAND_2_4GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"}, - {}}), + {}, + WifiAssocType::ML_SETUP}), m_nQosDataFrames(0), m_errorModel(CreateObject()), m_rtsCorrupted(false) @@ -2831,7 +2837,8 @@ StartSeqNoUpdateAfterAddBaTimeoutTest::StartSeqNoUpdateAfterAddBaTimeoutTest() 1, BaseParams{{"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"}, {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"}, - {}}), + {}, + WifiAssocType::ML_SETUP}), m_nQosDataCount(0), m_staErrorModel(CreateObject()) { @@ -3003,7 +3010,8 @@ WifiMultiLinkOperationsTestSuite::WifiMultiLinkOperationsTestSuite() MultiLinkOperationsTestBase::BaseParams{ {"{42, 80, BAND_5GHZ, 2}", "{5, 40, BAND_2_4GHZ, 0}", "{7, 80, BAND_6GHZ, 0}"}, {"{3, 40, BAND_2_4GHZ, 0}", "{15, 160, BAND_6GHZ, 7}", "{50, 160, BAND_5GHZ, 2}"}, - {}}, + {}, + WifiAssocType::ML_SETUP}, WifiScanType::PASSIVE, {0, 1, 2}, WifiTidToLinkMappingNegSupport::ANY_LINK_SET, @@ -3012,16 +3020,13 @@ WifiMultiLinkOperationsTestSuite::WifiMultiLinkOperationsTestSuite() false), TestCase::Duration::QUICK); - for (const auto& [baseParams, - setupLinks, - apNegSupport, - dlTidLinkMapping, - ulTidLinkMapping] : + for (const auto& [baseParams, setupLinks, apNegSupport, dlTidLinkMapping, ulTidLinkMapping] : {// matching channels: setup all links ParamsTuple( {{"{36, 0, BAND_5GHZ, 0}", "{2, 0, BAND_2_4GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"}, {"{36, 0, BAND_5GHZ, 0}", "{2, 0, BAND_2_4GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"}, - {}}, + {}, + WifiAssocType::ML_SETUP}, {0, 1, 2}, WifiTidToLinkMappingNegSupport::NOT_SUPPORTED, // AP MLD does not support TID-to-Link // Mapping negotiation @@ -3031,7 +3036,8 @@ WifiMultiLinkOperationsTestSuite::WifiMultiLinkOperationsTestSuite() // non-matching channels, matching PHY bands: setup all links ParamsTuple({{"{108, 0, BAND_5GHZ, 0}", "{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"}, {"{36, 0, BAND_5GHZ, 0}", "{120, 0, BAND_5GHZ, 0}", "{5, 0, BAND_6GHZ, 0}"}, - {}}, + {}, + WifiAssocType::ML_SETUP}, {0, 1, 2}, WifiTidToLinkMappingNegSupport::SAME_LINK_SET, // AP MLD does not support // distinct link sets for TIDs @@ -3040,7 +3046,8 @@ WifiMultiLinkOperationsTestSuite::WifiMultiLinkOperationsTestSuite() // non-AP MLD switches band on some links to setup 3 links ParamsTuple({{"{2, 0, BAND_2_4GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{36, 0, BAND_5GHZ, 0}"}, {"{36, 0, BAND_5GHZ, 0}", "{9, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"}, - {}}, + {}, + WifiAssocType::ML_SETUP}, {0, 1, 2}, WifiTidToLinkMappingNegSupport::ANY_LINK_SET, "0,1,2,3 0; 4,5,6,7 1,2", // frames of two TIDs are generated @@ -3051,7 +3058,8 @@ WifiMultiLinkOperationsTestSuite::WifiMultiLinkOperationsTestSuite() ParamsTuple( {{"{2, 0, BAND_2_4GHZ, 0}", "{36, 0, BAND_5GHZ, 0}", "{8, 20, BAND_2_4GHZ, 0}"}, {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"}, - {0}}, + {0}, + WifiAssocType::ML_SETUP}, {0, 1}, WifiTidToLinkMappingNegSupport::SAME_LINK_SET, // AP MLD does not support distinct // link sets for TIDs @@ -3063,7 +3071,8 @@ WifiMultiLinkOperationsTestSuite::WifiMultiLinkOperationsTestSuite() ParamsTuple( {{"{2, 0, BAND_2_4GHZ, 0}", "{36, 0, BAND_5GHZ, 0}", "{8, 20, BAND_2_4GHZ, 0}"}, {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"}, - {0, 1}}, + {0, 1}, + WifiAssocType::ML_SETUP}, {0, 1}, WifiTidToLinkMappingNegSupport::ANY_LINK_SET, "0,1,2,3 1", @@ -3075,7 +3084,8 @@ WifiMultiLinkOperationsTestSuite::WifiMultiLinkOperationsTestSuite() // hence 2 links are setup by switching channel (not band) on the third link ParamsTuple({{"{2, 0, BAND_2_4GHZ, 0}", "{36, 0, BAND_5GHZ, 0}", "{60, 0, BAND_5GHZ, 0}"}, {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"}, - {0, 1, 2}}, + {0, 1, 2}, + WifiAssocType::ML_SETUP}, {0, 2}, WifiTidToLinkMappingNegSupport::ANY_LINK_SET, "", @@ -3085,7 +3095,8 @@ WifiMultiLinkOperationsTestSuite::WifiMultiLinkOperationsTestSuite() // an AP operating on the same channel; hence one link only is setup ParamsTuple({{"{2, 0, BAND_2_4GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"}, {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"}, - {0, 1}}, + {0, 1}, + WifiAssocType::ML_SETUP}, {2}, WifiTidToLinkMappingNegSupport::ANY_LINK_SET, "", @@ -3093,23 +3104,26 @@ WifiMultiLinkOperationsTestSuite::WifiMultiLinkOperationsTestSuite() // non-AP MLD has only two STAs and setups two links ParamsTuple({{"{2, 0, BAND_2_4GHZ, 0}", "{36, 0, BAND_5GHZ, 0}"}, {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"}, - {}}, + {}, + WifiAssocType::ML_SETUP}, {1, 0}, WifiTidToLinkMappingNegSupport::ANY_LINK_SET, "0,1,2,3 1", ""), - // single link non-AP STA associates with an AP affiliated with an AP MLD + // single link non-AP STA performs legacy association with an AP affiliated with an AP MLD ParamsTuple({{"{120, 0, BAND_5GHZ, 0}"}, {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"}, - {}}, - {2}, // link ID of AP MLD only (non-AP STA is single link) + {}, + WifiAssocType::LEGACY}, + {2}, // link ID of AP MLD only (non-AP STA performs legacy association) WifiTidToLinkMappingNegSupport::ANY_LINK_SET, "", ""), // a STA affiliated with a non-AP MLD associates with a single link AP ParamsTuple({{"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"}, {"{120, 0, BAND_5GHZ, 0}"}, - {}}, + {}, + WifiAssocType::ML_SETUP}, {2}, // link ID of non-AP MLD only (AP is single link) WifiTidToLinkMappingNegSupport::NOT_SUPPORTED, "0,1,2,3 0,1; 4,5,6,7 0,1", // ignored by single link AP diff --git a/src/wifi/test/wifi-mlo-test.h b/src/wifi/test/wifi-mlo-test.h index c3325a66c..e9cb691a9 100644 --- a/src/wifi/test/wifi-mlo-test.h +++ b/src/wifi/test/wifi-mlo-test.h @@ -193,7 +193,8 @@ class MultiLinkOperationsTestBase : public TestCase std::vector apChannels; //!< the strings specifying the operating channels for the AP MLD std::vector - fixedPhyBands; //!< list of IDs of non-AP MLD PHYs that cannot switch band + fixedPhyBands; //!< list of IDs of non-AP MLD PHYs that cannot switch band + WifiAssocType assocType{}; //!< type of the association procedure used by non-AP devices }; /** @@ -295,13 +296,14 @@ class MultiLinkOperationsTestBase : public TestCase const std::vector m_staChannels; ///< strings specifying channels for STA const std::vector m_apChannels; ///< strings specifying channels for AP const std::vector m_fixedPhyBands; ///< links on non-AP MLD with fixed PHY band - Ptr m_apMac; ///< AP wifi MAC - std::vector> m_staMacs; ///< STA wifi MACs - uint8_t m_nStations; ///< number of stations to create - uint16_t m_lastAid; ///< AID of last associated station - Time m_duration{Seconds(1)}; ///< simulation duration - std::vector m_rxPkts; ///< number of packets received at application layer - ///< by each node (index is node ID) + WifiAssocType m_assocType; ///< type of the association procedure used by non-AP devices + Ptr m_apMac; ///< AP wifi MAC + std::vector> m_staMacs; ///< STA wifi MACs + uint8_t m_nStations; ///< number of stations to create + uint16_t m_lastAid; ///< AID of last associated station + Time m_duration{Seconds(1)}; ///< simulation duration + std::vector m_rxPkts; ///< number of packets received at application layer + ///< by each node (index is node ID) /** * Reset the given PHY helper, use the given strings to set the ChannelSettings