wifi: Add support for SLDs performing ML setup and MLDs performing legacy association

This commit is contained in:
Stefano Avallone
2024-11-06 19:56:24 +01:00
parent cd3edc44b3
commit a69155655a
8 changed files with 772 additions and 106 deletions

View File

@@ -0,0 +1,633 @@
/*
* Copyright (c) 2024 Universita' degli Studi di Napoli Federico II
*
* SPDX-License-Identifier: GPL-2.0-only
*
* Author: Stefano Avallone <stavallo@unina.it>
*/
#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 <array>
#include <list>
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<std::string>& apChannels,
const std::vector<std::string>& firstStaChannels,
const std::vector<std::string>& secondStaChannels,
WifiTrafficPattern trafficPattern,
bool amsduAggr);
protected:
void DoSetup() override;
void Transmit(Ptr<WifiMac> 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<std::string> 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<UdpServer> 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<std::string>& apChannels,
const std::vector<std::string>& firstStaChannels,
const std::vector<std::string>& 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<uint8_t>(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<MultiModelSpectrumChannel>()},
{WIFI_SPECTRUM_5_GHZ, CreateObject<MultiModelSpectrumChannel>()},
{WIFI_SPECTRUM_6_GHZ, CreateObject<MultiModelSpectrumChannel>()}};
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<ListPositionAllocator> positionAlloc = CreateObject<ListPositionAllocator>();
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<ApWifiMac>(DynamicCast<WifiNetDevice>(apDevices.Get(0))->GetMac());
for (uint8_t i = 0; i < m_nStations; i++)
{
m_staMacs[i] =
DynamicCast<StaWifiMac>(DynamicCast<WifiNetDevice>(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<UdpServer>(serverApps.Get(0));
break;
case WifiTrafficPattern::AP_TO_STA:
m_sink = DynamicCast<UdpServer>(serverApps.Get(1));
break;
case WifiTrafficPattern::STA_TO_STA:
m_sink = DynamicCast<UdpServer>(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<WifiMac> 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<uint8_t>(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<WifiMac> 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<Ptr<const Packet>> 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<WifiMac> 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<uint8_t>(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<WifiMac> 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<uint8_t>(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<uint8_t>(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<uint8_t>(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<std::vector<std::string>, 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

View File

@@ -111,7 +111,9 @@ EhtFrameExchangeManager::CreateAliasIfNeeded(Ptr<WifiMpdu> 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<StaWifiMac>(m_mac);
!mpdu->GetHeader().IsQosData() ||
(staMac ? (staMac->GetAssocType() == WifiAssocType::LEGACY) : (m_mac->GetNLinks() == 1)) ||
mpdu->GetHeader().GetAddr1().IsGroup() ||
!GetWifiRemoteStationManager()->GetMldAddress(mpdu->GetHeader().GetAddr1()))
{

View File

@@ -188,6 +188,9 @@ EmlsrManager::SetWifiMac(Ptr<StaWifiMac> 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<uint8_t>& linkIds)
std::copy(linkIds.cbegin(), linkIds.cend(), std::ostream_iterator<uint16_t>(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)
{

View File

@@ -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<const WifiMpdu> 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<uint8_t> 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<uint8_t> 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<MultiLinkElement>())
// 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<MultiLinkElement>())
{
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<Mac48Address> 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<Mac48Address> 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<const WifiMpdu> 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

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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<Node>();
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<ListErrorModel>()),
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<ListErrorModel>())
{
@@ -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

View File

@@ -193,7 +193,8 @@ class MultiLinkOperationsTestBase : public TestCase
std::vector<std::string>
apChannels; //!< the strings specifying the operating channels for the AP MLD
std::vector<uint8_t>
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<std::string> m_staChannels; ///< strings specifying channels for STA
const std::vector<std::string> m_apChannels; ///< strings specifying channels for AP
const std::vector<uint8_t> m_fixedPhyBands; ///< links on non-AP MLD with fixed PHY band
Ptr<ApWifiMac> m_apMac; ///< AP wifi MAC
std::vector<Ptr<StaWifiMac>> 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<std::size_t> 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<ApWifiMac> m_apMac; ///< AP wifi MAC
std::vector<Ptr<StaWifiMac>> 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<std::size_t> 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