diff --git a/src/wifi/model/ap-wifi-mac.cc b/src/wifi/model/ap-wifi-mac.cc index 7a802fb34..096f397fc 100644 --- a/src/wifi/model/ap-wifi-mac.cc +++ b/src/wifi/model/ap-wifi-mac.cc @@ -1249,105 +1249,58 @@ ApWifiMac::SetAid(MgtAssocResponseHeader& assoc, const LinkIdStaAddrMap& linkIdS return; } - // check if AIDs are already allocated to the STAs that are associating + const auto& [linkId, staAddr] = *linkIdStaAddrMap.cbegin(); + const auto addr = GetWifiRemoteStationManager(linkId)->GetMldAddress(staAddr).value_or(staAddr); + + // check if an AID is already allocated to the device that is associating std::set aids; - std::map linkIdAidMap; - for (const auto& [id, staAddr] : linkIdStaAddrMap) + for (const auto& [id, link] : GetLinks()) { - for (const auto& [aid, addr] : GetLink(id).staList) + if (const auto aid = link->stationManager->GetAssociationId(addr); aid != SU_STA_ID) { - if (addr == staAddr) - { - aids.insert(aid); - linkIdAidMap[id] = aid; - break; - } + aids.insert(aid); } } - // check if an AID already assigned to an STA can be assigned to all other STAs - // affiliated with the non-AP MLD we are associating with - while (!aids.empty()) - { - const uint16_t aid = *aids.begin(); - bool good = true; + NS_ABORT_MSG_IF(aids.size() > 1, addr << " cannot have more than one AID assigned"); - for (const auto& [id, staAddr] : linkIdStaAddrMap) - { - if (auto it = GetLink(id).staList.find(aid); - it != GetLink(id).staList.end() && it->second != staAddr) - { - // the AID is already assigned to an STA other than the one affiliated - // with the non-AP MLD we are associating with - aids.erase(aids.begin()); - good = false; - break; - } - } - - if (good) - { - break; - } - } - - uint16_t aid = 0; - - if (!aids.empty()) - { - // one of the AIDs already assigned to an STA can be assigned to all the other - // STAs affiliated with the non-AP MLD we are associating with - aid = *aids.begin(); - } - else - { - std::list linkIds; - std::transform(linkIdStaAddrMap.cbegin(), - linkIdStaAddrMap.cend(), - std::back_inserter(linkIds), - [](auto&& linkIdStaAddrPair) { return linkIdStaAddrPair.first; }); - aid = GetNextAssociationId(linkIds); - } + const auto aid = aids.empty() ? GetNextAssociationId() : *aids.cbegin(); // store the MLD or link address in the AID-to-address map - const auto& [linkId, staAddr] = *linkIdStaAddrMap.cbegin(); - m_aidToMldOrLinkAddress[aid] = - GetWifiRemoteStationManager(linkId)->GetMldAddress(staAddr).value_or(staAddr); + const auto [it, inserted] = m_aidToMldOrLinkAddress.emplace(aid, addr); + + NS_ABORT_MSG_IF(!inserted, "AID " << aid << " already present, cannot be assigned to " << addr); for (const auto& [id, staAddr] : linkIdStaAddrMap) { - auto remoteStationManager = GetWifiRemoteStationManager(id); auto& link = GetLink(id); - if (auto it = linkIdAidMap.find(id); it == linkIdAidMap.end() || it->second != aid) + if (const auto [it, inserted] = link.staList.emplace(aid, staAddr); inserted) { - // the STA on this link has no AID assigned or has a different AID assigned - link.staList.insert(std::make_pair(aid, staAddr)); + // the STA on this link had no AID assigned m_assocLogger(aid, staAddr); - remoteStationManager->SetAssociationId(staAddr, aid); + link.stationManager->SetAssociationId(staAddr, aid); - if (it == linkIdAidMap.end()) + if (link.stationManager->GetDsssSupported(staAddr) && + !link.stationManager->GetErpOfdmSupported(staAddr)) { - // the STA on this link had no AID assigned - if (remoteStationManager->GetDsssSupported(staAddr) && - !remoteStationManager->GetErpOfdmSupported(staAddr)) - { - link.numNonErpStations++; - } - if (!remoteStationManager->GetHtSupported(staAddr) && - !remoteStationManager->GetStationHe6GhzCapabilities(staAddr)) - { - link.numNonHtStations++; - } - UpdateShortSlotTimeEnabled(id); - UpdateShortPreambleEnabled(id); + link.numNonErpStations++; } - else + if (!link.stationManager->GetHtSupported(staAddr) && + !link.stationManager->GetStationHe6GhzCapabilities(staAddr)) { - // the STA on this link had a different AID assigned - link.staList.erase(it->second); // free the previous AID + link.numNonHtStations++; } + UpdateShortSlotTimeEnabled(id); + UpdateShortPreambleEnabled(id); + } + else + { + // the STA on this link had an AID assigned + NS_ABORT_MSG_IF(it->first != aid, + "AID " << it->first << " already assigned to " << staAddr + << ", could not assign " << aid); } } @@ -2527,14 +2480,15 @@ ApWifiMac::GetUseNonErpProtection(uint8_t linkId) const } uint16_t -ApWifiMac::GetNextAssociationId(std::list linkIds) +ApWifiMac::GetNextAssociationId() const { - // Return the first AID value between 1 and 2007 that is free for all the given links - for (uint16_t nextAid = 1; nextAid <= 2007; nextAid++) + const auto& links = GetLinks(); + + // Return the first AID value between 1 and 2007 that is free for all the links + for (uint16_t nextAid = 1; nextAid <= 2007; ++nextAid) { - if (std::all_of(linkIds.begin(), linkIds.end(), [&](auto&& linkId) { - auto& staList = GetLink(linkId).staList; - return !staList.contains(nextAid); + if (std::none_of(links.cbegin(), links.cend(), [&](auto&& idLinkPair) { + return GetStaList(idLinkPair.first).contains(nextAid); })) { return nextAid; diff --git a/src/wifi/model/ap-wifi-mac.h b/src/wifi/model/ap-wifi-mac.h index 1d6dcbb17..b7eec0353 100644 --- a/src/wifi/model/ap-wifi-mac.h +++ b/src/wifi/model/ap-wifi-mac.h @@ -565,10 +565,9 @@ class ApWifiMac : public WifiMac void DoInitialize() override; /** - * \param linkIds the IDs of the links for which the next Association ID is requested - * \return the next Association ID to be allocated by the AP on the given links + * \return the next Association ID to be allocated by the AP */ - uint16_t GetNextAssociationId(std::list linkIds); + uint16_t GetNextAssociationId() const; Ptr m_beaconTxop; //!< Dedicated Txop for beacons bool m_enableBeaconGeneration; //!< Flag whether beacons are being generated diff --git a/src/wifi/test/wifi-mlo-test.cc b/src/wifi/test/wifi-mlo-test.cc index 86d9dd9ad..48650b0da 100644 --- a/src/wifi/test/wifi-mlo-test.cc +++ b/src/wifi/test/wifi-mlo-test.cc @@ -309,6 +309,182 @@ MldSwapLinksTest::DoRun() RunOne("Move all links to a new set of IDs", 2, {{0, 2}, {1, 3}}, {{2, 0}, {3, 1}}); } +/** + * \ingroup wifi-test + * \ingroup tests + * + * Test that the AIDs that an AP MLD assigns to SLDs and MLDs are all unique. + */ +class AidAssignmentTest : public TestCase +{ + public: + /** + * Constructor. + * + * \param linkIds A vector specifying the set of link IDs each STA will setup + */ + AidAssignmentTest(const std::vector>& linkIds); + + private: + void DoSetup() override; + void DoRun() override; + + /** + * Set the SSID on the next station that needs to start the association procedure. + * This method is triggered every time a STA completes its association. + * + * \param staMac the MAC of the STA that completed association + */ + void SetSsid(Ptr staMac, Mac48Address /* apAddr */); + + const std::vector m_linkChannels; //!< channels for all AP links + const std::vector> m_linkIds; //!< link IDs for all non-AP STAs/MLDs + NetDeviceContainer m_staDevices; //!< non-AP STAs/MLDs devices + uint16_t m_expectedAid; //!< expected AID for current non-AP STA/MLD +}; + +AidAssignmentTest::AidAssignmentTest(const std::vector>& linkIds) + : TestCase("Test the assignment of AIDs"), + m_linkChannels({"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{2, 0, BAND_2_4GHZ, 0}"}), + m_linkIds(linkIds), + m_expectedAid(1) // AID for first station +{ +} + +void +AidAssignmentTest::DoSetup() +{ + RngSeedManager::SetSeed(1); + RngSeedManager::SetRun(1); + int64_t streamNumber{1}; + + NodeContainer wifiApNode; + wifiApNode.Create(1); + NodeContainer wifiStaNodes; + + WifiHelper wifi; + wifi.SetStandard(WIFI_STANDARD_80211be); + wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager", + "DataMode", + StringValue("EhtMcs0"), + "ControlMode", + StringValue("HtMcs0")); + + auto channel = CreateObject(); + + // AP MLD + SpectrumWifiPhyHelper phyHelper(3); + phyHelper.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11_RADIO); + uint8_t linkId = 0; + for (const auto& str : m_linkChannels) + { + phyHelper.Set(linkId++, "ChannelSettings", StringValue(str)); + } + phyHelper.SetChannel(channel); + + WifiMacHelper mac; + mac.SetType("ns3::ApWifiMac", + "Ssid", + SsidValue(Ssid("ns-3-ssid")), + "BeaconGeneration", + BooleanValue(true)); + + auto apDevice = wifi.Install(phyHelper, mac, wifiApNode); + + // non-AP STAs/MLDs + for (const auto& links : m_linkIds) + { + phyHelper = SpectrumWifiPhyHelper(links.size()); + phyHelper.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11_RADIO); + linkId = 0; + for (const auto& id : links) + { + phyHelper.Set(linkId++, "ChannelSettings", StringValue(m_linkChannels.at(id))); + } + phyHelper.SetChannel(channel); + phyHelper.Set("FixedPhyBand", BooleanValue(true)); + + WifiMacHelper mac; + mac.SetType("ns3::StaWifiMac", + "Ssid", // first non-AP STA/MLD only starts associating + SsidValue(Ssid(m_staDevices.GetN() == 0 ? "ns-3-ssid" : "default")), + "ActiveProbing", + BooleanValue(false)); + + auto staNode = CreateObject(); + auto staDevice = wifi.Install(phyHelper, mac, staNode); + wifiStaNodes.Add(staNode); + m_staDevices.Add(staDevice); + } + + // Assign fixed streams to random variables in use + streamNumber += WifiHelper::AssignStreams(apDevice, streamNumber); + streamNumber += WifiHelper::AssignStreams(m_staDevices, streamNumber); + + auto positionAlloc = CreateObject(); + positionAlloc->Add(Vector(0.0, 0.0, 0.0)); + MobilityHelper mobility; + mobility.SetPositionAllocator(positionAlloc); + mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel"); + mobility.Install(wifiApNode); + mobility.Install(wifiStaNodes); + + for (uint32_t i = 0; i < m_staDevices.GetN(); ++i) + { + auto mac = StaticCast(m_staDevices.Get(i))->GetMac(); + mac->TraceConnectWithoutContext( + "Assoc", + MakeCallback(&AidAssignmentTest::SetSsid, this).Bind(DynamicCast(mac))); + } +} + +void +AidAssignmentTest::SetSsid(Ptr staMac, Mac48Address /* apAddr */) +{ + const auto aid = staMac->GetAssociationId(); + + std::stringstream linksStr; + const auto setupLinks = staMac->GetSetupLinkIds(); + std::copy(setupLinks.cbegin(), setupLinks.cend(), std::ostream_iterator(linksStr, " ")); + + NS_LOG_INFO("STA " << staMac->GetAddress() << " associated with AID " << aid << " links " + << linksStr.str()); + + NS_TEST_EXPECT_MSG_EQ(aid, m_expectedAid, "Unexpected AID for STA " << staMac->GetAddress()); + // For non-AP MLDs, check that the requested links have been setup (for non-AP STAs, link ID + // as seen by the non-AP STAs is always zero and could not match link ID as seen by the AP MLD) + if (m_linkIds.at(aid - 1).size() > 1) + { + NS_TEST_EXPECT_MSG_EQ((staMac->GetSetupLinkIds() == m_linkIds.at(aid - 1)), + true, + "Unexpected set of setup links " << linksStr.str()); + } + + if (m_expectedAid < m_staDevices.GetN()) + { + // let the next STA associate with the AP + StaticCast(m_staDevices.Get(m_expectedAid)) + ->GetMac() + ->SetSsid(Ssid("ns-3-ssid")); + ++m_expectedAid; + } + else + { + Simulator::Stop(MilliSeconds(5)); // allow sending Ack response to Association Response + } +} + +void +AidAssignmentTest::DoRun() +{ + Simulator::Stop(Seconds(5)); // simulation will stop earlier if all STAs complete association + Simulator::Run(); + + NS_TEST_EXPECT_MSG_EQ(m_expectedAid, m_staDevices.GetN(), "Not all STAs completed association"); + + Simulator::Destroy(); +} + /** * \ingroup wifi-test * \ingroup tests @@ -3242,6 +3418,10 @@ WifiMultiLinkOperationsTestSuite::WifiMultiLinkOperationsTestSuite() AddTestCase(new GetRnrLinkInfoTest(), TestCase::Duration::QUICK); AddTestCase(new MldSwapLinksTest(), TestCase::Duration::QUICK); + AddTestCase( + new AidAssignmentTest( + std::vector>{{0, 1, 2}, {1, 2}, {0, 1}, {0, 2}, {0}, {1}, {2}}), + TestCase::Duration::QUICK); for (const auto& [baseParams, setupLinks,