A Discrete-Event Network Simulator
API
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 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation;
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 *
17 * Author: Stefano Avallone <stavallo@unina.it>
18 */
19
20#include "ns3/ap-wifi-mac.h"
21#include "ns3/config.h"
22#include "ns3/he-configuration.h"
23#include "ns3/he-frame-exchange-manager.h"
24#include "ns3/he-phy.h"
25#include "ns3/log.h"
26#include "ns3/mgt-headers.h"
27#include "ns3/mobility-helper.h"
28#include "ns3/multi-link-element.h"
29#include "ns3/multi-model-spectrum-channel.h"
30#include "ns3/node-list.h"
31#include "ns3/packet-socket-client.h"
32#include "ns3/packet-socket-helper.h"
33#include "ns3/packet-socket-server.h"
34#include "ns3/packet.h"
35#include "ns3/pointer.h"
36#include "ns3/qos-utils.h"
37#include "ns3/rng-seed-manager.h"
38#include "ns3/rr-multi-user-scheduler.h"
39#include "ns3/spectrum-wifi-helper.h"
40#include "ns3/spectrum-wifi-phy.h"
41#include "ns3/sta-wifi-mac.h"
42#include "ns3/string.h"
43#include "ns3/test.h"
44#include "ns3/wifi-acknowledgment.h"
45#include "ns3/wifi-assoc-manager.h"
46#include "ns3/wifi-mac-header.h"
47#include "ns3/wifi-mac-queue.h"
48#include "ns3/wifi-net-device.h"
49#include "ns3/wifi-protection.h"
50#include "ns3/wifi-psdu.h"
51
52#include <algorithm>
53#include <array>
54#include <iomanip>
55#include <sstream>
56#include <vector>
57
58using namespace ns3;
59
60NS_LOG_COMPONENT_DEFINE("WifiMloTest");
61
71{
72 public:
77 ~GetRnrLinkInfoTest() override = default;
78
79 private:
80 void DoRun() override;
81};
82
84 : TestCase("Check the implementation of WifiAssocManager::GetNextAffiliatedAp()")
85{
86}
87
88void
90{
92 std::size_t nbrId;
93 std::size_t tbttId;
94
95 // Add a first Neighbor AP Information field without MLD Parameters
97 nbrId = rnr.GetNNbrApInfoFields() - 1;
98
99 rnr.AddTbttInformationField(nbrId);
100 rnr.AddTbttInformationField(nbrId);
101
102 // Add a second Neighbor AP Information field with MLD Parameters; the first
103 // TBTT Information field is related to an AP affiliated to the same AP MLD
104 // as the reported AP; the second TBTT Information field is not (it does not
105 // make sense that two APs affiliated to the same AP MLD are using the same
106 // channel).
107 rnr.AddNbrApInfoField();
108 nbrId = rnr.GetNNbrApInfoFields() - 1;
109
110 rnr.AddTbttInformationField(nbrId);
111 tbttId = rnr.GetNTbttInformationFields(nbrId) - 1;
112 rnr.SetMldParameters(nbrId, tbttId, 0, 0, 0);
113
114 rnr.AddTbttInformationField(nbrId);
115 tbttId = rnr.GetNTbttInformationFields(nbrId) - 1;
116 rnr.SetMldParameters(nbrId, tbttId, 5, 0, 0);
117
118 // Add a third Neighbor AP Information field with MLD Parameters; none of the
119 // TBTT Information fields is related to an AP affiliated to the same AP MLD
120 // as the reported AP.
121 rnr.AddNbrApInfoField();
122 nbrId = rnr.GetNNbrApInfoFields() - 1;
123
124 rnr.AddTbttInformationField(nbrId);
125 tbttId = rnr.GetNTbttInformationFields(nbrId) - 1;
126 rnr.SetMldParameters(nbrId, tbttId, 3, 0, 0);
127
128 rnr.AddTbttInformationField(nbrId);
129 tbttId = rnr.GetNTbttInformationFields(nbrId) - 1;
130 rnr.SetMldParameters(nbrId, tbttId, 4, 0, 0);
131
132 // Add a fourth Neighbor AP Information field with MLD Parameters; the first
133 // TBTT Information field is not related to an AP affiliated to the same AP MLD
134 // as the reported AP; the second TBTT Information field is.
135 rnr.AddNbrApInfoField();
136 nbrId = rnr.GetNNbrApInfoFields() - 1;
137
138 rnr.AddTbttInformationField(nbrId);
139 tbttId = rnr.GetNTbttInformationFields(nbrId) - 1;
140 rnr.SetMldParameters(nbrId, tbttId, 6, 0, 0);
141
142 rnr.AddTbttInformationField(nbrId);
143 tbttId = rnr.GetNTbttInformationFields(nbrId) - 1;
144 rnr.SetMldParameters(nbrId, tbttId, 0, 0, 0);
145
146 // check implementation of WifiAssocManager::GetNextAffiliatedAp()
147 auto ret = WifiAssocManager::GetNextAffiliatedAp(rnr, 0);
148
149 NS_TEST_EXPECT_MSG_EQ(ret.has_value(), true, "Expected to find a suitable reported AP");
150 NS_TEST_EXPECT_MSG_EQ(ret->m_nbrApInfoId, 1, "Unexpected neighbor ID of the first reported AP");
151 NS_TEST_EXPECT_MSG_EQ(ret->m_tbttInfoFieldId, 0, "Unexpected tbtt ID of the first reported AP");
152
153 ret = WifiAssocManager::GetNextAffiliatedAp(rnr, ret->m_nbrApInfoId + 1);
154
155 NS_TEST_EXPECT_MSG_EQ(ret.has_value(), true, "Expected to find a second suitable reported AP");
156 NS_TEST_EXPECT_MSG_EQ(ret->m_nbrApInfoId,
157 3,
158 "Unexpected neighbor ID of the second reported AP");
159 NS_TEST_EXPECT_MSG_EQ(ret->m_tbttInfoFieldId,
160 1,
161 "Unexpected tbtt ID of the second reported AP");
162
163 ret = WifiAssocManager::GetNextAffiliatedAp(rnr, ret->m_nbrApInfoId + 1);
164
165 NS_TEST_EXPECT_MSG_EQ(ret.has_value(),
166 false,
167 "Did not expect to find a third suitable reported AP");
168
169 // check implementation of WifiAssocManager::GetAllAffiliatedAps()
170 auto allAps = WifiAssocManager::GetAllAffiliatedAps(rnr);
171
172 NS_TEST_EXPECT_MSG_EQ(allAps.size(), 2, "Expected to find two suitable reported APs");
173
174 auto apIt = allAps.begin();
175 NS_TEST_EXPECT_MSG_EQ(apIt->m_nbrApInfoId,
176 1,
177 "Unexpected neighbor ID of the first reported AP");
178 NS_TEST_EXPECT_MSG_EQ(apIt->m_tbttInfoFieldId,
179 0,
180 "Unexpected tbtt ID of the first reported AP");
181
182 apIt++;
183 NS_TEST_EXPECT_MSG_EQ(apIt->m_nbrApInfoId,
184 3,
185 "Unexpected neighbor ID of the second reported AP");
186 NS_TEST_EXPECT_MSG_EQ(apIt->m_tbttInfoFieldId,
187 1,
188 "Unexpected tbtt ID of the second reported AP");
189}
190
202{
203 public:
213 MultiLinkOperationsTestBase(const std::string& name,
214 uint8_t nStations,
215 std::vector<std::string> staChannels,
216 std::vector<std::string> apChannels,
217 std::vector<uint8_t> fixedPhyBands = {});
218 ~MultiLinkOperationsTestBase() override = default;
219
220 protected:
230 virtual void Transmit(uint8_t linkId,
231 std::string context,
232 WifiConstPsduMap psduMap,
233 WifiTxVector txVector,
234 double txPowerW);
235
236 void DoSetup() override;
237
242 {
243 DL = 0,
244 UL
245 };
246
254 std::optional<Direction> direction = std::nullopt);
255
258 {
262 uint8_t linkId;
263 };
264
265 std::vector<FrameInfo> m_txPsdus;
266 const std::vector<std::string> m_staChannels;
267 const std::vector<std::string> m_apChannels;
268 const std::vector<uint8_t> m_fixedPhyBands;
270 std::vector<Ptr<StaWifiMac>> m_staMacs;
271 uint8_t m_nStations;
272 uint16_t m_lastAid;
273
274 private:
285 const std::vector<std::string>& channels,
287
295 void SetSsid(uint16_t aid, Mac48Address /* addr */);
296
300 virtual void StartTraffic()
301 {
302 }
303};
304
306 uint8_t nStations,
307 std::vector<std::string> staChannels,
308 std::vector<std::string> apChannels,
309 std::vector<uint8_t> fixedPhyBands)
310 : TestCase(name),
311 m_staChannels(staChannels),
312 m_apChannels(apChannels),
313 m_fixedPhyBands(fixedPhyBands),
314 m_staMacs(nStations),
315 m_nStations(nStations),
316 m_lastAid(0)
317{
318}
319
320void
322 std::optional<Direction> direction)
323{
324 std::optional<Mac48Address> apAddr;
325 std::optional<Mac48Address> staAddr;
326
327 // direction for Data frames is derived from ToDS/FromDS flags
328 if (psdu->GetHeader(0).IsQosData())
329 {
330 direction = (!psdu->GetHeader(0).IsToDs() && psdu->GetHeader(0).IsFromDs()) ? DL : UL;
331 }
332 NS_ASSERT(direction);
333
334 if (direction == DL)
335 {
336 if (!psdu->GetAddr1().IsGroup())
337 {
338 staAddr = psdu->GetAddr1();
339 }
340 apAddr = psdu->GetAddr2();
341 }
342 else
343 {
344 if (!psdu->GetAddr1().IsGroup())
345 {
346 apAddr = psdu->GetAddr1();
347 }
348 staAddr = psdu->GetAddr2();
349 }
350
351 if (apAddr)
352 {
353 bool found = false;
354 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
355 {
356 if (m_apMac->GetFrameExchangeManager(linkId)->GetAddress() == *apAddr)
357 {
358 found = true;
359 break;
360 }
361 }
363 true,
364 "Address " << *apAddr << " is not an AP device address. "
365 << "PSDU: " << *psdu);
366 }
367
368 if (staAddr)
369 {
370 bool found = false;
371 for (uint8_t i = 0; i < m_nStations; i++)
372 {
373 for (uint8_t linkId = 0; linkId < m_staMacs[i]->GetNLinks(); linkId++)
374 {
375 if (m_staMacs[i]->GetFrameExchangeManager(linkId)->GetAddress() == *staAddr)
376 {
377 found = true;
378 break;
379 }
380 }
381 if (found)
382 {
383 break;
384 }
385 }
387 true,
388 "Address " << *staAddr << " is not a STA device address. "
389 << "PSDU: " << *psdu);
390 }
391}
392
393void
395 std::string context,
396 WifiConstPsduMap psduMap,
397 WifiTxVector txVector,
398 double txPowerW)
399{
400 m_txPsdus.push_back({Simulator::Now(), psduMap, txVector, linkId});
401
402 for (const auto& [aid, psdu] : psduMap)
403 {
404 std::stringstream ss;
405 ss << std::setprecision(10) << "PSDU #" << m_txPsdus.size() << " Link ID " << +linkId << " "
406 << psdu->GetHeader(0).GetTypeString() << " #MPDUs " << psdu->GetNMpdus()
407 << " duration/ID " << psdu->GetHeader(0).GetDuration() << " RA = " << psdu->GetAddr1()
408 << " TA = " << psdu->GetAddr2() << " ADDR3 = " << psdu->GetHeader(0).GetAddr3()
409 << " ToDS = " << psdu->GetHeader(0).IsToDs()
410 << " FromDS = " << psdu->GetHeader(0).IsFromDs();
411 if (psdu->GetHeader(0).IsQosData())
412 {
413 ss << " seqNo = {";
414 for (auto& mpdu : *PeekPointer(psdu))
415 {
416 ss << mpdu->GetHeader().GetSequenceNumber() << ",";
417 }
418 ss << "} TID = " << +psdu->GetHeader(0).GetQosTid();
419 }
420 NS_LOG_INFO(ss.str());
421 }
422 NS_LOG_INFO("TXVECTOR = " << txVector << "\n");
423}
424
425void
427 const std::vector<std::string>& channels,
429{
430 helper = SpectrumWifiPhyHelper(channels.size());
431 helper.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11_RADIO);
432
433 uint8_t linkId = 0;
434 for (const auto& str : channels)
435 {
436 helper.Set(linkId++, "ChannelSettings", StringValue(str));
437 }
438
439 helper.SetChannel(channel);
440}
441
442void
444{
445 RngSeedManager::SetSeed(1);
446 RngSeedManager::SetRun(2);
447 int64_t streamNumber = 100;
448
450 wifiApNode.Create(1);
451
454
456 // wifi.EnableLogComponents ();
457 wifi.SetStandard(WIFI_STANDARD_80211be);
458 wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager",
459 "DataMode",
460 StringValue("EhtMcs0"),
461 "ControlMode",
462 StringValue("HtMcs0"));
463
464 auto channel = CreateObject<MultiModelSpectrumChannel>();
465
466 SpectrumWifiPhyHelper staPhyHelper;
467 SpectrumWifiPhyHelper apPhyHelper;
468 SetChannels(staPhyHelper, m_staChannels, channel);
469 SetChannels(apPhyHelper, m_apChannels, channel);
470
471 for (const auto& linkId : m_fixedPhyBands)
472 {
473 staPhyHelper.Set(linkId, "FixedPhyBand", BooleanValue(true));
474 }
475
477 mac.SetType("ns3::StaWifiMac", // default SSID
478 "ActiveProbing",
479 BooleanValue(false));
480
481 NetDeviceContainer staDevices = wifi.Install(staPhyHelper, mac, wifiStaNodes);
482
483 mac.SetType("ns3::ApWifiMac",
484 "Ssid",
485 SsidValue(Ssid("ns-3-ssid")),
486 "BeaconGeneration",
487 BooleanValue(true));
488
489 NetDeviceContainer apDevices = wifi.Install(apPhyHelper, mac, wifiApNode);
490
491 // Uncomment the lines below to write PCAP files
492 // apPhyHelper.EnablePcap("wifi-mlo_AP", apDevices);
493 // staPhyHelper.EnablePcap("wifi-mlo_STA", staDevices);
494
495 // Assign fixed streams to random variables in use
496 streamNumber += wifi.AssignStreams(apDevices, streamNumber);
497 streamNumber += wifi.AssignStreams(staDevices, streamNumber);
498
500 Ptr<ListPositionAllocator> positionAlloc = CreateObject<ListPositionAllocator>();
501
502 positionAlloc->Add(Vector(0.0, 0.0, 0.0));
503 positionAlloc->Add(Vector(1.0, 0.0, 0.0));
504 mobility.SetPositionAllocator(positionAlloc);
505
506 mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
507 mobility.Install(wifiApNode);
508 mobility.Install(wifiStaNodes);
509
510 m_apMac = DynamicCast<ApWifiMac>(DynamicCast<WifiNetDevice>(apDevices.Get(0))->GetMac());
511 for (uint8_t i = 0; i < m_nStations; i++)
512 {
513 m_staMacs[i] =
514 DynamicCast<StaWifiMac>(DynamicCast<WifiNetDevice>(staDevices.Get(i))->GetMac());
515 }
516
517 // Trace PSDUs passed to the PHY on all devices
518 for (uint8_t linkId = 0; linkId < StaticCast<WifiNetDevice>(apDevices.Get(0))->GetNPhys();
519 linkId++)
520 {
521 Config::Connect("/NodeList/0/DeviceList/*/$ns3::WifiNetDevice/Phys/" +
522 std::to_string(linkId) + "/PhyTxPsduBegin",
524 }
525 for (uint8_t i = 0; i < m_nStations; i++)
526 {
527 for (uint8_t linkId = 0; linkId < StaticCast<WifiNetDevice>(staDevices.Get(i))->GetNPhys();
528 linkId++)
529 {
531 "/NodeList/" + std::to_string(i + 1) + "/DeviceList/*/$ns3::WifiNetDevice/Phys/" +
532 std::to_string(linkId) + "/PhyTxPsduBegin",
534 }
535 }
536
537 // schedule ML setup for one station at a time
538 m_apMac->TraceConnectWithoutContext("AssociatedSta",
540 Simulator::Schedule(Seconds(0), [&]() { m_staMacs[0]->SetSsid(Ssid("ns-3-ssid")); });
541}
542
543void
545{
546 if (m_lastAid == aid)
547 {
548 // another STA of this non-AP MLD has already fired this callback
549 return;
550 }
551 m_lastAid = aid;
552
553 // make the next STA to start ML discovery & setup
554 if (aid < m_nStations)
555 {
556 m_staMacs[aid]->SetSsid(Ssid("ns-3-ssid"));
557 return;
558 }
559 // wait some time (5ms) to allow the completion of association before generating traffic
560 Simulator::Schedule(MilliSeconds(5), &MultiLinkOperationsTestBase::StartTraffic, this);
561}
562
577{
578 public:
587 MultiLinkSetupTest(std::vector<std::string> staChannels,
588 std::vector<std::string> apChannels,
589 std::vector<std::pair<uint8_t, uint8_t>> setupLinks,
590 std::vector<uint8_t> fixedPhyBands = {});
591 ~MultiLinkSetupTest() override = default;
592
593 protected:
594 void DoRun() override;
595
596 private:
600 void CheckMlSetup();
601
605 void CheckDisabledLinks();
606
613 void CheckBeacon(Ptr<WifiMpdu> mpdu, uint8_t linkId);
614
621 void CheckAssocRequest(Ptr<WifiMpdu> mpdu, uint8_t linkId);
622
629 void CheckAssocResponse(Ptr<WifiMpdu> mpdu, uint8_t linkId);
630
632 const std::vector<std::pair<uint8_t, uint8_t>> m_setupLinks;
633};
634
635MultiLinkSetupTest::MultiLinkSetupTest(std::vector<std::string> staChannels,
636 std::vector<std::string> apChannels,
637 std::vector<std::pair<uint8_t, uint8_t>> setupLinks,
638 std::vector<uint8_t> fixedPhyBands)
639 : MultiLinkOperationsTestBase("Check correctness of Multi-Link Setup",
640 1,
641 staChannels,
642 apChannels,
643 fixedPhyBands),
644 m_setupLinks(setupLinks)
645{
646}
647
648void
650{
651 Simulator::Schedule(MilliSeconds(500), &MultiLinkSetupTest::CheckMlSetup, this);
652
653 Simulator::Stop(Seconds(1.5));
654 Simulator::Run();
655
659 for (const auto& frameInfo : m_txPsdus)
660 {
661 const auto& mpdu = *frameInfo.psduMap.begin()->second->begin();
662 const auto& linkId = frameInfo.linkId;
663
664 switch (mpdu->GetHeader().GetType())
665 {
667 CheckBeacon(mpdu, linkId);
668 break;
669
671 CheckAssocRequest(mpdu, linkId);
672 break;
673
675 CheckAssocResponse(mpdu, linkId);
676 break;
677
678 default:
679 break;
680 }
681 }
682
684
685 Simulator::Destroy();
686}
687
688void
690{
691 NS_ABORT_IF(mpdu->GetHeader().GetType() != WIFI_MAC_MGT_BEACON);
692
693 CheckAddresses(Create<WifiPsdu>(mpdu, false), MultiLinkOperationsTestBase::DL);
694
696 mpdu->GetHeader().GetAddr2(),
697 "TA of Beacon frame is not the address of the link it is transmitted on");
698 MgtBeaconHeader beacon;
699 mpdu->GetPacket()->PeekHeader(beacon);
700 const auto& rnr = beacon.GetReducedNeighborReport();
701 const auto& mle = beacon.GetMultiLinkElement();
702
703 if (m_apMac->GetNLinks() == 1)
704 {
705 NS_TEST_EXPECT_MSG_EQ(rnr.has_value(),
706 false,
707 "RNR Element in Beacon frame from single link AP");
708 NS_TEST_EXPECT_MSG_EQ(mle.has_value(),
709 false,
710 "Multi-Link Element in Beacon frame from single link AP");
711 return;
712 }
713
714 NS_TEST_EXPECT_MSG_EQ(rnr.has_value(), true, "No RNR Element in Beacon frame");
715 // All the other APs affiliated with the same AP MLD as the AP sending
716 // the Beacon frame must be reported in a separate Neighbor AP Info field
717 NS_TEST_EXPECT_MSG_EQ(rnr->GetNNbrApInfoFields(),
718 static_cast<std::size_t>(m_apMac->GetNLinks() - 1),
719 "Unexpected number of Neighbor AP Info fields in RNR");
720 for (std::size_t nbrApInfoId = 0; nbrApInfoId < rnr->GetNNbrApInfoFields(); nbrApInfoId++)
721 {
722 NS_TEST_EXPECT_MSG_EQ(rnr->HasMldParameters(nbrApInfoId),
723 true,
724 "MLD Parameters not present");
725 NS_TEST_EXPECT_MSG_EQ(rnr->GetNTbttInformationFields(nbrApInfoId),
726 1,
727 "Expected only one TBTT Info subfield per Neighbor AP Info");
728 uint8_t nbrLinkId = rnr->GetLinkId(nbrApInfoId, 0);
729 NS_TEST_EXPECT_MSG_EQ(rnr->GetBssid(nbrApInfoId, 0),
730 m_apMac->GetFrameExchangeManager(nbrLinkId)->GetAddress(),
731 "BSSID advertised in Neighbor AP Info field "
732 << nbrApInfoId
733 << " does not match the address configured on the link "
734 "advertised in the same field");
735 }
736
737 NS_TEST_EXPECT_MSG_EQ(mle.has_value(), true, "No Multi-Link Element in Beacon frame");
738 NS_TEST_EXPECT_MSG_EQ(mle->GetMldMacAddress(),
740 "Incorrect MLD address advertised in Multi-Link Element");
741 NS_TEST_EXPECT_MSG_EQ(mle->GetLinkIdInfo(),
742 +linkId,
743 "Incorrect Link ID advertised in Multi-Link Element");
744}
745
746void
748{
749 NS_ABORT_IF(mpdu->GetHeader().GetType() != WIFI_MAC_MGT_ASSOCIATION_REQUEST);
750
751 CheckAddresses(Create<WifiPsdu>(mpdu, false), MultiLinkOperationsTestBase::UL);
752
754 m_staMacs[0]->GetFrameExchangeManager(linkId)->GetAddress(),
755 mpdu->GetHeader().GetAddr2(),
756 "TA of Assoc Request frame is not the address of the link it is transmitted on");
758 mpdu->GetPacket()->PeekHeader(assoc);
759 const auto& mle = assoc.GetMultiLinkElement();
760
761 if (m_apMac->GetNLinks() == 1 || m_staMacs[0]->GetNLinks() == 1)
762 {
763 NS_TEST_EXPECT_MSG_EQ(mle.has_value(),
764 false,
765 "Multi-Link Element in Assoc Request frame from single link STA");
766 return;
767 }
768
769 NS_TEST_EXPECT_MSG_EQ(mle.has_value(), true, "No Multi-Link Element in Assoc Request frame");
770 NS_TEST_EXPECT_MSG_EQ(mle->GetMldMacAddress(),
771 m_staMacs[0]->GetAddress(),
772 "Incorrect MLD Address advertised in Multi-Link Element");
773 NS_TEST_EXPECT_MSG_EQ(mle->GetNPerStaProfileSubelements(),
774 m_setupLinks.size() - 1,
775 "Incorrect number of Per-STA Profile subelements in Multi-Link Element");
776 for (std::size_t i = 0; i < mle->GetNPerStaProfileSubelements(); i++)
777 {
778 auto& perStaProfile = mle->GetPerStaProfile(i);
779 NS_TEST_EXPECT_MSG_EQ(perStaProfile.HasStaMacAddress(),
780 true,
781 "Per-STA Profile must contain STA MAC address");
782 // find ID of the local link corresponding to this subelement
783 auto staLinkId = m_staMacs[0]->GetLinkIdByAddress(perStaProfile.GetStaMacAddress());
785 staLinkId.has_value(),
786 true,
787 "No link found with the STA MAC address advertised in Per-STA Profile");
789 +staLinkId.value(),
790 +linkId,
791 "The STA that sent the Assoc Request should not be included in a Per-STA Profile");
792 auto it = std::find_if(m_setupLinks.begin(), m_setupLinks.end(), [&staLinkId](auto&& pair) {
793 return pair.first == staLinkId.value();
794 });
796 true,
797 "Not expecting to setup STA link ID " << +staLinkId.value());
799 +it->second,
800 +perStaProfile.GetLinkId(),
801 "Not expecting to request association to AP Link ID in Per-STA Profile");
802 NS_TEST_EXPECT_MSG_EQ(perStaProfile.HasAssocRequest(),
803 true,
804 "Missing Association Request in Per-STA Profile");
805 }
806}
807
808void
810{
811 NS_ABORT_IF(mpdu->GetHeader().GetType() != WIFI_MAC_MGT_ASSOCIATION_RESPONSE);
812
813 CheckAddresses(Create<WifiPsdu>(mpdu, false), MultiLinkOperationsTestBase::DL);
814
816 m_apMac->GetFrameExchangeManager(linkId)->GetAddress(),
817 mpdu->GetHeader().GetAddr2(),
818 "TA of Assoc Response frame is not the address of the link it is transmitted on");
820 mpdu->GetPacket()->PeekHeader(assoc);
821 const auto& mle = assoc.GetMultiLinkElement();
822
823 if (m_apMac->GetNLinks() == 1 || m_staMacs[0]->GetNLinks() == 1)
824 {
826 mle.has_value(),
827 false,
828 "Multi-Link Element in Assoc Response frame with single link AP or single link STA");
829 return;
830 }
831
832 NS_TEST_EXPECT_MSG_EQ(mle.has_value(), true, "No Multi-Link Element in Assoc Request frame");
833 NS_TEST_EXPECT_MSG_EQ(mle->GetMldMacAddress(),
835 "Incorrect MLD Address advertised in Multi-Link Element");
836 NS_TEST_EXPECT_MSG_EQ(mle->GetNPerStaProfileSubelements(),
837 m_setupLinks.size() - 1,
838 "Incorrect number of Per-STA Profile subelements in Multi-Link Element");
839 for (std::size_t i = 0; i < mle->GetNPerStaProfileSubelements(); i++)
840 {
841 auto& perStaProfile = mle->GetPerStaProfile(i);
842 NS_TEST_EXPECT_MSG_EQ(perStaProfile.HasStaMacAddress(),
843 true,
844 "Per-STA Profile must contain STA MAC address");
845 // find ID of the local link corresponding to this subelement
846 auto apLinkId = m_apMac->GetLinkIdByAddress(perStaProfile.GetStaMacAddress());
848 apLinkId.has_value(),
849 true,
850 "No link found with the STA MAC address advertised in Per-STA Profile");
851 NS_TEST_EXPECT_MSG_EQ(+apLinkId.value(),
852 +perStaProfile.GetLinkId(),
853 "Link ID and MAC address advertised in Per-STA Profile do not match");
855 +apLinkId.value(),
856 +linkId,
857 "The AP that sent the Assoc Response should not be included in a Per-STA Profile");
858 auto it = std::find_if(m_setupLinks.begin(), m_setupLinks.end(), [&apLinkId](auto&& pair) {
859 return pair.second == apLinkId.value();
860 });
862 true,
863 "Not expecting to setup AP link ID " << +apLinkId.value());
864 NS_TEST_EXPECT_MSG_EQ(perStaProfile.HasAssocResponse(),
865 true,
866 "Missing Association Response in Per-STA Profile");
867 }
868}
869
870void
872{
876 NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->IsAssociated(), true, "Expected the STA to be associated");
877
878 for (const auto& [staLinkId, apLinkId] : m_setupLinks)
879 {
880 auto staAddr = m_staMacs[0]->GetFrameExchangeManager(staLinkId)->GetAddress();
881 auto apAddr = m_apMac->GetFrameExchangeManager(apLinkId)->GetAddress();
882
883 auto staRemoteMgr = m_staMacs[0]->GetWifiRemoteStationManager(staLinkId);
884 auto apRemoteMgr = m_apMac->GetWifiRemoteStationManager(apLinkId);
885
886 // STA side
887 NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetFrameExchangeManager(staLinkId)->GetBssid(),
888 apAddr,
889 "Unexpected BSSID for STA link ID " << +staLinkId);
890 if (m_apMac->GetNLinks() > 1 && m_staMacs[0]->GetNLinks() > 1)
891 {
892 NS_TEST_EXPECT_MSG_EQ((staRemoteMgr->GetMldAddress(apAddr) == m_apMac->GetAddress()),
893 true,
894 "Incorrect MLD address stored by STA on link ID " << +staLinkId);
896 (staRemoteMgr->GetAffiliatedStaAddress(m_apMac->GetAddress()) == apAddr),
897 true,
898 "Incorrect affiliated address stored by STA on link ID " << +staLinkId);
899 }
900
901 // AP side
902 NS_TEST_EXPECT_MSG_EQ(apRemoteMgr->IsAssociated(staAddr),
903 true,
904 "Expecting STA " << staAddr << " to be associated on link "
905 << +apLinkId);
906 if (m_apMac->GetNLinks() > 1 && m_staMacs[0]->GetNLinks() > 1)
907 {
909 (apRemoteMgr->GetMldAddress(staAddr) == m_staMacs[0]->GetAddress()),
910 true,
911 "Incorrect MLD address stored by AP on link ID " << +apLinkId);
913 (apRemoteMgr->GetAffiliatedStaAddress(m_staMacs[0]->GetAddress()) == staAddr),
914 true,
915 "Incorrect affiliated address stored by AP on link ID " << +apLinkId);
916 }
917 auto aid = m_apMac->GetAssociationId(staAddr, apLinkId);
918 const auto& staList = m_apMac->GetStaList(apLinkId);
919 NS_TEST_EXPECT_MSG_EQ((staList.find(aid) != staList.end()),
920 true,
921 "STA " << staAddr << " not found in list of associated STAs");
922
923 // STA of non-AP MLD operate on the same channel as the AP
925 +m_staMacs[0]->GetWifiPhy(staLinkId)->GetOperatingChannel().GetNumber(),
927 "Incorrect operating channel number for STA on link " << +staLinkId);
929 m_staMacs[0]->GetWifiPhy(staLinkId)->GetOperatingChannel().GetFrequency(),
931 "Incorrect operating channel frequency for STA on link " << +staLinkId);
932 NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetWifiPhy(staLinkId)->GetOperatingChannel().GetWidth(),
934 "Incorrect operating channel width for STA on link " << +staLinkId);
936 +m_staMacs[0]->GetWifiPhy(staLinkId)->GetOperatingChannel().GetPhyBand(),
938 "Incorrect operating PHY band for STA on link " << +staLinkId);
940 +m_staMacs[0]->GetWifiPhy(staLinkId)->GetOperatingChannel().GetPrimaryChannelIndex(20),
942 "Incorrect operating primary channel index for STA on link " << +staLinkId);
943 }
944}
945
946void
948{
949 for (std::size_t linkId = 0; linkId < m_staChannels.size(); linkId++)
950 {
951 auto it = std::find_if(m_setupLinks.begin(), m_setupLinks.end(), [&linkId](auto&& link) {
952 return link.first == linkId;
953 });
954 if (it == m_setupLinks.end())
955 {
956 // the link has not been setup
957 NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetWifiPhy(linkId)->GetState()->IsStateOff(),
958 true,
959 "Link " << +linkId << " has not been setup but is not disabled");
960 continue;
961 }
962
963 // the link has been setup and must be active
964 NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetWifiPhy(linkId)->GetState()->IsStateOff(),
965 false,
966 "Expecting link " << +linkId << " to be active");
967 }
968}
969
973enum class WifiTrafficPattern : uint8_t
974{
975 STA_TO_STA = 0,
976 STA_TO_AP,
977 AP_TO_STA,
980};
981
985enum class WifiBaEnabled : uint8_t
986{
987 NO = 0,
988 YES
989};
990
994enum class WifiUseBarAfterMissedBa : uint8_t
995{
996 NO = 0,
997 YES
998};
999
1029{
1030 public:
1043 MultiLinkTxTest(WifiTrafficPattern trafficPattern,
1044 WifiBaEnabled baEnabled,
1045 WifiUseBarAfterMissedBa useBarAfterMissedBa,
1046 uint8_t nMaxInflight,
1047 const std::vector<std::string>& staChannels,
1048 const std::vector<std::string>& apChannels,
1049 const std::vector<uint8_t>& fixedPhyBands = {});
1050 ~MultiLinkTxTest() override = default;
1051
1052 protected:
1059 void L7Receive(uint8_t nodeId, Ptr<const Packet> p, const Address& addr);
1060
1069 void CheckBlockAck(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector, uint8_t linkId);
1070
1071 void Transmit(uint8_t linkId,
1072 std::string context,
1073 WifiConstPsduMap psduMap,
1074 WifiTxVector txVector,
1075 double txPowerW) override;
1076 void DoSetup() override;
1077 void DoRun() override;
1078
1079 private:
1080 void StartTraffic() override;
1081
1083 using RxErrorModelMap = std::unordered_map<Mac48Address, Ptr<ListErrorModel>, WifiAddressHash>;
1084
1086 std::list<uint64_t> m_uidList;
1087 bool m_dataCorrupted{false};
1091 std::size_t m_nMaxInflight;
1092 std::size_t m_nPackets;
1093 std::size_t m_blockAckCount{0};
1094 std::size_t m_blockAckReqCount{0};
1095 std::array<std::size_t, 3> m_rxPkts{};
1097 std::map<uint16_t, std::size_t> m_inflightCount;
1100};
1101
1103 WifiBaEnabled baEnabled,
1104 WifiUseBarAfterMissedBa useBarAfterMissedBa,
1105 uint8_t nMaxInflight,
1106 const std::vector<std::string>& staChannels,
1107 const std::vector<std::string>& apChannels,
1108 const std::vector<uint8_t>& fixedPhyBands)
1110 std::string("Check data transmission between MLDs ") +
1111 (baEnabled == WifiBaEnabled::YES
1112 ? (useBarAfterMissedBa == WifiUseBarAfterMissedBa::YES
1113 ? "with BA agreement, send BAR after BlockAck timeout"
1114 : "with BA agreement, send Data frames after BlockAck timeout")
1115 : "without BA agreement") +
1116 " (Traffic pattern: " + std::to_string(static_cast<uint8_t>(trafficPattern)) +
1117 (baEnabled == WifiBaEnabled::YES ? ", nMaxInflight=" + std::to_string(nMaxInflight)
1118 : "") +
1119 ")",
1120 2,
1121 staChannels,
1122 apChannels,
1123 fixedPhyBands),
1124 m_trafficPattern(trafficPattern),
1125 m_baEnabled(baEnabled == WifiBaEnabled::YES),
1126 m_useBarAfterMissedBa(useBarAfterMissedBa == WifiUseBarAfterMissedBa::YES),
1127 m_nMaxInflight(nMaxInflight),
1128 m_nPackets(trafficPattern == WifiTrafficPattern::STA_TO_BCAST ||
1129 trafficPattern == WifiTrafficPattern::STA_TO_STA
1130 ? 4
1131 : 8)
1132{
1133}
1134
1135void
1137 std::string context,
1138 WifiConstPsduMap psduMap,
1139 WifiTxVector txVector,
1140 double txPowerW)
1141{
1142 auto psdu = psduMap.begin()->second;
1143
1144 switch (psdu->GetHeader(0).GetType())
1145 {
1147 // a management frame is a DL frame if TA equals BSSID
1148 CheckAddresses(psdu,
1149 psdu->GetHeader(0).GetAddr2() == psdu->GetHeader(0).GetAddr3() ? DL : UL);
1150 if (!m_baEnabled)
1151 {
1152 // corrupt all management action frames (ADDBA Request frames) to prevent
1153 // the establishment of a BA agreement
1154 m_uidList.push_front(psdu->GetPacket()->GetUid());
1155 m_errorModels.at(psdu->GetAddr1())->SetList(m_uidList);
1156 NS_LOG_INFO("CORRUPTED");
1157 }
1158 break;
1159 case WIFI_MAC_QOSDATA:
1160 CheckAddresses(psdu);
1161
1162 for (const auto& mpdu : *psdu)
1163 {
1164 // determine the max number of simultaneous transmissions for this MPDU
1165 // (only if sent by the traffic source and this is not a broadcast frame)
1166 if (m_baEnabled && linkId < m_sourceMac->GetNLinks() &&
1167 m_sourceMac->GetFrameExchangeManager(linkId)->GetAddress() ==
1168 mpdu->GetHeader().GetAddr2() &&
1169 !mpdu->GetHeader().GetAddr1().IsGroup())
1170 {
1171 auto seqNo = mpdu->GetHeader().GetSequenceNumber();
1172 auto [it, success] =
1173 m_inflightCount.insert({seqNo, mpdu->GetInFlightLinkIds().size()});
1174 if (!success)
1175 {
1176 it->second = std::max(it->second, mpdu->GetInFlightLinkIds().size());
1177 }
1178 }
1179 }
1180 for (std::size_t i = 0; i < psdu->GetNMpdus(); i++)
1181 {
1182 // corrupt QoS data frame with sequence number equal to 1 (only once) if we are
1183 // not in the AP to broadcast traffic pattern (broadcast frames are not retransmitted)
1184 // nor in the STA to broadcast or STA to STA traffic patterns (retransmissions from
1185 // STA 1 could collide with frames forwarded by the AP)
1186 if (psdu->GetHeader(i).GetSequenceNumber() != 1 ||
1190 {
1191 continue;
1192 }
1193 auto uid = psdu->GetPayload(i)->GetUid();
1194 if (!m_dataCorrupted)
1195 {
1196 m_uidList.push_front(uid);
1197 m_dataCorrupted = true;
1198 NS_LOG_INFO("CORRUPTED");
1199 m_errorModels.at(psdu->GetAddr1())->SetList(m_uidList);
1200 }
1201 else
1202 {
1203 // do not corrupt the QoS data frame anymore
1204 if (auto it = std::find(m_uidList.cbegin(), m_uidList.cend(), uid);
1205 it != m_uidList.cend())
1206 {
1207 m_uidList.erase(it);
1208 }
1209 m_errorModels.at(psdu->GetAddr1())->SetList(m_uidList);
1210 }
1211 break;
1212 }
1213 break;
1214 case WIFI_MAC_CTL_BACKRESP: {
1215 // ignore BlockAck frames not addressed to the source of the application packets
1216 if (!m_sourceMac->GetLinkIdByAddress(psdu->GetHeader(0).GetAddr1()))
1217 {
1218 break;
1219 }
1220 if (m_nMaxInflight > 1)
1221 {
1222 // we do not check the content of BlockAck when m_nMaxInflight is greater than 1
1223 break;
1224 }
1225 CheckBlockAck(psdu, txVector, linkId);
1227 if (m_blockAckCount == 2)
1228 {
1229 // corrupt the second BlockAck frame to simulate a missed BlockAck
1230 m_uidList.push_front(psdu->GetPacket()->GetUid());
1231 NS_LOG_INFO("CORRUPTED");
1232 m_errorModels.at(psdu->GetAddr1())->SetList(m_uidList);
1233 }
1234 break;
1236 // ignore BlockAckReq frames not transmitted by the source of the application packets
1237 if (m_sourceMac->GetLinkIdByAddress(psdu->GetHeader(0).GetAddr2()))
1238 {
1240 }
1241 break;
1242 }
1243 default:;
1244 }
1245
1246 MultiLinkOperationsTestBase::Transmit(linkId, context, psduMap, txVector, txPowerW);
1247}
1248
1249void
1251 const WifiTxVector& txVector,
1252 uint8_t linkId)
1253{
1254 NS_TEST_ASSERT_MSG_EQ(m_baEnabled, true, "No BlockAck expected without BA agreement");
1256 true,
1257 "No BlockAck expected in AP to broadcast traffic pattern");
1258
1259 /*
1260 * ┌───────┬───────X ┌───────┐
1261 * link 0 │ 0 │ 1 │ │ 1 │
1262 * ───────┴───────┴───────┴┬──┬────┴───────┴┬───┬────────────────────────
1263 * │BA│ │ACK│
1264 * └──┘ └───┘
1265 * ┌───────┬───────┐ ┌───────┬───────┐
1266 * link 1 │ 2 │ 3 │ │ 2 │ 3 │
1267 * ────────────────────┴───────┴───────┴┬──X───┴───────┴───────┴┬──┬─────
1268 * │BA│ │BA│
1269 * └──┘ └──┘
1270 */
1271 auto mpdu = *psdu->begin();
1272 CtrlBAckResponseHeader blockAck;
1273 mpdu->GetPacket()->PeekHeader(blockAck);
1274 bool isMpdu1corrupted = (m_trafficPattern == WifiTrafficPattern::STA_TO_AP ||
1276
1277 switch (m_blockAckCount)
1278 {
1279 case 0: // first BlockAck frame (all traffic patterns)
1281 true,
1282 "MPDU 0 expected to be successfully received");
1284 blockAck.IsPacketReceived(1),
1285 !isMpdu1corrupted,
1286 "MPDU 1 expected to be received only in STA_TO_STA/STA_TO_BCAST scenarios");
1287 // if there are at least two links setup, we expect all MPDUs to be inflight
1288 // (on distinct links)
1289 if (m_staMacs[0]->GetSetupLinkIds().size() > 1)
1290 {
1291 auto queue = m_sourceMac->GetTxopQueue(AC_BE);
1292 auto rcvMac = m_sourceMac == m_staMacs[0] ? StaticCast<WifiMac>(m_apMac)
1293 : StaticCast<WifiMac>(m_staMacs[1]);
1294 auto item = queue->PeekByTidAndAddress(0, rcvMac->GetAddress());
1295 std::size_t nQueuedPkt = 0;
1296 auto delay = WifiPhy::CalculateTxDuration(psdu,
1297 txVector,
1298 rcvMac->GetWifiPhy(linkId)->GetPhyBand()) +
1299 MicroSeconds(1); // to account for propagation delay
1300
1301 while (item)
1302 {
1303 auto seqNo = item->GetHeader().GetSequenceNumber();
1304 NS_TEST_EXPECT_MSG_EQ(item->IsInFlight(),
1305 true,
1306 "MPDU with seqNo=" << seqNo << " is not in flight");
1307 auto linkIds = item->GetInFlightLinkIds();
1308 NS_TEST_EXPECT_MSG_EQ(linkIds.size(),
1309 1,
1310 "MPDU with seqNo=" << seqNo
1311 << " is in flight on multiple links");
1312 // The first two MPDUs are in flight on the same link on which the BlockAck
1313 // is sent. The other two MPDUs (only for AP to STA/STA to AP scenarios) are
1314 // in flight on a different link.
1315 auto srcLinkId = m_sourceMac->GetLinkIdByAddress(mpdu->GetHeader().GetAddr1());
1316 NS_TEST_ASSERT_MSG_EQ(srcLinkId.has_value(),
1317 true,
1318 "Addr1 of BlockAck is not an originator's link address");
1319 NS_TEST_EXPECT_MSG_EQ((*linkIds.begin() == *srcLinkId),
1320 (seqNo <= 1),
1321 "MPDU with seqNo=" << seqNo
1322 << " in flight on unexpected link");
1323 // check the Retry subfield and whether this MPDU is still queued
1324 // after the originator has processed this BlockAck
1325
1326 // MPDUs acknowledged via this BlockAck are no longer queued
1327 bool isQueued = (seqNo > (isMpdu1corrupted ? 0 : 1));
1328 // The Retry subfield is set if the MPDU has not been acknowledged (i.e., it
1329 // is still queued) and has been transmitted on the same link as the BlockAck
1330 // (i.e., its sequence number is less than or equal to 1)
1331 bool isRetry = isQueued && seqNo <= 1;
1332
1333 Simulator::Schedule(delay, [this, item, isQueued, isRetry]() {
1334 NS_TEST_EXPECT_MSG_EQ(item->IsQueued(),
1335 isQueued,
1336 "MPDU with seqNo="
1337 << item->GetHeader().GetSequenceNumber() << " should "
1338 << (isQueued ? "" : "not") << " be queued");
1340 item->GetHeader().IsRetry(),
1341 isRetry,
1342 "Unexpected value for the Retry subfield of the MPDU with seqNo="
1343 << item->GetHeader().GetSequenceNumber());
1344 });
1345
1346 nQueuedPkt++;
1347 item = queue->PeekByTidAndAddress(0, rcvMac->GetAddress(), item);
1348 }
1349 // Each MPDU contains an A-MSDU consisting of two MSDUs
1350 NS_TEST_EXPECT_MSG_EQ(nQueuedPkt, m_nPackets / 2, "Unexpected number of queued MPDUs");
1351 }
1352 break;
1353 case 1: // second BlockAck frame (STA to AP and AP to STA traffic patterns only)
1354 case 2: // third BlockAck frame (STA to AP and AP to STA traffic patterns only)
1357 true,
1358 "Did not expect to receive a second BlockAck");
1359 // the second BlockAck is corrupted, but the data frames have been received successfully
1360 std::pair<uint16_t, uint16_t> seqNos;
1361 // if multiple links were setup, the transmission of the second A-MPDU started before
1362 // the end of the first one, so the second A-MPDU includes MPDUs with sequence numbers
1363 // 2 and 3. Otherwise, MPDU with sequence number 1 is retransmitted along with the MPDU
1364 // with sequence number 2.
1365 if (m_staMacs[0]->GetSetupLinkIds().size() > 1)
1366 {
1367 seqNos = {2, 3};
1368 }
1369 else
1370 {
1371 seqNos = {1, 2};
1372 }
1373 NS_TEST_EXPECT_MSG_EQ(blockAck.IsPacketReceived(seqNos.first),
1374 true,
1375 "MPDU " << seqNos.first << " expected to be successfully received");
1376 NS_TEST_EXPECT_MSG_EQ(blockAck.IsPacketReceived(seqNos.second),
1377 true,
1378 "MPDU " << seqNos.second << " expected to be successfully received");
1379 break;
1380 }
1381}
1382
1383void
1385{
1386 NS_LOG_INFO("Packet received by NODE " << +nodeId << "\n");
1387 m_rxPkts[nodeId]++;
1388}
1389
1390void
1392{
1394
1395 if (m_baEnabled)
1396 {
1397 // Enable A-MSDU aggregation. Max A-MSDU size is set such that two MSDUs can be aggregated
1398 for (auto mac : std::initializer_list<Ptr<WifiMac>>{m_apMac, m_staMacs[0], m_staMacs[1]})
1399 {
1400 mac->SetAttribute("BE_MaxAmsduSize", UintegerValue(2100));
1401 mac->GetQosTxop(AC_BE)->SetAttribute("UseExplicitBarAfterMissedBlockAck",
1403 mac->GetQosTxop(AC_BE)->SetAttribute("NMaxInflights", UintegerValue(m_nMaxInflight));
1404 }
1405 }
1406
1407 // install post reception error model on all devices
1408 for (std::size_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
1409 {
1410 auto errorModel = CreateObject<ListErrorModel>();
1411 m_errorModels[m_apMac->GetFrameExchangeManager(linkId)->GetAddress()] = errorModel;
1412 m_apMac->GetWifiPhy(linkId)->SetPostReceptionErrorModel(errorModel);
1413 }
1414 for (std::size_t linkId = 0; linkId < m_staMacs[0]->GetNLinks(); linkId++)
1415 {
1416 auto errorModel = CreateObject<ListErrorModel>();
1417 m_errorModels[m_staMacs[0]->GetFrameExchangeManager(linkId)->GetAddress()] = errorModel;
1418 m_staMacs[0]->GetWifiPhy(linkId)->SetPostReceptionErrorModel(errorModel);
1419
1420 errorModel = CreateObject<ListErrorModel>();
1421 m_errorModels[m_staMacs[1]->GetFrameExchangeManager(linkId)->GetAddress()] = errorModel;
1422 m_staMacs[1]->GetWifiPhy(linkId)->SetPostReceptionErrorModel(errorModel);
1423 }
1424}
1425
1426void
1428{
1429 const Time duration = Seconds(1);
1430 Address destAddr;
1431
1432 switch (m_trafficPattern)
1433 {
1436 destAddr = m_staMacs[1]->GetDevice()->GetAddress();
1437 break;
1440 destAddr = m_apMac->GetDevice()->GetAddress();
1441 break;
1444 destAddr = m_staMacs[1]->GetDevice()->GetAddress();
1445 break;
1448 destAddr = Mac48Address::GetBroadcast();
1449 break;
1452 destAddr = Mac48Address::GetBroadcast();
1453 break;
1454 }
1455
1456 PacketSocketHelper packetSocket;
1457 packetSocket.Install(m_apMac->GetDevice()->GetNode());
1458 packetSocket.Install(m_staMacs[0]->GetDevice()->GetNode());
1459 packetSocket.Install(m_staMacs[1]->GetDevice()->GetNode());
1460
1461 PacketSocketAddress socket;
1463 socket.SetPhysicalAddress(destAddr);
1464 socket.SetProtocol(1);
1465
1466 // install first client application generating at most 4 packets
1467 auto client1 = CreateObject<PacketSocketClient>();
1468 client1->SetAttribute("PacketSize", UintegerValue(1000));
1469 client1->SetAttribute("MaxPackets", UintegerValue(std::min<std::size_t>(m_nPackets, 4)));
1470 client1->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
1471 client1->SetRemote(socket);
1473 client1->SetStartTime(Seconds(0)); // now
1474 client1->SetStopTime(duration);
1475
1476 if (m_nPackets > 4)
1477 {
1478 // install a second client application generating the remaining packets
1479 auto client2 = CreateObject<PacketSocketClient>();
1480 client2->SetAttribute("PacketSize", UintegerValue(1000));
1481 client2->SetAttribute("MaxPackets", UintegerValue(m_nPackets - 4));
1482 client2->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
1483 client2->SetRemote(socket);
1485 // start during transmission of first A-MPDU, if multiple links are setup
1486 client2->SetStartTime(MilliSeconds(3));
1487 client2->SetStopTime(duration);
1488 }
1489
1490 // install a server on all nodes
1491 for (auto nodeIt = NodeList::Begin(); nodeIt != NodeList::End(); nodeIt++)
1492 {
1493 Ptr<PacketSocketServer> server = CreateObject<PacketSocketServer>();
1494 server->SetLocal(socket);
1495 (*nodeIt)->AddApplication(server);
1496 server->SetStartTime(Seconds(0)); // now
1497 server->SetStopTime(duration);
1498 }
1499
1500 for (std::size_t nodeId = 0; nodeId < NodeList::GetNNodes(); nodeId++)
1501 {
1502 Config::ConnectWithoutContext("/NodeList/" + std::to_string(nodeId) +
1503 "/ApplicationList/*/$ns3::PacketSocketServer/Rx",
1504 MakeCallback(&MultiLinkTxTest::L7Receive, this).Bind(nodeId));
1505 }
1506
1507 Simulator::Stop(duration);
1508}
1509
1510void
1512{
1513 Simulator::Run();
1514
1515 // Expected number of packets received by each node (AP, STA 0, STA 1) at application layer
1516 std::array<std::size_t, 3> expectedRxPkts{};
1517
1518 switch (m_trafficPattern)
1519 {
1522 // only STA 1 receives the m_nPackets packets that have been transmitted
1523 expectedRxPkts[2] = m_nPackets;
1524 break;
1526 // only the AP receives the m_nPackets packets that have been transmitted
1527 expectedRxPkts[0] = m_nPackets;
1528 break;
1530 // the AP replicates the broadcast frames on all the links, hence each station
1531 // receives the m_nPackets packets N times, where N is the number of setup link
1532 expectedRxPkts[1] = m_nPackets * m_staMacs[0]->GetSetupLinkIds().size();
1533 expectedRxPkts[2] = m_nPackets * m_staMacs[1]->GetSetupLinkIds().size();
1534 break;
1536 // the AP receives the m_nPackets packets and then replicates them on all the links,
1537 // hence STA 1 receives m_nPackets packets N times, where N is the number of setup link
1538 expectedRxPkts[0] = m_nPackets;
1539 expectedRxPkts[2] = m_nPackets * m_staMacs[1]->GetSetupLinkIds().size();
1540 break;
1541 }
1542
1544 +expectedRxPkts[0],
1545 "Unexpected number of packets received by the AP");
1547 +expectedRxPkts[1],
1548 "Unexpected number of packets received by STA 0");
1550 +expectedRxPkts[2],
1551 "Unexpected number of packets received by STA 1");
1552
1553 // check that the expected number of BlockAck frames are transmitted
1554 if (m_baEnabled && m_nMaxInflight == 1)
1555 {
1556 std::size_t expectedBaCount = 0;
1557 std::size_t expectedBarCount = 0;
1558
1559 switch (m_trafficPattern)
1560 {
1563 // two A-MPDUs are transmitted and one BlockAck is corrupted
1564 expectedBaCount = 3;
1565 // one BlockAckReq is sent if m_useBarAfterMissedBa is true
1566 expectedBarCount = m_useBarAfterMissedBa ? 1 : 0;
1567 break;
1570 // only one A-MPDU is transmitted and the BlockAck is not corrupted
1571 expectedBaCount = 1;
1572 break;
1573 default:;
1574 }
1576 expectedBaCount,
1577 "Unexpected number of BlockAck frames");
1579 expectedBarCount,
1580 "Unexpected number of BlockAckReq frames");
1581 }
1582
1583 // check that setting the QosTxop::NMaxInflights attribute has the expected effect.
1584 // We do not support sending an MPDU multiple times concurrently without Block Ack
1585 // agreement. Also, broadcast frames are already duplicated and sent on all links.
1587 {
1589 m_inflightCount.size(),
1590 m_nPackets / 2,
1591 "Did not collect number of simultaneous transmissions for all data frames");
1592
1593 auto nMaxInflight = std::min(m_nMaxInflight, m_staMacs[0]->GetSetupLinkIds().size());
1594 std::size_t maxCount = 0;
1595 for (const auto& [seqNo, count] : m_inflightCount)
1596 {
1598 count,
1599 nMaxInflight,
1600 "MPDU with seqNo=" << seqNo
1601 << " transmitted simultaneously more times than allowed");
1602 maxCount = std::max(maxCount, count);
1603 }
1604
1606 maxCount,
1607 nMaxInflight,
1608 "Expected that at least one data frame was transmitted simultaneously a number of "
1609 "times equal to the NMaxInflights attribute");
1610 }
1611
1612 Simulator::Destroy();
1613}
1614
1618enum class WifiMuTrafficPattern : uint8_t
1619{
1623 UL_MU
1624};
1625
1650{
1651 public:
1663 MultiLinkMuTxTest(WifiMuTrafficPattern muTrafficPattern,
1664 WifiUseBarAfterMissedBa useBarAfterMissedBa,
1665 uint8_t nMaxInflight,
1666 const std::vector<std::string>& staChannels,
1667 const std::vector<std::string>& apChannels,
1668 const std::vector<uint8_t>& fixedPhyBands = {});
1669 ~MultiLinkMuTxTest() override = default;
1670
1671 protected:
1678 void L7Receive(uint8_t nodeId, Ptr<const Packet> p, const Address& addr);
1679
1688 void CheckBlockAck(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector, uint8_t linkId);
1689
1690 void Transmit(uint8_t linkId,
1691 std::string context,
1692 WifiConstPsduMap psduMap,
1693 WifiTxVector txVector,
1694 double txPowerW) override;
1695 void DoSetup() override;
1696 void DoRun() override;
1697
1698 private:
1699 void StartTraffic() override;
1700
1702 using RxErrorModelMap = std::unordered_map<Mac48Address, Ptr<ListErrorModel>, WifiAddressHash>;
1703
1706 using AddrSeqNoPair = std::pair<Mac48Address, uint16_t>;
1707
1709 std::list<uint64_t> m_uidList;
1710 std::optional<Mac48Address> m_dataCorruptedSta;
1712 bool m_waitFirstTf{true};
1715 std::size_t m_nMaxInflight;
1716 std::vector<PacketSocketAddress> m_sockets;
1717 std::size_t m_nPackets;
1718 std::size_t m_blockAckCount{0};
1719 // std::size_t m_blockAckReqCount{0}; ///< transmitted BlockAckReq counter
1720 std::array<std::size_t, 3> m_rxPkts{};
1722 std::map<AddrSeqNoPair, std::size_t> m_inflightCount;
1725};
1726
1728 WifiUseBarAfterMissedBa useBarAfterMissedBa,
1729 uint8_t nMaxInflight,
1730 const std::vector<std::string>& staChannels,
1731 const std::vector<std::string>& apChannels,
1732 const std::vector<uint8_t>& fixedPhyBands)
1734 std::string("Check MU data transmission between MLDs ") +
1735 (useBarAfterMissedBa == WifiUseBarAfterMissedBa::YES
1736 ? "(send BAR after BlockAck timeout,"
1737 : "(send Data frames after BlockAck timeout,") +
1738 " MU Traffic pattern: " + std::to_string(static_cast<uint8_t>(muTrafficPattern)) +
1739 ", nMaxInflight=" + std::to_string(nMaxInflight) + ")",
1740 2,
1741 staChannels,
1742 apChannels,
1743 fixedPhyBands),
1744 m_muTrafficPattern(muTrafficPattern),
1745 m_useBarAfterMissedBa(useBarAfterMissedBa == WifiUseBarAfterMissedBa::YES),
1746 m_nMaxInflight(nMaxInflight),
1747 m_sockets(m_nStations),
1748 m_nPackets(muTrafficPattern == WifiMuTrafficPattern::UL_MU ? 4 : 8)
1749{
1750}
1751
1752void
1754 std::string context,
1755 WifiConstPsduMap psduMap,
1756 WifiTxVector txVector,
1757 double txPowerW)
1758{
1759 CtrlTriggerHeader trigger;
1760
1761 for (const auto& [staId, psdu] : psduMap)
1762 {
1763 switch (psdu->GetHeader(0).GetType())
1764 {
1765 case WIFI_MAC_QOSDATA:
1766 CheckAddresses(psdu);
1767 if (psdu->GetHeader(0).HasData())
1768 {
1769 bool isDl = psdu->GetHeader(0).IsFromDs();
1770 auto linkAddress =
1771 isDl ? psdu->GetHeader(0).GetAddr1() : psdu->GetHeader(0).GetAddr2();
1772 auto address = m_apMac->GetMldAddress(linkAddress).value_or(linkAddress);
1773
1774 for (const auto& mpdu : *psdu)
1775 {
1776 // determine the max number of simultaneous transmissions for this MPDU
1777 auto seqNo = mpdu->GetHeader().GetSequenceNumber();
1778 auto [it, success] = m_inflightCount.insert(
1779 {{address, seqNo}, mpdu->GetInFlightLinkIds().size()});
1780 if (!success)
1781 {
1782 it->second = std::max(it->second, mpdu->GetInFlightLinkIds().size());
1783 }
1784 }
1785 for (std::size_t i = 0; i < psdu->GetNMpdus(); i++)
1786 {
1787 // MPDUs with seqNo=2 are always transmitted in an MU PPDU
1788 if (psdu->GetHeader(i).GetSequenceNumber() == 2)
1789 {
1791 {
1792 NS_TEST_EXPECT_MSG_EQ(txVector.IsUlMu(),
1793 true,
1794 "MPDU " << **std::next(psdu->begin(), i)
1795 << " not transmitted in a TB PPDU");
1796 }
1797 else
1798 {
1799 NS_TEST_EXPECT_MSG_EQ(txVector.GetHeMuUserInfoMap().size(),
1800 2,
1801 "MPDU " << **std::next(psdu->begin(), i)
1802 << " not transmitted in a DL MU PPDU");
1803 }
1804 }
1805 // corrupt QoS data frame with sequence number equal to 3 (only once)
1806 if (psdu->GetHeader(i).GetSequenceNumber() != 3)
1807 {
1808 continue;
1809 }
1810 auto uid = psdu->GetPayload(i)->GetUid();
1811 if (!m_dataCorruptedSta)
1812 {
1813 m_uidList.push_front(uid);
1814 m_dataCorruptedSta = isDl ? psdu->GetAddr1() : psdu->GetAddr2();
1815 NS_LOG_INFO("CORRUPTED");
1816 m_errorModels.at(psdu->GetAddr1())->SetList(m_uidList);
1817 }
1818 else if ((isDl && m_dataCorruptedSta == psdu->GetAddr1()) ||
1819 (!isDl && m_dataCorruptedSta == psdu->GetAddr2()))
1820 {
1821 // do not corrupt the QoS data frame anymore
1822 if (auto it = std::find(m_uidList.cbegin(), m_uidList.cend(), uid);
1823 it != m_uidList.cend())
1824 {
1825 m_uidList.erase(it);
1826 }
1827 m_errorModels.at(psdu->GetAddr1())->SetList(m_uidList);
1828 }
1829 break;
1830 }
1831 }
1832 break;
1834 if (m_nMaxInflight > 1)
1835 {
1836 // we do not check the content of BlockAck when m_nMaxInflight is greater than 1
1837 break;
1838 }
1839 CheckBlockAck(psdu, txVector, linkId);
1841 // to simulate a missed BlockAck, corrupt the fifth BlockAck frame (the first
1842 // two BlockAck frames are sent to acknowledge the QoS data frames that triggered
1843 // the establishment of Block Ack agreements)
1844 if (m_blockAckCount == 5)
1845 {
1846 // corrupt the third BlockAck frame to simulate a missed BlockAck
1847 m_uidList.push_front(psdu->GetPacket()->GetUid());
1848 NS_LOG_INFO("CORRUPTED");
1849 m_errorModels.at(psdu->GetAddr1())->SetList(m_uidList);
1850 }
1851 break;
1853 psdu->GetPayload(0)->PeekHeader(trigger);
1854 // the MU scheduler requests channel access on all links but we have to perform the
1855 // following actions only once (hence why we only consider TF transmitted on link 0)
1856 if (trigger.IsBasic() && m_waitFirstTf)
1857 {
1858 m_waitFirstTf = false;
1859 // the AP is starting the transmission of the Basic Trigger frame, so generate
1860 // the configured number of packets at STAs, which are sent in TB PPDUs
1861 auto band = m_apMac->GetWifiPhy(linkId)->GetPhyBand();
1862 Time txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, band);
1863 for (uint8_t i = 0; i < m_nStations; i++)
1864 {
1865 Ptr<PacketSocketClient> client = CreateObject<PacketSocketClient>();
1866 client->SetAttribute("PacketSize", UintegerValue(450));
1867 client->SetAttribute("MaxPackets", UintegerValue(m_nPackets));
1868 client->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
1869 client->SetAttribute("Priority", UintegerValue(i * 4)); // 0 and 4
1870 client->SetRemote(m_sockets[i]);
1871 m_staMacs[i]->GetDevice()->GetNode()->AddApplication(client);
1872 client->SetStartTime(txDuration); // start when TX ends
1873 client->SetStopTime(Seconds(1.0)); // stop in a second
1874 client->Initialize();
1875 }
1876 }
1877 break;
1878 default:;
1879 }
1880 }
1881
1882 MultiLinkOperationsTestBase::Transmit(linkId, context, psduMap, txVector, txPowerW);
1883}
1884
1885void
1887 const WifiTxVector& txVector,
1888 uint8_t linkId)
1889{
1890 /*
1891 * Example sequence with DL_MU_BAR_BA_SEQUENCE
1892 * ┌───────┬───────X
1893 * (To:1) │ 2 │ 3 │
1894 * ├───────┼───────┤ ┌───┐ ┌───────┐
1895 * [link 0] (To:0) │ 2 │ 3 │ │BAR│ (To:1) │ 3 │
1896 * ────────────────┴───────┴───────┴┬──┼───┼──┬──────────┴───────┴┬───┬────────
1897 * │BA│ │BA│ │ACK│
1898 * └──┘ └──┘ └───┘
1899 * ┌───────┬───────┐
1900 * (To:1) │ 4 │ 5 │
1901 * ├───────┼───────┤ ┌───┐ ┌───┐
1902 * [link 1] (To:0) │ 4 │ 5 │ │BAR│ │BAR│
1903 * ────────────────────────────┴───────┴───────┴┬──X────┴───┴┬──┼───┼──┬───────
1904 * │BA│ │BA│ │BA│
1905 * └──┘ └──┘ └──┘
1906 *
1907 * Example sequence with UL_MU
1908 *
1909 * ┌──┐ ┌────┐ ┌───┐
1910 * [link 0] │TF│ │M-BA│ │ACK│
1911 * ─────────┴──┴──┬───────┬───────┬──┴────┴────────────┬───────┬─┴───┴─────────
1912 * (From:0) │ 2 │ 3 │ (From:1) │ 3 │
1913 * ├───────┼───────┤ └───────┘
1914 * (From:1) │ 2 │ 3 │
1915 * └───────┴───────X
1916 * ┌──┐
1917 * [link 1] │TF│
1918 * ─────────┴──┴──┬───────────────┬────────────────────────────────────────────
1919 * (From:0) │ QoS Null │
1920 * ├───────────────┤
1921 * (From:1) │ QoS Null │
1922 * └───────────────┘
1923 */
1924 auto mpdu = *psdu->begin();
1925 CtrlBAckResponseHeader blockAck;
1926 mpdu->GetPacket()->PeekHeader(blockAck);
1927 bool isMpdu3corrupted;
1928
1929 switch (m_blockAckCount)
1930 {
1931 case 0:
1932 case 1: // Ignore the first two BlockAck frames that acknowledged frames sent to establish BA
1933 break;
1934 case 2:
1936 {
1937 NS_TEST_EXPECT_MSG_EQ(blockAck.IsMultiSta(), true, "Expected a Multi-STA BlockAck");
1938 for (uint8_t i = 0; i < m_nStations; i++)
1939 {
1940 auto indices = blockAck.FindPerAidTidInfoWithAid(m_staMacs[i]->GetAssociationId());
1941 NS_TEST_ASSERT_MSG_EQ(indices.size(), 1, "Expected one Per AID TID Info per STA");
1942 auto index = indices.front();
1944 true,
1945 "Expected that a QoS data frame was corrupted");
1946 isMpdu3corrupted =
1947 m_staMacs[i]->GetLinkIdByAddress(*m_dataCorruptedSta).has_value();
1948 NS_TEST_EXPECT_MSG_EQ(blockAck.IsPacketReceived(2, index),
1949 true,
1950 "MPDU 2 expected to be successfully received");
1951 NS_TEST_EXPECT_MSG_EQ(blockAck.IsPacketReceived(3, index),
1952 !isMpdu3corrupted,
1953 "Unexpected reception status for MPDU 3");
1954 }
1955
1956 break;
1957 }
1958 case 3:
1959 // BlockAck frames in response to the first DL MU PPDU
1960 isMpdu3corrupted = (mpdu->GetHeader().GetAddr2() == m_dataCorruptedSta);
1962 true,
1963 "MPDU 2 expected to be successfully received");
1965 !isMpdu3corrupted,
1966 "Unexpected reception status for MPDU 3");
1967 // in case of DL MU, if there are at least two links setup, we expect all MPDUs to
1968 // be inflight (on distinct links)
1970 m_staMacs[0]->GetSetupLinkIds().size() > 1)
1971 {
1972 auto queue = m_apMac->GetTxopQueue(AC_BE);
1973 Ptr<StaWifiMac> rcvMac;
1974 if (m_staMacs[0]->GetFrameExchangeManager(linkId)->GetAddress() ==
1975 mpdu->GetHeader().GetAddr2())
1976 {
1977 rcvMac = m_staMacs[0];
1978 }
1979 else if (m_staMacs[1]->GetFrameExchangeManager(linkId)->GetAddress() ==
1980 mpdu->GetHeader().GetAddr2())
1981 {
1982 rcvMac = m_staMacs[1];
1983 }
1984 else
1985 {
1986 NS_ABORT_MSG("BlockAck frame not sent by a station in DL scenario");
1987 }
1988 auto item = queue->PeekByTidAndAddress(0, rcvMac->GetAddress());
1989 std::size_t nQueuedPkt = 0;
1990 auto delay = WifiPhy::CalculateTxDuration(psdu,
1991 txVector,
1992 rcvMac->GetWifiPhy(linkId)->GetPhyBand()) +
1993 MicroSeconds(1); // to account for propagation delay
1994
1995 while (item)
1996 {
1997 auto seqNo = item->GetHeader().GetSequenceNumber();
1998 NS_TEST_EXPECT_MSG_EQ(item->IsInFlight(),
1999 true,
2000 "MPDU with seqNo=" << seqNo << " is not in flight");
2001 auto linkIds = item->GetInFlightLinkIds();
2002 NS_TEST_EXPECT_MSG_EQ(linkIds.size(),
2003 1,
2004 "MPDU with seqNo=" << seqNo
2005 << " is in flight on multiple links");
2006 // The first two MPDUs are in flight on the same link on which the BlockAck
2007 // is sent. The other two MPDUs (only for AP to STA/STA to AP scenarios) are
2008 // in flight on a different link.
2009 auto srcLinkId = m_apMac->GetLinkIdByAddress(mpdu->GetHeader().GetAddr1());
2010 NS_TEST_ASSERT_MSG_EQ(srcLinkId.has_value(),
2011 true,
2012 "Addr1 of BlockAck is not an originator's link address");
2013 NS_TEST_EXPECT_MSG_EQ((*linkIds.begin() == *srcLinkId),
2014 (seqNo <= 3),
2015 "MPDU with seqNo=" << seqNo
2016 << " in flight on unexpected link");
2017 // check the Retry subfield and whether this MPDU is still queued
2018 // after the originator has processed this BlockAck
2019
2020 // MPDUs acknowledged via this BlockAck are no longer queued
2021 bool isQueued = (seqNo > (isMpdu3corrupted ? 2 : 3));
2022 // The Retry subfield is set if the MPDU has not been acknowledged (i.e., it
2023 // is still queued) and has been transmitted on the same link as the BlockAck
2024 // (i.e., its sequence number is less than or equal to 2)
2025 bool isRetry = isQueued && seqNo <= 3;
2026
2027 Simulator::Schedule(delay, [this, item, isQueued, isRetry]() {
2028 NS_TEST_EXPECT_MSG_EQ(item->IsQueued(),
2029 isQueued,
2030 "MPDU with seqNo="
2031 << item->GetHeader().GetSequenceNumber() << " should "
2032 << (isQueued ? "" : "not") << " be queued");
2034 item->GetHeader().IsRetry(),
2035 isRetry,
2036 "Unexpected value for the Retry subfield of the MPDU with seqNo="
2037 << item->GetHeader().GetSequenceNumber());
2038 });
2039
2040 nQueuedPkt++;
2041 item = queue->PeekByTidAndAddress(0, rcvMac->GetAddress(), item);
2042 }
2043 // Each MPDU contains an A-MSDU consisting of two MSDUs
2044 NS_TEST_EXPECT_MSG_EQ(nQueuedPkt, m_nPackets / 2, "Unexpected number of queued MPDUs");
2045 }
2046 break;
2047 }
2048}
2049
2050void
2052{
2053 NS_LOG_INFO("Packet received by NODE " << +nodeId << "\n");
2054 m_rxPkts[nodeId]++;
2055}
2056
2057void
2059{
2060 switch (m_muTrafficPattern)
2061 {
2063 Config::SetDefault("ns3::WifiDefaultAckManager::DlMuAckSequenceType",
2064 EnumValue(WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE));
2065 break;
2067 Config::SetDefault("ns3::WifiDefaultAckManager::DlMuAckSequenceType",
2068 EnumValue(WifiAcknowledgment::DL_MU_TF_MU_BAR));
2069 break;
2071 Config::SetDefault("ns3::WifiDefaultAckManager::DlMuAckSequenceType",
2072 EnumValue(WifiAcknowledgment::DL_MU_AGGREGATE_TF));
2073 break;
2074 default:;
2075 }
2076
2078
2079 // Enable A-MSDU aggregation. Max A-MSDU size is set such that two MSDUs can be aggregated
2080 for (auto mac : std::initializer_list<Ptr<WifiMac>>{m_apMac, m_staMacs[0], m_staMacs[1]})
2081 {
2082 mac->SetAttribute("BE_MaxAmsduSize", UintegerValue(1050));
2083 mac->GetQosTxop(AC_BE)->SetAttribute("UseExplicitBarAfterMissedBlockAck",
2085 mac->GetQosTxop(AC_BE)->SetAttribute("NMaxInflights", UintegerValue(m_nMaxInflight));
2086
2087 mac->SetAttribute("VI_MaxAmsduSize", UintegerValue(1050));
2088 mac->GetQosTxop(AC_VI)->SetAttribute("UseExplicitBarAfterMissedBlockAck",
2090 mac->GetQosTxop(AC_VI)->SetAttribute("NMaxInflights", UintegerValue(m_nMaxInflight));
2091 }
2092
2093 // aggregate MU scheduler
2094 auto muScheduler = CreateObjectWithAttributes<RrMultiUserScheduler>(
2095 "EnableUlOfdma",
2097 "EnableBsrp",
2098 BooleanValue(false),
2099 "UlPsduSize",
2100 UintegerValue(2000));
2101 m_apMac->AggregateObject(muScheduler);
2102
2103 // install post reception error model on all devices
2104 for (std::size_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
2105 {
2106 auto errorModel = CreateObject<ListErrorModel>();
2107 m_errorModels[m_apMac->GetFrameExchangeManager(linkId)->GetAddress()] = errorModel;
2108 m_apMac->GetWifiPhy(linkId)->SetPostReceptionErrorModel(errorModel);
2109 }
2110 for (std::size_t linkId = 0; linkId < m_staMacs[0]->GetNLinks(); linkId++)
2111 {
2112 auto errorModel = CreateObject<ListErrorModel>();
2113 m_errorModels[m_staMacs[0]->GetFrameExchangeManager(linkId)->GetAddress()] = errorModel;
2114 m_staMacs[0]->GetWifiPhy(linkId)->SetPostReceptionErrorModel(errorModel);
2115
2116 errorModel = CreateObject<ListErrorModel>();
2117 m_errorModels[m_staMacs[1]->GetFrameExchangeManager(linkId)->GetAddress()] = errorModel;
2118 m_staMacs[1]->GetWifiPhy(linkId)->SetPostReceptionErrorModel(errorModel);
2119 }
2120}
2121
2122void
2124{
2125 const Time duration = Seconds(1);
2126 Address destAddr;
2127
2128 PacketSocketHelper packetSocket;
2129 packetSocket.Install(m_apMac->GetDevice()->GetNode());
2130 packetSocket.Install(m_staMacs[0]->GetDevice()->GetNode());
2131 packetSocket.Install(m_staMacs[1]->GetDevice()->GetNode());
2132
2134 {
2135 // DL Traffic
2136 for (uint8_t i = 0; i < m_nStations; i++)
2137 {
2138 PacketSocketAddress socket;
2140 socket.SetPhysicalAddress(m_staMacs[i]->GetDevice()->GetAddress());
2141 socket.SetProtocol(1);
2142
2143 // the first client application generates three packets in order
2144 // to trigger the establishment of a Block Ack agreement
2145 auto client1 = CreateObject<PacketSocketClient>();
2146 client1->SetAttribute("PacketSize", UintegerValue(450));
2147 client1->SetAttribute("MaxPackets", UintegerValue(3));
2148 client1->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
2149 client1->SetRemote(socket);
2150 m_apMac->GetDevice()->GetNode()->AddApplication(client1);
2151 client1->SetStartTime(i * MilliSeconds(50));
2152 client1->SetStopTime(duration);
2153
2154 // the second client application generates the first half of the selected number
2155 // of packets, which are sent in DL MU PPDUs.
2156 auto client2 = CreateObject<PacketSocketClient>();
2157 client2->SetAttribute("PacketSize", UintegerValue(450));
2158 client2->SetAttribute("MaxPackets", UintegerValue(m_nPackets / 2));
2159 client2->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
2160 client2->SetRemote(socket);
2161 m_apMac->GetDevice()->GetNode()->AddApplication(client2);
2162 // start after all BA agreements are established
2163 client2->SetStartTime(m_nStations * MilliSeconds(50));
2164 client2->SetStopTime(duration);
2165
2166 // the third client application generates the second half of the selected number
2167 // of packets, which are sent in DL MU PPDUs.
2168 auto client3 = CreateObject<PacketSocketClient>();
2169 client3->SetAttribute("PacketSize", UintegerValue(450));
2170 client3->SetAttribute("MaxPackets", UintegerValue(m_nPackets / 2));
2171 client3->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
2172 client3->SetRemote(socket);
2173 m_apMac->GetDevice()->GetNode()->AddApplication(client3);
2174 // start during transmission of first A-MPDU, if multiple links are setup
2175 client3->SetStartTime(m_nStations * MilliSeconds(50) + MilliSeconds(3));
2176 client3->SetStopTime(duration);
2177 }
2178 }
2179 else
2180 {
2181 // UL Traffic
2182 for (uint8_t i = 0; i < m_nStations; i++)
2183 {
2184 m_sockets[i].SetSingleDevice(m_staMacs[i]->GetDevice()->GetIfIndex());
2185 m_sockets[i].SetPhysicalAddress(m_apMac->GetDevice()->GetAddress());
2186 m_sockets[i].SetProtocol(1);
2187
2188 // the first client application generates three packets in order
2189 // to trigger the establishment of a Block Ack agreement
2190 Ptr<PacketSocketClient> client1 = CreateObject<PacketSocketClient>();
2191 client1->SetAttribute("PacketSize", UintegerValue(450));
2192 client1->SetAttribute("MaxPackets", UintegerValue(3));
2193 client1->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
2194 client1->SetAttribute("Priority", UintegerValue(i * 4)); // 0 and 4
2195 client1->SetRemote(m_sockets[i]);
2196 m_staMacs[i]->GetDevice()->GetNode()->AddApplication(client1);
2197 client1->SetStartTime(i * MilliSeconds(50));
2198 client1->SetStopTime(duration);
2199
2200 // packets to be included in TB PPDUs are generated (by Transmit()) when
2201 // the first Basic Trigger Frame is sent by the AP
2202 }
2203
2204 // MU scheduler starts requesting channel access when we are done with BA agreements
2205 Simulator::Schedule(m_nStations * MilliSeconds(50), [this]() {
2206 auto muScheduler = m_apMac->GetObject<MultiUserScheduler>();
2207 NS_TEST_ASSERT_MSG_NE(muScheduler, nullptr, "Expected an aggregated MU scheduler");
2208 muScheduler->SetAccessReqInterval(MilliSeconds(3));
2209 // channel access is requested only once
2210 muScheduler->SetAccessReqInterval(Seconds(0));
2211 });
2212 }
2213
2214 // install a server on all nodes and connect traced callback
2215 for (auto nodeIt = NodeList::Begin(); nodeIt != NodeList::End(); nodeIt++)
2216 {
2217 PacketSocketAddress srvAddr;
2218 auto device = DynamicCast<WifiNetDevice>((*nodeIt)->GetDevice(0));
2219 NS_TEST_ASSERT_MSG_NE(device, nullptr, "Expected a WifiNetDevice");
2220 srvAddr.SetSingleDevice(device->GetIfIndex());
2221 srvAddr.SetProtocol(1);
2222
2223 Ptr<PacketSocketServer> server = CreateObject<PacketSocketServer>();
2224 server->SetLocal(srvAddr);
2225 (*nodeIt)->AddApplication(server);
2226 server->SetStartTime(Seconds(0)); // now
2227 server->SetStopTime(duration);
2228 server->TraceConnectWithoutContext(
2229 "Rx",
2230 MakeCallback(&MultiLinkMuTxTest::L7Receive, this).Bind(device->GetNode()->GetId()));
2231 }
2232
2233 Simulator::Stop(duration);
2234}
2235
2236void
2238{
2239 Simulator::Run();
2240
2241 // Expected number of packets received by each node (AP, STA 0, STA 1) at application layer
2242 std::array<std::size_t, 3> expectedRxPkts{};
2243
2244 switch (m_muTrafficPattern)
2245 {
2249 // both STA 0 and STA 1 receive m_nPackets + 3 (sent to trigger BA establishment) packets
2250 expectedRxPkts[1] = m_nPackets + 3;
2251 expectedRxPkts[2] = m_nPackets + 3;
2252 break;
2254 // AP receives m_nPackets + 3 (sent to trigger BA establishment) packets from each station
2255 expectedRxPkts[0] = 2 * (m_nPackets + 3);
2256 break;
2257 }
2258
2260 +expectedRxPkts[0],
2261 "Unexpected number of packets received by the AP");
2263 +expectedRxPkts[1],
2264 "Unexpected number of packets received by STA 0");
2266 +expectedRxPkts[2],
2267 "Unexpected number of packets received by STA 1");
2268
2269 // check that setting the QosTxop::NMaxInflights attribute has the expected effect.
2270 // For DL, for each station we send 2 MPDUs to trigger BA agreement and m_nPackets / 2 MPDUs
2271 // For UL, each station sends 2 MPDUs to trigger BA agreement and m_nPackets / 2 MPDUs
2273 m_inflightCount.size(),
2274 2 * (2 + m_nPackets / 2),
2275 "Did not collect number of simultaneous transmissions for all data frames");
2276
2277 auto nMaxInflight = std::min(m_nMaxInflight, m_staMacs[0]->GetSetupLinkIds().size());
2278 std::size_t maxCount = 0;
2279 for (const auto& [txSeqNoPair, count] : m_inflightCount)
2280 {
2282 nMaxInflight,
2283 "MPDU with seqNo="
2284 << txSeqNoPair.second
2285 << " transmitted simultaneously more times than allowed");
2286 maxCount = std::max(maxCount, count);
2287 }
2288
2290 maxCount,
2291 nMaxInflight,
2292 "Expected that at least one data frame was transmitted simultaneously a number of "
2293 "times equal to the NMaxInflights attribute");
2294
2295 Simulator::Destroy();
2296}
2297
2317{
2318 public:
2321
2322 protected:
2323 void DoSetup() override;
2324 void DoRun() override;
2325 void Transmit(uint8_t linkId,
2326 std::string context,
2327 WifiConstPsduMap psduMap,
2328 WifiTxVector txVector,
2329 double txPowerW) override;
2330
2331 private:
2332 void StartTraffic() override;
2333
2338
2340 std::size_t m_nQosDataFrames;
2343};
2344
2347 "Check sequence numbers after CTS timeout",
2348 1,
2349 {"{36, 0, BAND_5GHZ, 0}", "{2, 0, BAND_2_4GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"},
2350 {"{36, 0, BAND_5GHZ, 0}", "{2, 0, BAND_2_4GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"}),
2351 m_nQosDataFrames(0),
2352 m_errorModel(CreateObject<ListErrorModel>()),
2353 m_rtsCorrupted(false)
2354{
2355}
2356
2357void
2359{
2360 // Enable RTS/CTS
2361 Config::SetDefault("ns3::WifiRemoteStationManager::RtsCtsThreshold", StringValue("1000"));
2362
2364
2365 // install post reception error model on all STAs affiliated with non-AP MLD
2366 for (std::size_t linkId = 0; linkId < m_staMacs[0]->GetNLinks(); linkId++)
2367 {
2368 m_staMacs[0]->GetWifiPhy(linkId)->SetPostReceptionErrorModel(m_errorModel);
2369 }
2370}
2371
2374{
2375 const Time duration = Seconds(1);
2376
2377 auto client = CreateObject<PacketSocketClient>();
2378 client->SetAttribute("PacketSize", UintegerValue(1000));
2379 client->SetAttribute("MaxPackets", UintegerValue(4));
2380 client->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
2381 client->SetRemote(m_socket);
2382 client->SetStartTime(Seconds(0)); // now
2383 client->SetStopTime(duration);
2384
2385 return client;
2386}
2387
2388void
2390{
2391 const Time duration = Seconds(1);
2392
2393 PacketSocketHelper packetSocket;
2394 packetSocket.Install(m_apMac->GetDevice()->GetNode());
2395 packetSocket.Install(m_staMacs[0]->GetDevice()->GetNode());
2396
2398 m_socket.SetPhysicalAddress(m_staMacs[0]->GetAddress());
2400
2401 // install client application generating 4 packets
2403
2404 // install a server on all nodes
2405 for (auto nodeIt = NodeList::Begin(); nodeIt != NodeList::End(); nodeIt++)
2406 {
2407 Ptr<PacketSocketServer> server = CreateObject<PacketSocketServer>();
2408 server->SetLocal(m_socket);
2409 (*nodeIt)->AddApplication(server);
2410 server->SetStartTime(Seconds(0)); // now
2411 server->SetStopTime(duration);
2412 }
2413}
2414
2415void
2417 std::string context,
2418 WifiConstPsduMap psduMap,
2419 WifiTxVector txVector,
2420 double txPowerW)
2421{
2422 auto psdu = psduMap.begin()->second;
2423
2424 if (psdu->GetHeader(0).IsRts() && !m_rtsCorrupted)
2425 {
2426 m_errorModel->SetList({psdu->GetPacket()->GetUid()});
2427 m_rtsCorrupted = true;
2428 // generate other packets when the first RTS is transmitted
2430 }
2431 else if (psdu->GetHeader(0).IsQosData())
2432 {
2434
2435 if (m_nQosDataFrames == 2)
2436 {
2437 // generate other packets when the second QoS data frame is transmitted
2439 }
2440 }
2441
2442 MultiLinkOperationsTestBase::Transmit(linkId, context, psduMap, txVector, txPowerW);
2443}
2444
2445void
2447{
2448 Simulator::Stop(Seconds(1.0));
2449 Simulator::Run();
2450
2451 NS_TEST_EXPECT_MSG_EQ(m_nQosDataFrames, 3, "Unexpected number of transmitted QoS data frames");
2452
2453 std::size_t count{};
2454
2455 for (const auto& txPsdu : m_txPsdus)
2456 {
2457 auto psdu = txPsdu.psduMap.begin()->second;
2458
2459 if (!psdu->GetHeader(0).IsQosData())
2460 {
2461 continue;
2462 }
2463
2464 NS_TEST_EXPECT_MSG_EQ(psdu->GetNMpdus(), 4, "Unexpected number of MPDUs in A-MPDU");
2465
2466 count++;
2467 uint16_t expectedSeqNo{};
2468
2469 switch (count)
2470 {
2471 case 1:
2472 expectedSeqNo = 4;
2473 break;
2474 case 2:
2475 expectedSeqNo = 0;
2476 break;
2477 case 3:
2478 expectedSeqNo = 8;
2479 break;
2480 }
2481
2482 for (const auto& mpdu : *PeekPointer(psdu))
2483 {
2484 NS_TEST_EXPECT_MSG_EQ(mpdu->GetHeader().GetSequenceNumber(),
2485 expectedSeqNo++,
2486 "Unexpected sequence number");
2487 }
2488 }
2489
2490 Simulator::Destroy();
2491}
2492
2500{
2501 public:
2503};
2504
2506 : TestSuite("wifi-mlo", UNIT)
2507{
2508 using ParamsTuple =
2509 std::tuple<std::vector<std::string>, // non-AP MLD channels
2510 std::vector<std::string>, // AP MLD channels
2511 std::vector<std::pair<uint8_t, uint8_t>>, // (STA link ID, AP link ID) of setup
2512 // links
2513 std::vector<uint8_t>>; // IDs of link that cannot change PHY band
2514
2515 AddTestCase(new GetRnrLinkInfoTest(), TestCase::QUICK);
2516
2517 for (const auto& [staChannels, apChannels, setupLinks, fixedPhyBands] :
2518 {// matching channels: setup all links
2519 ParamsTuple({"{36, 0, BAND_5GHZ, 0}", "{2, 0, BAND_2_4GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"},
2520 {"{36, 0, BAND_5GHZ, 0}", "{2, 0, BAND_2_4GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"},
2521 {{0, 0}, {1, 1}, {2, 2}},
2522 {}),
2523 // non-matching channels, matching PHY bands: setup all links
2524 ParamsTuple({"{108, 0, BAND_5GHZ, 0}", "{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"},
2525 {"{36, 0, BAND_5GHZ, 0}", "{120, 0, BAND_5GHZ, 0}", "{5, 0, BAND_6GHZ, 0}"},
2526 {{1, 0}, {0, 1}, {2, 2}},
2527 {}),
2528 // non-AP MLD switches band on some links to setup 3 links
2529 ParamsTuple({"{2, 0, BAND_2_4GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{36, 0, BAND_5GHZ, 0}"},
2530 {"{36, 0, BAND_5GHZ, 0}", "{9, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
2531 {{2, 0}, {0, 1}, {1, 2}},
2532 {}),
2533 // the first link of the non-AP MLD cannot change PHY band and no AP is operating on
2534 // that band, hence only 2 links are setup
2535 ParamsTuple(
2536 {"{2, 0, BAND_2_4GHZ, 0}", "{36, 0, BAND_5GHZ, 0}", "{8, 20, BAND_2_4GHZ, 0}"},
2537 {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
2538 {{1, 0}, {2, 1}},
2539 {0}),
2540 // the first link of the non-AP MLD cannot change PHY band and no AP is operating on
2541 // that band; the second link of the non-AP MLD cannot change PHY band and there is
2542 // an AP operating on the same channel; hence 2 links are setup
2543 ParamsTuple(
2544 {"{2, 0, BAND_2_4GHZ, 0}", "{36, 0, BAND_5GHZ, 0}", "{8, 20, BAND_2_4GHZ, 0}"},
2545 {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
2546 {{1, 0}, {2, 1}},
2547 {0, 1}),
2548 // the first link of the non-AP MLD cannot change PHY band and no AP is operating on
2549 // that band; the second link of the non-AP MLD cannot change PHY band and there is
2550 // an AP operating on the same channel; the third link of the non-AP MLD cannot
2551 // change PHY band and there is an AP operating on the same band (different channel);
2552 // hence 2 links are setup by switching channel (not band) on the third link
2553 ParamsTuple({"{2, 0, BAND_2_4GHZ, 0}", "{36, 0, BAND_5GHZ, 0}", "{60, 0, BAND_5GHZ, 0}"},
2554 {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
2555 {{1, 0}, {2, 2}},
2556 {0, 1, 2}),
2557 // the first link of the non-AP MLD cannot change PHY band and no AP is operating on
2558 // that band; the second link of the non-AP MLD cannot change PHY band and there is
2559 // an AP operating on the same channel; hence one link only is setup
2560 ParamsTuple({"{2, 0, BAND_2_4GHZ, 0}", "{36, 0, BAND_5GHZ, 0}"},
2561 {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
2562 {{1, 0}},
2563 {0, 1}),
2564 // non-AP MLD has only two STAs and setups two links
2565 ParamsTuple({"{2, 0, BAND_2_4GHZ, 0}", "{36, 0, BAND_5GHZ, 0}"},
2566 {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
2567 {{0, 1}, {1, 0}},
2568 {}),
2569 // single link non-AP STA associates with an AP affiliated with an AP MLD
2570 ParamsTuple({"{120, 0, BAND_5GHZ, 0}"},
2571 {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
2572 {{0, 2}},
2573 {}),
2574 // a STA affiliated with a non-AP MLD associates with a single link AP
2575 ParamsTuple({"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
2576 {"{120, 0, BAND_5GHZ, 0}"},
2577 {{2, 0}},
2578 {})})
2579 {
2580 AddTestCase(new MultiLinkSetupTest(staChannels, apChannels, setupLinks, fixedPhyBands),
2581 TestCase::QUICK);
2582
2583 for (const auto& trafficPattern : {WifiTrafficPattern::STA_TO_STA,
2588 {
2589 // No Block Ack agreement
2590 AddTestCase(new MultiLinkTxTest(trafficPattern,
2593 1,
2594 staChannels,
2595 apChannels,
2596 fixedPhyBands),
2597 TestCase::QUICK);
2598 for (const auto& useBarAfterMissedBa :
2600 {
2601 // Block Ack agreement with nMaxInflight=1
2602 AddTestCase(new MultiLinkTxTest(trafficPattern,
2604 useBarAfterMissedBa,
2605 1,
2606 staChannels,
2607 apChannels,
2608 fixedPhyBands),
2609 TestCase::QUICK);
2610 // Block Ack agreement with nMaxInflight=2
2611 AddTestCase(new MultiLinkTxTest(trafficPattern,
2613 useBarAfterMissedBa,
2614 2,
2615 staChannels,
2616 apChannels,
2617 fixedPhyBands),
2618 TestCase::QUICK);
2619 }
2620 }
2621
2622 for (const auto& muTrafficPattern : {WifiMuTrafficPattern::DL_MU_BAR_BA_SEQUENCE,
2626 {
2627 for (const auto& useBarAfterMissedBa :
2629 {
2630 // Block Ack agreement with nMaxInflight=1
2631 AddTestCase(new MultiLinkMuTxTest(muTrafficPattern,
2632 useBarAfterMissedBa,
2633 1,
2634 staChannels,
2635 apChannels,
2636 fixedPhyBands),
2637 TestCase::QUICK);
2638 // Block Ack agreement with nMaxInflight=2
2639 AddTestCase(new MultiLinkMuTxTest(muTrafficPattern,
2640 useBarAfterMissedBa,
2641 2,
2642 staChannels,
2643 apChannels,
2644 fixedPhyBands),
2645 TestCase::QUICK);
2646 }
2647 }
2648 }
2649
2650 AddTestCase(new ReleaseSeqNoAfterCtsTimeoutTest(), TestCase::QUICK);
2651}
2652
#define min(a, b)
Definition: 80211b.c:42
#define max(a, b)
Definition: 80211b.c:43
Test release of sequence numbers upon CTS timeout in multi-link operations.
Ptr< ListErrorModel > m_errorModel
error rate model to corrupt first RTS frame
void Transmit(uint8_t linkId, std::string context, WifiConstPsduMap psduMap, WifiTxVector txVector, double txPowerW) override
Callback invoked when a FEM passes PSDUs to the PHY.
std::size_t m_nQosDataFrames
counter for transmitted QoS data frames
void StartTraffic() override
Start the generation of traffic (needs to be overridden)
PacketSocketAddress m_socket
packet socket address
Ptr< PacketSocketClient > GetApplication() const
bool m_rtsCorrupted
whether the first RTS frame has been corrupted
void DoSetup() override
Implementation to do any local setup required for this TestCase.
~ReleaseSeqNoAfterCtsTimeoutTest() override=default
void DoRun() override
Implementation to actually run this TestCase.
a polymophic address class
Definition: address.h:100
uint16_t GetAssociationId(Mac48Address addr, uint8_t linkId) const
const std::map< uint16_t, Mac48Address > & GetStaList(uint8_t linkId) const
Get a const reference to the map of associated stations on the given link.
Ptr< WifiMacQueue > GetTxopQueue(AcIndex ac) const override
Get the wifi MAC queue of the (Qos)Txop associated with the given AC, if such (Qos)Txop is installed,...
Definition: ap-wifi-mac.cc:169
AttributeValue implementation for Boolean.
Definition: boolean.h:37
Headers for BlockAck response.
Definition: ctrl-headers.h:203
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...
bool IsMultiSta() const
Check if the BlockAck frame variant is Multi-STA Block Ack.
Headers for Trigger frames.
Definition: ctrl-headers.h:945
bool IsBasic() const
Check if this is a Basic Trigger frame.
Hold variables of type enum.
Definition: enum.h:56
void SetList(const std::list< uint64_t > &packetlist)
Definition: error-model.cc:456
an EUI-48 address
Definition: mac48-address.h:46
bool IsGroup() const
Implement the header for management frames of type association request.
Definition: mgt-headers.h:55
const std::optional< MultiLinkElement > & GetMultiLinkElement() const
Return the Multi-Link Element information element, if present.
Implement the header for management frames of type association and reassociation response.
Definition: mgt-headers.h:447
const std::optional< MultiLinkElement > & GetMultiLinkElement() const
Return the Multi-Link Element information element, if present.
Implement the header for management frames of type beacon.
Definition: mgt-headers.h:1257
const std::optional< ReducedNeighborReport > & GetReducedNeighborReport() const
Return the Reduced Neighbor Report information element, if present.
Definition: mgt-headers.cc:637
const std::optional< MultiLinkElement > & GetMultiLinkElement() const
Return the Multi-Link Element information element, if present.
Definition: mgt-headers.cc:643
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
keep track of a set of node pointers.
uint32_t AddApplication(Ptr< Application > application)
Associate an Application to this Node.
Definition: node.cc:169
bool TraceConnectWithoutContext(std::string name, const CallbackBase &cb)
Connect a TraceSource to a Callback without a context.
Definition: object-base.cc:311
Ptr< T > GetObject() const
Get a pointer to the requested aggregated Object.
Definition: object.h:471
void AggregateObject(Ptr< Object > other)
Aggregate two Objects together.
Definition: object.cc:259
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.
The Reduced Neighbor Report element.
std::size_t GetNNbrApInfoFields() const
Get the number of Neighbor AP Information fields.
void SetMldParameters(std::size_t nbrApInfoId, std::size_t index, uint8_t mldId, uint8_t linkId, uint8_t changeSequence)
Set the MLD Parameters subfield of the i-th TBTT Information field of the given Neighbor AP Informati...
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 AddTbttInformationField(std::size_t nbrApInfoId)
Add a TBTT Information fields to the TBTT Information Set field of the given Neighbor AP Information ...
Make it easy to create and manage PHY objects for the spectrum model.
void SetChannel(Ptr< SpectrumChannel > channel)
The IEEE 802.11 SSID Information Element.
Definition: ssid.h:36
AttributeValue implementation for Ssid.
Hold variables of type string.
Definition: string.h:56
encapsulates test code
Definition: test.h:1060
void AddTestCase(TestCase *testCase, TestDuration duration=QUICK)
Add an individual child TestCase to this test suite.
Definition: test.cc:305
A suite of tests to run.
Definition: test.h:1256
Simulation virtual time values and global simulation resolution.
Definition: nstime.h:105
AttributeValue implementation for Time.
Definition: nstime.h:1423
Hold an unsigned integer type.
Definition: uinteger.h:45
helps to create WifiNetDevice objects
Definition: wifi-helper.h:324
bool IsQosData() const
Return true if the Type is DATA and Subtype is one of the possible values for QoS Data.
create MAC layers for a ns3::WifiNetDevice.
Ptr< FrameExchangeManager > GetFrameExchangeManager(uint8_t linkId=SINGLE_LINK_OP_ID) const
Get the Frame Exchange Manager associated with the given link.
Definition: wifi-mac.cc:840
std::optional< Mac48Address > GetMldAddress(const Mac48Address &remoteAddr) const
Definition: wifi-mac.cc:1236
uint8_t GetNLinks() const
Get the number of links (can be greater than 1 for 11be devices only).
Definition: wifi-mac.cc:906
Ptr< WifiPhy > GetWifiPhy(uint8_t linkId=SINGLE_LINK_OP_ID) const
Definition: wifi-mac.cc:954
virtual std::optional< uint8_t > GetLinkIdByAddress(const Mac48Address &address) const
Get the ID of the link having the given MAC address, if any.
Definition: wifi-mac.cc:912
Ptr< WifiNetDevice > GetDevice() const
Return the device this PHY is associated with.
Definition: wifi-mac.cc:430
virtual Ptr< WifiMacQueue > GetTxopQueue(AcIndex ac) const
Get the wifi MAC queue of the (Qos)Txop associated with the given AC, if such (Qos)Txop is installed,...
Definition: wifi-mac.cc:536
Ptr< WifiRemoteStationManager > GetWifiRemoteStationManager(uint8_t linkId=0) const
Definition: wifi-mac.cc:886
Mac48Address GetAddress() const
Definition: wifi-mac.cc:443
uint32_t GetIfIndex() const override
Address GetAddress() const override
Ptr< Node > GetNode() const override
void SetPcapDataLinkType(SupportedPcapDataLinkTypes dlt)
Set the data link type of PCAP traces to be used.
Definition: wifi-helper.cc:543
void Set(std::string name, const AttributeValue &v)
Definition: wifi-helper.cc:163
void SetPostReceptionErrorModel(const Ptr< ErrorModel > em)
Attach a receive ErrorModel to the WifiPhy.
Definition: wifi-phy.cc:639
WifiPhyBand GetPhyBand() const
Get the configured Wi-Fi band.
Definition: wifi-phy.cc:996
const WifiPhyOperatingChannel & GetOperatingChannel() const
Get a const reference to the operating channel.
Definition: wifi-phy.cc:1008
uint8_t GetPrimaryChannelIndex(uint16_t primaryChannelWidth) const
If the operating channel width is a multiple of 20 MHz, return the index of the primary channel of th...
uint16_t GetWidth() const
Return the width of the whole operating channel (in MHz).
WifiPhyBand GetPhyBand() const
Return the PHY band of the operating channel.
uint8_t GetNumber() const
Return the channel number identifying the whole operating channel.
uint16_t GetFrequency() const
Return the center frequency of the operating channel (in MHz).
const WifiMacHeader & GetHeader(std::size_t i) const
Get the header of the i-th MPDU.
Definition: wifi-psdu.cc:279
std::vector< Ptr< WifiMpdu > >::const_iterator begin() const
Return a const iterator to the first MPDU.
Definition: wifi-psdu.cc:333
Mac48Address GetAddr2() const
Get the Transmitter Address (TA), which is common to all the MPDUs.
Definition: wifi-psdu.cc:128
Mac48Address GetAddr1() const
Get the Receiver Address (RA), which is common to all the MPDUs.
Definition: wifi-psdu.cc:113
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.
bool IsUlMu() 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:66
void SetDefault(std::string name, const AttributeValue &value)
Definition: config.cc:891
void Connect(std::string path, const CallbackBase &cb)
Definition: config.cc:975
void ConnectWithoutContext(std::string path, const CallbackBase &cb)
Definition: config.cc:951
#define NS_ABORT_MSG(msg)
Unconditional abnormal program termination with a message.
Definition: abort.h:49
#define NS_ABORT_IF(cond)
Abnormal program termination if a condition is true.
Definition: abort.h:76
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition: log.h:275
Time Now()
create an ns3::Time instance which contains the current simulation time.
Definition: simulator.cc:296
#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:144
#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:830
#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:666
#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:251
#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:564
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1360
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1336
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1348
@ WIFI_STANDARD_80211be
@ AC_BE
Best Effort.
Definition: qos-utils.h:74
@ AC_VI
Video.
Definition: qos-utils.h:78
address
Definition: first.py:40
Every class exported by the ns3 library is enclosed in the ns3 namespace.
U * PeekPointer(const Ptr< U > &p)
Definition: ptr.h:488
std::unordered_map< uint16_t, Ptr< const WifiPsdu > > WifiConstPsduMap
Map of const PSDUs indexed by STA-ID.
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:707
@ WIFI_MAC_CTL_TRIGGER
@ 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_QOSDATA
STL namespace.
staDevices
Definition: third.py:91
channel
Definition: third.py:81
mac
Definition: third.py:85
wifi
Definition: third.py:88
apDevices
Definition: third.py:94
wifiApNode
Definition: third.py:79
mobility
Definition: third.py:96
wifiStaNodes
Definition: third.py:77
Function object to compute the hash of a MAC address.
Definition: qos-utils.h:55
WifiMuTrafficPattern
Tested MU traffic patterns.
WifiTrafficPattern
Tested traffic patterns.
WifiUseBarAfterMissedBa
Whether to send a BlockAckReq after a missed BlockAck.
static WifiMultiLinkOperationsTestSuite g_wifiMultiLinkOperationsTestSuite
the test suite
WifiBaEnabled
Block Ack agreement enabled/disabled.