wifi: Fix assignment of unique AIDs to non-AP STAs/MLDs

Reported by Sharan Naribole that SLDs operating on distinct
links could be assigned the same AID by an AP MLD
This commit is contained in:
Stefano Avallone
2024-05-27 22:54:07 +02:00
committed by Stefano Avallone
parent b5f649297e
commit 1f23f4ea33
3 changed files with 219 additions and 86 deletions

View File

@@ -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<uint16_t> aids;
std::map<uint8_t /* link ID */, uint16_t /* AID */> 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<uint8_t> 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<uint8_t> 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;

View File

@@ -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<uint8_t> linkIds);
uint16_t GetNextAssociationId() const;
Ptr<Txop> m_beaconTxop; //!< Dedicated Txop for beacons
bool m_enableBeaconGeneration; //!< Flag whether beacons are being generated

View File

@@ -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<std::set<uint8_t>>& 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<StaWifiMac> staMac, Mac48Address /* apAddr */);
const std::vector<std::string> m_linkChannels; //!< channels for all AP links
const std::vector<std::set<uint8_t>> 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<std::set<uint8_t>>& 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<MultiModelSpectrumChannel>();
// 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<Node>();
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<ListPositionAllocator>();
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<WifiNetDevice>(m_staDevices.Get(i))->GetMac();
mac->TraceConnectWithoutContext(
"Assoc",
MakeCallback(&AidAssignmentTest::SetSsid, this).Bind(DynamicCast<StaWifiMac>(mac)));
}
}
void
AidAssignmentTest::SetSsid(Ptr<StaWifiMac> 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<int>(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<WifiNetDevice>(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<std::set<uint8_t>>{{0, 1, 2}, {1, 2}, {0, 1}, {0, 2}, {0}, {1}, {2}}),
TestCase::Duration::QUICK);
for (const auto& [baseParams,
setupLinks,