A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
wifi-mlo-test.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2022 Universita' degli Studi di Napoli Federico II
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 * Author: Stefano Avallone <stavallo@unina.it>
7 */
8
9#include "wifi-mlo-test.h"
10
11#include "ns3/config.h"
12#include "ns3/eht-configuration.h"
13#include "ns3/ht-frame-exchange-manager.h"
14#include "ns3/log.h"
15#include "ns3/mgt-action-headers.h"
16#include "ns3/mgt-headers.h"
17#include "ns3/mobility-helper.h"
18#include "ns3/multi-link-element.h"
19#include "ns3/node-list.h"
20#include "ns3/packet-socket-helper.h"
21#include "ns3/packet.h"
22#include "ns3/pointer.h"
23#include "ns3/rng-seed-manager.h"
24#include "ns3/rr-multi-user-scheduler.h"
25#include "ns3/spectrum-wifi-phy.h"
26#include "ns3/string.h"
27#include "ns3/vht-configuration.h"
28#include "ns3/wifi-acknowledgment.h"
29#include "ns3/wifi-assoc-manager.h"
30#include "ns3/wifi-mac-header.h"
31#include "ns3/wifi-mac-queue.h"
32#include "ns3/wifi-net-device.h"
33#include "ns3/wifi-protection.h"
34
35#include <algorithm>
36#include <array>
37#include <iomanip>
38#include <sstream>
39#include <tuple>
40
41using namespace ns3;
42
43NS_LOG_COMPONENT_DEFINE("WifiMloTest");
44
46 : TestCase("Check the implementation of WifiAssocManager::GetNextAffiliatedAp()")
47{
48}
49
50void
52{
54 std::size_t nbrId;
55 std::size_t tbttId;
56
57 // Add a first Neighbor AP Information field without MLD Parameters
59 nbrId = rnr.GetNNbrApInfoFields() - 1;
60
61 rnr.AddTbttInformationField(nbrId);
62 rnr.AddTbttInformationField(nbrId);
63
64 // Add a second Neighbor AP Information field with MLD Parameters; the first
65 // TBTT Information field is related to an AP affiliated to the same AP MLD
66 // as the reported AP; the second TBTT Information field is not (it does not
67 // make sense that two APs affiliated to the same AP MLD are using the same
68 // channel).
70 nbrId = rnr.GetNNbrApInfoFields() - 1;
71
72 rnr.AddTbttInformationField(nbrId);
73 tbttId = rnr.GetNTbttInformationFields(nbrId) - 1;
74 rnr.SetMldParameters(nbrId, tbttId, {0, 0, 0, 1, 1});
75
76 rnr.AddTbttInformationField(nbrId);
77 tbttId = rnr.GetNTbttInformationFields(nbrId) - 1;
78 rnr.SetMldParameters(nbrId, tbttId, {5, 0, 0, 1, 0});
79
80 // Add a third Neighbor AP Information field with MLD Parameters; none of the
81 // TBTT Information fields is related to an AP affiliated to the same AP MLD
82 // as the reported AP.
84 nbrId = rnr.GetNNbrApInfoFields() - 1;
85
86 rnr.AddTbttInformationField(nbrId);
87 tbttId = rnr.GetNTbttInformationFields(nbrId) - 1;
88 rnr.SetMldParameters(nbrId, tbttId, {3, 0, 0, 0, 1});
89
90 rnr.AddTbttInformationField(nbrId);
91 tbttId = rnr.GetNTbttInformationFields(nbrId) - 1;
92 rnr.SetMldParameters(nbrId, tbttId, {4, 0, 0, 0, 0});
93
94 // Add a fourth Neighbor AP Information field with MLD Parameters; the first
95 // TBTT Information field is not related to an AP affiliated to the same AP MLD
96 // as the reported AP; the second TBTT Information field is.
98 nbrId = rnr.GetNNbrApInfoFields() - 1;
99
100 rnr.AddTbttInformationField(nbrId);
101 tbttId = rnr.GetNTbttInformationFields(nbrId) - 1;
102 rnr.SetMldParameters(nbrId, tbttId, {6, 0, 0, 1, 1});
103
104 rnr.AddTbttInformationField(nbrId);
105 tbttId = rnr.GetNTbttInformationFields(nbrId) - 1;
106 rnr.SetMldParameters(nbrId, tbttId, {0, 0, 0, 0, 0});
107
108 // check implementation of WifiAssocManager::GetNextAffiliatedAp()
109 auto ret = WifiAssocManager::GetNextAffiliatedAp(rnr, 0);
110
111 NS_TEST_EXPECT_MSG_EQ(ret.has_value(), true, "Expected to find a suitable reported AP");
112 NS_TEST_EXPECT_MSG_EQ(ret->m_nbrApInfoId, 1, "Unexpected neighbor ID of the first reported AP");
113 NS_TEST_EXPECT_MSG_EQ(ret->m_tbttInfoFieldId, 0, "Unexpected tbtt ID of the first reported AP");
114
115 ret = WifiAssocManager::GetNextAffiliatedAp(rnr, ret->m_nbrApInfoId + 1);
116
117 NS_TEST_EXPECT_MSG_EQ(ret.has_value(), true, "Expected to find a second suitable reported AP");
118 NS_TEST_EXPECT_MSG_EQ(ret->m_nbrApInfoId,
119 3,
120 "Unexpected neighbor ID of the second reported AP");
121 NS_TEST_EXPECT_MSG_EQ(ret->m_tbttInfoFieldId,
122 1,
123 "Unexpected tbtt ID of the second reported AP");
124
125 ret = WifiAssocManager::GetNextAffiliatedAp(rnr, ret->m_nbrApInfoId + 1);
126
127 NS_TEST_EXPECT_MSG_EQ(ret.has_value(),
128 false,
129 "Did not expect to find a third suitable reported AP");
130
131 // check implementation of WifiAssocManager::GetAllAffiliatedAps()
132 auto allAps = WifiAssocManager::GetAllAffiliatedAps(rnr);
133
134 NS_TEST_EXPECT_MSG_EQ(allAps.size(), 2, "Expected to find two suitable reported APs");
135
136 auto apIt = allAps.begin();
137 NS_TEST_EXPECT_MSG_EQ(apIt->m_nbrApInfoId,
138 1,
139 "Unexpected neighbor ID of the first reported AP");
140 NS_TEST_EXPECT_MSG_EQ(apIt->m_tbttInfoFieldId,
141 0,
142 "Unexpected tbtt ID of the first reported AP");
143
144 apIt++;
145 NS_TEST_EXPECT_MSG_EQ(apIt->m_nbrApInfoId,
146 3,
147 "Unexpected neighbor ID of the second reported AP");
148 NS_TEST_EXPECT_MSG_EQ(apIt->m_tbttInfoFieldId,
149 1,
150 "Unexpected tbtt ID of the second reported AP");
151}
152
153TypeId
155{
156 static TypeId tid = TypeId("ns3::TestWifiMac")
158 .SetGroupName("Wifi")
159 .AddConstructor<TestWifiMac>();
160 return tid;
161}
162
164 : TestCase("Test the WifiMac::SwapLinks() method")
165{
166}
167
168void
170 std::size_t nLinks,
171 const std::map<uint8_t, uint8_t>& links,
172 const std::map<uint8_t, uint8_t>& expected)
173{
174 auto mac = CreateObjectWithAttributes<TestWifiMac>("QosSupported",
175 BooleanValue(false),
176 "Txop",
178
179 std::vector<Ptr<WifiPhy>> phys;
180 std::vector<Ptr<FrameExchangeManager>> feManagers;
181 std::vector<Ptr<WifiRemoteStationManager>> rsManagers;
182
183 for (std::size_t i = 0; i < nLinks; ++i)
184 {
186 phy->SetPhyId(i);
187 phys.emplace_back(phy);
188 feManagers.emplace_back(CreateObject<TestFrameExchangeManager>());
189 rsManagers.emplace_back(CreateObject<TestRemoteStationManager>());
190 }
191 mac->SetWifiPhys(phys); // create links containing the given PHYs
192 mac->SetFrameExchangeManagers(feManagers);
193 mac->SetWifiRemoteStationManagers(rsManagers);
194 mac->GetTxop()->SetWifiMac(mac);
195
196 // set CWmin of each Txop LinkEntity to the link ID, so that we can check where it has moved
197 for (std::size_t id = 0; id < nLinks; ++id)
198 {
199 mac->GetTxop()->SetMinCw(id, id);
200 }
201
202 mac->SwapLinks(links);
203
204 NS_TEST_EXPECT_MSG_EQ(mac->GetNLinks(), nLinks, "Number of links changed after swapping");
205
206 for (const auto& [linkId, phyId] : expected)
207 {
208 NS_TEST_ASSERT_MSG_EQ(mac->GetLinks().contains(linkId),
209 true,
210 "Link ID " << +linkId << " does not exist");
211
212 NS_TEST_ASSERT_MSG_LT(+phyId, nLinks, "Invalid PHY ID");
213
214 // the id of the PHY operating on a link is the original ID of the link
215 NS_TEST_EXPECT_MSG_EQ(+mac->GetWifiPhy(linkId)->GetPhyId(),
216 +phyId,
217 text << ": Link " << +phyId << " has not been moved to link "
218 << +linkId);
219
221 +DynamicCast<TestFrameExchangeManager>(mac->GetFrameExchangeManager(linkId))
222 ->GetLinkId(),
223 +linkId,
224 text << ": Link ID stored by FrameExchangeManager has not been updated");
225
227 +DynamicCast<TestRemoteStationManager>(mac->GetWifiRemoteStationManager(linkId))
228 ->GetLinkId(),
229 +linkId,
230 text << ": Link ID stored by RemoteStationManager has not been updated");
231
232 NS_TEST_EXPECT_MSG_EQ(mac->GetTxop()->GetMinCw(linkId),
233 +phyId,
234 text << ": Txop Link entity " << +phyId
235 << " has not been moved to link " << +linkId);
236 }
237
238 mac->Dispose();
239}
240
241void
243{
244 RunOne("No change needed", 3, {{0, 0}, {1, 1}, {2, 2}}, {{0, 0}, {1, 1}, {2, 2}});
245 RunOne("Circular swapping", 3, {{0, 2}, {1, 0}, {2, 1}}, {{0, 1}, {1, 2}, {2, 0}});
246 RunOne("Swapping two links, one unchanged", 3, {{0, 2}, {2, 0}}, {{0, 2}, {1, 1}, {2, 0}});
247 RunOne("Non-circular swapping, autodetect how to close the loop",
248 3,
249 {{0, 2}, {2, 1}},
250 {{0, 1}, {1, 2}, {2, 0}});
251 RunOne("A different non-circular swapping, same result",
252 3,
253 {{1, 0}, {2, 1}},
254 {{0, 1}, {1, 2}, {2, 0}});
255 RunOne("One move only, autodetect how to complete the swapping",
256 3,
257 {{2, 0}},
258 {{0, 2}, {1, 1}, {2, 0}});
259 RunOne("Create a new link ID (2), remove the unused one (0)",
260 2,
261 {{0, 1}, {1, 2}},
262 {{1, 0}, {2, 1}});
263 RunOne("One move only that creates a new link ID (2)", 2, {{0, 2}}, {{1, 1}, {2, 0}});
264 RunOne("Move all links to a new set of IDs", 2, {{0, 2}, {1, 3}}, {{2, 0}, {3, 1}});
265}
266
267AidAssignmentTest::AidAssignmentTest(const std::vector<std::set<uint8_t>>& linkIds,
268 WifiAssocType assocType)
269 : TestCase("Test the assignment of AIDs"),
270 m_linkChannels({"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{2, 0, BAND_2_4GHZ, 0}"}),
271 m_linkIds(linkIds),
272 m_assocType(assocType),
273 m_expectedAid(1) // AID for first station
274{
275}
276
277void
279{
282 int64_t streamNumber{1};
283
284 NodeContainer wifiApNode;
285 wifiApNode.Create(1);
286 NodeContainer wifiStaNodes;
287
288 WifiHelper wifi;
289 wifi.SetStandard(WIFI_STANDARD_80211be);
290 wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager",
291 "DataMode",
292 StringValue("EhtMcs0"),
293 "ControlMode",
294 StringValue("HtMcs0"));
295
297
298 // AP MLD
299 SpectrumWifiPhyHelper phyHelper(3);
301 phyHelper.SetPcapCaptureType(WifiPhyHelper::PcapCaptureType::PCAP_PER_LINK);
302 uint8_t linkId = 0;
303 for (const auto& str : m_linkChannels)
304 {
305 phyHelper.Set(linkId++, "ChannelSettings", StringValue(str));
306 }
307 phyHelper.SetChannel(channel);
308
309 WifiMacHelper mac;
310 mac.SetType("ns3::ApWifiMac",
311 "Ssid",
312 SsidValue(Ssid("ns-3-ssid")),
313 "BeaconGeneration",
314 BooleanValue(true));
315
316 auto apDevice = wifi.Install(phyHelper, mac, wifiApNode);
317
318 // non-AP STAs/MLDs
319 for (const auto& links : m_linkIds)
320 {
321 phyHelper = SpectrumWifiPhyHelper(links.size());
323 phyHelper.SetPcapCaptureType(WifiPhyHelper::PcapCaptureType::PCAP_PER_LINK);
324 linkId = 0;
325 for (const auto& id : links)
326 {
327 phyHelper.Set(linkId++, "ChannelSettings", StringValue(m_linkChannels.at(id)));
328 }
329 phyHelper.SetChannel(channel);
330 phyHelper.Set("FixedPhyBand", BooleanValue(true));
331
332 WifiMacHelper mac;
333 mac.SetType("ns3::StaWifiMac",
334 "Ssid", // first non-AP STA/MLD only starts associating
335 SsidValue(Ssid(m_staDevices.GetN() == 0 ? "ns-3-ssid" : "default")),
336 "ActiveProbing",
337 BooleanValue(false),
338 "AssocType",
340
341 auto staNode = CreateObject<Node>();
342 auto staDevice = wifi.Install(phyHelper, mac, staNode);
343 wifiStaNodes.Add(staNode);
344 m_staDevices.Add(staDevice);
345 }
346
347 // Assign fixed streams to random variables in use
348 streamNumber += WifiHelper::AssignStreams(apDevice, streamNumber);
349 streamNumber += WifiHelper::AssignStreams(m_staDevices, streamNumber);
350
351 auto positionAlloc = CreateObject<ListPositionAllocator>();
352 positionAlloc->Add(Vector(0.0, 0.0, 0.0));
353 MobilityHelper mobility;
354 mobility.SetPositionAllocator(positionAlloc);
355 mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
356 mobility.Install(wifiApNode);
357 mobility.Install(wifiStaNodes);
358
359 for (uint32_t i = 0; i < m_staDevices.GetN(); ++i)
360 {
361 auto mac = StaticCast<WifiNetDevice>(m_staDevices.Get(i))->GetMac();
362 mac->TraceConnectWithoutContext(
363 "Assoc",
365 }
366}
367
368void
370{
371 const auto aid = staMac->GetAssociationId();
372
373 std::stringstream linksStr;
374 const auto setupLinks = staMac->GetSetupLinkIds();
375 std::copy(setupLinks.cbegin(), setupLinks.cend(), std::ostream_iterator<int>(linksStr, " "));
376
377 NS_LOG_INFO("STA " << staMac->GetAddress() << " associated with AID " << aid << " links "
378 << linksStr.str());
379
380 NS_TEST_EXPECT_MSG_EQ(aid, m_expectedAid, "Unexpected AID for STA " << staMac->GetAddress());
381
382 // if ML setup is performed, check that the requested links have been setup; otherwise, link 0
383 // only is setup
384 const auto expectedLinks =
385 (m_assocType == WifiAssocType::ML_SETUP ? m_linkIds.at(aid - 1)
386 : std::set{SINGLE_LINK_OP_ID});
387
388 NS_TEST_EXPECT_MSG_EQ((staMac->GetSetupLinkIds() == expectedLinks),
389 true,
390 "Unexpected set of setup links " << linksStr.str());
391
393 {
394 // let the next STA associate with the AP
396 ->GetMac()
397 ->SetSsid(Ssid("ns-3-ssid"));
399 }
400 else
401 {
402 Simulator::Stop(MilliSeconds(5)); // allow sending Ack response to Association Response
403 }
404}
405
406void
408{
409 Simulator::Stop(Seconds(5)); // simulation will stop earlier if all STAs complete association
411
412 NS_TEST_EXPECT_MSG_EQ(m_expectedAid, m_staDevices.GetN(), "Not all STAs completed association");
413
414 for (uint32_t i = 0; i < m_staDevices.GetN(); ++i)
415 {
416 auto mac = StaticCast<WifiNetDevice>(m_staDevices.Get(i))->GetMac();
417 mac->TraceDisconnectWithoutContext(
418 "Assoc",
420 }
421
423}
424
426 uint8_t nStations,
427 const BaseParams& baseParams)
428 : TestCase(name),
429 m_staChannels(baseParams.staChannels),
430 m_apChannels(baseParams.apChannels),
431 m_fixedPhyBands(baseParams.fixedPhyBands),
432 m_assocType(baseParams.assocType),
433 m_staMacs(nStations),
434 m_nStations(nStations),
435 m_lastAid(0),
436 m_rxPkts(nStations + 1)
437{
438}
439
440void
442 std::optional<Direction> direction)
443{
444 std::optional<Mac48Address> apAddr;
445 std::optional<Mac48Address> staAddr;
446
447 // direction for Data frames is derived from ToDS/FromDS flags
448 if (psdu->GetHeader(0).IsQosData())
449 {
450 direction = (!psdu->GetHeader(0).IsToDs() && psdu->GetHeader(0).IsFromDs()) ? DL : UL;
451 }
452 NS_ASSERT(direction);
453
454 if (direction == DL)
455 {
456 if (!psdu->GetAddr1().IsGroup())
457 {
458 staAddr = psdu->GetAddr1();
459 }
460 apAddr = psdu->GetAddr2();
461 }
462 else
463 {
464 if (!psdu->GetAddr1().IsGroup())
465 {
466 apAddr = psdu->GetAddr1();
467 }
468 staAddr = psdu->GetAddr2();
469 }
470
471 if (apAddr)
472 {
473 bool found = false;
474 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
475 {
476 if (m_apMac->GetFrameExchangeManager(linkId)->GetAddress() == *apAddr)
477 {
478 found = true;
479 break;
480 }
481 }
483 true,
484 "Address " << *apAddr << " is not an AP device address. "
485 << "PSDU: " << *psdu);
486 }
487
488 if (staAddr)
489 {
490 bool found = false;
491 for (uint8_t i = 0; i < m_nStations; i++)
492 {
493 for (const auto& linkId : m_staMacs[i]->GetLinkIds())
494 {
495 if (m_staMacs[i]->GetFrameExchangeManager(linkId)->GetAddress() == *staAddr)
496 {
497 found = true;
498 break;
499 }
500 }
501 if (found)
502 {
503 break;
504 }
505 }
507 true,
508 "Address " << *staAddr << " is not a STA device address. "
509 << "PSDU: " << *psdu);
510 }
511}
512
513void
515 uint8_t phyId,
516 WifiConstPsduMap psduMap,
517 WifiTxVector txVector,
518 double txPowerW)
519{
520 auto linkId = mac->GetLinkForPhy(phyId);
521 NS_TEST_ASSERT_MSG_EQ(linkId.has_value(), true, "No link found for PHY ID " << +phyId);
522 m_txPsdus.push_back({Simulator::Now(), psduMap, txVector, *linkId, phyId});
523
524 for (const auto& [aid, psdu] : psduMap)
525 {
526 std::stringstream ss;
527 ss << std::setprecision(10) << "PSDU #" << m_txPsdus.size() << " Link ID "
528 << +linkId.value() << " Phy ID " << +phyId << " #MPDUs " << psdu->GetNMpdus();
529 for (auto it = psdu->begin(); it != psdu->end(); ++it)
530 {
531 ss << "\n" << **it;
532 }
533 NS_LOG_INFO(ss.str());
534
535 CheckCapabilities(*psdu->begin(), mac, phyId);
536 }
537 NS_LOG_INFO("TXVECTOR = " << txVector << "\n");
538}
539
540void
542{
543 auto band = mac->GetDevice()->GetPhy(phyId)->GetPhyBand();
544 bool hasHtCapabilities;
545 bool hasVhtCapabilities;
546 bool hasHeCapabilities;
547 bool hasHe6GhzCapabilities;
548 bool hasEhtCapabilities;
549
550 auto findCapabilities = [&](auto&& frame) {
551 hasHtCapabilities = frame.template Get<HtCapabilities>().has_value();
552 hasVhtCapabilities = frame.template Get<VhtCapabilities>().has_value();
553 hasHeCapabilities = frame.template Get<HeCapabilities>().has_value();
554 hasHe6GhzCapabilities = frame.template Get<He6GhzBandCapabilities>().has_value();
555 hasEhtCapabilities = frame.template Get<EhtCapabilities>().has_value();
556 };
557
558 switch (mpdu->GetHeader().GetType())
559 {
560 case WIFI_MAC_MGT_BEACON: {
561 MgtBeaconHeader beacon;
562 mpdu->GetPacket()->PeekHeader(beacon);
563 findCapabilities(beacon);
564 }
565 break;
566
568 MgtProbeRequestHeader probeReq;
569 mpdu->GetPacket()->PeekHeader(probeReq);
570 findCapabilities(probeReq);
571 }
572 break;
573
575 MgtProbeResponseHeader probeResp;
576 mpdu->GetPacket()->PeekHeader(probeResp);
577 findCapabilities(probeResp);
578 }
579 break;
580
582 MgtAssocRequestHeader assocReq;
583 mpdu->GetPacket()->PeekHeader(assocReq);
584 findCapabilities(assocReq);
585 }
586 break;
587
589 MgtAssocResponseHeader assocResp;
590 mpdu->GetPacket()->PeekHeader(assocResp);
591 findCapabilities(assocResp);
592 }
593 break;
594
595 default:
596 return;
597 }
598
600 hasHtCapabilities,
601 (band != WIFI_PHY_BAND_6GHZ),
602 "HT Capabilities should not be present in a mgt frame sent in 6 GHz band");
604 hasVhtCapabilities,
605 (band == WIFI_PHY_BAND_5GHZ),
606 "VHT Capabilities should only be present in a mgt frame sent in 5 GHz band");
607 NS_TEST_EXPECT_MSG_EQ(hasHeCapabilities,
608 true,
609 "HE Capabilities should always be present in a mgt frame");
611 hasHe6GhzCapabilities,
612 (band == WIFI_PHY_BAND_6GHZ),
613 "HE 6GHz Band Capabilities should only be present in a mgt frame sent in 6 GHz band");
614 NS_TEST_EXPECT_MSG_EQ(hasEhtCapabilities,
615 true,
616 "EHT Capabilities should always be present in a mgt frame");
617}
618
619void
621{
622 NS_LOG_INFO("Packet received by NODE " << +nodeId << "\n");
623 m_rxPkts[nodeId]++;
624}
625
626void
628 const std::vector<std::string>& channels,
629 const ChannelMap& channelMap)
630{
631 helper = SpectrumWifiPhyHelper(channels.size());
633 helper.SetPcapCaptureType(WifiPhyHelper::PcapCaptureType::PCAP_PER_LINK);
634
635 uint8_t linkId = 0;
636 for (const auto& str : channels)
637 {
638 helper.Set(linkId++, "ChannelSettings", StringValue(str));
639 }
640
641 // NOTE replace this for loop with the line below to use a single spectrum channel
642 // helper.SetChannel(channelMap.begin()->second);
643 for (const auto& [band, channel] : channelMap)
644 {
645 helper.AddChannel(channel, band);
646 }
647}
648
649void
651{
654 int64_t streamNumber = 30;
655
656 NodeContainer wifiApNode;
657 wifiApNode.Create(1);
658
659 NodeContainer wifiStaNodes;
660 wifiStaNodes.Create(m_nStations);
661
662 WifiHelper wifi;
663 // wifi.EnableLogComponents ();
664 wifi.SetStandard(WIFI_STANDARD_80211be);
665 wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager",
666 "DataMode",
667 StringValue("EhtMcs0"),
668 "ControlMode",
669 StringValue("HtMcs0"));
670
674
675 SpectrumWifiPhyHelper staPhyHelper;
676 SpectrumWifiPhyHelper apPhyHelper;
677 SetChannels(staPhyHelper, m_staChannels, channelMap);
678 SetChannels(apPhyHelper, m_apChannels, channelMap);
679
680 for (const auto& linkId : m_fixedPhyBands)
681 {
682 staPhyHelper.Set(linkId, "FixedPhyBand", BooleanValue(true));
683 }
684
685 WifiMacHelper mac;
686 mac.SetType("ns3::StaWifiMac", // default SSID
687 "MaxMissedBeacons",
688 UintegerValue(1e6), // do not deassociate
689 "ActiveProbing",
690 BooleanValue(false),
691 "AssocType",
693
694 NetDeviceContainer staDevices = wifi.Install(staPhyHelper, mac, wifiStaNodes);
695
696 mac.SetType("ns3::ApWifiMac",
697 "Ssid",
698 SsidValue(Ssid("ns-3-ssid")),
699 "BeaconGeneration",
700 BooleanValue(true));
701
702 NetDeviceContainer apDevices = wifi.Install(apPhyHelper, mac, wifiApNode);
703
704 // Uncomment the lines below to write PCAP files
705 // apPhyHelper.EnablePcap("wifi-mlo_AP", apDevices);
706 // staPhyHelper.EnablePcap("wifi-mlo_STA", staDevices);
707
708 // Assign fixed streams to random variables in use
709 streamNumber += WifiHelper::AssignStreams(apDevices, streamNumber);
710 streamNumber += WifiHelper::AssignStreams(staDevices, streamNumber);
711
712 MobilityHelper mobility;
714
715 positionAlloc->Add(Vector(0.0, 0.0, 0.0));
716 positionAlloc->Add(Vector(1.0, 0.0, 0.0));
717 mobility.SetPositionAllocator(positionAlloc);
718
719 mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
720 mobility.Install(wifiApNode);
721 mobility.Install(wifiStaNodes);
722
723 m_apMac = DynamicCast<ApWifiMac>(DynamicCast<WifiNetDevice>(apDevices.Get(0))->GetMac());
724 for (uint8_t i = 0; i < m_nStations; i++)
725 {
726 m_staMacs[i] =
727 DynamicCast<StaWifiMac>(DynamicCast<WifiNetDevice>(staDevices.Get(i))->GetMac());
728 }
729
730 // Trace PSDUs passed to the PHY on all devices
731 for (uint8_t phyId = 0; phyId < m_apMac->GetDevice()->GetNPhys(); phyId++)
732 {
734 "/NodeList/0/DeviceList/*/$ns3::WifiNetDevice/Phys/" + std::to_string(phyId) +
735 "/PhyTxPsduBegin",
737 }
738 for (uint8_t i = 0; i < m_nStations; i++)
739 {
740 for (uint8_t phyId = 0; phyId < m_staMacs[i]->GetDevice()->GetNPhys(); phyId++)
741 {
742 Config::ConnectWithoutContext("/NodeList/" + std::to_string(i + 1) +
743 "/DeviceList/*/$ns3::WifiNetDevice/Phys/" +
744 std::to_string(phyId) + "/PhyTxPsduBegin",
746 .Bind(m_staMacs[i], phyId));
747 }
748 }
749
750 // install packet socket on all nodes
751 PacketSocketHelper packetSocket;
752 packetSocket.Install(wifiApNode);
753 packetSocket.Install(wifiStaNodes);
754
755 // install a packet socket server on all nodes
756 for (auto nodeIt = NodeList::Begin(); nodeIt != NodeList::End(); ++nodeIt)
757 {
758 PacketSocketAddress srvAddr;
759 auto device = DynamicCast<WifiNetDevice>((*nodeIt)->GetDevice(0));
760 NS_TEST_ASSERT_MSG_NE(device, nullptr, "Expected a WifiNetDevice");
761 srvAddr.SetSingleDevice(device->GetIfIndex());
762 srvAddr.SetProtocol(1);
763
764 auto server = CreateObject<PacketSocketServer>();
765 server->SetLocal(srvAddr);
766 (*nodeIt)->AddApplication(server);
767 server->SetStartTime(Seconds(0)); // now
768 server->SetStopTime(m_duration);
769 }
770
771 for (std::size_t nodeId = 0; nodeId < NodeList::GetNNodes(); nodeId++)
772 {
774 "/NodeList/" + std::to_string(nodeId) +
775 "/ApplicationList/*/$ns3::PacketSocketServer/Rx",
777 }
778
779 // schedule ML setup for one station at a time
780 m_apMac->TraceConnectWithoutContext("AssociatedSta",
782 m_staMacs[0]->SetSsid(Ssid("ns-3-ssid"));
783}
784
787 std::size_t count,
788 std::size_t pktSize,
789 Time delay,
790 uint8_t priority) const
791{
792 auto client = CreateObject<PacketSocketClient>();
793 client->SetAttribute("PacketSize", UintegerValue(pktSize));
794 client->SetAttribute("MaxPackets", UintegerValue(count));
795 client->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
796 client->SetAttribute("Priority", UintegerValue(priority));
797 client->SetRemote(sockAddr);
798 client->SetStartTime(delay);
799 client->SetStopTime(m_duration - Simulator::Now());
800
801 return client;
802}
803
804void
806{
807 if (m_lastAid == aid)
808 {
809 // another STA of this non-AP MLD has already fired this callback
810 return;
811 }
812 m_lastAid = aid;
813
814 // make the next STA to start ML discovery & setup
815 if (aid < m_nStations)
816 {
817 m_staMacs[aid]->SetSsid(Ssid("ns-3-ssid"));
818 return;
819 }
820 // stop generation of beacon frames in order to avoid interference
821 m_apMac->SetAttribute("BeaconGeneration", BooleanValue(false));
822
823 // wait some time (5ms) to allow the completion of association before generating traffic
825}
826
828 WifiScanType scanType,
829 const std::vector<uint8_t>& setupLinks,
830 const std::vector<uint8_t>& staSetupLinks,
832 const std::string& dlTidToLinkMapping,
833 const std::string& ulTidToLinkMapping,
834 bool support160MHzOp)
835 : MultiLinkOperationsTestBase("Check correctness of Multi-Link Setup", 1, baseParams),
836 m_setupLinks(setupLinks),
837 m_staSetupLinks(staSetupLinks.empty() ? setupLinks : staSetupLinks),
838 m_scanType(scanType),
839 m_nProbeResp(0),
840 m_apNegSupport(apNegSupport),
841 m_dlTidLinkMappingStr(dlTidToLinkMapping),
842 m_ulTidLinkMappingStr(ulTidToLinkMapping),
843 m_support160MHzOp(support160MHzOp)
844{
846 m_staSetupLinks.size(),
847 "Number of setup links must be the same for AP and non-AP device");
848}
849
850void
852{
854
855 m_staMacs[0]->SetAttribute("ActiveProbing", BooleanValue(m_scanType == WifiScanType::ACTIVE));
856 m_apMac->GetEhtConfiguration()->m_tidLinkMappingSupport = m_apNegSupport;
857 // For non-AP MLD, it does not make sense to set the negotiation type to 0 (unless the AP MLD
858 // also advertises 0) or 1 (the AP MLD is discarded if it advertises a support of 3)
859 auto staEhtConfig = m_staMacs[0]->GetEhtConfiguration();
860 staEhtConfig->m_tidLinkMappingSupport = WifiTidToLinkMappingNegSupport::ANY_LINK_SET;
861 staEhtConfig->SetAttribute("TidToLinkMappingDl", StringValue(m_dlTidLinkMappingStr));
862 staEhtConfig->SetAttribute("TidToLinkMappingUl", StringValue(m_ulTidLinkMappingStr));
863
864 // the negotiated link mapping matches the one configured in EHT configuration, unless
865 // the AP MLD does not support TID-to-link mapping negotiation or the AP MLD supports
866 // the negotiation type 1 and the non-AP MLD is configured with a link mapping that
867 // maps distinct link sets to the TIDs, in which case the default link mapping is used
868 m_dlTidLinkMapping = staEhtConfig->GetTidLinkMapping(WifiDirection::DOWNLINK);
869 m_ulTidLinkMapping = staEhtConfig->GetTidLinkMapping(WifiDirection::UPLINK);
870
871 if (m_apNegSupport == WifiTidToLinkMappingNegSupport::NOT_SUPPORTED ||
872 (m_apNegSupport == WifiTidToLinkMappingNegSupport::SAME_LINK_SET &&
874 {
875 m_dlTidLinkMapping.clear(); // default link mapping
876 m_ulTidLinkMapping.clear(); // default link mapping
877 }
878
879 // find (if any) a TID that is not mapped to all setup links
880 using TupleRefs = std::tuple<std::reference_wrapper<const WifiTidLinkMapping>,
881 std::reference_wrapper<uint8_t>,
882 std::reference_wrapper<std::optional<uint8_t>>,
884 for (auto& [mappingRef, tid1Ref, tid2Ref, mac] :
887 {
888 tid1Ref.get() = 0;
889 for (uint8_t tid1 = 0; tid1 < 8; tid1++)
890 {
891 if (auto it1 = mappingRef.get().find(tid1);
892 it1 != mappingRef.get().cend() && it1->second.size() != m_setupLinks.size())
893 {
894 // found. Now search for another TID with a disjoint mapped link set
895 for (uint8_t tid2 = tid1 + 1; tid2 < 8; tid2++)
896 {
897 if (auto it2 = mappingRef.get().find(tid2);
898 it2 != mappingRef.get().cend() && it2->second.size() != m_setupLinks.size())
899 {
900 std::list<uint8_t> intersection;
901 std::set_intersection(it1->second.cbegin(),
902 it1->second.cend(),
903 it2->second.cbegin(),
904 it2->second.cend(),
905 std::back_inserter(intersection));
906 if (intersection.empty())
907 {
908 // found a second TID
909 tid2Ref.get() = tid2;
910 break;
911 }
912 }
913 }
914 tid1Ref.get() = tid1;
915 break;
916 }
917 }
918
919 std::list<uint8_t> tids = {tid1Ref.get()};
920 if (tid2Ref.get())
921 {
922 tids.emplace_back(*tid2Ref.get());
923 }
924
925 // prevent aggregation of MPDUs
926 for (auto tid : tids)
927 {
928 std::string attrName;
929 switch (QosUtilsMapTidToAc(tid))
930 {
931 case AC_VI:
932 attrName = "VI_MaxAmpduSize";
933 break;
934 case AC_VO:
935 attrName = "VO_MaxAmpduSize";
936 break;
937 case AC_BE:
938 attrName = "BE_MaxAmpduSize";
939 break;
940 case AC_BK:
941 attrName = "BK_MaxAmpduSize";
942 break;
943 default:
944 NS_FATAL_ERROR("Invalid TID " << +tid);
945 }
946
947 mac->SetAttribute(attrName, UintegerValue(100));
948 }
949 }
950
951 // configure support for 160 MHz operations and set the channels again to check that they
952 // are compatible
953 for (auto staMac : m_staMacs)
954 {
955 staMac->GetVhtConfiguration()->m_160MHzSupported = m_support160MHzOp;
956 uint8_t linkId = 0;
957 for (const auto& str : m_staChannels)
958 {
959 staMac->GetWifiPhy(linkId++)->SetAttribute("ChannelSettings", StringValue(str));
960 }
961 }
962}
963
964void
966{
967 // DL traffic
968 {
969 PacketSocketAddress sockAddr;
970 sockAddr.SetSingleDevice(m_apMac->GetDevice()->GetIfIndex());
971 sockAddr.SetPhysicalAddress(m_staMacs[0]->GetDevice()->GetAddress());
972 sockAddr.SetProtocol(1);
973
974 m_apMac->GetDevice()->GetNode()->AddApplication(
975 GetApplication(sockAddr, m_setupLinks.size(), 500, Seconds(0), m_dlTid1));
976 if (m_dlTid2)
977 {
978 m_apMac->GetDevice()->GetNode()->AddApplication(
979 GetApplication(sockAddr, m_setupLinks.size(), 500, Seconds(0), *m_dlTid2));
980 }
981 }
982
983 // UL Traffic
984 {
985 PacketSocketAddress sockAddr;
986 sockAddr.SetSingleDevice(m_staMacs[0]->GetDevice()->GetIfIndex());
987 sockAddr.SetPhysicalAddress(m_apMac->GetDevice()->GetAddress());
988 sockAddr.SetProtocol(1);
989
990 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(
991 GetApplication(sockAddr, m_setupLinks.size(), 500, MilliSeconds(500), m_ulTid1));
992 if (m_ulTid2)
993 {
994 m_staMacs[0]->GetDevice()->GetNode()->AddApplication(
995 GetApplication(sockAddr, m_setupLinks.size(), 500, MilliSeconds(500), *m_ulTid2));
996 }
997 }
998}
999
1000void
1002{
1004
1007
1008 /**
1009 * Check content of management frames
1010 */
1011 std::size_t index = 0;
1012
1013 for (const auto& frameInfo : m_txPsdus)
1014 {
1015 const auto& mpdu = *frameInfo.psduMap.begin()->second->begin();
1016 const auto& linkId = frameInfo.linkId;
1017
1018 switch (mpdu->GetHeader().GetType())
1019 {
1021 CheckBeacon(mpdu, linkId);
1022 break;
1023
1025 CheckProbeResponse(mpdu, linkId);
1026 m_nProbeResp++;
1027 break;
1028
1030 CheckAssocRequest(mpdu, linkId);
1031 break;
1032
1034 CheckAssocResponse(mpdu, linkId);
1035 break;
1036
1037 case WIFI_MAC_QOSDATA:
1038 CheckQosData(mpdu, frameInfo.txVector, linkId, index);
1039 break;
1040
1041 default:
1042 break;
1043 }
1044
1045 index++;
1046 }
1047
1049
1050 std::size_t expectedProbeResp = 0;
1051 if (m_scanType == WifiScanType::ACTIVE)
1052 {
1053 // the number of Probe Response frames that we expect to receive in active mode equals
1054 // the number of channels in common between AP MLD and non-AP MLD at initialization
1055 for (const auto& staChannel : m_staChannels)
1056 {
1057 for (const auto& apChannel : m_apChannels)
1058 {
1059 if (staChannel == apChannel)
1060 {
1061 expectedProbeResp++;
1062 break;
1063 }
1064 }
1065 }
1066 }
1067
1068 NS_TEST_EXPECT_MSG_EQ(m_nProbeResp, expectedProbeResp, "Unexpected number of Probe Responses");
1069
1070 std::size_t expectedRxDlPkts = m_setupLinks.size();
1071 if (m_dlTid2)
1072 {
1073 expectedRxDlPkts *= 2;
1074 }
1075 NS_TEST_EXPECT_MSG_EQ(m_rxPkts[m_staMacs[0]->GetDevice()->GetNode()->GetId()],
1076 expectedRxDlPkts,
1077 "Unexpected number of DL packets received");
1078
1079 std::size_t expectedRxUlPkts = m_setupLinks.size();
1080 if (m_ulTid2)
1081 {
1082 expectedRxUlPkts *= 2;
1083 }
1084 NS_TEST_EXPECT_MSG_EQ(m_rxPkts[m_apMac->GetDevice()->GetNode()->GetId()],
1085 expectedRxUlPkts,
1086 "Unexpected number of UL packets received");
1087
1089}
1090
1091void
1093{
1094 NS_ABORT_IF(mpdu->GetHeader().GetType() != WIFI_MAC_MGT_BEACON);
1095
1097
1098 NS_TEST_EXPECT_MSG_EQ(m_apMac->GetFrameExchangeManager(linkId)->GetAddress(),
1099 mpdu->GetHeader().GetAddr2(),
1100 "TA of Beacon frame is not the address of the link it is transmitted on");
1101 MgtBeaconHeader beacon;
1102 mpdu->GetPacket()->PeekHeader(beacon);
1103 const auto& rnr = beacon.Get<ReducedNeighborReport>();
1104 const auto& mle = beacon.Get<MultiLinkElement>();
1105
1106 if (m_apMac->GetNLinks() == 1)
1107 {
1108 NS_TEST_EXPECT_MSG_EQ(rnr.has_value(),
1109 false,
1110 "RNR Element in Beacon frame from single link AP");
1111 NS_TEST_EXPECT_MSG_EQ(mle.has_value(),
1112 false,
1113 "Multi-Link Element in Beacon frame from single link AP");
1114 return;
1115 }
1116
1117 NS_TEST_EXPECT_MSG_EQ(rnr.has_value(), true, "No RNR Element in Beacon frame");
1118 // All the other APs affiliated with the same AP MLD as the AP sending
1119 // the Beacon frame must be reported in a separate Neighbor AP Info field
1120 NS_TEST_EXPECT_MSG_EQ(rnr->GetNNbrApInfoFields(),
1121 static_cast<std::size_t>(m_apMac->GetNLinks() - 1),
1122 "Unexpected number of Neighbor AP Info fields in RNR");
1123 for (std::size_t nbrApInfoId = 0; nbrApInfoId < rnr->GetNNbrApInfoFields(); nbrApInfoId++)
1124 {
1125 NS_TEST_EXPECT_MSG_EQ(rnr->HasMldParameters(nbrApInfoId),
1126 true,
1127 "MLD Parameters not present");
1128 NS_TEST_EXPECT_MSG_EQ(rnr->GetNTbttInformationFields(nbrApInfoId),
1129 1,
1130 "Expected only one TBTT Info subfield per Neighbor AP Info");
1131 uint8_t nbrLinkId = rnr->GetMldParameters(nbrApInfoId, 0).linkId;
1132 NS_TEST_EXPECT_MSG_EQ(rnr->GetBssid(nbrApInfoId, 0),
1133 m_apMac->GetFrameExchangeManager(nbrLinkId)->GetAddress(),
1134 "BSSID advertised in Neighbor AP Info field "
1135 << nbrApInfoId
1136 << " does not match the address configured on the link "
1137 "advertised in the same field");
1138 }
1139
1140 NS_TEST_EXPECT_MSG_EQ(mle.has_value(), true, "No Multi-Link Element in Beacon frame");
1141 NS_TEST_EXPECT_MSG_EQ(mle->GetMldMacAddress(),
1142 m_apMac->GetAddress(),
1143 "Incorrect MLD address advertised in Multi-Link Element");
1144 NS_TEST_EXPECT_MSG_EQ(mle->GetLinkIdInfo(),
1145 +linkId,
1146 "Incorrect Link ID advertised in Multi-Link Element");
1147}
1148
1149void
1151{
1152 NS_ABORT_IF(mpdu->GetHeader().GetType() != WIFI_MAC_MGT_PROBE_RESPONSE);
1153
1155
1157 m_apMac->GetFrameExchangeManager(linkId)->GetAddress(),
1158 mpdu->GetHeader().GetAddr2(),
1159 "TA of Probe Response is not the address of the link it is transmitted on");
1160 MgtProbeResponseHeader probeResp;
1161 mpdu->GetPacket()->PeekHeader(probeResp);
1162 const auto& rnr = probeResp.Get<ReducedNeighborReport>();
1163 const auto& mle = probeResp.Get<MultiLinkElement>();
1164
1165 if (m_apMac->GetNLinks() == 1)
1166 {
1167 NS_TEST_EXPECT_MSG_EQ(rnr.has_value(),
1168 false,
1169 "RNR Element in Probe Response frame from single link AP");
1170 NS_TEST_EXPECT_MSG_EQ(mle.has_value(),
1171 false,
1172 "Multi-Link Element in Probe Response frame from single link AP");
1173 return;
1174 }
1175
1176 NS_TEST_EXPECT_MSG_EQ(rnr.has_value(), true, "No RNR Element in Probe Response frame");
1177 // All the other APs affiliated with the same AP MLD as the AP sending
1178 // the Probe Response frame must be reported in a separate Neighbor AP Info field
1179 NS_TEST_EXPECT_MSG_EQ(rnr->GetNNbrApInfoFields(),
1180 static_cast<std::size_t>(m_apMac->GetNLinks() - 1),
1181 "Unexpected number of Neighbor AP Info fields in RNR");
1182 for (std::size_t nbrApInfoId = 0; nbrApInfoId < rnr->GetNNbrApInfoFields(); nbrApInfoId++)
1183 {
1184 NS_TEST_EXPECT_MSG_EQ(rnr->HasMldParameters(nbrApInfoId),
1185 true,
1186 "MLD Parameters not present");
1187 NS_TEST_EXPECT_MSG_EQ(rnr->GetNTbttInformationFields(nbrApInfoId),
1188 1,
1189 "Expected only one TBTT Info subfield per Neighbor AP Info");
1190 uint8_t nbrLinkId = rnr->GetMldParameters(nbrApInfoId, 0).linkId;
1191 NS_TEST_EXPECT_MSG_EQ(rnr->GetBssid(nbrApInfoId, 0),
1192 m_apMac->GetFrameExchangeManager(nbrLinkId)->GetAddress(),
1193 "BSSID advertised in Neighbor AP Info field "
1194 << nbrApInfoId
1195 << " does not match the address configured on the link "
1196 "advertised in the same field");
1197 }
1198
1199 NS_TEST_EXPECT_MSG_EQ(mle.has_value(), true, "No Multi-Link Element in Probe Response frame");
1200 NS_TEST_EXPECT_MSG_EQ(mle->GetMldMacAddress(),
1201 m_apMac->GetAddress(),
1202 "Incorrect MLD address advertised in Multi-Link Element");
1203 NS_TEST_EXPECT_MSG_EQ(mle->GetLinkIdInfo(),
1204 +linkId,
1205 "Incorrect Link ID advertised in Multi-Link Element");
1206}
1207
1208void
1210{
1211 NS_ABORT_IF(mpdu->GetHeader().GetType() != WIFI_MAC_MGT_ASSOCIATION_REQUEST);
1212
1214
1216 m_staMacs[0]->GetFrameExchangeManager(linkId)->GetAddress(),
1217 mpdu->GetHeader().GetAddr2(),
1218 "TA of Assoc Request frame is not the address of the link it is transmitted on");
1220 mpdu->GetPacket()->PeekHeader(assoc);
1221 const auto& mle = assoc.Get<MultiLinkElement>();
1222
1223 if (m_apMac->GetNLinks() == 1)
1224 {
1225 NS_TEST_EXPECT_MSG_EQ(mle.has_value(),
1226 false,
1227 "Multi-Link Element in Assoc Request frame to single link AP");
1228 }
1229 else if (m_staMacs[0]->GetAssocType() == WifiAssocType::LEGACY)
1230 {
1232 mle.has_value(),
1233 false,
1234 "Multi-Link Element in Assoc Request frame from non-AP using legacy association");
1235 }
1236 else
1237 {
1238 NS_TEST_EXPECT_MSG_EQ(mle.has_value(),
1239 true,
1240 "No Multi-Link Element in Assoc Request frame");
1241 NS_TEST_EXPECT_MSG_EQ(mle->GetMldMacAddress(),
1242 m_staMacs[0]->GetAddress(),
1243 "Incorrect MLD Address advertised in Multi-Link Element");
1245 mle->GetNPerStaProfileSubelements(),
1246 m_setupLinks.size() - 1,
1247 "Incorrect number of Per-STA Profile subelements in Multi-Link Element");
1248 for (std::size_t i = 0; i < mle->GetNPerStaProfileSubelements(); i++)
1249 {
1250 auto& perStaProfile = mle->GetPerStaProfile(i);
1251 NS_TEST_EXPECT_MSG_EQ(perStaProfile.HasStaMacAddress(),
1252 true,
1253 "Per-STA Profile must contain STA MAC address");
1254 // find ID of the local link corresponding to this subelement
1255 auto staLinkId = m_staMacs[0]->GetLinkIdByAddress(perStaProfile.GetStaMacAddress());
1257 staLinkId.has_value(),
1258 true,
1259 "No link found with the STA MAC address advertised in Per-STA Profile");
1261 +staLinkId.value(),
1262 +linkId,
1263 "The STA that sent the Assoc Request should not be included in a Per-STA Profile");
1264 auto it = std::find(m_setupLinks.begin(), m_setupLinks.end(), staLinkId.value());
1265 NS_TEST_EXPECT_MSG_EQ((it != m_setupLinks.end()),
1266 true,
1267 "Not expecting to setup STA link ID " << +staLinkId.value());
1269 +staLinkId.value(),
1270 +perStaProfile.GetLinkId(),
1271 "Not expecting to request association to AP Link ID in Per-STA Profile");
1272 NS_TEST_EXPECT_MSG_EQ(perStaProfile.HasAssocRequest(),
1273 true,
1274 "Missing Association Request in Per-STA Profile");
1275 }
1276 }
1277
1278 const auto& tlm = assoc.Get<TidToLinkMapping>();
1279
1280 // A TID-to-Link Mapping IE is included in the Association Request if and only if the AP MLD
1281 // and the non-AP MLD are performing ML setup and the AP MLD advertises a non-null negotiation
1282 // support type
1283 if (m_apMac->GetNLinks() == 1 || m_staMacs[0]->GetAssocType() == WifiAssocType::LEGACY ||
1284 m_apNegSupport == WifiTidToLinkMappingNegSupport::NOT_SUPPORTED)
1285 {
1286 NS_TEST_EXPECT_MSG_EQ(tlm.empty(),
1287 true,
1288 "Didn't expect a TID-to-Link Mapping IE in Assoc Request frame");
1289 }
1290 else
1291 {
1292 std::size_t expectedNTlm = (m_dlTidLinkMapping == m_ulTidLinkMapping ? 1 : 2);
1293
1294 NS_TEST_ASSERT_MSG_EQ(tlm.size(),
1295 expectedNTlm,
1296 "Unexpected number of TID-to-Link Mapping IE in Assoc Request");
1297
1298 // lambda to check content of TID-to-Link Mapping IE(s)
1299 auto checkTlm = [&](std::size_t tlmId, WifiDirection dir) {
1300 NS_TEST_EXPECT_MSG_EQ(+static_cast<uint8_t>(tlm[tlmId].m_control.direction),
1301 +static_cast<uint8_t>(dir),
1302 "Unexpected direction in TID-to-Link Mapping IE " << tlmId);
1303 auto& expectedMapping =
1304 (dir == WifiDirection::UPLINK ? m_ulTidLinkMapping : m_dlTidLinkMapping);
1305
1306 NS_TEST_EXPECT_MSG_EQ(tlm[tlmId].m_control.defaultMapping,
1307 expectedMapping.empty(),
1308 "Default Link Mapping bit not set correctly");
1309 NS_TEST_EXPECT_MSG_EQ(tlm[tlmId].m_linkMapping.size(),
1310 expectedMapping.size(),
1311 "Unexpected number of Link Mapping Of TID n fields");
1312 for (uint8_t tid = 0; tid < 8; tid++)
1313 {
1314 if (auto it = expectedMapping.find(tid); it != expectedMapping.cend())
1315 {
1316 NS_TEST_EXPECT_MSG_EQ((tlm[tlmId].GetLinkMappingOfTid(tid) == it->second),
1317 true,
1318 "Unexpected link mapping for TID "
1319 << +tid << " direction " << dir);
1320 }
1321 else
1322 {
1323 NS_TEST_EXPECT_MSG_EQ(tlm[tlmId].GetLinkMappingOfTid(tid).empty(),
1324 true,
1325 "Expecting no Link Mapping Of TID n field for TID "
1326 << +tid << " direction " << dir);
1327 }
1328 }
1329 };
1330
1331 if (tlm.size() == 1)
1332 {
1333 checkTlm(0, WifiDirection::BOTH_DIRECTIONS);
1334 }
1335 else
1336 {
1337 std::size_t dlId = (tlm[0].m_control.direction == WifiDirection::DOWNLINK ? 0 : 1);
1338 std::size_t ulId = (dlId == 0 ? 1 : 0);
1339
1340 checkTlm(dlId, WifiDirection::DOWNLINK);
1341 checkTlm(ulId, WifiDirection::UPLINK);
1342 }
1343 }
1344}
1345
1346void
1348{
1349 NS_ABORT_IF(mpdu->GetHeader().GetType() != WIFI_MAC_MGT_ASSOCIATION_RESPONSE);
1350
1352
1354 m_apMac->GetFrameExchangeManager(linkId)->GetAddress(),
1355 mpdu->GetHeader().GetAddr2(),
1356 "TA of Assoc Response frame is not the address of the link it is transmitted on");
1358 mpdu->GetPacket()->PeekHeader(assoc);
1359 const auto& mle = assoc.Get<MultiLinkElement>();
1360
1361 if (m_apMac->GetNLinks() == 1)
1362 {
1363 NS_TEST_EXPECT_MSG_EQ(mle.has_value(),
1364 false,
1365 "Multi-Link Element in Assoc Response frame with single link AP");
1366 return;
1367 }
1368
1369 if (m_staMacs[0]->GetAssocType() == WifiAssocType::LEGACY)
1370 {
1372 mle.has_value(),
1373 false,
1374 "Multi-Link Element in Assoc Response frame with non-AP using legacy association");
1375 return;
1376 }
1377
1378 NS_TEST_EXPECT_MSG_EQ(mle.has_value(), true, "No Multi-Link Element in Assoc Request frame");
1379 NS_TEST_EXPECT_MSG_EQ(mle->GetMldMacAddress(),
1380 m_apMac->GetAddress(),
1381 "Incorrect MLD Address advertised in Multi-Link Element");
1382 NS_TEST_EXPECT_MSG_EQ(mle->GetNPerStaProfileSubelements(),
1383 m_setupLinks.size() - 1,
1384 "Incorrect number of Per-STA Profile subelements in Multi-Link Element");
1385 for (std::size_t i = 0; i < mle->GetNPerStaProfileSubelements(); i++)
1386 {
1387 auto& perStaProfile = mle->GetPerStaProfile(i);
1388 NS_TEST_EXPECT_MSG_EQ(perStaProfile.HasStaMacAddress(),
1389 true,
1390 "Per-STA Profile must contain STA MAC address");
1391 // find ID of the local link corresponding to this subelement
1392 auto apLinkId = m_apMac->GetLinkIdByAddress(perStaProfile.GetStaMacAddress());
1394 apLinkId.has_value(),
1395 true,
1396 "No link found with the STA MAC address advertised in Per-STA Profile");
1397 NS_TEST_EXPECT_MSG_EQ(+apLinkId.value(),
1398 +perStaProfile.GetLinkId(),
1399 "Link ID and MAC address advertised in Per-STA Profile do not match");
1401 +apLinkId.value(),
1402 +linkId,
1403 "The AP that sent the Assoc Response should not be included in a Per-STA Profile");
1404 auto it = std::find(m_setupLinks.begin(), m_setupLinks.end(), apLinkId.value());
1405 NS_TEST_EXPECT_MSG_EQ((it != m_setupLinks.end()),
1406 true,
1407 "Not expecting to setup AP link ID " << +apLinkId.value());
1408 NS_TEST_EXPECT_MSG_EQ(perStaProfile.HasAssocResponse(),
1409 true,
1410 "Missing Association Response in Per-STA Profile");
1411 }
1412
1413 // For the moment, the AP MLD always accepts a valid TID-to-Link Mapping request, hence
1414 // in every case there is no TID-to-Link Mapping IE in the Association Response
1415 NS_TEST_EXPECT_MSG_EQ(assoc.Get<TidToLinkMapping>().empty(),
1416 true,
1417 "Didn't expect to find a TID-to-Link Mapping IE in Association Response");
1418}
1419
1420void
1422{
1423 /**
1424 * Check outcome of Multi-Link Setup
1425 */
1426 NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->IsAssociated(), true, "Expected the STA to be associated");
1427
1428 for (auto linkIdIter = m_setupLinks.cbegin(), staLinkIdIter = m_staSetupLinks.cbegin();
1429 linkIdIter != m_setupLinks.cend();
1430 ++linkIdIter, ++staLinkIdIter)
1431 {
1432 auto staLinkId = *staLinkIdIter;
1433 auto apLinkId = *linkIdIter;
1434
1435 auto staAddr = m_staMacs[0]->GetFrameExchangeManager(staLinkId)->GetAddress();
1436 auto apAddr = m_apMac->GetFrameExchangeManager(apLinkId)->GetAddress();
1437
1438 auto staRemoteMgr = m_staMacs[0]->GetWifiRemoteStationManager(staLinkId);
1439 auto apRemoteMgr = m_apMac->GetWifiRemoteStationManager(apLinkId);
1440
1441 // STA side
1442 NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetFrameExchangeManager(staLinkId)->GetBssid(),
1443 apAddr,
1444 "Unexpected BSSID for STA link ID " << +staLinkId);
1445 if (m_apMac->GetNLinks() > 1 && m_assocType == WifiAssocType::ML_SETUP)
1446 {
1447 NS_TEST_EXPECT_MSG_EQ((staRemoteMgr->GetMldAddress(apAddr) == m_apMac->GetAddress()),
1448 true,
1449 "Incorrect MLD address stored by STA on link ID " << +staLinkId);
1451 (staRemoteMgr->GetAffiliatedStaAddress(m_apMac->GetAddress()) == apAddr),
1452 true,
1453 "Incorrect affiliated address stored by STA on link ID " << +staLinkId);
1454 }
1455
1456 // AP side
1457 NS_TEST_EXPECT_MSG_EQ(apRemoteMgr->IsAssociated(staAddr),
1458 true,
1459 "Expecting STA " << staAddr << " to be associated on link "
1460 << +apLinkId);
1461 if (m_apMac->GetNLinks() > 1 && m_assocType == WifiAssocType::ML_SETUP)
1462 {
1464 (apRemoteMgr->GetMldAddress(staAddr) == m_staMacs[0]->GetAddress()),
1465 true,
1466 "Incorrect MLD address stored by AP on link ID " << +apLinkId);
1468 (apRemoteMgr->GetAffiliatedStaAddress(m_staMacs[0]->GetAddress()) == staAddr),
1469 true,
1470 "Incorrect affiliated address stored by AP on link ID " << +apLinkId);
1471 }
1472 auto aid = m_apMac->GetAssociationId(staAddr, apLinkId);
1473 const auto& staList = m_apMac->GetStaList(apLinkId);
1474 NS_TEST_EXPECT_MSG_EQ((staList.find(aid) != staList.end()),
1475 true,
1476 "STA " << staAddr << " not found in list of associated STAs");
1477
1478 // STA of non-AP MLD operate on the same channel as the AP (or on its primary80 if the AP
1479 // operates on a 160 MHz channel and non-AP MLD does not support 160 MHz operations)
1480 const auto& staChannel = m_staMacs[0]->GetWifiPhy(staLinkId)->GetOperatingChannel();
1481 const auto& apChannel = m_apMac->GetWifiPhy(apLinkId)->GetOperatingChannel();
1482
1483 auto width = apChannel.GetTotalWidth();
1484 auto primary20 = apChannel.GetPrimaryChannelIndex(MHz_u{20});
1485
1486 if (width > MHz_u{80} && !m_support160MHzOp)
1487 {
1488 width = MHz_u{80};
1489 primary20 -= apChannel.GetPrimaryChannelIndex(MHz_u{80}) * 4;
1490 }
1491
1492 NS_TEST_EXPECT_MSG_EQ(+staChannel.GetNumber(),
1493 +apChannel.GetPrimaryChannelNumber(width, WIFI_STANDARD_80211be),
1494 "Incorrect operating channel number for STA on link " << +staLinkId);
1495 NS_TEST_EXPECT_MSG_EQ(staChannel.GetFrequency(),
1496 apChannel.GetPrimaryChannelCenterFrequency(width),
1497 "Incorrect operating channel frequency for STA on link "
1498 << +staLinkId);
1499 NS_TEST_EXPECT_MSG_EQ(staChannel.GetWidth(),
1500 width,
1501 "Incorrect operating channel width for STA on link " << +staLinkId);
1502 NS_TEST_EXPECT_MSG_EQ(+staChannel.GetPhyBand(),
1503 +apChannel.GetPhyBand(),
1504 "Incorrect operating PHY band for STA on link " << +staLinkId);
1505 NS_TEST_EXPECT_MSG_EQ(+staChannel.GetPrimaryChannelIndex(MHz_u{20}),
1506 +primary20,
1507 "Incorrect operating primary channel index for STA on link "
1508 << +staLinkId);
1509 }
1510
1511 // lambda to check the link mapping stored at wifi MAC
1512 auto checkStoredMapping =
1513 [this](Ptr<WifiMac> mac, Ptr<WifiMac> dest, WifiDirection dir, bool present) {
1514 NS_TEST_ASSERT_MSG_EQ(mac->GetTidToLinkMapping(dest->GetAddress(), dir).has_value(),
1515 present,
1516 "Link mapping stored by "
1517 << (mac->GetTypeOfStation() == AP ? "AP" : "non-AP")
1518 << " MLD for " << dir << " direction "
1519 << (present ? "expected" : "not expected"));
1520 if (present)
1521 {
1522 const auto& mapping =
1523 (dir == WifiDirection::DOWNLINK ? m_dlTidLinkMapping : m_ulTidLinkMapping);
1525 (mac->GetTidToLinkMapping(dest->GetAddress(), dir)->get() == mapping),
1526 true,
1527 "Incorrect link mapping stored by "
1528 << (mac->GetTypeOfStation() == AP ? "AP" : "non-AP") << " MLD for " << dir
1529 << " direction");
1530
1531 // check correctness of WifiMac::TidMappedOnLink function
1532 std::set<uint8_t> setupLinks(m_setupLinks.cbegin(), m_setupLinks.cend());
1533 for (uint8_t tid = 0; tid < 8; ++tid)
1534 {
1535 const auto& linkSet = mapping.contains(tid) ? mapping.at(tid) : setupLinks;
1536
1537 for (const auto linkId : setupLinks)
1538 {
1540 mac->TidMappedOnLink(dest->GetAddress(), dir, tid, linkId),
1541 linkSet.contains(linkId),
1542 "Incorrect return value on " << (mac == m_apMac ? "AP" : "STA")
1543 << " direction " << dir << " TID " << +tid
1544 << " linkID " << +linkId);
1545 }
1546 }
1547 }
1548 };
1549
1550 auto storedMapping = (m_apMac->GetNLinks() > 1) &&
1551 (m_staMacs[0]->GetAssocType() == WifiAssocType::ML_SETUP) &&
1552 (m_apNegSupport > WifiTidToLinkMappingNegSupport::NOT_SUPPORTED);
1553 checkStoredMapping(m_apMac, m_staMacs[0], WifiDirection::DOWNLINK, storedMapping);
1554 checkStoredMapping(m_apMac, m_staMacs[0], WifiDirection::UPLINK, storedMapping);
1555 checkStoredMapping(m_staMacs[0], m_apMac, WifiDirection::DOWNLINK, storedMapping);
1556 checkStoredMapping(m_staMacs[0], m_apMac, WifiDirection::UPLINK, storedMapping);
1557}
1558
1559void
1561{
1562 const auto legacyAssoc = (m_assocType == WifiAssocType::LEGACY || m_apMac->GetNLinks() == 1);
1563
1564 if (legacyAssoc)
1565 {
1567 1,
1568 "One link is expected to be setup with legacy association");
1569 }
1570
1571 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); ++linkId)
1572 {
1573 auto it = std::find(m_setupLinks.cbegin(), m_setupLinks.cend(), linkId);
1574 auto addr =
1575 (legacyAssoc
1576 ? m_staMacs[0]->GetFrameExchangeManager(m_staSetupLinks.front())->GetAddress()
1577 : m_staMacs[0]->GetAddress());
1579
1580 // the queue on the AP should have a mask if and only if the link has been setup
1581 auto mask = m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, linkId);
1582 NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
1583 (it != m_setupLinks.cend()),
1584 "Unexpected presence/absence of mask on link " << +linkId);
1585 }
1586
1587 for (const auto& linkId : m_staMacs[0]->GetLinkIds())
1588 {
1589 auto it = std::find(m_staSetupLinks.begin(), m_staSetupLinks.end(), linkId);
1590 if (it == m_staSetupLinks.end())
1591 {
1592 // the link has not been setup
1593 NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetWifiPhy(linkId)->GetState()->IsStateOff(),
1594 true,
1595 "Link " << +linkId << " has not been setup but is not disabled");
1596 continue;
1597 }
1598
1599 // the link has been setup and must be active
1600 NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetWifiPhy(linkId)->GetState()->IsStateOff(),
1601 false,
1602 "Expecting link " << +linkId << " to be active");
1603 }
1604}
1605
1606void
1608 const WifiTxVector& txvector,
1609 uint8_t linkId,
1610 std::size_t index)
1611{
1613 const auto& hdr = mpdu->GetHeader();
1614
1615 NS_TEST_ASSERT_MSG_EQ(hdr.IsQosData(), true, "Expected a QoS data frame");
1616
1617 // check TX width
1618 // STA of non-AP MLD operate on the same channel as the AP (or on its primary80 if the AP
1619 // operates on a 160 MHz channel and non-AP MLD does not support 160 MHz operations)
1620 MHz_u width;
1621
1622 if (!hdr.IsToDs() && hdr.IsFromDs())
1623 {
1624 dir = WifiDirection::DOWNLINK;
1625 width = m_apMac->GetWifiPhy(linkId)->GetOperatingChannel().GetTotalWidth();
1626 }
1627 else if (hdr.IsToDs() && !hdr.IsFromDs())
1628 {
1629 dir = WifiDirection::UPLINK;
1630 width = m_staMacs[0]->GetWifiPhy(linkId)->GetOperatingChannel().GetTotalWidth();
1631 }
1632 else
1633 {
1634 NS_ABORT_MSG("Invalid combination for QoS data frame: ToDS(" << hdr.IsToDs() << ") FromDS("
1635 << hdr.IsFromDs() << ")");
1636 }
1637
1638 if (width > MHz_u{80} && !m_support160MHzOp)
1639 {
1640 width = MHz_u{80};
1641 }
1642 NS_TEST_EXPECT_MSG_EQ(txvector.GetChannelWidth(), width, "Unexpected TX width");
1643
1644 const auto& tid1 = (dir == WifiDirection::DOWNLINK) ? m_dlTid1 : m_ulTid1;
1645 const auto& tid2 = (dir == WifiDirection::DOWNLINK) ? m_dlTid2 : m_ulTid2;
1646 uint8_t tid = hdr.GetQosTid();
1647
1648 NS_TEST_ASSERT_MSG_NE((tid == tid1),
1649 (tid2.has_value() && tid == *tid2),
1650 "QoS frame with unexpected TID " << +tid);
1651
1652 // lambda to find the link set the given TID is mapped to
1653 auto findLinkSet = [this, dir](uint8_t tid) -> std::set<uint8_t> {
1654 std::set<uint8_t> linkSet(m_setupLinks.cbegin(), m_setupLinks.cend());
1655 if (auto mappingOptRef = m_apMac->GetTidToLinkMapping(m_staMacs[0]->GetAddress(), dir))
1656 {
1657 // if the TID is not present in the mapping, it is mapped to all setup links
1658 if (auto it = mappingOptRef->get().find(tid); it != mappingOptRef->get().cend())
1659 {
1660 linkSet = it->second;
1661 NS_ASSERT_MSG(!linkSet.empty(), "TID " << +tid << " mapped to no link");
1662 }
1663 }
1664 return linkSet;
1665 };
1666
1667 auto linkSet = findLinkSet(tid);
1668 auto& qosFrames = (tid == tid1) ? m_qosFrames1 : m_qosFrames2;
1669
1670 // Let N the size of the link set, the first N QoS data frames are sent simultaneously
1671 // on the links of the set, the others (if any) will be sent afterwards on such links
1672
1673 // number of concurrent frames of the same TID transmitted so far (excluding current frame)
1674 std::size_t nConcurFrames = std::min(qosFrames.size(), linkSet.size());
1675
1676 // iterate over the concurrent frames of the same TID transmitted so far
1677 for (std::size_t i = 0; i < nConcurFrames; i++)
1678 {
1679 auto prev = qosFrames[i];
1680
1681 // TX duration of i-th frame
1682 auto band = m_apMac->GetWifiPhy(m_txPsdus[prev].linkId)->GetPhyBand();
1683 Time txDuration =
1684 WifiPhy::CalculateTxDuration(m_txPsdus[prev].psduMap, m_txPsdus[prev].txVector, band);
1685
1686 // the current frame is transmitted concurrently with this previous frame if it is
1687 // within the first N (size of the link set) frames, otherwise it is transmitted after
1688 // this previous frame if they have been transmitted on the same link
1689 if (qosFrames.size() < linkSet.size())
1690 {
1691 // the current frame can be sent concurrently with this previous frame
1692 NS_TEST_EXPECT_MSG_LT(m_txPsdus[index].startTx,
1693 m_txPsdus[prev].startTx + txDuration,
1694 "The " << dir << " QoS frame number " << qosFrames.size()
1695 << " was not sent concurrently with others on link "
1696 << +linkId << " which TID " << +tid << " is mapped to");
1697 }
1698 else if (m_txPsdus[prev].linkId == linkId)
1699 {
1700 // the current frame is sent afterwards
1701 NS_TEST_EXPECT_MSG_GT(m_txPsdus[index].startTx,
1702 m_txPsdus[prev].startTx + txDuration,
1703 "The " << dir << " QoS frame number " << qosFrames.size()
1704 << " was sent concurrently with others on a link "
1705 << +linkId << " which TID " << +tid << " is mapped to");
1706 }
1707 }
1708
1709 if (m_apMac->GetNLinks() > 1 && m_assocType == WifiAssocType::ML_SETUP)
1710 {
1711 NS_TEST_EXPECT_MSG_EQ(std::count(linkSet.cbegin(), linkSet.cend(), linkId),
1712 1,
1713 "QoS frame sent on Link ID "
1714 << +linkId << " that does not belong to the link set of TID "
1715 << +tid);
1716 }
1717
1718 if (tid2)
1719 {
1720 // QoS frames of two distinct TIDs are sent.
1721 auto otherTid = (tid == tid1) ? *tid2 : tid1;
1722 const auto& otherQosFrames = (tid == tid1) ? m_qosFrames2 : m_qosFrames1;
1723 auto otherLinkSet = findLinkSet(otherTid);
1724
1725 // number of concurrent frames of the other TID transmitted so far
1726 std::size_t nOtherConcurFrames = std::min(otherQosFrames.size(), otherLinkSet.size());
1727
1728 // iterate over the concurrent frames of the other TID
1729 for (std::size_t i = 0; i < nOtherConcurFrames; i++)
1730 {
1731 auto prev = otherQosFrames[i];
1732
1733 // TX duration of i-th frame
1734 auto band = m_apMac->GetWifiPhy(m_txPsdus[prev].linkId)->GetPhyBand();
1735 Time txDuration = WifiPhy::CalculateTxDuration(m_txPsdus[prev].psduMap,
1736 m_txPsdus[prev].txVector,
1737 band);
1738
1739 // the current frame is transmitted concurrently with this previous frame of the
1740 // other TID if it is within the first N (size of the link set) frames of its TID
1741 if (qosFrames.size() < linkSet.size())
1742 {
1743 // the current frame can be sent concurrently with this previous frame
1744 NS_TEST_EXPECT_MSG_LT(m_txPsdus[index].startTx,
1745 m_txPsdus[prev].startTx + txDuration,
1746 "The " << dir << " QoS frame number " << qosFrames.size()
1747 << " was not sent concurrently with others with TID "
1748 << +otherTid);
1749 }
1750 }
1751 }
1752
1753 // insert the frame
1754 qosFrames.emplace_back(index);
1755
1756 if (qosFrames.size() == m_setupLinks.size())
1757 {
1758 qosFrames.clear();
1759 }
1760}
1761
1763 WifiTrafficPattern trafficPattern,
1764 WifiBaEnabled baEnabled,
1765 WifiUseBarAfterMissedBa useBarAfterMissedBa,
1766 uint8_t nMaxInflight)
1768 std::string("Check data transmission between MLDs ") +
1769 (baEnabled == WifiBaEnabled::YES
1770 ? (useBarAfterMissedBa == WifiUseBarAfterMissedBa::YES
1771 ? "with BA agreement, send BAR after BlockAck timeout"
1772 : "with BA agreement, send Data frames after BlockAck timeout")
1773 : "without BA agreement") +
1774 " (Traffic pattern: " + std::to_string(static_cast<uint8_t>(trafficPattern)) +
1775 (baEnabled == WifiBaEnabled::YES ? ", nMaxInflight=" + std::to_string(nMaxInflight)
1776 : "") +
1777 ")",
1778 2,
1779 baseParams),
1780 m_trafficPattern(trafficPattern),
1781 m_baEnabled(baEnabled == WifiBaEnabled::YES),
1782 m_useBarAfterMissedBa(useBarAfterMissedBa == WifiUseBarAfterMissedBa::YES),
1783 m_nMaxInflight(nMaxInflight),
1784 m_nPackets(trafficPattern == WifiTrafficPattern::STA_TO_BCAST ||
1785 trafficPattern == WifiTrafficPattern::STA_TO_STA
1786 ? 4
1787 : 8)
1788{
1789}
1790
1791void
1793 uint8_t phyId,
1794 WifiConstPsduMap psduMap,
1795 WifiTxVector txVector,
1796 double txPowerW)
1797{
1798 MultiLinkOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
1799 auto linkId = m_txPsdus.back().linkId;
1800
1801 auto psdu = psduMap.begin()->second;
1802
1803 switch (psdu->GetHeader(0).GetType())
1804 {
1806 // a management frame is a DL frame if TA equals BSSID
1807 CheckAddresses(psdu,
1808 psdu->GetHeader(0).GetAddr2() == psdu->GetHeader(0).GetAddr3() ? DL : UL);
1809 if (!m_baEnabled)
1810 {
1811 // corrupt all management action frames (ADDBA Request frames) to prevent
1812 // the establishment of a BA agreement
1813 m_uidList.push_front(psdu->GetPacket()->GetUid());
1814 m_errorModels.at(psdu->GetAddr1())->SetList(m_uidList);
1815 NS_LOG_INFO("CORRUPTED");
1816 }
1817 break;
1818 case WIFI_MAC_QOSDATA:
1819 CheckAddresses(psdu);
1820
1821 for (const auto& mpdu : *psdu)
1822 {
1823 // determine the max number of simultaneous transmissions for this MPDU
1824 // (only if sent by the traffic source and this is not a broadcast frame)
1825 if (m_baEnabled && m_sourceMac->GetLinkIds().count(linkId) == 1 &&
1826 m_sourceMac->GetFrameExchangeManager(linkId)->GetAddress() ==
1827 mpdu->GetHeader().GetAddr2() &&
1828 !mpdu->GetHeader().GetAddr1().IsGroup())
1829 {
1830 auto seqNo = mpdu->GetHeader().GetSequenceNumber();
1831 auto [it, success] =
1832 m_inflightCount.insert({seqNo, mpdu->GetInFlightLinkIds().size()});
1833 if (!success)
1834 {
1835 it->second = std::max(it->second, mpdu->GetInFlightLinkIds().size());
1836 }
1837 }
1838 }
1839 for (std::size_t i = 0; i < psdu->GetNMpdus(); i++)
1840 {
1841 // corrupt QoS data frame with sequence number equal to 1 (only once) if we are
1842 // not in the AP to broadcast traffic pattern (broadcast frames are not retransmitted)
1843 // nor in the STA to broadcast or STA to STA traffic patterns (retransmissions from
1844 // STA 1 could collide with frames forwarded by the AP)
1845 if (psdu->GetHeader(i).GetSequenceNumber() != 1 ||
1849 {
1850 continue;
1851 }
1852 auto uid = psdu->GetPayload(i)->GetUid();
1853 if (!m_dataCorrupted)
1854 {
1855 m_uidList.push_front(uid);
1856 m_dataCorrupted = true;
1857 NS_LOG_INFO("CORRUPTED");
1858 m_errorModels.at(psdu->GetAddr1())->SetList(m_uidList);
1859 }
1860 else
1861 {
1862 // do not corrupt the QoS data frame anymore
1863 if (auto it = std::find(m_uidList.cbegin(), m_uidList.cend(), uid);
1864 it != m_uidList.cend())
1865 {
1866 m_uidList.erase(it);
1867 }
1868 m_errorModels.at(psdu->GetAddr1())->SetList(m_uidList);
1869 }
1870 break;
1871 }
1872 break;
1873 case WIFI_MAC_CTL_BACKRESP: {
1874 // ignore BlockAck frames not addressed to the source of the application packets
1875 if (!m_sourceMac->GetLinkIdByAddress(psdu->GetHeader(0).GetAddr1()))
1876 {
1877 break;
1878 }
1879 if (m_nMaxInflight > 1)
1880 {
1881 // we do not check the content of BlockAck when m_nMaxInflight is greater than 1
1882 break;
1883 }
1884 CheckBlockAck(psdu, txVector, linkId);
1886 if (m_blockAckCount == 2)
1887 {
1888 // corrupt the second BlockAck frame to simulate a missed BlockAck
1889 m_uidList.push_front(psdu->GetPacket()->GetUid());
1890 NS_LOG_INFO("CORRUPTED");
1891 m_errorModels.at(psdu->GetAddr1())->SetList(m_uidList);
1892 }
1893 break;
1895 // ignore BlockAckReq frames not transmitted by the source of the application packets
1896 if (m_sourceMac->GetLinkIdByAddress(psdu->GetHeader(0).GetAddr2()))
1897 {
1899 }
1900 break;
1901 }
1902 default:;
1903 }
1904}
1905
1906void
1908 const WifiTxVector& txVector,
1909 uint8_t linkId)
1910{
1911 NS_TEST_ASSERT_MSG_EQ(m_baEnabled, true, "No BlockAck expected without BA agreement");
1913 true,
1914 "No BlockAck expected in AP to broadcast traffic pattern");
1915
1916 /*
1917 * ┌───────┬───────X ┌───────┐
1918 * link 0 │ 0 │ 1 │ │ 1 │
1919 * ───────┴───────┴───────┴┬──┬────┴───────┴┬───┬────────────────────────
1920 * │BA│ │ACK│
1921 * └──┘ └───┘
1922 * ┌───────┬───────┐ ┌───────┬───────┐
1923 * link 1 │ 2 │ 3 │ │ 2 │ 3 │
1924 * ────────────────────┴───────┴───────┴┬──X───┴───────┴───────┴┬──┬─────
1925 * │BA│ │BA│
1926 * └──┘ └──┘
1927 */
1928 auto mpdu = *psdu->begin();
1929 CtrlBAckResponseHeader blockAck;
1930 mpdu->GetPacket()->PeekHeader(blockAck);
1931 bool isMpdu1corrupted = (m_trafficPattern == WifiTrafficPattern::STA_TO_AP ||
1933
1934 switch (m_blockAckCount)
1935 {
1936 case 0: // first BlockAck frame (all traffic patterns)
1938 true,
1939 "MPDU 0 expected to be successfully received");
1941 blockAck.IsPacketReceived(1),
1942 !isMpdu1corrupted,
1943 "MPDU 1 expected to be received only in STA_TO_STA/STA_TO_BCAST scenarios");
1944 // if there are at least two links setup, we expect all MPDUs to be inflight
1945 // (on distinct links)
1946 if (m_staMacs[0]->GetSetupLinkIds().size() > 1)
1947 {
1948 auto queue = m_sourceMac->GetTxopQueue(AC_BE);
1949 auto rcvMac = m_sourceMac == m_staMacs[0] ? StaticCast<WifiMac>(m_apMac)
1951 auto item = queue->PeekByTidAndAddress(0, rcvMac->GetAddress());
1952 std::size_t nQueuedPkt = 0;
1953 auto delay = WifiPhy::CalculateTxDuration(psdu,
1954 txVector,
1955 rcvMac->GetWifiPhy(linkId)->GetPhyBand()) +
1956 MicroSeconds(1); // to account for propagation delay
1957
1958 while (item)
1959 {
1960 auto seqNo = item->GetHeader().GetSequenceNumber();
1961 NS_TEST_EXPECT_MSG_EQ(item->IsInFlight(),
1962 true,
1963 "MPDU with seqNo=" << seqNo << " is not in flight");
1964 auto linkIds = item->GetInFlightLinkIds();
1965 NS_TEST_EXPECT_MSG_EQ(linkIds.size(),
1966 1,
1967 "MPDU with seqNo=" << seqNo
1968 << " is in flight on multiple links");
1969 // The first two MPDUs are in flight on the same link on which the BlockAck
1970 // is sent. The other two MPDUs (only for AP to STA/STA to AP scenarios) are
1971 // in flight on a different link.
1972 auto srcLinkId = m_sourceMac->GetLinkIdByAddress(mpdu->GetHeader().GetAddr1());
1973 NS_TEST_ASSERT_MSG_EQ(srcLinkId.has_value(),
1974 true,
1975 "Addr1 of BlockAck is not an originator's link address");
1976 NS_TEST_EXPECT_MSG_EQ((*linkIds.begin() == *srcLinkId),
1977 (seqNo <= 1),
1978 "MPDU with seqNo=" << seqNo
1979 << " in flight on unexpected link");
1980 // check the Retry subfield and whether this MPDU is still queued
1981 // after the originator has processed this BlockAck
1982
1983 // MPDUs acknowledged via this BlockAck are no longer queued
1984 bool isQueued = (seqNo > (isMpdu1corrupted ? 0 : 1));
1985 // The Retry subfield is set if the MPDU has not been acknowledged (i.e., it
1986 // is still queued) and has been transmitted on the same link as the BlockAck
1987 // (i.e., its sequence number is less than or equal to 1)
1988 bool isRetry = isQueued && seqNo <= 1;
1989
1990 Simulator::Schedule(delay, [this, item, isQueued, isRetry]() {
1991 NS_TEST_EXPECT_MSG_EQ(item->IsQueued(),
1992 isQueued,
1993 "MPDU with seqNo="
1994 << item->GetHeader().GetSequenceNumber() << " should "
1995 << (isQueued ? "" : "not") << " be queued");
1997 item->GetHeader().IsRetry(),
1998 isRetry,
1999 "Unexpected value for the Retry subfield of the MPDU with seqNo="
2000 << item->GetHeader().GetSequenceNumber());
2001 });
2002
2003 nQueuedPkt++;
2004 item = queue->PeekByTidAndAddress(0, rcvMac->GetAddress(), item);
2005 }
2006 // Each MPDU contains an A-MSDU consisting of two MSDUs
2007 NS_TEST_EXPECT_MSG_EQ(nQueuedPkt, m_nPackets / 2, "Unexpected number of queued MPDUs");
2008 }
2009 break;
2010 case 1: // second BlockAck frame (STA to AP and AP to STA traffic patterns only)
2011 case 2: // third BlockAck frame (STA to AP and AP to STA traffic patterns only)
2014 true,
2015 "Did not expect to receive a second BlockAck");
2016 // the second BlockAck is corrupted, but the data frames have been received successfully
2017 std::pair<uint16_t, uint16_t> seqNos;
2018 // if multiple links were setup, the transmission of the second A-MPDU started before
2019 // the end of the first one, so the second A-MPDU includes MPDUs with sequence numbers
2020 // 2 and 3. Otherwise, MPDU with sequence number 1 is retransmitted along with the MPDU
2021 // with sequence number 2.
2022 if (m_staMacs[0]->GetSetupLinkIds().size() > 1)
2023 {
2024 seqNos = {2, 3};
2025 }
2026 else
2027 {
2028 seqNos = {1, 2};
2029 }
2030 NS_TEST_EXPECT_MSG_EQ(blockAck.IsPacketReceived(seqNos.first),
2031 true,
2032 "MPDU " << seqNos.first << " expected to be successfully received");
2033 NS_TEST_EXPECT_MSG_EQ(blockAck.IsPacketReceived(seqNos.second),
2034 true,
2035 "MPDU " << seqNos.second << " expected to be successfully received");
2036 break;
2037 }
2038}
2039
2040void
2042{
2044
2045 if (m_baEnabled)
2046 {
2047 // Enable A-MSDU aggregation. Max A-MSDU size is set such that two MSDUs can be aggregated
2048 for (auto mac : std::initializer_list<Ptr<WifiMac>>{m_apMac, m_staMacs[0], m_staMacs[1]})
2049 {
2050 mac->SetAttribute("BE_MaxAmsduSize", UintegerValue(2100));
2051 mac->GetQosTxop(AC_BE)->SetAttribute("UseExplicitBarAfterMissedBlockAck",
2053 mac->GetQosTxop(AC_BE)->SetAttribute("NMaxInflights", UintegerValue(m_nMaxInflight));
2054 }
2055 }
2056
2057 // install post reception error model on all devices
2058 for (std::size_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
2059 {
2060 auto errorModel = CreateObject<ListErrorModel>();
2061 m_errorModels[m_apMac->GetFrameExchangeManager(linkId)->GetAddress()] = errorModel;
2062 m_apMac->GetWifiPhy(linkId)->SetPostReceptionErrorModel(errorModel);
2063 }
2064 for (std::size_t i : {0, 1})
2065 {
2066 for (const auto linkId : m_staMacs[i]->GetLinkIds())
2067 {
2068 auto errorModel = CreateObject<ListErrorModel>();
2069 m_errorModels[m_staMacs[i]->GetFrameExchangeManager(linkId)->GetAddress()] = errorModel;
2070 m_staMacs[i]->GetWifiPhy(linkId)->SetPostReceptionErrorModel(errorModel);
2071 }
2072 }
2073}
2074
2075void
2077{
2078 Address destAddr;
2079
2080 switch (m_trafficPattern)
2081 {
2084 destAddr = m_staMacs[1]->GetDevice()->GetAddress();
2085 break;
2088 destAddr = m_apMac->GetDevice()->GetAddress();
2089 break;
2092 destAddr = m_staMacs[1]->GetDevice()->GetAddress();
2093 break;
2096 destAddr = Mac48Address::GetBroadcast();
2097 break;
2100 destAddr = Mac48Address::GetBroadcast();
2101 break;
2102 }
2103
2104 PacketSocketAddress sockAddr;
2105 sockAddr.SetSingleDevice(m_sourceMac->GetDevice()->GetIfIndex());
2106 sockAddr.SetPhysicalAddress(destAddr);
2107 sockAddr.SetProtocol(1);
2108
2109 // install first client application generating at most 4 packets
2110 m_sourceMac->GetDevice()->GetNode()->AddApplication(
2111 GetApplication(sockAddr, std::min<std::size_t>(m_nPackets, 4), 1000));
2112
2113 if (m_nPackets > 4)
2114 {
2115 // install a second client application generating the remaining packets and
2116 // starting during transmission of first A-MPDU, if multiple links are setup
2117 m_sourceMac->GetDevice()->GetNode()->AddApplication(
2118 GetApplication(sockAddr, m_nPackets - 4, 1000, MilliSeconds(4)));
2119 }
2120
2122}
2123
2124void
2126{
2128
2129 // Expected number of packets received by each node (AP, STA 0, STA 1) at application layer
2130 std::array<std::size_t, 3> expectedRxPkts{};
2131
2132 switch (m_trafficPattern)
2133 {
2136 // only STA 1 receives the m_nPackets packets that have been transmitted
2137 expectedRxPkts[2] = m_nPackets;
2138 break;
2140 // only the AP receives the m_nPackets packets that have been transmitted
2141 expectedRxPkts[0] = m_nPackets;
2142 break;
2144 // the AP replicates the broadcast frames on all the links, hence each station
2145 // receives the m_nPackets packets N times, where N is the number of setup link
2146 expectedRxPkts[1] = m_nPackets * m_staMacs[0]->GetSetupLinkIds().size();
2147 expectedRxPkts[2] = m_nPackets * m_staMacs[1]->GetSetupLinkIds().size();
2148 break;
2150 // the AP receives the m_nPackets packets and then replicates them on all the links,
2151 // hence STA 1 receives m_nPackets packets N times, where N is the number of setup link
2152 expectedRxPkts[0] = m_nPackets;
2153 expectedRxPkts[2] = m_nPackets * m_staMacs[1]->GetSetupLinkIds().size();
2154 break;
2155 }
2156
2158 +expectedRxPkts[0],
2159 "Unexpected number of packets received by the AP");
2161 +expectedRxPkts[1],
2162 "Unexpected number of packets received by STA 0");
2164 +expectedRxPkts[2],
2165 "Unexpected number of packets received by STA 1");
2166
2167 // check that the expected number of BlockAck frames are transmitted
2168 if (m_baEnabled && m_nMaxInflight == 1)
2169 {
2170 std::size_t expectedBaCount = 0;
2171 std::size_t expectedBarCount = 0;
2172
2173 switch (m_trafficPattern)
2174 {
2177 // two A-MPDUs are transmitted and one BlockAck is corrupted
2178 expectedBaCount = 3;
2179 // one BlockAckReq is sent if m_useBarAfterMissedBa is true
2180 expectedBarCount = m_useBarAfterMissedBa ? 1 : 0;
2181 break;
2184 // only one A-MPDU is transmitted and the BlockAck is not corrupted
2185 expectedBaCount = 1;
2186 break;
2187 default:;
2188 }
2190 expectedBaCount,
2191 "Unexpected number of BlockAck frames");
2193 expectedBarCount,
2194 "Unexpected number of BlockAckReq frames");
2195 }
2196
2197 // check that setting the QosTxop::NMaxInflights attribute has the expected effect.
2198 // We do not support sending an MPDU multiple times concurrently without Block Ack
2199 // agreement. Also, broadcast frames are already duplicated and sent on all links.
2201 {
2203 m_inflightCount.size(),
2204 m_nPackets / 2,
2205 "Did not collect number of simultaneous transmissions for all data frames");
2206
2207 auto nMaxInflight = std::min(m_nMaxInflight, m_staMacs[0]->GetSetupLinkIds().size());
2208 std::size_t maxCount = 0;
2209 for (const auto& [seqNo, count] : m_inflightCount)
2210 {
2212 count,
2213 nMaxInflight,
2214 "MPDU with seqNo=" << seqNo
2215 << " transmitted simultaneously more times than allowed");
2216 maxCount = std::max(maxCount, count);
2217 }
2218
2220 maxCount,
2221 nMaxInflight,
2222 "Expected that at least one data frame was transmitted simultaneously a number of "
2223 "times equal to the NMaxInflights attribute");
2224 }
2225
2227}
2228
2230 WifiMuTrafficPattern muTrafficPattern,
2231 WifiUseBarAfterMissedBa useBarAfterMissedBa,
2232 uint8_t nMaxInflight)
2234 std::string("Check MU data transmission between MLDs ") +
2235 (useBarAfterMissedBa == WifiUseBarAfterMissedBa::YES
2236 ? "(send BAR after BlockAck timeout,"
2237 : "(send Data frames after BlockAck timeout,") +
2238 " MU Traffic pattern: " + std::to_string(static_cast<uint8_t>(muTrafficPattern)) +
2239 ", nMaxInflight=" + std::to_string(nMaxInflight) + ")",
2240 2,
2241 baseParams),
2242 m_muTrafficPattern(muTrafficPattern),
2243 m_useBarAfterMissedBa(useBarAfterMissedBa == WifiUseBarAfterMissedBa::YES),
2244 m_nMaxInflight(nMaxInflight),
2245 m_sockets(m_nStations),
2246 m_nPackets(muTrafficPattern == WifiMuTrafficPattern::UL_MU ? 4 : 8)
2247{
2248}
2249
2250void
2252 uint8_t phyId,
2253 WifiConstPsduMap psduMap,
2254 WifiTxVector txVector,
2255 double txPowerW)
2256{
2257 MultiLinkOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
2258 auto linkId = m_txPsdus.back().linkId;
2259
2260 CtrlTriggerHeader trigger;
2261
2262 for (const auto& [staId, psdu] : psduMap)
2263 {
2264 switch (psdu->GetHeader(0).GetType())
2265 {
2266 case WIFI_MAC_QOSDATA:
2267 CheckAddresses(psdu);
2268 if (psdu->GetHeader(0).HasData())
2269 {
2270 bool isDl = psdu->GetHeader(0).IsFromDs();
2271 auto linkAddress =
2272 isDl ? psdu->GetHeader(0).GetAddr1() : psdu->GetHeader(0).GetAddr2();
2273 auto address = m_apMac->GetMldAddress(linkAddress).value_or(linkAddress);
2274
2275 for (const auto& mpdu : *psdu)
2276 {
2277 // determine the max number of simultaneous transmissions for this MPDU
2278 auto seqNo = mpdu->GetHeader().GetSequenceNumber();
2279 auto [it, success] = m_inflightCount.insert(
2280 {{address, seqNo}, mpdu->GetInFlightLinkIds().size()});
2281 if (!success)
2282 {
2283 it->second = std::max(it->second, mpdu->GetInFlightLinkIds().size());
2284 }
2285 }
2286 for (std::size_t i = 0; i < psdu->GetNMpdus(); i++)
2287 {
2288 // MPDUs with seqNo=2 are always transmitted in an MU PPDU
2289 if (psdu->GetHeader(i).GetSequenceNumber() == 2)
2290 {
2292 {
2293 NS_TEST_EXPECT_MSG_EQ(txVector.IsUlMu(),
2294 true,
2295 "MPDU " << **std::next(psdu->begin(), i)
2296 << " not transmitted in a TB PPDU");
2297 }
2298 else
2299 {
2300 NS_TEST_EXPECT_MSG_EQ(txVector.GetHeMuUserInfoMap().size(),
2301 2,
2302 "MPDU " << **std::next(psdu->begin(), i)
2303 << " not transmitted in a DL MU PPDU");
2304 }
2305 }
2306 // corrupt QoS data frame with sequence number equal to 3 (only once)
2307 if (psdu->GetHeader(i).GetSequenceNumber() != 3)
2308 {
2309 continue;
2310 }
2311 auto uid = psdu->GetPayload(i)->GetUid();
2312 if (!m_dataCorruptedSta)
2313 {
2314 m_uidList.push_front(uid);
2315 m_dataCorruptedSta = isDl ? psdu->GetAddr1() : psdu->GetAddr2();
2316 NS_LOG_INFO("CORRUPTED");
2317 m_errorModels.at(psdu->GetAddr1())->SetList(m_uidList);
2318 }
2319 else if ((isDl && m_dataCorruptedSta == psdu->GetAddr1()) ||
2320 (!isDl && m_dataCorruptedSta == psdu->GetAddr2()))
2321 {
2322 // do not corrupt the QoS data frame anymore
2323 if (auto it = std::find(m_uidList.cbegin(), m_uidList.cend(), uid);
2324 it != m_uidList.cend())
2325 {
2326 m_uidList.erase(it);
2327 }
2328 m_errorModels.at(psdu->GetAddr1())->SetList(m_uidList);
2329 }
2330 break;
2331 }
2332 }
2333 break;
2335 if (m_nMaxInflight > 1)
2336 {
2337 // we do not check the content of BlockAck when m_nMaxInflight is greater than 1
2338 break;
2339 }
2340 CheckBlockAck(psdu, txVector, linkId);
2342 // to simulate a missed BlockAck, corrupt the fifth BlockAck frame (the first
2343 // two BlockAck frames are sent to acknowledge the QoS data frames that triggered
2344 // the establishment of Block Ack agreements)
2345 if (m_blockAckCount == 5)
2346 {
2347 // corrupt the third BlockAck frame to simulate a missed BlockAck
2348 m_uidList.push_front(psdu->GetPacket()->GetUid());
2349 NS_LOG_INFO("CORRUPTED");
2350 m_errorModels.at(psdu->GetAddr1())->SetList(m_uidList);
2351 }
2352 break;
2354 psdu->GetPayload(0)->PeekHeader(trigger);
2355 // the MU scheduler requests channel access on all links but we have to perform the
2356 // following actions only once (hence why we only consider TF transmitted on link 0)
2357 if (trigger.IsBasic() && m_waitFirstTf)
2358 {
2359 m_waitFirstTf = false;
2360 // the AP is starting the transmission of the Basic Trigger frame, so generate
2361 // the configured number of packets at STAs, which are sent in TB PPDUs, when
2362 // transmission of the Trigger Frame ends
2363 auto band = mac->GetWifiPhy(linkId)->GetPhyBand();
2364 Time txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, band);
2365 for (uint8_t i = 0; i < m_nStations; i++)
2366 {
2367 m_staMacs[i]->GetDevice()->GetNode()->AddApplication(
2368 GetApplication(m_sockets[i], m_nPackets, 450, txDuration, i * 4));
2369 }
2370 }
2371 if (++m_tfCount == m_staMacs[0]->GetSetupLinkIds().size())
2372 {
2373 // a TF has been sent on all the setup links, we can now disable UL OFDMA
2374 auto muScheduler = m_apMac->GetObject<MultiUserScheduler>();
2375 NS_TEST_ASSERT_MSG_NE(muScheduler, nullptr, "Expected an aggregated MU scheduler");
2376 muScheduler->SetAttribute("EnableUlOfdma", BooleanValue(false));
2377 }
2378 break;
2379 default:;
2380 }
2381 }
2382}
2383
2384void
2386 const WifiTxVector& txVector,
2387 uint8_t linkId)
2388{
2389 /*
2390 * Example sequence with DL_MU_BAR_BA_SEQUENCE
2391 * ┌───────┬───────X
2392 * (To:1) │ 2 │ 3 │
2393 * ├───────┼───────┤ ┌───┐ ┌───────┐
2394 * [link 0] (To:0) │ 2 │ 3 │ │BAR│ (To:1) │ 3 │
2395 * ────────────────┴───────┴───────┴┬──┼───┼──┬──────────┴───────┴┬───┬────────
2396 * │BA│ │BA│ │ACK│
2397 * └──┘ └──┘ └───┘
2398 * ┌───────┬───────┐
2399 * (To:1) │ 4 │ 5 │
2400 * ├───────┼───────┤ ┌───┐ ┌───┐
2401 * [link 1] (To:0) │ 4 │ 5 │ │BAR│ │BAR│
2402 * ────────────────────────────┴───────┴───────┴┬──X────┴───┴┬──┼───┼──┬───────
2403 * │BA│ │BA│ │BA│
2404 * └──┘ └──┘ └──┘
2405 *
2406 * Example sequence with UL_MU
2407 *
2408 * ┌──┐ ┌────┐ ┌───┐
2409 * [link 0] │TF│ │M-BA│ │ACK│
2410 * ─────────┴──┴──┬───────┬───────┬──┴────┴────────────┬───────┬─┴───┴─────────
2411 * (From:0) │ 2 │ 3 │ (From:1) │ 3 │
2412 * ├───────┼───────┤ └───────┘
2413 * (From:1) │ 2 │ 3 │
2414 * └───────┴───────X
2415 * ┌──┐
2416 * [link 1] │TF│
2417 * ─────────┴──┴──┬───────────────┬────────────────────────────────────────────
2418 * (From:0) │ QoS Null │
2419 * ├───────────────┤
2420 * (From:1) │ QoS Null │
2421 * └───────────────┘
2422 */
2423 auto mpdu = *psdu->begin();
2424 CtrlBAckResponseHeader blockAck;
2425 mpdu->GetPacket()->PeekHeader(blockAck);
2426 bool isMpdu3corrupted;
2427
2428 switch (m_blockAckCount)
2429 {
2430 case 0:
2431 case 1: // Ignore the first two BlockAck frames that acknowledged frames sent to establish BA
2432 break;
2433 case 2:
2435 {
2436 NS_TEST_EXPECT_MSG_EQ(blockAck.IsMultiSta(), true, "Expected a Multi-STA BlockAck");
2437 for (uint8_t i = 0; i < m_nStations; i++)
2438 {
2439 auto indices = blockAck.FindPerAidTidInfoWithAid(m_staMacs[i]->GetAssociationId());
2440 NS_TEST_ASSERT_MSG_EQ(indices.size(), 1, "Expected one Per AID TID Info per STA");
2441 auto index = indices.front();
2443 true,
2444 "Expected that a QoS data frame was corrupted");
2445 isMpdu3corrupted =
2446 m_staMacs[i]->GetLinkIdByAddress(*m_dataCorruptedSta).has_value();
2447 NS_TEST_EXPECT_MSG_EQ(blockAck.IsPacketReceived(2, index),
2448 true,
2449 "MPDU 2 expected to be successfully received");
2450 NS_TEST_EXPECT_MSG_EQ(blockAck.IsPacketReceived(3, index),
2451 !isMpdu3corrupted,
2452 "Unexpected reception status for MPDU 3");
2453 }
2454
2455 break;
2456 }
2457 case 3:
2458 // BlockAck frames in response to the first DL MU PPDU
2459 isMpdu3corrupted = (mpdu->GetHeader().GetAddr2() == m_dataCorruptedSta);
2461 true,
2462 "MPDU 2 expected to be successfully received");
2464 !isMpdu3corrupted,
2465 "Unexpected reception status for MPDU 3");
2466 // in case of DL MU, if there are at least two links setup, we expect all MPDUs to
2467 // be inflight (on distinct links)
2469 m_staMacs[0]->GetSetupLinkIds().size() > 1)
2470 {
2471 auto queue = m_apMac->GetTxopQueue(AC_BE);
2472 Ptr<StaWifiMac> rcvMac;
2473 if (m_staMacs[0]->GetFrameExchangeManager(linkId)->GetAddress() ==
2474 mpdu->GetHeader().GetAddr2())
2475 {
2476 rcvMac = m_staMacs[0];
2477 }
2478 else if (m_staMacs[1]->GetFrameExchangeManager(linkId)->GetAddress() ==
2479 mpdu->GetHeader().GetAddr2())
2480 {
2481 rcvMac = m_staMacs[1];
2482 }
2483 else
2484 {
2485 NS_ABORT_MSG("BlockAck frame not sent by a station in DL scenario");
2486 }
2487 auto item = queue->PeekByTidAndAddress(0, rcvMac->GetAddress());
2488 std::size_t nQueuedPkt = 0;
2489 auto delay = WifiPhy::CalculateTxDuration(psdu,
2490 txVector,
2491 rcvMac->GetWifiPhy(linkId)->GetPhyBand()) +
2492 MicroSeconds(1); // to account for propagation delay
2493
2494 while (item)
2495 {
2496 auto seqNo = item->GetHeader().GetSequenceNumber();
2497 NS_TEST_EXPECT_MSG_EQ(item->IsInFlight(),
2498 true,
2499 "MPDU with seqNo=" << seqNo << " is not in flight");
2500 auto linkIds = item->GetInFlightLinkIds();
2501 NS_TEST_EXPECT_MSG_EQ(linkIds.size(),
2502 1,
2503 "MPDU with seqNo=" << seqNo
2504 << " is in flight on multiple links");
2505 // The first two MPDUs are in flight on the same link on which the BlockAck
2506 // is sent. The other two MPDUs (only for AP to STA/STA to AP scenarios) are
2507 // in flight on a different link.
2508 auto srcLinkId = m_apMac->GetLinkIdByAddress(mpdu->GetHeader().GetAddr1());
2509 NS_TEST_ASSERT_MSG_EQ(srcLinkId.has_value(),
2510 true,
2511 "Addr1 of BlockAck is not an originator's link address");
2512 NS_TEST_EXPECT_MSG_EQ((*linkIds.begin() == *srcLinkId),
2513 (seqNo <= 3),
2514 "MPDU with seqNo=" << seqNo
2515 << " in flight on unexpected link");
2516 // check the Retry subfield and whether this MPDU is still queued
2517 // after the originator has processed this BlockAck
2518
2519 // MPDUs acknowledged via this BlockAck are no longer queued
2520 bool isQueued = (seqNo > (isMpdu3corrupted ? 2 : 3));
2521 // The Retry subfield is set if the MPDU has not been acknowledged (i.e., it
2522 // is still queued) and has been transmitted on the same link as the BlockAck
2523 // (i.e., its sequence number is less than or equal to 2)
2524 bool isRetry = isQueued && seqNo <= 3;
2525
2526 Simulator::Schedule(delay, [this, item, isQueued, isRetry]() {
2527 NS_TEST_EXPECT_MSG_EQ(item->IsQueued(),
2528 isQueued,
2529 "MPDU with seqNo="
2530 << item->GetHeader().GetSequenceNumber() << " should "
2531 << (isQueued ? "" : "not") << " be queued");
2533 item->GetHeader().IsRetry(),
2534 isRetry,
2535 "Unexpected value for the Retry subfield of the MPDU with seqNo="
2536 << item->GetHeader().GetSequenceNumber());
2537 });
2538
2539 nQueuedPkt++;
2540 item = queue->PeekByTidAndAddress(0, rcvMac->GetAddress(), item);
2541 }
2542 // Each MPDU contains an A-MSDU consisting of two MSDUs
2543 NS_TEST_EXPECT_MSG_EQ(nQueuedPkt, m_nPackets / 2, "Unexpected number of queued MPDUs");
2544 }
2545 break;
2546 }
2547}
2548
2549void
2551{
2552 switch (m_muTrafficPattern)
2553 {
2555 Config::SetDefault("ns3::WifiDefaultAckManager::DlMuAckSequenceType",
2557 break;
2559 Config::SetDefault("ns3::WifiDefaultAckManager::DlMuAckSequenceType",
2561 break;
2563 Config::SetDefault("ns3::WifiDefaultAckManager::DlMuAckSequenceType",
2565 break;
2566 default:;
2567 }
2568
2570
2571 // Enable A-MSDU aggregation. Max A-MSDU size is set such that two MSDUs can be aggregated
2572 for (auto mac : std::initializer_list<Ptr<WifiMac>>{m_apMac, m_staMacs[0], m_staMacs[1]})
2573 {
2574 mac->SetAttribute("BE_MaxAmsduSize", UintegerValue(1050));
2575 mac->GetQosTxop(AC_BE)->SetAttribute("UseExplicitBarAfterMissedBlockAck",
2577 mac->GetQosTxop(AC_BE)->SetAttribute("NMaxInflights", UintegerValue(m_nMaxInflight));
2578
2579 mac->SetAttribute("VI_MaxAmsduSize", UintegerValue(1050));
2580 mac->GetQosTxop(AC_VI)->SetAttribute("UseExplicitBarAfterMissedBlockAck",
2582 mac->GetQosTxop(AC_VI)->SetAttribute("NMaxInflights", UintegerValue(m_nMaxInflight));
2583 }
2584
2585 // aggregate MU scheduler
2586 auto muScheduler = CreateObjectWithAttributes<RrMultiUserScheduler>("EnableUlOfdma",
2587 BooleanValue(false),
2588 "EnableBsrp",
2589 BooleanValue(false),
2590 "UlPsduSize",
2591 UintegerValue(2000));
2592 m_apMac->AggregateObject(muScheduler);
2593
2594 // install post reception error model on all devices
2595 for (std::size_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
2596 {
2597 auto errorModel = CreateObject<ListErrorModel>();
2598 m_errorModels[m_apMac->GetFrameExchangeManager(linkId)->GetAddress()] = errorModel;
2599 m_apMac->GetWifiPhy(linkId)->SetPostReceptionErrorModel(errorModel);
2600 }
2601 for (std::size_t i : {0, 1})
2602 {
2603 for (const auto linkId : m_staMacs[i]->GetLinkIds())
2604 {
2605 auto errorModel = CreateObject<ListErrorModel>();
2606 m_errorModels[m_staMacs[i]->GetFrameExchangeManager(linkId)->GetAddress()] = errorModel;
2607 m_staMacs[i]->GetWifiPhy(linkId)->SetPostReceptionErrorModel(errorModel);
2608 }
2609 }
2610}
2611
2612void
2614{
2616 {
2617 // DL Traffic
2618 for (uint8_t i = 0; i < m_nStations; i++)
2619 {
2620 PacketSocketAddress sockAddr;
2621 sockAddr.SetSingleDevice(m_apMac->GetDevice()->GetIfIndex());
2622 sockAddr.SetPhysicalAddress(m_staMacs[i]->GetDevice()->GetAddress());
2623 sockAddr.SetProtocol(1);
2624
2625 // the first client application generates three packets in order
2626 // to trigger the establishment of a Block Ack agreement
2627 m_apMac->GetDevice()->GetNode()->AddApplication(
2628 GetApplication(sockAddr, 3, 450, i * MilliSeconds(50)));
2629
2630 // the second client application generates the first half of the selected number
2631 // of packets, which are sent in DL MU PPDUs, and starts after all BA agreements
2632 // are established
2633 m_apMac->GetDevice()->GetNode()->AddApplication(
2634 GetApplication(sockAddr, m_nPackets / 2, 450, m_nStations * MilliSeconds(50)));
2635
2636 // the third client application generates the second half of the selected number
2637 // of packets, which are sent in DL MU PPDUs, and starts during transmission of
2638 // first A-MPDU, if multiple links are setup
2639 m_apMac->GetDevice()->GetNode()->AddApplication(
2640 GetApplication(sockAddr,
2641 m_nPackets / 2,
2642 450,
2644 }
2645 }
2646 else
2647 {
2648 // UL Traffic
2649 for (uint8_t i = 0; i < m_nStations; i++)
2650 {
2651 m_sockets[i].SetSingleDevice(m_staMacs[i]->GetDevice()->GetIfIndex());
2652 m_sockets[i].SetPhysicalAddress(m_apMac->GetDevice()->GetAddress());
2653 m_sockets[i].SetProtocol(1);
2654
2655 // the first client application generates three packets in order
2656 // to trigger the establishment of a Block Ack agreement
2657 m_staMacs[i]->GetDevice()->GetNode()->AddApplication(
2658 GetApplication(m_sockets[i], 3, 450, i * MilliSeconds(50), i * 4));
2659
2660 // packets to be included in TB PPDUs are generated (by Transmit()) when
2661 // the first Basic Trigger Frame is sent by the AP
2662 }
2663
2664 // MU scheduler starts requesting channel access when we are done with BA agreements
2666 auto muScheduler = m_apMac->GetObject<MultiUserScheduler>();
2667 NS_TEST_ASSERT_MSG_NE(muScheduler, nullptr, "Expected an aggregated MU scheduler");
2668 muScheduler->SetAttribute("EnableUlOfdma", BooleanValue(true));
2669 muScheduler->SetAccessReqInterval(MilliSeconds(3));
2670 // channel access is requested only once
2671 muScheduler->SetAccessReqInterval(Seconds(0));
2672 });
2673 }
2674
2676}
2677
2678void
2680{
2682
2683 // Expected number of packets received by each node (AP, STA 0, STA 1) at application layer
2684 std::array<std::size_t, 3> expectedRxPkts{};
2685
2686 switch (m_muTrafficPattern)
2687 {
2691 // both STA 0 and STA 1 receive m_nPackets + 3 (sent to trigger BA establishment) packets
2692 expectedRxPkts[1] = m_nPackets + 3;
2693 expectedRxPkts[2] = m_nPackets + 3;
2694 break;
2696 // AP receives m_nPackets + 3 (sent to trigger BA establishment) packets from each station
2697 expectedRxPkts[0] = 2 * (m_nPackets + 3);
2698 break;
2699 }
2700
2702 +expectedRxPkts[0],
2703 "Unexpected number of packets received by the AP");
2705 +expectedRxPkts[1],
2706 "Unexpected number of packets received by STA 0");
2708 +expectedRxPkts[2],
2709 "Unexpected number of packets received by STA 1");
2710
2711 // check that setting the QosTxop::NMaxInflights attribute has the expected effect.
2712 // For DL, for each station we send 2 MPDUs to trigger BA agreement and m_nPackets / 2 MPDUs
2713 // For UL, each station sends 2 MPDUs to trigger BA agreement and m_nPackets / 2 MPDUs
2715 m_inflightCount.size(),
2716 2 * (2 + m_nPackets / 2),
2717 "Did not collect number of simultaneous transmissions for all data frames");
2718
2719 auto nMaxInflight = std::min(m_nMaxInflight, m_staMacs[0]->GetSetupLinkIds().size());
2720 std::size_t maxCount = 0;
2721 for (const auto& [txSeqNoPair, count] : m_inflightCount)
2722 {
2724 nMaxInflight,
2725 "MPDU with seqNo="
2726 << txSeqNoPair.second
2727 << " transmitted simultaneously more times than allowed");
2728 maxCount = std::max(maxCount, count);
2729 }
2730
2732 maxCount,
2733 nMaxInflight,
2734 "Expected that at least one data frame was transmitted simultaneously a number of "
2735 "times equal to the NMaxInflights attribute");
2736
2738}
2739
2742 "Check sequence numbers after CTS timeout",
2743 1,
2744 BaseParams{{"{36, 0, BAND_5GHZ, 0}", "{2, 0, BAND_2_4GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"},
2745 {"{36, 0, BAND_5GHZ, 0}", "{2, 0, BAND_2_4GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"},
2746 {},
2747 WifiAssocType::ML_SETUP}),
2748 m_nQosDataFrames(0),
2749 m_errorModel(CreateObject<ListErrorModel>()),
2750 m_rtsCorrupted(false)
2751{
2752}
2753
2754void
2756{
2757 // Enable RTS/CTS
2758 Config::SetDefault("ns3::WifiRemoteStationManager::RtsCtsThreshold", StringValue("1000"));
2759
2761
2762 // install post reception error model on all STAs affiliated with non-AP MLD
2763 for (const auto linkId : m_staMacs[0]->GetLinkIds())
2764 {
2765 m_staMacs[0]->GetWifiPhy(linkId)->SetPostReceptionErrorModel(m_errorModel);
2766 }
2767}
2768
2769void
2771{
2772 m_sockAddr.SetSingleDevice(m_apMac->GetDevice()->GetIfIndex());
2773 m_sockAddr.SetPhysicalAddress(m_staMacs[0]->GetAddress());
2775
2776 // install client application generating 4 packets
2777 m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(m_sockAddr, 4, 1000));
2778}
2779
2780void
2782 uint8_t phyId,
2783 WifiConstPsduMap psduMap,
2784 WifiTxVector txVector,
2785 double txPowerW)
2786{
2787 MultiLinkOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
2788
2789 auto psdu = psduMap.begin()->second;
2790
2791 if (psdu->GetHeader(0).IsRts() && !m_rtsCorrupted)
2792 {
2793 m_errorModel->SetList({psdu->GetPacket()->GetUid()});
2794 m_rtsCorrupted = true;
2795 // generate other packets when the first RTS is transmitted
2796 m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(m_sockAddr, 4, 1000));
2797 }
2798 else if (psdu->GetHeader(0).IsQosData())
2799 {
2801
2802 if (m_nQosDataFrames == 2)
2803 {
2804 // generate other packets when the second QoS data frame is transmitted
2805 m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(m_sockAddr, 4, 1000));
2806 }
2807 }
2808}
2809
2810void
2812{
2815
2816 NS_TEST_EXPECT_MSG_EQ(m_nQosDataFrames, 3, "Unexpected number of transmitted QoS data frames");
2817
2818 std::size_t count{};
2819
2820 for (const auto& txPsdu : m_txPsdus)
2821 {
2822 auto psdu = txPsdu.psduMap.begin()->second;
2823
2824 if (!psdu->GetHeader(0).IsQosData())
2825 {
2826 continue;
2827 }
2828
2829 NS_TEST_EXPECT_MSG_EQ(psdu->GetNMpdus(), 4, "Unexpected number of MPDUs in A-MPDU");
2830
2831 count++;
2832 uint16_t expectedSeqNo{};
2833
2834 switch (count)
2835 {
2836 case 1:
2837 expectedSeqNo = 4;
2838 break;
2839 case 2:
2840 expectedSeqNo = 0;
2841 break;
2842 case 3:
2843 expectedSeqNo = 8;
2844 break;
2845 }
2846
2847 for (const auto& mpdu : *PeekPointer(psdu))
2848 {
2849 NS_TEST_EXPECT_MSG_EQ(mpdu->GetHeader().GetSequenceNumber(),
2850 expectedSeqNo++,
2851 "Unexpected sequence number");
2852 }
2853 }
2854
2856}
2857
2860 "Check starting sequence number update after ADDBA Response timeout",
2861 1,
2862 BaseParams{{"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"},
2863 {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"},
2864 {},
2865 WifiAssocType::ML_SETUP}),
2866 m_nQosDataCount(0),
2867 m_staErrorModel(CreateObject<ListErrorModel>())
2868{
2869}
2870
2871void
2873{
2874 // Enable RTS/CTS by setting a threshold lower than packet size (1000)
2875 Config::SetDefault("ns3::WifiRemoteStationManager::RtsCtsThreshold", UintegerValue(900));
2876
2878
2879 // install post reception error model on all STAs affiliated with non-AP MLD
2880 for (const auto linkId : m_staMacs[0]->GetLinkIds())
2881 {
2882 m_staMacs[0]->GetWifiPhy(linkId)->SetPostReceptionErrorModel(m_staErrorModel);
2883 }
2884}
2885
2886void
2888{
2889 m_sockAddr.SetSingleDevice(m_apMac->GetDevice()->GetIfIndex());
2890 m_sockAddr.SetPhysicalAddress(m_staMacs[0]->GetAddress());
2892
2893 // install client application generating 1 packet of 1000 bytes on the AP MLD
2894 m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(m_sockAddr, 1, 1000));
2895}
2896
2897void
2899 uint8_t phyId,
2900 WifiConstPsduMap psduMap,
2901 WifiTxVector txVector,
2902 double txPowerW)
2903{
2904 auto psdu = psduMap.begin()->second;
2905 const auto& hdr = psdu->GetHeader(0);
2906
2907 if (hdr.IsAck())
2908 {
2909 NS_TEST_ASSERT_MSG_EQ(m_txPsdus.empty(), false, "No frame preceding transmitted Ack");
2910
2911 auto prevPsdu = m_txPsdus.back().psduMap.begin()->second;
2912
2913 if (prevPsdu->GetHeader(0).IsAction())
2914 {
2915 WifiActionHeader actionHdr;
2916 (*prevPsdu->begin())->GetPacket()->PeekHeader(actionHdr);
2917 if (actionHdr.GetCategory() == WifiActionHeader::BLOCK_ACK &&
2919 {
2920 // non-AP MLD is acknowledging the ADDBA Request sent by the AP MLD. When the
2921 // AP MLD receives the Ack, it starts an AddBaResponse timer; when the timer
2922 // expires, the AP MLD starts sending data frames with normal acknowledgment.
2923 // Block transmissions of the non-AP MLD on the link that has to be used to send
2924 // the ADDBA Response from now until the end of the timer.
2925
2926 m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2927 m_apMac->GetAddress(),
2928 {phyId});
2929
2930 auto band = m_apMac->GetWifiPhy(m_txPsdus.back().linkId)->GetPhyBand();
2931 auto ackDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, band);
2932
2933 // After the AddBaResponse timeout, unblock transmissions of the non-AP MLD on the
2934 // link on which the ADDBA Response has to be sent and block transmissions of the
2935 // AP MLD on the same link, so that we recreate the situation where the AP MLD sends
2936 // the QoS data frame on a link while the non-AP MLD is sending the ADDBA Response
2937 // frame on another link.
2939 ackDuration + m_apMac->GetQosTxop(AC_BE)->GetAddBaResponseTimeout(),
2940 [=, this]() {
2941 m_apMac->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
2942 m_staMacs[0]->GetAddress(),
2943 {phyId});
2944 m_staMacs[0]->UnblockUnicastTxOnLinks(
2945 WifiQueueBlockedReason::TID_NOT_MAPPED,
2946 m_apMac->GetAddress(),
2947 {phyId});
2948 });
2949 }
2950 }
2951 }
2952
2953 MultiLinkOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
2954
2955 if (hdr.IsAction())
2956 {
2957 WifiActionHeader actionHdr;
2958 (*psdu->begin())->GetPacket()->PeekHeader(actionHdr);
2959 if (actionHdr.GetCategory() == WifiActionHeader::BLOCK_ACK &&
2961 {
2962 auto band = m_staMacs[0]->GetDevice()->GetPhy(phyId)->GetPhyBand();
2963 auto addBaRespDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, band);
2964
2965 Simulator::Schedule(addBaRespDuration + TimeStep(1), [=, this]() {
2966 // After the AP MLD has received the ADDBA Response frame:
2967 // - check that the AP has one queued QoS data frame that is in flight
2968 auto mpdu = m_apMac->GetTxopQueue(AC_BE)->Peek();
2969 NS_TEST_ASSERT_MSG_NE(mpdu, nullptr, "Expected an MPDU in the AP MLD queue");
2970 NS_TEST_EXPECT_MSG_EQ(mpdu->GetHeader().IsQosData(),
2971 true,
2972 "Expected a QoS data frame");
2974 mpdu->IsInFlight(),
2975 true,
2976 "Expected the data frame to be inflight when ADDBA RESP is received");
2977
2978 // - check that the starting sequence number at the originator (AP MLD) equals
2979 // the sequence number of the inflight MPDU
2981 m_apMac->GetQosTxop(AC_BE)->GetBaStartingSequence(m_staMacs[0]->GetAddress(),
2982 0),
2983 mpdu->GetHeader().GetSequenceNumber(),
2984 "Unexpected BA Starting Sequence Number");
2985 });
2986 }
2987 }
2988 else if (hdr.IsQosData())
2989 {
2990 // corrupt the reception of the data frame the first time it is sent
2991 if (m_nQosDataCount++ == 0)
2992 {
2993 m_staErrorModel->SetList({psdu->GetPacket()->GetUid()});
2994 }
2995 else
2996 {
2997 m_staErrorModel->SetList({});
2998 }
2999 }
3000}
3001
3002void
3004{
3007
3008 NS_TEST_EXPECT_MSG_EQ(+m_rxPkts[1], 1, "Unexpected number of packets received by STA 0");
3009 NS_TEST_EXPECT_MSG_EQ(m_nQosDataCount, 2, "QoS data frame should be transmitted twice");
3010
3012}
3013
3015 : TestSuite("wifi-mlo", Type::UNIT)
3016{
3017 using ParamsTuple =
3018 std::tuple<MultiLinkOperationsTestBase::BaseParams, // base config params
3019 std::vector<uint8_t>, // link ID (as seen by AP device) of setup links
3020 std::vector<uint8_t>, // link ID (as seen by non-AP device) of setup links
3021 WifiTidToLinkMappingNegSupport, // AP negotiation support
3022 std::string, // DL TID-to-Link Mapping
3023 std::string>; // UL TID-to-Link Mapping
3024
3025 AddTestCase(new GetRnrLinkInfoTest(), TestCase::Duration::QUICK);
3026 AddTestCase(new MldSwapLinksTest(), TestCase::Duration::QUICK);
3029 std::vector<std::set<uint8_t>>{{0, 1, 2}, {1, 2}, {0, 1}, {0, 2}, {0}, {1}, {2}},
3030 WifiAssocType::ML_SETUP),
3031 TestCase::Duration::QUICK);
3034 std::vector<std::set<uint8_t>>{{0, 1, 2}, {1, 2}, {0, 1}, {0, 2}, {0}, {1}, {2}},
3035 WifiAssocType::LEGACY),
3036 TestCase::Duration::QUICK);
3037
3038 // check that the selection of channels in ML setup accounts for the inability of a
3039 // non-AP MLD to operate on a 160 MHz channel
3043 {"{42, 80, BAND_5GHZ, 2}", "{5, 40, BAND_2_4GHZ, 0}", "{7, 80, BAND_6GHZ, 0}"},
3044 {"{3, 40, BAND_2_4GHZ, 0}", "{15, 160, BAND_6GHZ, 7}", "{50, 160, BAND_5GHZ, 2}"},
3045 {},
3046 WifiAssocType::ML_SETUP},
3047 WifiScanType::PASSIVE,
3048 {0, 1, 2},
3049 {}, // IDs of setup links are the same for AP and non-AP devices
3050 WifiTidToLinkMappingNegSupport::ANY_LINK_SET,
3051 "",
3052 "",
3053 false),
3054 TestCase::Duration::QUICK);
3055
3056 for (const auto& [baseParams,
3057 setupLinks,
3058 staSetupLinks,
3059 apNegSupport,
3060 dlTidLinkMapping,
3061 ulTidLinkMapping] :
3062 {// matching channels: setup all links
3063 ParamsTuple(
3064 {{"{36, 0, BAND_5GHZ, 0}", "{2, 0, BAND_2_4GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"},
3065 {"{36, 0, BAND_5GHZ, 0}", "{2, 0, BAND_2_4GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"},
3066 {},
3067 WifiAssocType::ML_SETUP},
3068 {0, 1, 2},
3069 {}, // IDs of setup links are the same for AP and non-AP devices
3070 WifiTidToLinkMappingNegSupport::NOT_SUPPORTED, // AP MLD does not support TID-to-Link
3071 // Mapping negotiation
3072 "0,1,2,3 0,1,2; 4,5 0,1", // default mapping used instead
3073 "0,1,2,3 1,2; 6,7 0,1" // default mapping used instead
3074 ),
3075 // non-matching channels, matching PHY bands: setup all links
3076 ParamsTuple({{"{108, 0, BAND_5GHZ, 0}", "{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"},
3077 {"{36, 0, BAND_5GHZ, 0}", "{120, 0, BAND_5GHZ, 0}", "{5, 0, BAND_6GHZ, 0}"},
3078 {},
3079 WifiAssocType::ML_SETUP},
3080 {0, 1, 2},
3081 {}, // IDs of setup links are the same for AP and non-AP devices
3082 WifiTidToLinkMappingNegSupport::SAME_LINK_SET, // AP MLD does not support
3083 // distinct link sets for TIDs
3084 "0,1,2,3 0,1,2; 4,5 0,1", // default mapping used instead
3085 ""),
3086 // non-AP MLD switches band on some links to setup 3 links
3087 ParamsTuple({{"{2, 0, BAND_2_4GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{36, 0, BAND_5GHZ, 0}"},
3088 {"{36, 0, BAND_5GHZ, 0}", "{9, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
3089 {},
3090 WifiAssocType::ML_SETUP},
3091 {0, 1, 2},
3092 {}, // IDs of setup links are the same for AP and non-AP devices
3093 WifiTidToLinkMappingNegSupport::ANY_LINK_SET,
3094 "0,1,2,3 0; 4,5,6,7 1,2", // frames of two TIDs are generated
3095 "0,2,3 1,2; 1,4,5,6,7 0" // frames of two TIDs are generated
3096 ),
3097 // the first link of the non-AP MLD cannot change PHY band and no AP is operating on
3098 // that band, hence only 2 links are setup
3099 ParamsTuple(
3100 {{"{2, 0, BAND_2_4GHZ, 0}", "{36, 0, BAND_5GHZ, 0}", "{8, 20, BAND_2_4GHZ, 0}"},
3101 {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
3102 {0},
3103 WifiAssocType::ML_SETUP},
3104 {0, 1},
3105 {}, // IDs of setup links are the same for AP and non-AP devices
3106 WifiTidToLinkMappingNegSupport::SAME_LINK_SET, // AP MLD does not support distinct
3107 // link sets for TIDs
3108 "0,1,2,3,4,5,6,7 0",
3109 "0,1,2,3,4,5,6,7 0"),
3110 // the first link of the non-AP MLD cannot change PHY band and no AP is operating on
3111 // that band; the second link of the non-AP MLD cannot change PHY band and there is
3112 // an AP operating on the same channel; hence 2 links are setup
3113 ParamsTuple(
3114 {{"{2, 0, BAND_2_4GHZ, 0}", "{36, 0, BAND_5GHZ, 0}", "{8, 20, BAND_2_4GHZ, 0}"},
3115 {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
3116 {0, 1},
3117 WifiAssocType::ML_SETUP},
3118 {0, 1},
3119 {}, // IDs of setup links are the same for AP and non-AP devices
3120 WifiTidToLinkMappingNegSupport::ANY_LINK_SET,
3121 "0,1,2,3 1",
3122 "0,1,2,3 1"),
3123 // the first link of the non-AP MLD cannot change PHY band and no AP is operating on
3124 // that band; the second link of the non-AP MLD cannot change PHY band and there is
3125 // an AP operating on the same channel; the third link of the non-AP MLD cannot
3126 // change PHY band and there is an AP operating on the same band (different channel);
3127 // hence 2 links are setup by switching channel (not band) on the third link
3128 ParamsTuple({{"{2, 0, BAND_2_4GHZ, 0}", "{36, 0, BAND_5GHZ, 0}", "{60, 0, BAND_5GHZ, 0}"},
3129 {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
3130 {0, 1, 2},
3131 WifiAssocType::ML_SETUP},
3132 {0, 2},
3133 {}, // IDs of setup links are the same for AP and non-AP devices
3134 WifiTidToLinkMappingNegSupport::ANY_LINK_SET,
3135 "",
3136 ""),
3137 // the first link of the non-AP MLD cannot change PHY band and no AP is operating on
3138 // that band; the second link of the non-AP MLD cannot change PHY band and there is
3139 // an AP operating on the same channel; hence one link only is setup
3140 ParamsTuple({{"{2, 0, BAND_2_4GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
3141 {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
3142 {0, 1},
3143 WifiAssocType::ML_SETUP},
3144 {2},
3145 {}, // IDs of setup links are the same for AP and non-AP devices
3146 WifiTidToLinkMappingNegSupport::ANY_LINK_SET,
3147 "",
3148 ""),
3149 // non-AP MLD has only two STAs and setups two links
3150 ParamsTuple({{"{2, 0, BAND_2_4GHZ, 0}", "{36, 0, BAND_5GHZ, 0}"},
3151 {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
3152 {},
3153 WifiAssocType::ML_SETUP},
3154 {1, 0},
3155 {}, // IDs of setup links are the same for AP and non-AP devices
3156 WifiTidToLinkMappingNegSupport::ANY_LINK_SET,
3157 "0,1,2,3 1",
3158 ""),
3159 // AP MLD and non-AP MLD setup only one link using legacy association
3160 ParamsTuple({{"{2, 0, BAND_2_4GHZ, 0}", "{36, 0, BAND_5GHZ, 0}", "{60, 0, BAND_5GHZ, 0}"},
3161 {"{120, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{36, 0, BAND_5GHZ, 0}"},
3162 {},
3163 WifiAssocType::LEGACY},
3164 {2},
3165 {1},
3166 WifiTidToLinkMappingNegSupport::ANY_LINK_SET,
3167 "",
3168 ""),
3169 // single link non-AP STA performs legacy association with an AP affiliated with an AP MLD
3170 ParamsTuple({{"{120, 0, BAND_5GHZ, 0}"},
3171 {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
3172 {},
3173 WifiAssocType::LEGACY},
3174 {2},
3175 {0}, // non-AP STA performs legacy association
3176 WifiTidToLinkMappingNegSupport::ANY_LINK_SET,
3177 "",
3178 ""),
3179 // single link non-AP STA performs ML setup with an AP affiliated with an AP MLD
3180 ParamsTuple({{"{120, 0, BAND_5GHZ, 0}"},
3181 {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
3182 {},
3183 WifiAssocType::ML_SETUP},
3184 {2},
3185 {2}, // IDs of setup links are the same for AP and non-AP devices
3186 WifiTidToLinkMappingNegSupport::ANY_LINK_SET,
3187 "",
3188 ""),
3189 // a STA affiliated with a non-AP MLD performs legacy association with a single link AP
3190 ParamsTuple({{"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
3191 {"{120, 0, BAND_5GHZ, 0}"},
3192 {},
3193 WifiAssocType::LEGACY},
3194 {0}, // AP is single link
3195 {2},
3196 WifiTidToLinkMappingNegSupport::NOT_SUPPORTED,
3197 "0,1,2,3 0,1; 4,5,6,7 0,1", // ignored by single link AP
3198 ""),
3199 // a STA affiliated with a non-AP MLD performs ML setup with a single link AP
3200 ParamsTuple({{"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
3201 {"{120, 0, BAND_5GHZ, 0}"},
3202 {},
3203 WifiAssocType::ML_SETUP},
3204 {0}, // AP is single link
3205 {2}, // legacy association is performed anyway because AP is single link
3206 WifiTidToLinkMappingNegSupport::NOT_SUPPORTED,
3207 "0,1,2,3 0,1; 4,5,6,7 0,1", // ignored by single link AP
3208 "")})
3209 {
3210 AddTestCase(new MultiLinkSetupTest(baseParams,
3211 WifiScanType::PASSIVE,
3212 setupLinks,
3213 staSetupLinks,
3214 apNegSupport,
3215 dlTidLinkMapping,
3216 ulTidLinkMapping),
3217 TestCase::Duration::QUICK);
3218 AddTestCase(new MultiLinkSetupTest(baseParams,
3219 WifiScanType::ACTIVE,
3220 setupLinks,
3221 staSetupLinks,
3222 apNegSupport,
3223 dlTidLinkMapping,
3224 ulTidLinkMapping),
3225 TestCase::Duration::QUICK);
3226
3227 for (const auto& trafficPattern : {WifiTrafficPattern::STA_TO_STA,
3232 {
3233 // No Block Ack agreement
3234 AddTestCase(new MultiLinkTxTest(baseParams,
3235 trafficPattern,
3238 1),
3239 TestCase::Duration::QUICK);
3240 for (const auto& useBarAfterMissedBa :
3242 {
3243 // Block Ack agreement with nMaxInflight=1
3244 AddTestCase(new MultiLinkTxTest(baseParams,
3245 trafficPattern,
3247 useBarAfterMissedBa,
3248 1),
3249 TestCase::Duration::QUICK);
3250 // Block Ack agreement with nMaxInflight=2
3251 AddTestCase(new MultiLinkTxTest(baseParams,
3252 trafficPattern,
3254 useBarAfterMissedBa,
3255 2),
3256 TestCase::Duration::QUICK);
3257 }
3258 }
3259
3260 for (const auto& muTrafficPattern : {WifiMuTrafficPattern::DL_MU_BAR_BA_SEQUENCE,
3264 {
3265 for (const auto& useBarAfterMissedBa :
3267 {
3268 // Block Ack agreement with nMaxInflight=1
3270 new MultiLinkMuTxTest(baseParams, muTrafficPattern, useBarAfterMissedBa, 1),
3271 TestCase::Duration::QUICK);
3272 // Block Ack agreement with nMaxInflight=2
3274 new MultiLinkMuTxTest(baseParams, muTrafficPattern, useBarAfterMissedBa, 2),
3275 TestCase::Duration::QUICK);
3276 }
3277 }
3278 }
3279
3280 AddTestCase(new ReleaseSeqNoAfterCtsTimeoutTest(), TestCase::Duration::QUICK);
3281 AddTestCase(new StartSeqNoUpdateAfterAddBaTimeoutTest(), TestCase::Duration::QUICK);
3282}
3283
Test that the AIDs that an AP MLD assigns to SLDs and MLDs are all unique.
WifiAssocType m_assocType
association type
AidAssignmentTest(const std::vector< std::set< uint8_t > > &linkIds, WifiAssocType assocType)
Constructor.
const std::vector< std::set< uint8_t > > m_linkIds
link IDs for all non-AP STAs/MLDs
void DoSetup() override
Implementation to do any local setup required for this TestCase.
void SetSsid(Ptr< StaWifiMac > staMac, Mac48Address)
Set the SSID on the next station that needs to start the association procedure.
void DoRun() override
Implementation to actually run this TestCase.
uint16_t m_expectedAid
expected AID for current non-AP STA/MLD
const std::vector< std::string > m_linkChannels
channels for all AP links
NetDeviceContainer m_staDevices
non-AP STAs/MLDs devices
Test release of sequence numbers upon CTS timeout in multi-link operations.
PacketSocketAddress m_sockAddr
packet socket address
Ptr< ListErrorModel > m_errorModel
error rate model to corrupt first RTS frame
std::size_t m_nQosDataFrames
counter for transmitted QoS data frames
void StartTraffic() override
Start the generation of traffic (needs to be overridden)
bool m_rtsCorrupted
whether the first RTS frame has been corrupted
void DoSetup() override
Implementation to do any local setup required for this TestCase.
void Transmit(Ptr< WifiMac > mac, uint8_t phyId, WifiConstPsduMap psduMap, WifiTxVector txVector, double txPowerW) override
Callback invoked when a FEM passes PSDUs to the PHY.
void DoRun() override
Implementation to actually run this TestCase.
Test update of BA starting sequence number after ADDBA Response timeout in multi-link operations.
void DoSetup() override
Implementation to do any local setup required for this TestCase.
void DoRun() override
Implementation to actually run this TestCase.
PacketSocketAddress m_sockAddr
packet socket address
std::size_t m_nQosDataCount
counter for transmitted QoS data frames
void StartTraffic() override
Start the generation of traffic (needs to be overridden)
void Transmit(Ptr< WifiMac > mac, uint8_t phyId, WifiConstPsduMap psduMap, WifiTxVector txVector, double txPowerW) override
Callback invoked when a FEM passes PSDUs to the PHY.
Ptr< ListErrorModel > m_staErrorModel
error rate model to corrupt frames at the non-AP MLD
a polymophic address class
Definition address.h:90
AttributeValue implementation for Boolean.
Definition boolean.h:26
Headers for BlockAck response.
bool IsPacketReceived(uint16_t seq, std::size_t index=0) const
Check if the packet with the given sequence number was acknowledged in this BlockAck response.
std::vector< uint32_t > FindPerAidTidInfoWithAid(uint16_t aid) const
For Multi-STA Block Acks, get the indices of the Per AID TID Info subfields carrying the given AID in...
Headers for Trigger frames.
bool IsBasic() const
Check if this is a Basic Trigger frame.
Hold variables of type enum.
Definition enum.h:52
void SetList(const std::list< uint64_t > &packetlist)
an EUI-48 address
static Mac48Address GetBroadcast()
Implement the header for management frames of type association request.
Implement the header for management frames of type association and reassociation response.
Implement the header for management frames of type beacon.
Implement the header for management frames of type probe request.
Implement the header for management frames of type probe response.
Helper class used to assign positions and mobility models to nodes.
MultiUserScheduler is an abstract base class defining the API that APs supporting at least VHT can us...
holds a vector of ns3::NetDevice pointers
uint32_t GetN() const
Get the number of Ptr<NetDevice> stored in this container.
void Add(NetDeviceContainer other)
Append the contents of another NetDeviceContainer to the end of this container.
Ptr< NetDevice > Get(uint32_t i) const
Get the Ptr<NetDevice> stored in this container at a given index.
keep track of a set of node pointers.
static Iterator Begin()
Definition node-list.cc:226
static uint32_t GetNNodes()
Definition node-list.cc:247
static Iterator End()
Definition node-list.cc:233
an address for a packet socket
void SetProtocol(uint16_t protocol)
Set the protocol.
void SetPhysicalAddress(const Address address)
Set the destination address.
void SetSingleDevice(uint32_t device)
Set the address to match only a specified NetDevice.
Give ns3::PacketSocket powers to ns3::Node.
void Install(Ptr< Node > node) const
Aggregate an instance of a ns3::PacketSocketFactory onto the provided node.
AttributeValue implementation for Pointer.
Smart pointer class similar to boost::intrusive_ptr.
The Reduced Neighbor Report element.
std::size_t GetNNbrApInfoFields() const
Get the number of Neighbor AP Information fields.
std::size_t GetNTbttInformationFields(std::size_t nbrApInfoId) const
Get the number of TBTT Information fields included in the TBTT Information Set field of the given Nei...
void AddNbrApInfoField()
Add a Neighbor AP Information field.
void SetMldParameters(std::size_t nbrApInfoId, std::size_t index, const MldParameters &mldParams)
Set the MLD Parameters subfield of the i-th TBTT Information field of the given Neighbor AP Informati...
void AddTbttInformationField(std::size_t nbrApInfoId)
Add a TBTT Information fields to the TBTT Information Set field of the given Neighbor AP Information ...
static void SetRun(uint64_t run)
Set the run number of simulation.
static void SetSeed(uint32_t seed)
Set the seed.
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition simulator.h:561
static void Destroy()
Execute the events scheduled with ScheduleDestroy().
Definition simulator.cc:131
static Time Now()
Return the current simulation virtual time.
Definition simulator.cc:197
static void Run()
Run the simulation.
Definition simulator.cc:167
static void Stop()
Tell the Simulator the calling event should be the last one executed.
Definition simulator.cc:175
Make it easy to create and manage PHY objects for the spectrum model.
void AddChannel(const Ptr< SpectrumChannel > channel, const FrequencyRange &freqRange=WHOLE_WIFI_SPECTRUM)
void SetChannel(const Ptr< SpectrumChannel > channel)
The IEEE 802.11 SSID Information Element.
Definition ssid.h:25
AttributeValue implementation for Ssid.
Definition ssid.h:85
Hold variables of type string.
Definition string.h:45
encapsulates test code
Definition test.h:1050
void AddTestCase(TestCase *testCase, Duration duration=Duration::QUICK)
Add an individual child TestCase to this test suite.
Definition test.cc:292
A suite of tests to run.
Definition test.h:1267
Type
Type of test.
Definition test.h:1274
Simulation virtual time values and global simulation resolution.
Definition nstime.h:94
AttributeValue implementation for Time.
Definition nstime.h:1432
a unique identifier for an interface.
Definition type-id.h:49
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition type-id.cc:1001
Hold an unsigned integer type.
Definition uinteger.h:34
See IEEE 802.11 chapter 7.3.1.11 Header format: | category: 1 | action value: 1 |.
CategoryValue GetCategory() const
Return the category value.
ActionValue GetAction() const
Return the action value.
static std::optional< WifiAssocManager::RnrLinkInfo > GetNextAffiliatedAp(const ReducedNeighborReport &rnr, std::size_t nbrApInfoId)
Search the given RNR element for APs affiliated to the same AP MLD as the reporting AP.
static std::list< WifiAssocManager::RnrLinkInfo > GetAllAffiliatedAps(const ReducedNeighborReport &rnr)
Find all the APs affiliated to the same AP MLD as the reporting AP that sent the given RNR element.
helps to create WifiNetDevice objects
static int64_t AssignStreams(NetDeviceContainer c, int64_t stream)
Assign a fixed random variable stream number to the random variables used by the PHY and MAC aspects ...
create MAC layers for a ns3::WifiNetDevice.
base class for all MAC-level wifi objects.
Definition wifi-mac.h:90
void SetPcapCaptureType(PcapCaptureType type)
Set the PCAP capture type to be used.
void SetPcapDataLinkType(SupportedPcapDataLinkTypes dlt)
Set the data link type of PCAP traces to be used.
void Set(std::string name, const AttributeValue &v)
@ DLT_IEEE802_11_RADIO
Include Radiotap link layer information.
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
Definition wifi-phy.cc:1588
This class mimics the TXVECTOR which is to be passed to the PHY in order to define the parameters whi...
const HeMuUserInfoMap & GetHeMuUserInfoMap() const
Get a const reference to the map HE MU user-specific transmission information indexed by STA-ID.
MHz_u GetChannelWidth() const
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition assert.h:55
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition assert.h:75
void SetDefault(std::string name, const AttributeValue &value)
Definition config.cc:886
void ConnectWithoutContext(std::string path, const CallbackBase &cb)
Definition config.cc:946
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
#define NS_ABORT_MSG(msg)
Unconditional abnormal program termination with a message.
Definition abort.h:38
#define NS_ABORT_IF(cond)
Abnormal program termination if a condition is true.
Definition abort.h:65
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:191
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition log.h:264
Ptr< T > CreateObject(Args &&... args)
Create an object by type, with varying number of constructor parameters.
Definition object.h:619
Ptr< T > CreateObjectWithAttributes(Args... args)
Allocate an Object on the heap and initialize with a set of attributes.
Ptr< T > Create(Ts &&... args)
Create class instances by constructors with varying numbers of arguments and return them by Ptr.
Definition ptr.h:436
#define NS_TEST_ASSERT_MSG_LT(actual, limit, msg)
Test that an actual value is less than a limit and report and abort if not.
Definition test.h:699
#define NS_TEST_ASSERT_MSG_EQ(actual, limit, msg)
Test that an actual and expected (limit) value are equal and report and abort if not.
Definition test.h:134
#define NS_TEST_EXPECT_MSG_LT_OR_EQ(actual, limit, msg)
Test that an actual value is less than or equal to a limit and report if not.
Definition test.h:820
#define NS_TEST_EXPECT_MSG_LT(actual, limit, msg)
Test that an actual value is less than a limit and report if not.
Definition test.h:780
#define NS_TEST_EXPECT_MSG_GT(actual, limit, msg)
Test that an actual value is greater than a limit and report if not.
Definition test.h:946
#define NS_TEST_EXPECT_MSG_NE(actual, limit, msg)
Test that an actual and expected (limit) value are not equal and report if not.
Definition test.h:656
#define NS_TEST_EXPECT_MSG_EQ(actual, limit, msg)
Test that an actual and expected (limit) value are equal and report if not.
Definition test.h:241
#define NS_TEST_ASSERT_MSG_NE(actual, limit, msg)
Test that an actual and expected (limit) value are not equal and report and abort if not.
Definition test.h:554
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1369
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1345
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1357
WifiScanType
Scan type (active or passive)
AcIndex QosUtilsMapTidToAc(uint8_t tid)
Maps TID (Traffic ID) to Access classes.
Definition qos-utils.cc:123
WifiAssocType
Type of association performed by this device (provided that it is supported by the standard configure...
@ AP
Definition wifi-mac.h:60
@ WIFI_STANDARD_80211be
@ WIFI_PHY_BAND_6GHZ
The 6 GHz band.
@ WIFI_PHY_BAND_5GHZ
The 5 GHz band.
@ AC_BE
Best Effort.
Definition qos-utils.h:64
@ AC_VO
Voice.
Definition qos-utils.h:70
@ AC_VI
Video.
Definition qos-utils.h:68
@ AC_BK
Background.
Definition qos-utils.h:66
Every class exported by the ns3 library is enclosed in the ns3 namespace.
constexpr FrequencyRange WIFI_SPECTRUM_6_GHZ
Identifier for the frequency range covering the wifi spectrum in the 6 GHz band.
U * PeekPointer(const Ptr< U > &p)
Definition ptr.h:443
std:: tuple< WifiContainerQueueType, WifiReceiverAddressType, Mac48Address, std::optional< uint8_t > > WifiContainerQueueId
Tuple (queue type, receiver address type, Address, TID) identifying a container queue.
Callback< R, Args... > MakeCallback(R(T::*memPtr)(Args...), OBJ objPtr)
Build Callbacks for class method members which take varying numbers of arguments and potentially retu...
Definition callback.h:684
WifiTidToLinkMappingNegSupport
TID-to-Link Mapping Negotiation Support.
Ptr< T1 > DynamicCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:580
static constexpr uint8_t SINGLE_LINK_OP_ID
Link ID for single link operations (helps tracking places where correct link ID is to be used to supp...
Definition wifi-utils.h:272
constexpr FrequencyRange WIFI_SPECTRUM_5_GHZ
Identifier for the frequency range covering the wifi spectrum in the 5 GHz band.
@ WIFI_MAC_CTL_TRIGGER
@ WIFI_MAC_MGT_PROBE_REQUEST
@ WIFI_MAC_CTL_BACKREQ
@ WIFI_MAC_MGT_BEACON
@ WIFI_MAC_MGT_ACTION
@ WIFI_MAC_MGT_ASSOCIATION_RESPONSE
@ WIFI_MAC_MGT_ASSOCIATION_REQUEST
@ WIFI_MAC_CTL_BACKRESP
@ WIFI_MAC_MGT_PROBE_RESPONSE
@ WIFI_MAC_QOSDATA
WifiDirection
Wifi direction.
Definition wifi-utils.h:36
bool TidToLinkMappingValidForNegType1(const WifiTidLinkMapping &dlLinkMapping, const WifiTidLinkMapping &ulLinkMapping)
Check if the given TID-to-Link Mappings are valid for a negotiation type of 1.
Ptr< T1 > StaticCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:587
std::unordered_map< uint16_t, Ptr< const WifiPsdu > > WifiConstPsduMap
Map of const PSDUs indexed by STA-ID.
Definition wifi-ppdu.h:38
constexpr FrequencyRange WIFI_SPECTRUM_2_4_GHZ
Identifier for the frequency range covering the wifi spectrum in the 2.4 GHz band.
STL namespace.
uint32_t prev
std::string dir
BlockAckActionValue blockAck
block ack
uint32_t pktSize
packet size used for the simulation (in bytes)
static WifiMultiLinkOperationsTestSuite g_wifiMultiLinkOperationsTestSuite
the test suite
WifiMuTrafficPattern
Tested MU traffic patterns.
WifiTrafficPattern
Tested traffic patterns.
WifiUseBarAfterMissedBa
Whether to send a BlockAckReq after a missed BlockAck.
WifiBaEnabled
Block Ack agreement enabled/disabled.