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 * 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 <optional>
56#include <sstream>
57#include <vector>
58
59using namespace ns3;
60
61NS_LOG_COMPONENT_DEFINE("WifiMloTest");
62
72{
73 public:
78 ~GetRnrLinkInfoTest() override = default;
79
80 private:
81 void DoRun() override;
82};
83
85 : TestCase("Check the implementation of WifiAssocManager::GetNextAffiliatedAp()")
86{
87}
88
89void
91{
93 std::size_t nbrId;
94 std::size_t tbttId;
95
96 // Add a first Neighbor AP Information field without MLD Parameters
98 nbrId = rnr.GetNNbrApInfoFields() - 1;
99
100 rnr.AddTbttInformationField(nbrId);
101 rnr.AddTbttInformationField(nbrId);
102
103 // Add a second Neighbor AP Information field with MLD Parameters; the first
104 // TBTT Information field is related to an AP affiliated to the same AP MLD
105 // as the reported AP; the second TBTT Information field is not (it does not
106 // make sense that two APs affiliated to the same AP MLD are using the same
107 // channel).
108 rnr.AddNbrApInfoField();
109 nbrId = rnr.GetNNbrApInfoFields() - 1;
110
111 rnr.AddTbttInformationField(nbrId);
112 tbttId = rnr.GetNTbttInformationFields(nbrId) - 1;
113 rnr.SetMldParameters(nbrId, tbttId, 0, 0, 0);
114
115 rnr.AddTbttInformationField(nbrId);
116 tbttId = rnr.GetNTbttInformationFields(nbrId) - 1;
117 rnr.SetMldParameters(nbrId, tbttId, 5, 0, 0);
118
119 // Add a third Neighbor AP Information field with MLD Parameters; none of the
120 // TBTT Information fields is related to an AP affiliated to the same AP MLD
121 // as the reported AP.
122 rnr.AddNbrApInfoField();
123 nbrId = rnr.GetNNbrApInfoFields() - 1;
124
125 rnr.AddTbttInformationField(nbrId);
126 tbttId = rnr.GetNTbttInformationFields(nbrId) - 1;
127 rnr.SetMldParameters(nbrId, tbttId, 3, 0, 0);
128
129 rnr.AddTbttInformationField(nbrId);
130 tbttId = rnr.GetNTbttInformationFields(nbrId) - 1;
131 rnr.SetMldParameters(nbrId, tbttId, 4, 0, 0);
132
133 // Add a fourth Neighbor AP Information field with MLD Parameters; the first
134 // TBTT Information field is not related to an AP affiliated to the same AP MLD
135 // as the reported AP; the second TBTT Information field is.
136 rnr.AddNbrApInfoField();
137 nbrId = rnr.GetNNbrApInfoFields() - 1;
138
139 rnr.AddTbttInformationField(nbrId);
140 tbttId = rnr.GetNTbttInformationFields(nbrId) - 1;
141 rnr.SetMldParameters(nbrId, tbttId, 6, 0, 0);
142
143 rnr.AddTbttInformationField(nbrId);
144 tbttId = rnr.GetNTbttInformationFields(nbrId) - 1;
145 rnr.SetMldParameters(nbrId, tbttId, 0, 0, 0);
146
147 // check implementation of WifiAssocManager::GetNextAffiliatedAp()
148 auto ret = WifiAssocManager::GetNextAffiliatedAp(rnr, 0);
149
150 NS_TEST_EXPECT_MSG_EQ(ret.has_value(), true, "Expected to find a suitable reported AP");
151 NS_TEST_EXPECT_MSG_EQ(ret->m_nbrApInfoId, 1, "Unexpected neighbor ID of the first reported AP");
152 NS_TEST_EXPECT_MSG_EQ(ret->m_tbttInfoFieldId, 0, "Unexpected tbtt ID of the first reported AP");
153
154 ret = WifiAssocManager::GetNextAffiliatedAp(rnr, ret->m_nbrApInfoId + 1);
155
156 NS_TEST_EXPECT_MSG_EQ(ret.has_value(), true, "Expected to find a second suitable reported AP");
157 NS_TEST_EXPECT_MSG_EQ(ret->m_nbrApInfoId,
158 3,
159 "Unexpected neighbor ID of the second reported AP");
160 NS_TEST_EXPECT_MSG_EQ(ret->m_tbttInfoFieldId,
161 1,
162 "Unexpected tbtt ID of the second reported AP");
163
164 ret = WifiAssocManager::GetNextAffiliatedAp(rnr, ret->m_nbrApInfoId + 1);
165
166 NS_TEST_EXPECT_MSG_EQ(ret.has_value(),
167 false,
168 "Did not expect to find a third suitable reported AP");
169
170 // check implementation of WifiAssocManager::GetAllAffiliatedAps()
171 auto allAps = WifiAssocManager::GetAllAffiliatedAps(rnr);
172
173 NS_TEST_EXPECT_MSG_EQ(allAps.size(), 2, "Expected to find two suitable reported APs");
174
175 auto apIt = allAps.begin();
176 NS_TEST_EXPECT_MSG_EQ(apIt->m_nbrApInfoId,
177 1,
178 "Unexpected neighbor ID of the first reported AP");
179 NS_TEST_EXPECT_MSG_EQ(apIt->m_tbttInfoFieldId,
180 0,
181 "Unexpected tbtt ID of the first reported AP");
182
183 apIt++;
184 NS_TEST_EXPECT_MSG_EQ(apIt->m_nbrApInfoId,
185 3,
186 "Unexpected neighbor ID of the second reported AP");
187 NS_TEST_EXPECT_MSG_EQ(apIt->m_tbttInfoFieldId,
188 1,
189 "Unexpected tbtt ID of the second reported AP");
190}
191
203{
204 public:
214 MultiLinkOperationsTestBase(const std::string& name,
215 uint8_t nStations,
216 std::vector<std::string> staChannels,
217 std::vector<std::string> apChannels,
218 std::vector<uint8_t> fixedPhyBands = {});
219 ~MultiLinkOperationsTestBase() override = default;
220
221 protected:
231 virtual void Transmit(uint8_t linkId,
232 std::string context,
233 WifiConstPsduMap psduMap,
234 WifiTxVector txVector,
235 double txPowerW);
236
237 void DoSetup() override;
238
240 using ChannelMap = std::map<FrequencyRange, Ptr<MultiModelSpectrumChannel>>;
241
246 {
247 DL = 0,
248 UL
249 };
250
258 std::optional<Direction> direction = std::nullopt);
259
262 {
266 uint8_t linkId;
267 };
268
269 std::vector<FrameInfo> m_txPsdus;
270 const std::vector<std::string> m_staChannels;
271 const std::vector<std::string> m_apChannels;
272 const std::vector<uint8_t> m_fixedPhyBands;
274 std::vector<Ptr<StaWifiMac>> m_staMacs;
275 uint8_t m_nStations;
276 uint16_t m_lastAid;
277
278 private:
289 const std::vector<std::string>& channels,
290 const ChannelMap& channelMap);
291
299 void SetSsid(uint16_t aid, Mac48Address /* addr */);
300
304 virtual void StartTraffic()
305 {
306 }
307};
308
310 uint8_t nStations,
311 std::vector<std::string> staChannels,
312 std::vector<std::string> apChannels,
313 std::vector<uint8_t> fixedPhyBands)
314 : TestCase(name),
315 m_staChannels(staChannels),
316 m_apChannels(apChannels),
317 m_fixedPhyBands(fixedPhyBands),
318 m_staMacs(nStations),
319 m_nStations(nStations),
320 m_lastAid(0)
321{
322}
323
324void
326 std::optional<Direction> direction)
327{
328 std::optional<Mac48Address> apAddr;
329 std::optional<Mac48Address> staAddr;
330
331 // direction for Data frames is derived from ToDS/FromDS flags
332 if (psdu->GetHeader(0).IsQosData())
333 {
334 direction = (!psdu->GetHeader(0).IsToDs() && psdu->GetHeader(0).IsFromDs()) ? DL : UL;
335 }
336 NS_ASSERT(direction);
337
338 if (direction == DL)
339 {
340 if (!psdu->GetAddr1().IsGroup())
341 {
342 staAddr = psdu->GetAddr1();
343 }
344 apAddr = psdu->GetAddr2();
345 }
346 else
347 {
348 if (!psdu->GetAddr1().IsGroup())
349 {
350 apAddr = psdu->GetAddr1();
351 }
352 staAddr = psdu->GetAddr2();
353 }
354
355 if (apAddr)
356 {
357 bool found = false;
358 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
359 {
360 if (m_apMac->GetFrameExchangeManager(linkId)->GetAddress() == *apAddr)
361 {
362 found = true;
363 break;
364 }
365 }
367 true,
368 "Address " << *apAddr << " is not an AP device address. "
369 << "PSDU: " << *psdu);
370 }
371
372 if (staAddr)
373 {
374 bool found = false;
375 for (uint8_t i = 0; i < m_nStations; i++)
376 {
377 for (uint8_t linkId = 0; linkId < m_staMacs[i]->GetNLinks(); linkId++)
378 {
379 if (m_staMacs[i]->GetFrameExchangeManager(linkId)->GetAddress() == *staAddr)
380 {
381 found = true;
382 break;
383 }
384 }
385 if (found)
386 {
387 break;
388 }
389 }
391 true,
392 "Address " << *staAddr << " is not a STA device address. "
393 << "PSDU: " << *psdu);
394 }
395}
396
397void
399 std::string context,
400 WifiConstPsduMap psduMap,
401 WifiTxVector txVector,
402 double txPowerW)
403{
404 m_txPsdus.push_back({Simulator::Now(), psduMap, txVector, linkId});
405
406 for (const auto& [aid, psdu] : psduMap)
407 {
408 std::stringstream ss;
409 ss << std::setprecision(10) << "PSDU #" << m_txPsdus.size() << " Link ID " << +linkId << " "
410 << psdu->GetHeader(0).GetTypeString() << " #MPDUs " << psdu->GetNMpdus()
411 << " duration/ID " << psdu->GetHeader(0).GetDuration() << " RA = " << psdu->GetAddr1()
412 << " TA = " << psdu->GetAddr2() << " ADDR3 = " << psdu->GetHeader(0).GetAddr3()
413 << " ToDS = " << psdu->GetHeader(0).IsToDs()
414 << " FromDS = " << psdu->GetHeader(0).IsFromDs();
415 if (psdu->GetHeader(0).IsQosData())
416 {
417 ss << " seqNo = {";
418 for (auto& mpdu : *PeekPointer(psdu))
419 {
420 ss << mpdu->GetHeader().GetSequenceNumber() << ",";
421 }
422 ss << "} TID = " << +psdu->GetHeader(0).GetQosTid();
423 }
424 NS_LOG_INFO(ss.str());
425 }
426 NS_LOG_INFO("TXVECTOR = " << txVector << "\n");
427}
428
429void
431 const std::vector<std::string>& channels,
432 const ChannelMap& channelMap)
433{
434 helper = SpectrumWifiPhyHelper(channels.size());
436
437 uint8_t linkId = 0;
438 for (const auto& str : channels)
439 {
440 helper.Set(linkId++, "ChannelSettings", StringValue(str));
441 }
442
443 // NOTE replace this for loop with the line below to use a single spectrum channel
444 // helper.SetChannel(channelMap.begin()->second);
445 for (const auto& [band, channel] : channelMap)
446 {
447 helper.AddChannel(channel, band);
448 }
449}
450
451void
453{
456 int64_t streamNumber = 30;
457
458 NodeContainer wifiApNode;
459 wifiApNode.Create(1);
460
461 NodeContainer wifiStaNodes;
462 wifiStaNodes.Create(m_nStations);
463
464 WifiHelper wifi;
465 // wifi.EnableLogComponents ();
466 wifi.SetStandard(WIFI_STANDARD_80211be);
467 wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager",
468 "DataMode",
469 StringValue("EhtMcs0"),
470 "ControlMode",
471 StringValue("HtMcs0"));
472
473 ChannelMap channelMap{{WIFI_SPECTRUM_2_4_GHZ, CreateObject<MultiModelSpectrumChannel>()},
474 {WIFI_SPECTRUM_5_GHZ, CreateObject<MultiModelSpectrumChannel>()},
475 {WIFI_SPECTRUM_6_GHZ, CreateObject<MultiModelSpectrumChannel>()}};
476
477 SpectrumWifiPhyHelper staPhyHelper;
478 SpectrumWifiPhyHelper apPhyHelper;
479 SetChannels(staPhyHelper, m_staChannels, channelMap);
480 SetChannels(apPhyHelper, m_apChannels, channelMap);
481
482 for (const auto& linkId : m_fixedPhyBands)
483 {
484 staPhyHelper.Set(linkId, "FixedPhyBand", BooleanValue(true));
485 }
486
487 WifiMacHelper mac;
488 mac.SetType("ns3::StaWifiMac", // default SSID
489 "ActiveProbing",
490 BooleanValue(false));
491
492 NetDeviceContainer staDevices = wifi.Install(staPhyHelper, mac, wifiStaNodes);
493
494 mac.SetType("ns3::ApWifiMac",
495 "Ssid",
496 SsidValue(Ssid("ns-3-ssid")),
497 "BeaconGeneration",
498 BooleanValue(true));
499
500 NetDeviceContainer apDevices = wifi.Install(apPhyHelper, mac, wifiApNode);
501
502 // Uncomment the lines below to write PCAP files
503 // apPhyHelper.EnablePcap("wifi-mlo_AP", apDevices);
504 // staPhyHelper.EnablePcap("wifi-mlo_STA", staDevices);
505
506 // Assign fixed streams to random variables in use
507 streamNumber += wifi.AssignStreams(apDevices, streamNumber);
508 streamNumber += wifi.AssignStreams(staDevices, streamNumber);
509
510 MobilityHelper mobility;
511 Ptr<ListPositionAllocator> positionAlloc = CreateObject<ListPositionAllocator>();
512
513 positionAlloc->Add(Vector(0.0, 0.0, 0.0));
514 positionAlloc->Add(Vector(1.0, 0.0, 0.0));
515 mobility.SetPositionAllocator(positionAlloc);
516
517 mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
518 mobility.Install(wifiApNode);
519 mobility.Install(wifiStaNodes);
520
521 m_apMac = DynamicCast<ApWifiMac>(DynamicCast<WifiNetDevice>(apDevices.Get(0))->GetMac());
522 for (uint8_t i = 0; i < m_nStations; i++)
523 {
524 m_staMacs[i] =
525 DynamicCast<StaWifiMac>(DynamicCast<WifiNetDevice>(staDevices.Get(i))->GetMac());
526 }
527
528 // Trace PSDUs passed to the PHY on all devices
529 for (uint8_t linkId = 0; linkId < StaticCast<WifiNetDevice>(apDevices.Get(0))->GetNPhys();
530 linkId++)
531 {
532 Config::Connect("/NodeList/0/DeviceList/*/$ns3::WifiNetDevice/Phys/" +
533 std::to_string(linkId) + "/PhyTxPsduBegin",
535 }
536 for (uint8_t i = 0; i < m_nStations; i++)
537 {
538 for (uint8_t linkId = 0; linkId < StaticCast<WifiNetDevice>(staDevices.Get(i))->GetNPhys();
539 linkId++)
540 {
542 "/NodeList/" + std::to_string(i + 1) + "/DeviceList/*/$ns3::WifiNetDevice/Phys/" +
543 std::to_string(linkId) + "/PhyTxPsduBegin",
545 }
546 }
547
548 // schedule ML setup for one station at a time
549 m_apMac->TraceConnectWithoutContext("AssociatedSta",
551 m_staMacs[0]->SetSsid(Ssid("ns-3-ssid"));
552}
553
554void
556{
557 if (m_lastAid == aid)
558 {
559 // another STA of this non-AP MLD has already fired this callback
560 return;
561 }
562 m_lastAid = aid;
563
564 // make the next STA to start ML discovery & setup
565 if (aid < m_nStations)
566 {
567 m_staMacs[aid]->SetSsid(Ssid("ns-3-ssid"));
568 return;
569 }
570 // wait some time (5ms) to allow the completion of association before generating traffic
572}
573
588{
589 public:
599 MultiLinkSetupTest(std::vector<std::string> staChannels,
600 std::vector<std::string> apChannels,
601 WifiScanType scanType,
602 std::vector<std::pair<uint8_t, uint8_t>> setupLinks,
603 std::vector<uint8_t> fixedPhyBands = {});
604 ~MultiLinkSetupTest() override = default;
605
606 protected:
607 void DoSetup() override;
608 void DoRun() override;
609
610 private:
614 void CheckMlSetup();
615
619 void CheckDisabledLinks();
620
627 void CheckBeacon(Ptr<WifiMpdu> mpdu, uint8_t linkId);
628
635 void CheckProbeResponse(Ptr<WifiMpdu> mpdu, uint8_t linkId);
636
643 void CheckAssocRequest(Ptr<WifiMpdu> mpdu, uint8_t linkId);
644
651 void CheckAssocResponse(Ptr<WifiMpdu> mpdu, uint8_t linkId);
652
654 const std::vector<std::pair<uint8_t, uint8_t>> m_setupLinks;
656 std::size_t m_nProbeResp;
657};
658
659MultiLinkSetupTest::MultiLinkSetupTest(std::vector<std::string> staChannels,
660 std::vector<std::string> apChannels,
661 WifiScanType scanType,
662 std::vector<std::pair<uint8_t, uint8_t>> setupLinks,
663 std::vector<uint8_t> fixedPhyBands)
664 : MultiLinkOperationsTestBase("Check correctness of Multi-Link Setup",
665 1,
666 staChannels,
667 apChannels,
668 fixedPhyBands),
669 m_setupLinks(setupLinks),
670 m_scanType(scanType),
671 m_nProbeResp(0)
672{
673}
674
675void
677{
679
680 m_staMacs[0]->SetAttribute("ActiveProbing", BooleanValue(m_scanType == WifiScanType::ACTIVE));
681}
682
683void
685{
687
690
694 for (const auto& frameInfo : m_txPsdus)
695 {
696 const auto& mpdu = *frameInfo.psduMap.begin()->second->begin();
697 const auto& linkId = frameInfo.linkId;
698
699 switch (mpdu->GetHeader().GetType())
700 {
702 CheckBeacon(mpdu, linkId);
703 break;
704
706 CheckProbeResponse(mpdu, linkId);
707 m_nProbeResp++;
708 break;
709
711 CheckAssocRequest(mpdu, linkId);
712 break;
713
715 CheckAssocResponse(mpdu, linkId);
716 break;
717
718 default:
719 break;
720 }
721 }
722
724
725 std::size_t expectedProbeResp = 0;
726 if (m_scanType == WifiScanType::ACTIVE)
727 {
728 // the number of Probe Response frames that we expect to receive in active mode equals
729 // the number of channels in common between AP MLD and non-AP MLD at initialization
730 for (const auto& staChannel : m_staChannels)
731 {
732 for (const auto& apChannel : m_apChannels)
733 {
734 if (staChannel == apChannel)
735 {
736 expectedProbeResp++;
737 break;
738 }
739 }
740 }
741 }
742
743 NS_TEST_EXPECT_MSG_EQ(m_nProbeResp, expectedProbeResp, "Unexpected number of Probe Responses");
744
746}
747
748void
750{
751 NS_ABORT_IF(mpdu->GetHeader().GetType() != WIFI_MAC_MGT_BEACON);
752
753 CheckAddresses(Create<WifiPsdu>(mpdu, false), MultiLinkOperationsTestBase::DL);
754
756 mpdu->GetHeader().GetAddr2(),
757 "TA of Beacon frame is not the address of the link it is transmitted on");
758 MgtBeaconHeader beacon;
759 mpdu->GetPacket()->PeekHeader(beacon);
760 const auto& rnr = beacon.Get<ReducedNeighborReport>();
761 const auto& mle = beacon.Get<MultiLinkElement>();
762
763 if (m_apMac->GetNLinks() == 1)
764 {
765 NS_TEST_EXPECT_MSG_EQ(rnr.has_value(),
766 false,
767 "RNR Element in Beacon frame from single link AP");
768 NS_TEST_EXPECT_MSG_EQ(mle.has_value(),
769 false,
770 "Multi-Link Element in Beacon frame from single link AP");
771 return;
772 }
773
774 NS_TEST_EXPECT_MSG_EQ(rnr.has_value(), true, "No RNR Element in Beacon frame");
775 // All the other APs affiliated with the same AP MLD as the AP sending
776 // the Beacon frame must be reported in a separate Neighbor AP Info field
777 NS_TEST_EXPECT_MSG_EQ(rnr->GetNNbrApInfoFields(),
778 static_cast<std::size_t>(m_apMac->GetNLinks() - 1),
779 "Unexpected number of Neighbor AP Info fields in RNR");
780 for (std::size_t nbrApInfoId = 0; nbrApInfoId < rnr->GetNNbrApInfoFields(); nbrApInfoId++)
781 {
782 NS_TEST_EXPECT_MSG_EQ(rnr->HasMldParameters(nbrApInfoId),
783 true,
784 "MLD Parameters not present");
785 NS_TEST_EXPECT_MSG_EQ(rnr->GetNTbttInformationFields(nbrApInfoId),
786 1,
787 "Expected only one TBTT Info subfield per Neighbor AP Info");
788 uint8_t nbrLinkId = rnr->GetLinkId(nbrApInfoId, 0);
789 NS_TEST_EXPECT_MSG_EQ(rnr->GetBssid(nbrApInfoId, 0),
790 m_apMac->GetFrameExchangeManager(nbrLinkId)->GetAddress(),
791 "BSSID advertised in Neighbor AP Info field "
792 << nbrApInfoId
793 << " does not match the address configured on the link "
794 "advertised in the same field");
795 }
796
797 NS_TEST_EXPECT_MSG_EQ(mle.has_value(), true, "No Multi-Link Element in Beacon frame");
798 NS_TEST_EXPECT_MSG_EQ(mle->GetMldMacAddress(),
800 "Incorrect MLD address advertised in Multi-Link Element");
801 NS_TEST_EXPECT_MSG_EQ(mle->GetLinkIdInfo(),
802 +linkId,
803 "Incorrect Link ID advertised in Multi-Link Element");
804}
805
806void
808{
809 NS_ABORT_IF(mpdu->GetHeader().GetType() != WIFI_MAC_MGT_PROBE_RESPONSE);
810
811 CheckAddresses(Create<WifiPsdu>(mpdu, false), MultiLinkOperationsTestBase::DL);
812
814 m_apMac->GetFrameExchangeManager(linkId)->GetAddress(),
815 mpdu->GetHeader().GetAddr2(),
816 "TA of Probe Response is not the address of the link it is transmitted on");
817 MgtProbeResponseHeader probeResp;
818 mpdu->GetPacket()->PeekHeader(probeResp);
819 const auto& rnr = probeResp.Get<ReducedNeighborReport>();
820 const auto& mle = probeResp.Get<MultiLinkElement>();
821
822 if (m_apMac->GetNLinks() == 1)
823 {
824 NS_TEST_EXPECT_MSG_EQ(rnr.has_value(),
825 false,
826 "RNR Element in Probe Response frame from single link AP");
827 NS_TEST_EXPECT_MSG_EQ(mle.has_value(),
828 false,
829 "Multi-Link Element in Probe Response frame from single link AP");
830 return;
831 }
832
833 NS_TEST_EXPECT_MSG_EQ(rnr.has_value(), true, "No RNR Element in Probe Response frame");
834 // All the other APs affiliated with the same AP MLD as the AP sending
835 // the Probe Response frame must be reported in a separate Neighbor AP Info field
836 NS_TEST_EXPECT_MSG_EQ(rnr->GetNNbrApInfoFields(),
837 static_cast<std::size_t>(m_apMac->GetNLinks() - 1),
838 "Unexpected number of Neighbor AP Info fields in RNR");
839 for (std::size_t nbrApInfoId = 0; nbrApInfoId < rnr->GetNNbrApInfoFields(); nbrApInfoId++)
840 {
841 NS_TEST_EXPECT_MSG_EQ(rnr->HasMldParameters(nbrApInfoId),
842 true,
843 "MLD Parameters not present");
844 NS_TEST_EXPECT_MSG_EQ(rnr->GetNTbttInformationFields(nbrApInfoId),
845 1,
846 "Expected only one TBTT Info subfield per Neighbor AP Info");
847 uint8_t nbrLinkId = rnr->GetLinkId(nbrApInfoId, 0);
848 NS_TEST_EXPECT_MSG_EQ(rnr->GetBssid(nbrApInfoId, 0),
849 m_apMac->GetFrameExchangeManager(nbrLinkId)->GetAddress(),
850 "BSSID advertised in Neighbor AP Info field "
851 << nbrApInfoId
852 << " does not match the address configured on the link "
853 "advertised in the same field");
854 }
855
856 NS_TEST_EXPECT_MSG_EQ(mle.has_value(), true, "No Multi-Link Element in Probe Response frame");
857 NS_TEST_EXPECT_MSG_EQ(mle->GetMldMacAddress(),
859 "Incorrect MLD address advertised in Multi-Link Element");
860 NS_TEST_EXPECT_MSG_EQ(mle->GetLinkIdInfo(),
861 +linkId,
862 "Incorrect Link ID advertised in Multi-Link Element");
863}
864
865void
867{
868 NS_ABORT_IF(mpdu->GetHeader().GetType() != WIFI_MAC_MGT_ASSOCIATION_REQUEST);
869
870 CheckAddresses(Create<WifiPsdu>(mpdu, false), MultiLinkOperationsTestBase::UL);
871
873 m_staMacs[0]->GetFrameExchangeManager(linkId)->GetAddress(),
874 mpdu->GetHeader().GetAddr2(),
875 "TA of Assoc Request frame is not the address of the link it is transmitted on");
877 mpdu->GetPacket()->PeekHeader(assoc);
878 const auto& mle = assoc.Get<MultiLinkElement>();
879
880 if (m_apMac->GetNLinks() == 1 || m_staMacs[0]->GetNLinks() == 1)
881 {
882 NS_TEST_EXPECT_MSG_EQ(mle.has_value(),
883 false,
884 "Multi-Link Element in Assoc Request frame from single link STA");
885 return;
886 }
887
888 NS_TEST_EXPECT_MSG_EQ(mle.has_value(), true, "No Multi-Link Element in Assoc Request frame");
889 NS_TEST_EXPECT_MSG_EQ(mle->GetMldMacAddress(),
890 m_staMacs[0]->GetAddress(),
891 "Incorrect MLD Address advertised in Multi-Link Element");
892 NS_TEST_EXPECT_MSG_EQ(mle->GetNPerStaProfileSubelements(),
893 m_setupLinks.size() - 1,
894 "Incorrect number of Per-STA Profile subelements in Multi-Link Element");
895 for (std::size_t i = 0; i < mle->GetNPerStaProfileSubelements(); i++)
896 {
897 auto& perStaProfile = mle->GetPerStaProfile(i);
898 NS_TEST_EXPECT_MSG_EQ(perStaProfile.HasStaMacAddress(),
899 true,
900 "Per-STA Profile must contain STA MAC address");
901 // find ID of the local link corresponding to this subelement
902 auto staLinkId = m_staMacs[0]->GetLinkIdByAddress(perStaProfile.GetStaMacAddress());
904 staLinkId.has_value(),
905 true,
906 "No link found with the STA MAC address advertised in Per-STA Profile");
908 +staLinkId.value(),
909 +linkId,
910 "The STA that sent the Assoc Request should not be included in a Per-STA Profile");
911 auto it = std::find_if(m_setupLinks.begin(), m_setupLinks.end(), [&staLinkId](auto&& pair) {
912 return pair.first == staLinkId.value();
913 });
915 true,
916 "Not expecting to setup STA link ID " << +staLinkId.value());
918 +it->second,
919 +perStaProfile.GetLinkId(),
920 "Not expecting to request association to AP Link ID in Per-STA Profile");
921 NS_TEST_EXPECT_MSG_EQ(perStaProfile.HasAssocRequest(),
922 true,
923 "Missing Association Request in Per-STA Profile");
924 }
925}
926
927void
929{
930 NS_ABORT_IF(mpdu->GetHeader().GetType() != WIFI_MAC_MGT_ASSOCIATION_RESPONSE);
931
932 CheckAddresses(Create<WifiPsdu>(mpdu, false), MultiLinkOperationsTestBase::DL);
933
935 m_apMac->GetFrameExchangeManager(linkId)->GetAddress(),
936 mpdu->GetHeader().GetAddr2(),
937 "TA of Assoc Response frame is not the address of the link it is transmitted on");
939 mpdu->GetPacket()->PeekHeader(assoc);
940 const auto& mle = assoc.Get<MultiLinkElement>();
941
942 if (m_apMac->GetNLinks() == 1 || m_staMacs[0]->GetNLinks() == 1)
943 {
945 mle.has_value(),
946 false,
947 "Multi-Link Element in Assoc Response frame with single link AP or single link STA");
948 return;
949 }
950
951 NS_TEST_EXPECT_MSG_EQ(mle.has_value(), true, "No Multi-Link Element in Assoc Request frame");
952 NS_TEST_EXPECT_MSG_EQ(mle->GetMldMacAddress(),
954 "Incorrect MLD Address advertised in Multi-Link Element");
955 NS_TEST_EXPECT_MSG_EQ(mle->GetNPerStaProfileSubelements(),
956 m_setupLinks.size() - 1,
957 "Incorrect number of Per-STA Profile subelements in Multi-Link Element");
958 for (std::size_t i = 0; i < mle->GetNPerStaProfileSubelements(); i++)
959 {
960 auto& perStaProfile = mle->GetPerStaProfile(i);
961 NS_TEST_EXPECT_MSG_EQ(perStaProfile.HasStaMacAddress(),
962 true,
963 "Per-STA Profile must contain STA MAC address");
964 // find ID of the local link corresponding to this subelement
965 auto apLinkId = m_apMac->GetLinkIdByAddress(perStaProfile.GetStaMacAddress());
967 apLinkId.has_value(),
968 true,
969 "No link found with the STA MAC address advertised in Per-STA Profile");
970 NS_TEST_EXPECT_MSG_EQ(+apLinkId.value(),
971 +perStaProfile.GetLinkId(),
972 "Link ID and MAC address advertised in Per-STA Profile do not match");
974 +apLinkId.value(),
975 +linkId,
976 "The AP that sent the Assoc Response should not be included in a Per-STA Profile");
977 auto it = std::find_if(m_setupLinks.begin(), m_setupLinks.end(), [&apLinkId](auto&& pair) {
978 return pair.second == apLinkId.value();
979 });
981 true,
982 "Not expecting to setup AP link ID " << +apLinkId.value());
983 NS_TEST_EXPECT_MSG_EQ(perStaProfile.HasAssocResponse(),
984 true,
985 "Missing Association Response in Per-STA Profile");
986 }
987}
988
989void
991{
995 NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->IsAssociated(), true, "Expected the STA to be associated");
996
997 for (const auto& [staLinkId, apLinkId] : m_setupLinks)
998 {
999 auto staAddr = m_staMacs[0]->GetFrameExchangeManager(staLinkId)->GetAddress();
1000 auto apAddr = m_apMac->GetFrameExchangeManager(apLinkId)->GetAddress();
1001
1002 auto staRemoteMgr = m_staMacs[0]->GetWifiRemoteStationManager(staLinkId);
1003 auto apRemoteMgr = m_apMac->GetWifiRemoteStationManager(apLinkId);
1004
1005 // STA side
1006 NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetFrameExchangeManager(staLinkId)->GetBssid(),
1007 apAddr,
1008 "Unexpected BSSID for STA link ID " << +staLinkId);
1009 if (m_apMac->GetNLinks() > 1 && m_staMacs[0]->GetNLinks() > 1)
1010 {
1011 NS_TEST_EXPECT_MSG_EQ((staRemoteMgr->GetMldAddress(apAddr) == m_apMac->GetAddress()),
1012 true,
1013 "Incorrect MLD address stored by STA on link ID " << +staLinkId);
1015 (staRemoteMgr->GetAffiliatedStaAddress(m_apMac->GetAddress()) == apAddr),
1016 true,
1017 "Incorrect affiliated address stored by STA on link ID " << +staLinkId);
1018 }
1019
1020 // AP side
1021 NS_TEST_EXPECT_MSG_EQ(apRemoteMgr->IsAssociated(staAddr),
1022 true,
1023 "Expecting STA " << staAddr << " to be associated on link "
1024 << +apLinkId);
1025 if (m_apMac->GetNLinks() > 1 && m_staMacs[0]->GetNLinks() > 1)
1026 {
1028 (apRemoteMgr->GetMldAddress(staAddr) == m_staMacs[0]->GetAddress()),
1029 true,
1030 "Incorrect MLD address stored by AP on link ID " << +apLinkId);
1032 (apRemoteMgr->GetAffiliatedStaAddress(m_staMacs[0]->GetAddress()) == staAddr),
1033 true,
1034 "Incorrect affiliated address stored by AP on link ID " << +apLinkId);
1035 }
1036 auto aid = m_apMac->GetAssociationId(staAddr, apLinkId);
1037 const auto& staList = m_apMac->GetStaList(apLinkId);
1038 NS_TEST_EXPECT_MSG_EQ((staList.find(aid) != staList.end()),
1039 true,
1040 "STA " << staAddr << " not found in list of associated STAs");
1041
1042 // STA of non-AP MLD operate on the same channel as the AP
1044 +m_staMacs[0]->GetWifiPhy(staLinkId)->GetOperatingChannel().GetNumber(),
1046 "Incorrect operating channel number for STA on link " << +staLinkId);
1048 m_staMacs[0]->GetWifiPhy(staLinkId)->GetOperatingChannel().GetFrequency(),
1050 "Incorrect operating channel frequency for STA on link " << +staLinkId);
1051 NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetWifiPhy(staLinkId)->GetOperatingChannel().GetWidth(),
1053 "Incorrect operating channel width for STA on link " << +staLinkId);
1055 +m_staMacs[0]->GetWifiPhy(staLinkId)->GetOperatingChannel().GetPhyBand(),
1057 "Incorrect operating PHY band for STA on link " << +staLinkId);
1059 +m_staMacs[0]->GetWifiPhy(staLinkId)->GetOperatingChannel().GetPrimaryChannelIndex(20),
1061 "Incorrect operating primary channel index for STA on link " << +staLinkId);
1062 }
1063}
1064
1065void
1067{
1068 for (std::size_t linkId = 0; linkId < m_staChannels.size(); linkId++)
1069 {
1070 auto it = std::find_if(m_setupLinks.begin(), m_setupLinks.end(), [&linkId](auto&& link) {
1071 return link.first == linkId;
1072 });
1073 if (it == m_setupLinks.end())
1074 {
1075 // the link has not been setup
1076 NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetWifiPhy(linkId)->GetState()->IsStateOff(),
1077 true,
1078 "Link " << +linkId << " has not been setup but is not disabled");
1079 continue;
1080 }
1081
1082 // the link has been setup and must be active
1083 NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetWifiPhy(linkId)->GetState()->IsStateOff(),
1084 false,
1085 "Expecting link " << +linkId << " to be active");
1086 }
1087}
1088
1092enum class WifiTrafficPattern : uint8_t
1093{
1094 STA_TO_STA = 0,
1095 STA_TO_AP,
1096 AP_TO_STA,
1099};
1100
1104enum class WifiBaEnabled : uint8_t
1105{
1106 NO = 0,
1107 YES
1108};
1109
1113enum class WifiUseBarAfterMissedBa : uint8_t
1114{
1115 NO = 0,
1116 YES
1117};
1118
1148{
1149 public:
1162 MultiLinkTxTest(WifiTrafficPattern trafficPattern,
1163 WifiBaEnabled baEnabled,
1164 WifiUseBarAfterMissedBa useBarAfterMissedBa,
1165 uint8_t nMaxInflight,
1166 const std::vector<std::string>& staChannels,
1167 const std::vector<std::string>& apChannels,
1168 const std::vector<uint8_t>& fixedPhyBands = {});
1169 ~MultiLinkTxTest() override = default;
1170
1171 protected:
1178 void L7Receive(uint8_t nodeId, Ptr<const Packet> p, const Address& addr);
1179
1188 void CheckBlockAck(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector, uint8_t linkId);
1189
1190 void Transmit(uint8_t linkId,
1191 std::string context,
1192 WifiConstPsduMap psduMap,
1193 WifiTxVector txVector,
1194 double txPowerW) override;
1195 void DoSetup() override;
1196 void DoRun() override;
1197
1198 private:
1199 void StartTraffic() override;
1200
1202 using RxErrorModelMap = std::unordered_map<Mac48Address, Ptr<ListErrorModel>, WifiAddressHash>;
1203
1205 std::list<uint64_t> m_uidList;
1206 bool m_dataCorrupted{false};
1210 std::size_t m_nMaxInflight;
1211 std::size_t m_nPackets;
1212 std::size_t m_blockAckCount{0};
1213 std::size_t m_blockAckReqCount{0};
1214 std::array<std::size_t, 3> m_rxPkts{};
1216 std::map<uint16_t, std::size_t> m_inflightCount;
1219};
1220
1222 WifiBaEnabled baEnabled,
1223 WifiUseBarAfterMissedBa useBarAfterMissedBa,
1224 uint8_t nMaxInflight,
1225 const std::vector<std::string>& staChannels,
1226 const std::vector<std::string>& apChannels,
1227 const std::vector<uint8_t>& fixedPhyBands)
1229 std::string("Check data transmission between MLDs ") +
1230 (baEnabled == WifiBaEnabled::YES
1231 ? (useBarAfterMissedBa == WifiUseBarAfterMissedBa::YES
1232 ? "with BA agreement, send BAR after BlockAck timeout"
1233 : "with BA agreement, send Data frames after BlockAck timeout")
1234 : "without BA agreement") +
1235 " (Traffic pattern: " + std::to_string(static_cast<uint8_t>(trafficPattern)) +
1236 (baEnabled == WifiBaEnabled::YES ? ", nMaxInflight=" + std::to_string(nMaxInflight)
1237 : "") +
1238 ")",
1239 2,
1240 staChannels,
1241 apChannels,
1242 fixedPhyBands),
1243 m_trafficPattern(trafficPattern),
1244 m_baEnabled(baEnabled == WifiBaEnabled::YES),
1245 m_useBarAfterMissedBa(useBarAfterMissedBa == WifiUseBarAfterMissedBa::YES),
1246 m_nMaxInflight(nMaxInflight),
1247 m_nPackets(trafficPattern == WifiTrafficPattern::STA_TO_BCAST ||
1248 trafficPattern == WifiTrafficPattern::STA_TO_STA
1249 ? 4
1250 : 8)
1251{
1252}
1253
1254void
1256 std::string context,
1257 WifiConstPsduMap psduMap,
1258 WifiTxVector txVector,
1259 double txPowerW)
1260{
1261 auto psdu = psduMap.begin()->second;
1262
1263 switch (psdu->GetHeader(0).GetType())
1264 {
1266 // a management frame is a DL frame if TA equals BSSID
1267 CheckAddresses(psdu,
1268 psdu->GetHeader(0).GetAddr2() == psdu->GetHeader(0).GetAddr3() ? DL : UL);
1269 if (!m_baEnabled)
1270 {
1271 // corrupt all management action frames (ADDBA Request frames) to prevent
1272 // the establishment of a BA agreement
1273 m_uidList.push_front(psdu->GetPacket()->GetUid());
1274 m_errorModels.at(psdu->GetAddr1())->SetList(m_uidList);
1275 NS_LOG_INFO("CORRUPTED");
1276 }
1277 break;
1278 case WIFI_MAC_QOSDATA:
1279 CheckAddresses(psdu);
1280
1281 for (const auto& mpdu : *psdu)
1282 {
1283 // determine the max number of simultaneous transmissions for this MPDU
1284 // (only if sent by the traffic source and this is not a broadcast frame)
1285 if (m_baEnabled && linkId < m_sourceMac->GetNLinks() &&
1286 m_sourceMac->GetFrameExchangeManager(linkId)->GetAddress() ==
1287 mpdu->GetHeader().GetAddr2() &&
1288 !mpdu->GetHeader().GetAddr1().IsGroup())
1289 {
1290 auto seqNo = mpdu->GetHeader().GetSequenceNumber();
1291 auto [it, success] =
1292 m_inflightCount.insert({seqNo, mpdu->GetInFlightLinkIds().size()});
1293 if (!success)
1294 {
1295 it->second = std::max(it->second, mpdu->GetInFlightLinkIds().size());
1296 }
1297 }
1298 }
1299 for (std::size_t i = 0; i < psdu->GetNMpdus(); i++)
1300 {
1301 // corrupt QoS data frame with sequence number equal to 1 (only once) if we are
1302 // not in the AP to broadcast traffic pattern (broadcast frames are not retransmitted)
1303 // nor in the STA to broadcast or STA to STA traffic patterns (retransmissions from
1304 // STA 1 could collide with frames forwarded by the AP)
1305 if (psdu->GetHeader(i).GetSequenceNumber() != 1 ||
1306 m_trafficPattern == WifiTrafficPattern::AP_TO_BCAST ||
1307 m_trafficPattern == WifiTrafficPattern::STA_TO_BCAST ||
1308 m_trafficPattern == WifiTrafficPattern::STA_TO_STA)
1309 {
1310 continue;
1311 }
1312 auto uid = psdu->GetPayload(i)->GetUid();
1313 if (!m_dataCorrupted)
1314 {
1315 m_uidList.push_front(uid);
1316 m_dataCorrupted = true;
1317 NS_LOG_INFO("CORRUPTED");
1318 m_errorModels.at(psdu->GetAddr1())->SetList(m_uidList);
1319 }
1320 else
1321 {
1322 // do not corrupt the QoS data frame anymore
1323 if (auto it = std::find(m_uidList.cbegin(), m_uidList.cend(), uid);
1324 it != m_uidList.cend())
1325 {
1326 m_uidList.erase(it);
1327 }
1328 m_errorModels.at(psdu->GetAddr1())->SetList(m_uidList);
1329 }
1330 break;
1331 }
1332 break;
1333 case WIFI_MAC_CTL_BACKRESP: {
1334 // ignore BlockAck frames not addressed to the source of the application packets
1335 if (!m_sourceMac->GetLinkIdByAddress(psdu->GetHeader(0).GetAddr1()))
1336 {
1337 break;
1338 }
1339 if (m_nMaxInflight > 1)
1340 {
1341 // we do not check the content of BlockAck when m_nMaxInflight is greater than 1
1342 break;
1343 }
1344 CheckBlockAck(psdu, txVector, linkId);
1346 if (m_blockAckCount == 2)
1347 {
1348 // corrupt the second BlockAck frame to simulate a missed BlockAck
1349 m_uidList.push_front(psdu->GetPacket()->GetUid());
1350 NS_LOG_INFO("CORRUPTED");
1351 m_errorModels.at(psdu->GetAddr1())->SetList(m_uidList);
1352 }
1353 break;
1355 // ignore BlockAckReq frames not transmitted by the source of the application packets
1356 if (m_sourceMac->GetLinkIdByAddress(psdu->GetHeader(0).GetAddr2()))
1357 {
1359 }
1360 break;
1361 }
1362 default:;
1363 }
1364
1365 MultiLinkOperationsTestBase::Transmit(linkId, context, psduMap, txVector, txPowerW);
1366}
1367
1368void
1370 const WifiTxVector& txVector,
1371 uint8_t linkId)
1372{
1373 NS_TEST_ASSERT_MSG_EQ(m_baEnabled, true, "No BlockAck expected without BA agreement");
1374 NS_TEST_ASSERT_MSG_EQ((m_trafficPattern != WifiTrafficPattern::AP_TO_BCAST),
1375 true,
1376 "No BlockAck expected in AP to broadcast traffic pattern");
1377
1378 /*
1379 * ┌───────┬───────X ┌───────┐
1380 * link 0 │ 0 │ 1 │ │ 1 │
1381 * ───────┴───────┴───────┴┬──┬────┴───────┴┬───┬────────────────────────
1382 * │BA│ │ACK│
1383 * └──┘ └───┘
1384 * ┌───────┬───────┐ ┌───────┬───────┐
1385 * link 1 │ 2 │ 3 │ │ 2 │ 3 │
1386 * ────────────────────┴───────┴───────┴┬──X───┴───────┴───────┴┬──┬─────
1387 * │BA│ │BA│
1388 * └──┘ └──┘
1389 */
1390 auto mpdu = *psdu->begin();
1391 CtrlBAckResponseHeader blockAck;
1392 mpdu->GetPacket()->PeekHeader(blockAck);
1393 bool isMpdu1corrupted = (m_trafficPattern == WifiTrafficPattern::STA_TO_AP ||
1394 m_trafficPattern == WifiTrafficPattern::AP_TO_STA);
1395
1396 switch (m_blockAckCount)
1397 {
1398 case 0: // first BlockAck frame (all traffic patterns)
1400 true,
1401 "MPDU 0 expected to be successfully received");
1403 blockAck.IsPacketReceived(1),
1404 !isMpdu1corrupted,
1405 "MPDU 1 expected to be received only in STA_TO_STA/STA_TO_BCAST scenarios");
1406 // if there are at least two links setup, we expect all MPDUs to be inflight
1407 // (on distinct links)
1408 if (m_staMacs[0]->GetSetupLinkIds().size() > 1)
1409 {
1410 auto queue = m_sourceMac->GetTxopQueue(AC_BE);
1411 auto rcvMac = m_sourceMac == m_staMacs[0] ? StaticCast<WifiMac>(m_apMac)
1412 : StaticCast<WifiMac>(m_staMacs[1]);
1413 auto item = queue->PeekByTidAndAddress(0, rcvMac->GetAddress());
1414 std::size_t nQueuedPkt = 0;
1415 auto delay = WifiPhy::CalculateTxDuration(psdu,
1416 txVector,
1417 rcvMac->GetWifiPhy(linkId)->GetPhyBand()) +
1418 MicroSeconds(1); // to account for propagation delay
1419
1420 while (item)
1421 {
1422 auto seqNo = item->GetHeader().GetSequenceNumber();
1423 NS_TEST_EXPECT_MSG_EQ(item->IsInFlight(),
1424 true,
1425 "MPDU with seqNo=" << seqNo << " is not in flight");
1426 auto linkIds = item->GetInFlightLinkIds();
1427 NS_TEST_EXPECT_MSG_EQ(linkIds.size(),
1428 1,
1429 "MPDU with seqNo=" << seqNo
1430 << " is in flight on multiple links");
1431 // The first two MPDUs are in flight on the same link on which the BlockAck
1432 // is sent. The other two MPDUs (only for AP to STA/STA to AP scenarios) are
1433 // in flight on a different link.
1434 auto srcLinkId = m_sourceMac->GetLinkIdByAddress(mpdu->GetHeader().GetAddr1());
1435 NS_TEST_ASSERT_MSG_EQ(srcLinkId.has_value(),
1436 true,
1437 "Addr1 of BlockAck is not an originator's link address");
1438 NS_TEST_EXPECT_MSG_EQ((*linkIds.begin() == *srcLinkId),
1439 (seqNo <= 1),
1440 "MPDU with seqNo=" << seqNo
1441 << " in flight on unexpected link");
1442 // check the Retry subfield and whether this MPDU is still queued
1443 // after the originator has processed this BlockAck
1444
1445 // MPDUs acknowledged via this BlockAck are no longer queued
1446 bool isQueued = (seqNo > (isMpdu1corrupted ? 0 : 1));
1447 // The Retry subfield is set if the MPDU has not been acknowledged (i.e., it
1448 // is still queued) and has been transmitted on the same link as the BlockAck
1449 // (i.e., its sequence number is less than or equal to 1)
1450 bool isRetry = isQueued && seqNo <= 1;
1451
1452 Simulator::Schedule(delay, [this, item, isQueued, isRetry]() {
1453 NS_TEST_EXPECT_MSG_EQ(item->IsQueued(),
1454 isQueued,
1455 "MPDU with seqNo="
1456 << item->GetHeader().GetSequenceNumber() << " should "
1457 << (isQueued ? "" : "not") << " be queued");
1459 item->GetHeader().IsRetry(),
1460 isRetry,
1461 "Unexpected value for the Retry subfield of the MPDU with seqNo="
1462 << item->GetHeader().GetSequenceNumber());
1463 });
1464
1465 nQueuedPkt++;
1466 item = queue->PeekByTidAndAddress(0, rcvMac->GetAddress(), item);
1467 }
1468 // Each MPDU contains an A-MSDU consisting of two MSDUs
1469 NS_TEST_EXPECT_MSG_EQ(nQueuedPkt, m_nPackets / 2, "Unexpected number of queued MPDUs");
1470 }
1471 break;
1472 case 1: // second BlockAck frame (STA to AP and AP to STA traffic patterns only)
1473 case 2: // third BlockAck frame (STA to AP and AP to STA traffic patterns only)
1474 NS_TEST_EXPECT_MSG_EQ((m_trafficPattern == WifiTrafficPattern::AP_TO_STA ||
1475 m_trafficPattern == WifiTrafficPattern::STA_TO_AP),
1476 true,
1477 "Did not expect to receive a second BlockAck");
1478 // the second BlockAck is corrupted, but the data frames have been received successfully
1479 std::pair<uint16_t, uint16_t> seqNos;
1480 // if multiple links were setup, the transmission of the second A-MPDU started before
1481 // the end of the first one, so the second A-MPDU includes MPDUs with sequence numbers
1482 // 2 and 3. Otherwise, MPDU with sequence number 1 is retransmitted along with the MPDU
1483 // with sequence number 2.
1484 if (m_staMacs[0]->GetSetupLinkIds().size() > 1)
1485 {
1486 seqNos = {2, 3};
1487 }
1488 else
1489 {
1490 seqNos = {1, 2};
1491 }
1492 NS_TEST_EXPECT_MSG_EQ(blockAck.IsPacketReceived(seqNos.first),
1493 true,
1494 "MPDU " << seqNos.first << " expected to be successfully received");
1495 NS_TEST_EXPECT_MSG_EQ(blockAck.IsPacketReceived(seqNos.second),
1496 true,
1497 "MPDU " << seqNos.second << " expected to be successfully received");
1498 break;
1499 }
1500}
1501
1502void
1504{
1505 NS_LOG_INFO("Packet received by NODE " << +nodeId << "\n");
1506 m_rxPkts[nodeId]++;
1507}
1508
1509void
1511{
1513
1514 if (m_baEnabled)
1515 {
1516 // Enable A-MSDU aggregation. Max A-MSDU size is set such that two MSDUs can be aggregated
1517 for (auto mac : std::initializer_list<Ptr<WifiMac>>{m_apMac, m_staMacs[0], m_staMacs[1]})
1518 {
1519 mac->SetAttribute("BE_MaxAmsduSize", UintegerValue(2100));
1520 mac->GetQosTxop(AC_BE)->SetAttribute("UseExplicitBarAfterMissedBlockAck",
1522 mac->GetQosTxop(AC_BE)->SetAttribute("NMaxInflights", UintegerValue(m_nMaxInflight));
1523 }
1524 }
1525
1526 // install post reception error model on all devices
1527 for (std::size_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
1528 {
1529 auto errorModel = CreateObject<ListErrorModel>();
1530 m_errorModels[m_apMac->GetFrameExchangeManager(linkId)->GetAddress()] = errorModel;
1531 m_apMac->GetWifiPhy(linkId)->SetPostReceptionErrorModel(errorModel);
1532 }
1533 for (std::size_t linkId = 0; linkId < m_staMacs[0]->GetNLinks(); linkId++)
1534 {
1535 auto errorModel = CreateObject<ListErrorModel>();
1536 m_errorModels[m_staMacs[0]->GetFrameExchangeManager(linkId)->GetAddress()] = errorModel;
1537 m_staMacs[0]->GetWifiPhy(linkId)->SetPostReceptionErrorModel(errorModel);
1538
1539 errorModel = CreateObject<ListErrorModel>();
1540 m_errorModels[m_staMacs[1]->GetFrameExchangeManager(linkId)->GetAddress()] = errorModel;
1541 m_staMacs[1]->GetWifiPhy(linkId)->SetPostReceptionErrorModel(errorModel);
1542 }
1543}
1544
1545void
1547{
1548 const Time duration = Seconds(1);
1549 Address destAddr;
1550
1551 switch (m_trafficPattern)
1552 {
1553 case WifiTrafficPattern::STA_TO_STA:
1555 destAddr = m_staMacs[1]->GetDevice()->GetAddress();
1556 break;
1557 case WifiTrafficPattern::STA_TO_AP:
1559 destAddr = m_apMac->GetDevice()->GetAddress();
1560 break;
1561 case WifiTrafficPattern::AP_TO_STA:
1563 destAddr = m_staMacs[1]->GetDevice()->GetAddress();
1564 break;
1565 case WifiTrafficPattern::AP_TO_BCAST:
1567 destAddr = Mac48Address::GetBroadcast();
1568 break;
1569 case WifiTrafficPattern::STA_TO_BCAST:
1571 destAddr = Mac48Address::GetBroadcast();
1572 break;
1573 }
1574
1575 PacketSocketHelper packetSocket;
1576 packetSocket.Install(m_apMac->GetDevice()->GetNode());
1577 packetSocket.Install(m_staMacs[0]->GetDevice()->GetNode());
1578 packetSocket.Install(m_staMacs[1]->GetDevice()->GetNode());
1579
1580 PacketSocketAddress socket;
1582 socket.SetPhysicalAddress(destAddr);
1583 socket.SetProtocol(1);
1584
1585 // install first client application generating at most 4 packets
1586 auto client1 = CreateObject<PacketSocketClient>();
1587 client1->SetAttribute("PacketSize", UintegerValue(1000));
1588 client1->SetAttribute("MaxPackets", UintegerValue(std::min<std::size_t>(m_nPackets, 4)));
1589 client1->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
1590 client1->SetRemote(socket);
1592 client1->SetStartTime(Seconds(0)); // now
1593 client1->SetStopTime(duration);
1594
1595 if (m_nPackets > 4)
1596 {
1597 // install a second client application generating the remaining packets
1598 auto client2 = CreateObject<PacketSocketClient>();
1599 client2->SetAttribute("PacketSize", UintegerValue(1000));
1600 client2->SetAttribute("MaxPackets", UintegerValue(m_nPackets - 4));
1601 client2->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
1602 client2->SetRemote(socket);
1604 // start during transmission of first A-MPDU, if multiple links are setup
1605 client2->SetStartTime(MilliSeconds(4));
1606 client2->SetStopTime(duration);
1607 }
1608
1609 // install a server on all nodes
1610 for (auto nodeIt = NodeList::Begin(); nodeIt != NodeList::End(); nodeIt++)
1611 {
1612 Ptr<PacketSocketServer> server = CreateObject<PacketSocketServer>();
1613 server->SetLocal(socket);
1614 (*nodeIt)->AddApplication(server);
1615 server->SetStartTime(Seconds(0)); // now
1616 server->SetStopTime(duration);
1617 }
1618
1619 for (std::size_t nodeId = 0; nodeId < NodeList::GetNNodes(); nodeId++)
1620 {
1621 Config::ConnectWithoutContext("/NodeList/" + std::to_string(nodeId) +
1622 "/ApplicationList/*/$ns3::PacketSocketServer/Rx",
1623 MakeCallback(&MultiLinkTxTest::L7Receive, this).Bind(nodeId));
1624 }
1625
1626 Simulator::Stop(duration);
1627}
1628
1629void
1631{
1633
1634 // Expected number of packets received by each node (AP, STA 0, STA 1) at application layer
1635 std::array<std::size_t, 3> expectedRxPkts{};
1636
1637 switch (m_trafficPattern)
1638 {
1639 case WifiTrafficPattern::STA_TO_STA:
1640 case WifiTrafficPattern::AP_TO_STA:
1641 // only STA 1 receives the m_nPackets packets that have been transmitted
1642 expectedRxPkts[2] = m_nPackets;
1643 break;
1644 case WifiTrafficPattern::STA_TO_AP:
1645 // only the AP receives the m_nPackets packets that have been transmitted
1646 expectedRxPkts[0] = m_nPackets;
1647 break;
1648 case WifiTrafficPattern::AP_TO_BCAST:
1649 // the AP replicates the broadcast frames on all the links, hence each station
1650 // receives the m_nPackets packets N times, where N is the number of setup link
1651 expectedRxPkts[1] = m_nPackets * m_staMacs[0]->GetSetupLinkIds().size();
1652 expectedRxPkts[2] = m_nPackets * m_staMacs[1]->GetSetupLinkIds().size();
1653 break;
1654 case WifiTrafficPattern::STA_TO_BCAST:
1655 // the AP receives the m_nPackets packets and then replicates them on all the links,
1656 // hence STA 1 receives m_nPackets packets N times, where N is the number of setup link
1657 expectedRxPkts[0] = m_nPackets;
1658 expectedRxPkts[2] = m_nPackets * m_staMacs[1]->GetSetupLinkIds().size();
1659 break;
1660 }
1661
1663 +expectedRxPkts[0],
1664 "Unexpected number of packets received by the AP");
1666 +expectedRxPkts[1],
1667 "Unexpected number of packets received by STA 0");
1669 +expectedRxPkts[2],
1670 "Unexpected number of packets received by STA 1");
1671
1672 // check that the expected number of BlockAck frames are transmitted
1673 if (m_baEnabled && m_nMaxInflight == 1)
1674 {
1675 std::size_t expectedBaCount = 0;
1676 std::size_t expectedBarCount = 0;
1677
1678 switch (m_trafficPattern)
1679 {
1680 case WifiTrafficPattern::STA_TO_AP:
1681 case WifiTrafficPattern::AP_TO_STA:
1682 // two A-MPDUs are transmitted and one BlockAck is corrupted
1683 expectedBaCount = 3;
1684 // one BlockAckReq is sent if m_useBarAfterMissedBa is true
1685 expectedBarCount = m_useBarAfterMissedBa ? 1 : 0;
1686 break;
1687 case WifiTrafficPattern::STA_TO_STA:
1688 case WifiTrafficPattern::STA_TO_BCAST:
1689 // only one A-MPDU is transmitted and the BlockAck is not corrupted
1690 expectedBaCount = 1;
1691 break;
1692 default:;
1693 }
1695 expectedBaCount,
1696 "Unexpected number of BlockAck frames");
1698 expectedBarCount,
1699 "Unexpected number of BlockAckReq frames");
1700 }
1701
1702 // check that setting the QosTxop::NMaxInflights attribute has the expected effect.
1703 // We do not support sending an MPDU multiple times concurrently without Block Ack
1704 // agreement. Also, broadcast frames are already duplicated and sent on all links.
1705 if (m_baEnabled && m_trafficPattern != WifiTrafficPattern::AP_TO_BCAST)
1706 {
1708 m_inflightCount.size(),
1709 m_nPackets / 2,
1710 "Did not collect number of simultaneous transmissions for all data frames");
1711
1712 auto nMaxInflight = std::min(m_nMaxInflight, m_staMacs[0]->GetSetupLinkIds().size());
1713 std::size_t maxCount = 0;
1714 for (const auto& [seqNo, count] : m_inflightCount)
1715 {
1717 count,
1718 nMaxInflight,
1719 "MPDU with seqNo=" << seqNo
1720 << " transmitted simultaneously more times than allowed");
1721 maxCount = std::max(maxCount, count);
1722 }
1723
1725 maxCount,
1726 nMaxInflight,
1727 "Expected that at least one data frame was transmitted simultaneously a number of "
1728 "times equal to the NMaxInflights attribute");
1729 }
1730
1732}
1733
1737enum class WifiMuTrafficPattern : uint8_t
1738{
1742 UL_MU
1743};
1744
1769{
1770 public:
1782 MultiLinkMuTxTest(WifiMuTrafficPattern muTrafficPattern,
1783 WifiUseBarAfterMissedBa useBarAfterMissedBa,
1784 uint8_t nMaxInflight,
1785 const std::vector<std::string>& staChannels,
1786 const std::vector<std::string>& apChannels,
1787 const std::vector<uint8_t>& fixedPhyBands = {});
1788 ~MultiLinkMuTxTest() override = default;
1789
1790 protected:
1797 void L7Receive(uint8_t nodeId, Ptr<const Packet> p, const Address& addr);
1798
1807 void CheckBlockAck(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector, uint8_t linkId);
1808
1809 void Transmit(uint8_t linkId,
1810 std::string context,
1811 WifiConstPsduMap psduMap,
1812 WifiTxVector txVector,
1813 double txPowerW) override;
1814 void DoSetup() override;
1815 void DoRun() override;
1816
1817 private:
1818 void StartTraffic() override;
1819
1821 using RxErrorModelMap = std::unordered_map<Mac48Address, Ptr<ListErrorModel>, WifiAddressHash>;
1822
1825 using AddrSeqNoPair = std::pair<Mac48Address, uint16_t>;
1826
1828 std::list<uint64_t> m_uidList;
1829 std::optional<Mac48Address> m_dataCorruptedSta;
1831 bool m_waitFirstTf{true};
1834 std::size_t m_nMaxInflight;
1835 std::vector<PacketSocketAddress> m_sockets;
1836 std::size_t m_nPackets;
1837 std::size_t m_blockAckCount{0};
1838 // std::size_t m_blockAckReqCount{0}; ///< transmitted BlockAckReq counter
1839 std::array<std::size_t, 3> m_rxPkts{};
1841 std::map<AddrSeqNoPair, std::size_t> m_inflightCount;
1844};
1845
1847 WifiUseBarAfterMissedBa useBarAfterMissedBa,
1848 uint8_t nMaxInflight,
1849 const std::vector<std::string>& staChannels,
1850 const std::vector<std::string>& apChannels,
1851 const std::vector<uint8_t>& fixedPhyBands)
1853 std::string("Check MU data transmission between MLDs ") +
1854 (useBarAfterMissedBa == WifiUseBarAfterMissedBa::YES
1855 ? "(send BAR after BlockAck timeout,"
1856 : "(send Data frames after BlockAck timeout,") +
1857 " MU Traffic pattern: " + std::to_string(static_cast<uint8_t>(muTrafficPattern)) +
1858 ", nMaxInflight=" + std::to_string(nMaxInflight) + ")",
1859 2,
1860 staChannels,
1861 apChannels,
1862 fixedPhyBands),
1863 m_muTrafficPattern(muTrafficPattern),
1864 m_useBarAfterMissedBa(useBarAfterMissedBa == WifiUseBarAfterMissedBa::YES),
1865 m_nMaxInflight(nMaxInflight),
1866 m_sockets(m_nStations),
1867 m_nPackets(muTrafficPattern == WifiMuTrafficPattern::UL_MU ? 4 : 8)
1868{
1869}
1870
1871void
1873 std::string context,
1874 WifiConstPsduMap psduMap,
1875 WifiTxVector txVector,
1876 double txPowerW)
1877{
1878 CtrlTriggerHeader trigger;
1879
1880 for (const auto& [staId, psdu] : psduMap)
1881 {
1882 switch (psdu->GetHeader(0).GetType())
1883 {
1884 case WIFI_MAC_QOSDATA:
1885 CheckAddresses(psdu);
1886 if (psdu->GetHeader(0).HasData())
1887 {
1888 bool isDl = psdu->GetHeader(0).IsFromDs();
1889 auto linkAddress =
1890 isDl ? psdu->GetHeader(0).GetAddr1() : psdu->GetHeader(0).GetAddr2();
1891 auto address = m_apMac->GetMldAddress(linkAddress).value_or(linkAddress);
1892
1893 for (const auto& mpdu : *psdu)
1894 {
1895 // determine the max number of simultaneous transmissions for this MPDU
1896 auto seqNo = mpdu->GetHeader().GetSequenceNumber();
1897 auto [it, success] = m_inflightCount.insert(
1898 {{address, seqNo}, mpdu->GetInFlightLinkIds().size()});
1899 if (!success)
1900 {
1901 it->second = std::max(it->second, mpdu->GetInFlightLinkIds().size());
1902 }
1903 }
1904 for (std::size_t i = 0; i < psdu->GetNMpdus(); i++)
1905 {
1906 // MPDUs with seqNo=2 are always transmitted in an MU PPDU
1907 if (psdu->GetHeader(i).GetSequenceNumber() == 2)
1908 {
1909 if (m_muTrafficPattern == WifiMuTrafficPattern::UL_MU)
1910 {
1911 NS_TEST_EXPECT_MSG_EQ(txVector.IsUlMu(),
1912 true,
1913 "MPDU " << **std::next(psdu->begin(), i)
1914 << " not transmitted in a TB PPDU");
1915 }
1916 else
1917 {
1918 NS_TEST_EXPECT_MSG_EQ(txVector.GetHeMuUserInfoMap().size(),
1919 2,
1920 "MPDU " << **std::next(psdu->begin(), i)
1921 << " not transmitted in a DL MU PPDU");
1922 }
1923 }
1924 // corrupt QoS data frame with sequence number equal to 3 (only once)
1925 if (psdu->GetHeader(i).GetSequenceNumber() != 3)
1926 {
1927 continue;
1928 }
1929 auto uid = psdu->GetPayload(i)->GetUid();
1930 if (!m_dataCorruptedSta)
1931 {
1932 m_uidList.push_front(uid);
1933 m_dataCorruptedSta = isDl ? psdu->GetAddr1() : psdu->GetAddr2();
1934 NS_LOG_INFO("CORRUPTED");
1935 m_errorModels.at(psdu->GetAddr1())->SetList(m_uidList);
1936 }
1937 else if ((isDl && m_dataCorruptedSta == psdu->GetAddr1()) ||
1938 (!isDl && m_dataCorruptedSta == psdu->GetAddr2()))
1939 {
1940 // do not corrupt the QoS data frame anymore
1941 if (auto it = std::find(m_uidList.cbegin(), m_uidList.cend(), uid);
1942 it != m_uidList.cend())
1943 {
1944 m_uidList.erase(it);
1945 }
1946 m_errorModels.at(psdu->GetAddr1())->SetList(m_uidList);
1947 }
1948 break;
1949 }
1950 }
1951 break;
1953 if (m_nMaxInflight > 1)
1954 {
1955 // we do not check the content of BlockAck when m_nMaxInflight is greater than 1
1956 break;
1957 }
1958 CheckBlockAck(psdu, txVector, linkId);
1960 // to simulate a missed BlockAck, corrupt the fifth BlockAck frame (the first
1961 // two BlockAck frames are sent to acknowledge the QoS data frames that triggered
1962 // the establishment of Block Ack agreements)
1963 if (m_blockAckCount == 5)
1964 {
1965 // corrupt the third BlockAck frame to simulate a missed BlockAck
1966 m_uidList.push_front(psdu->GetPacket()->GetUid());
1967 NS_LOG_INFO("CORRUPTED");
1968 m_errorModels.at(psdu->GetAddr1())->SetList(m_uidList);
1969 }
1970 break;
1972 psdu->GetPayload(0)->PeekHeader(trigger);
1973 // the MU scheduler requests channel access on all links but we have to perform the
1974 // following actions only once (hence why we only consider TF transmitted on link 0)
1975 if (trigger.IsBasic() && m_waitFirstTf)
1976 {
1977 m_waitFirstTf = false;
1978 // the AP is starting the transmission of the Basic Trigger frame, so generate
1979 // the configured number of packets at STAs, which are sent in TB PPDUs
1980 auto band = m_apMac->GetWifiPhy(linkId)->GetPhyBand();
1981 Time txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, band);
1982 for (uint8_t i = 0; i < m_nStations; i++)
1983 {
1984 Ptr<PacketSocketClient> client = CreateObject<PacketSocketClient>();
1985 client->SetAttribute("PacketSize", UintegerValue(450));
1986 client->SetAttribute("MaxPackets", UintegerValue(m_nPackets));
1987 client->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
1988 client->SetAttribute("Priority", UintegerValue(i * 4)); // 0 and 4
1989 client->SetRemote(m_sockets[i]);
1990 m_staMacs[i]->GetDevice()->GetNode()->AddApplication(client);
1991 client->SetStartTime(txDuration); // start when TX ends
1992 client->SetStopTime(Seconds(1.0)); // stop in a second
1993 client->Initialize();
1994 }
1995 }
1996 break;
1997 default:;
1998 }
1999 }
2000
2001 MultiLinkOperationsTestBase::Transmit(linkId, context, psduMap, txVector, txPowerW);
2002}
2003
2004void
2006 const WifiTxVector& txVector,
2007 uint8_t linkId)
2008{
2009 /*
2010 * Example sequence with DL_MU_BAR_BA_SEQUENCE
2011 * ┌───────┬───────X
2012 * (To:1) │ 2 │ 3 │
2013 * ├───────┼───────┤ ┌───┐ ┌───────┐
2014 * [link 0] (To:0) │ 2 │ 3 │ │BAR│ (To:1) │ 3 │
2015 * ────────────────┴───────┴───────┴┬──┼───┼──┬──────────┴───────┴┬───┬────────
2016 * │BA│ │BA│ │ACK│
2017 * └──┘ └──┘ └───┘
2018 * ┌───────┬───────┐
2019 * (To:1) │ 4 │ 5 │
2020 * ├───────┼───────┤ ┌───┐ ┌───┐
2021 * [link 1] (To:0) │ 4 │ 5 │ │BAR│ │BAR│
2022 * ────────────────────────────┴───────┴───────┴┬──X────┴───┴┬──┼───┼──┬───────
2023 * │BA│ │BA│ │BA│
2024 * └──┘ └──┘ └──┘
2025 *
2026 * Example sequence with UL_MU
2027 *
2028 * ┌──┐ ┌────┐ ┌───┐
2029 * [link 0] │TF│ │M-BA│ │ACK│
2030 * ─────────┴──┴──┬───────┬───────┬──┴────┴────────────┬───────┬─┴───┴─────────
2031 * (From:0) │ 2 │ 3 │ (From:1) │ 3 │
2032 * ├───────┼───────┤ └───────┘
2033 * (From:1) │ 2 │ 3 │
2034 * └───────┴───────X
2035 * ┌──┐
2036 * [link 1] │TF│
2037 * ─────────┴──┴──┬───────────────┬────────────────────────────────────────────
2038 * (From:0) │ QoS Null │
2039 * ├───────────────┤
2040 * (From:1) │ QoS Null │
2041 * └───────────────┘
2042 */
2043 auto mpdu = *psdu->begin();
2044 CtrlBAckResponseHeader blockAck;
2045 mpdu->GetPacket()->PeekHeader(blockAck);
2046 bool isMpdu3corrupted;
2047
2048 switch (m_blockAckCount)
2049 {
2050 case 0:
2051 case 1: // Ignore the first two BlockAck frames that acknowledged frames sent to establish BA
2052 break;
2053 case 2:
2054 if (m_muTrafficPattern == WifiMuTrafficPattern::UL_MU)
2055 {
2056 NS_TEST_EXPECT_MSG_EQ(blockAck.IsMultiSta(), true, "Expected a Multi-STA BlockAck");
2057 for (uint8_t i = 0; i < m_nStations; i++)
2058 {
2059 auto indices = blockAck.FindPerAidTidInfoWithAid(m_staMacs[i]->GetAssociationId());
2060 NS_TEST_ASSERT_MSG_EQ(indices.size(), 1, "Expected one Per AID TID Info per STA");
2061 auto index = indices.front();
2063 true,
2064 "Expected that a QoS data frame was corrupted");
2065 isMpdu3corrupted =
2066 m_staMacs[i]->GetLinkIdByAddress(*m_dataCorruptedSta).has_value();
2067 NS_TEST_EXPECT_MSG_EQ(blockAck.IsPacketReceived(2, index),
2068 true,
2069 "MPDU 2 expected to be successfully received");
2070 NS_TEST_EXPECT_MSG_EQ(blockAck.IsPacketReceived(3, index),
2071 !isMpdu3corrupted,
2072 "Unexpected reception status for MPDU 3");
2073 }
2074
2075 break;
2076 }
2077 case 3:
2078 // BlockAck frames in response to the first DL MU PPDU
2079 isMpdu3corrupted = (mpdu->GetHeader().GetAddr2() == m_dataCorruptedSta);
2081 true,
2082 "MPDU 2 expected to be successfully received");
2084 !isMpdu3corrupted,
2085 "Unexpected reception status for MPDU 3");
2086 // in case of DL MU, if there are at least two links setup, we expect all MPDUs to
2087 // be inflight (on distinct links)
2088 if (m_muTrafficPattern != WifiMuTrafficPattern::UL_MU &&
2089 m_staMacs[0]->GetSetupLinkIds().size() > 1)
2090 {
2091 auto queue = m_apMac->GetTxopQueue(AC_BE);
2092 Ptr<StaWifiMac> rcvMac;
2093 if (m_staMacs[0]->GetFrameExchangeManager(linkId)->GetAddress() ==
2094 mpdu->GetHeader().GetAddr2())
2095 {
2096 rcvMac = m_staMacs[0];
2097 }
2098 else if (m_staMacs[1]->GetFrameExchangeManager(linkId)->GetAddress() ==
2099 mpdu->GetHeader().GetAddr2())
2100 {
2101 rcvMac = m_staMacs[1];
2102 }
2103 else
2104 {
2105 NS_ABORT_MSG("BlockAck frame not sent by a station in DL scenario");
2106 }
2107 auto item = queue->PeekByTidAndAddress(0, rcvMac->GetAddress());
2108 std::size_t nQueuedPkt = 0;
2109 auto delay = WifiPhy::CalculateTxDuration(psdu,
2110 txVector,
2111 rcvMac->GetWifiPhy(linkId)->GetPhyBand()) +
2112 MicroSeconds(1); // to account for propagation delay
2113
2114 while (item)
2115 {
2116 auto seqNo = item->GetHeader().GetSequenceNumber();
2117 NS_TEST_EXPECT_MSG_EQ(item->IsInFlight(),
2118 true,
2119 "MPDU with seqNo=" << seqNo << " is not in flight");
2120 auto linkIds = item->GetInFlightLinkIds();
2121 NS_TEST_EXPECT_MSG_EQ(linkIds.size(),
2122 1,
2123 "MPDU with seqNo=" << seqNo
2124 << " is in flight on multiple links");
2125 // The first two MPDUs are in flight on the same link on which the BlockAck
2126 // is sent. The other two MPDUs (only for AP to STA/STA to AP scenarios) are
2127 // in flight on a different link.
2128 auto srcLinkId = m_apMac->GetLinkIdByAddress(mpdu->GetHeader().GetAddr1());
2129 NS_TEST_ASSERT_MSG_EQ(srcLinkId.has_value(),
2130 true,
2131 "Addr1 of BlockAck is not an originator's link address");
2132 NS_TEST_EXPECT_MSG_EQ((*linkIds.begin() == *srcLinkId),
2133 (seqNo <= 3),
2134 "MPDU with seqNo=" << seqNo
2135 << " in flight on unexpected link");
2136 // check the Retry subfield and whether this MPDU is still queued
2137 // after the originator has processed this BlockAck
2138
2139 // MPDUs acknowledged via this BlockAck are no longer queued
2140 bool isQueued = (seqNo > (isMpdu3corrupted ? 2 : 3));
2141 // The Retry subfield is set if the MPDU has not been acknowledged (i.e., it
2142 // is still queued) and has been transmitted on the same link as the BlockAck
2143 // (i.e., its sequence number is less than or equal to 2)
2144 bool isRetry = isQueued && seqNo <= 3;
2145
2146 Simulator::Schedule(delay, [this, item, isQueued, isRetry]() {
2147 NS_TEST_EXPECT_MSG_EQ(item->IsQueued(),
2148 isQueued,
2149 "MPDU with seqNo="
2150 << item->GetHeader().GetSequenceNumber() << " should "
2151 << (isQueued ? "" : "not") << " be queued");
2153 item->GetHeader().IsRetry(),
2154 isRetry,
2155 "Unexpected value for the Retry subfield of the MPDU with seqNo="
2156 << item->GetHeader().GetSequenceNumber());
2157 });
2158
2159 nQueuedPkt++;
2160 item = queue->PeekByTidAndAddress(0, rcvMac->GetAddress(), item);
2161 }
2162 // Each MPDU contains an A-MSDU consisting of two MSDUs
2163 NS_TEST_EXPECT_MSG_EQ(nQueuedPkt, m_nPackets / 2, "Unexpected number of queued MPDUs");
2164 }
2165 break;
2166 }
2167}
2168
2169void
2171{
2172 NS_LOG_INFO("Packet received by NODE " << +nodeId << "\n");
2173 m_rxPkts[nodeId]++;
2174}
2175
2176void
2178{
2179 switch (m_muTrafficPattern)
2180 {
2181 case WifiMuTrafficPattern::DL_MU_BAR_BA_SEQUENCE:
2182 Config::SetDefault("ns3::WifiDefaultAckManager::DlMuAckSequenceType",
2184 break;
2185 case WifiMuTrafficPattern::DL_MU_MU_BAR:
2186 Config::SetDefault("ns3::WifiDefaultAckManager::DlMuAckSequenceType",
2188 break;
2189 case WifiMuTrafficPattern::DL_MU_AGGR_MU_BAR:
2190 Config::SetDefault("ns3::WifiDefaultAckManager::DlMuAckSequenceType",
2192 break;
2193 default:;
2194 }
2195
2197
2198 // Enable A-MSDU aggregation. Max A-MSDU size is set such that two MSDUs can be aggregated
2199 for (auto mac : std::initializer_list<Ptr<WifiMac>>{m_apMac, m_staMacs[0], m_staMacs[1]})
2200 {
2201 mac->SetAttribute("BE_MaxAmsduSize", UintegerValue(1050));
2202 mac->GetQosTxop(AC_BE)->SetAttribute("UseExplicitBarAfterMissedBlockAck",
2204 mac->GetQosTxop(AC_BE)->SetAttribute("NMaxInflights", UintegerValue(m_nMaxInflight));
2205
2206 mac->SetAttribute("VI_MaxAmsduSize", UintegerValue(1050));
2207 mac->GetQosTxop(AC_VI)->SetAttribute("UseExplicitBarAfterMissedBlockAck",
2209 mac->GetQosTxop(AC_VI)->SetAttribute("NMaxInflights", UintegerValue(m_nMaxInflight));
2210 }
2211
2212 // aggregate MU scheduler
2213 auto muScheduler = CreateObjectWithAttributes<RrMultiUserScheduler>(
2214 "EnableUlOfdma",
2215 BooleanValue(m_muTrafficPattern == WifiMuTrafficPattern::UL_MU),
2216 "EnableBsrp",
2217 BooleanValue(false),
2218 "UlPsduSize",
2219 UintegerValue(2000));
2220 m_apMac->AggregateObject(muScheduler);
2221
2222 // install post reception error model on all devices
2223 for (std::size_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
2224 {
2225 auto errorModel = CreateObject<ListErrorModel>();
2226 m_errorModels[m_apMac->GetFrameExchangeManager(linkId)->GetAddress()] = errorModel;
2227 m_apMac->GetWifiPhy(linkId)->SetPostReceptionErrorModel(errorModel);
2228 }
2229 for (std::size_t linkId = 0; linkId < m_staMacs[0]->GetNLinks(); linkId++)
2230 {
2231 auto errorModel = CreateObject<ListErrorModel>();
2232 m_errorModels[m_staMacs[0]->GetFrameExchangeManager(linkId)->GetAddress()] = errorModel;
2233 m_staMacs[0]->GetWifiPhy(linkId)->SetPostReceptionErrorModel(errorModel);
2234
2235 errorModel = CreateObject<ListErrorModel>();
2236 m_errorModels[m_staMacs[1]->GetFrameExchangeManager(linkId)->GetAddress()] = errorModel;
2237 m_staMacs[1]->GetWifiPhy(linkId)->SetPostReceptionErrorModel(errorModel);
2238 }
2239}
2240
2241void
2243{
2244 const Time duration = Seconds(1);
2245 Address destAddr;
2246
2247 PacketSocketHelper packetSocket;
2248 packetSocket.Install(m_apMac->GetDevice()->GetNode());
2249 packetSocket.Install(m_staMacs[0]->GetDevice()->GetNode());
2250 packetSocket.Install(m_staMacs[1]->GetDevice()->GetNode());
2251
2252 if (m_muTrafficPattern < WifiMuTrafficPattern::UL_MU)
2253 {
2254 // DL Traffic
2255 for (uint8_t i = 0; i < m_nStations; i++)
2256 {
2257 PacketSocketAddress socket;
2259 socket.SetPhysicalAddress(m_staMacs[i]->GetDevice()->GetAddress());
2260 socket.SetProtocol(1);
2261
2262 // the first client application generates three packets in order
2263 // to trigger the establishment of a Block Ack agreement
2264 auto client1 = CreateObject<PacketSocketClient>();
2265 client1->SetAttribute("PacketSize", UintegerValue(450));
2266 client1->SetAttribute("MaxPackets", UintegerValue(3));
2267 client1->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
2268 client1->SetRemote(socket);
2269 m_apMac->GetDevice()->GetNode()->AddApplication(client1);
2270 client1->SetStartTime(i * MilliSeconds(50));
2271 client1->SetStopTime(duration);
2272
2273 // the second client application generates the first half of the selected number
2274 // of packets, which are sent in DL MU PPDUs.
2275 auto client2 = CreateObject<PacketSocketClient>();
2276 client2->SetAttribute("PacketSize", UintegerValue(450));
2277 client2->SetAttribute("MaxPackets", UintegerValue(m_nPackets / 2));
2278 client2->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
2279 client2->SetRemote(socket);
2280 m_apMac->GetDevice()->GetNode()->AddApplication(client2);
2281 // start after all BA agreements are established
2282 client2->SetStartTime(m_nStations * MilliSeconds(50));
2283 client2->SetStopTime(duration);
2284
2285 // the third client application generates the second half of the selected number
2286 // of packets, which are sent in DL MU PPDUs.
2287 auto client3 = CreateObject<PacketSocketClient>();
2288 client3->SetAttribute("PacketSize", UintegerValue(450));
2289 client3->SetAttribute("MaxPackets", UintegerValue(m_nPackets / 2));
2290 client3->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
2291 client3->SetRemote(socket);
2292 m_apMac->GetDevice()->GetNode()->AddApplication(client3);
2293 // start during transmission of first A-MPDU, if multiple links are setup
2294 client3->SetStartTime(m_nStations * MilliSeconds(50) + MilliSeconds(3));
2295 client3->SetStopTime(duration);
2296 }
2297 }
2298 else
2299 {
2300 // UL Traffic
2301 for (uint8_t i = 0; i < m_nStations; i++)
2302 {
2303 m_sockets[i].SetSingleDevice(m_staMacs[i]->GetDevice()->GetIfIndex());
2304 m_sockets[i].SetPhysicalAddress(m_apMac->GetDevice()->GetAddress());
2305 m_sockets[i].SetProtocol(1);
2306
2307 // the first client application generates three packets in order
2308 // to trigger the establishment of a Block Ack agreement
2309 Ptr<PacketSocketClient> client1 = CreateObject<PacketSocketClient>();
2310 client1->SetAttribute("PacketSize", UintegerValue(450));
2311 client1->SetAttribute("MaxPackets", UintegerValue(3));
2312 client1->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
2313 client1->SetAttribute("Priority", UintegerValue(i * 4)); // 0 and 4
2314 client1->SetRemote(m_sockets[i]);
2315 m_staMacs[i]->GetDevice()->GetNode()->AddApplication(client1);
2316 client1->SetStartTime(i * MilliSeconds(50));
2317 client1->SetStopTime(duration);
2318
2319 // packets to be included in TB PPDUs are generated (by Transmit()) when
2320 // the first Basic Trigger Frame is sent by the AP
2321 }
2322
2323 // MU scheduler starts requesting channel access when we are done with BA agreements
2325 auto muScheduler = m_apMac->GetObject<MultiUserScheduler>();
2326 NS_TEST_ASSERT_MSG_NE(muScheduler, nullptr, "Expected an aggregated MU scheduler");
2327 muScheduler->SetAccessReqInterval(MilliSeconds(3));
2328 // channel access is requested only once
2329 muScheduler->SetAccessReqInterval(Seconds(0));
2330 });
2331 }
2332
2333 // install a server on all nodes and connect traced callback
2334 for (auto nodeIt = NodeList::Begin(); nodeIt != NodeList::End(); nodeIt++)
2335 {
2336 PacketSocketAddress srvAddr;
2337 auto device = DynamicCast<WifiNetDevice>((*nodeIt)->GetDevice(0));
2338 NS_TEST_ASSERT_MSG_NE(device, nullptr, "Expected a WifiNetDevice");
2339 srvAddr.SetSingleDevice(device->GetIfIndex());
2340 srvAddr.SetProtocol(1);
2341
2342 Ptr<PacketSocketServer> server = CreateObject<PacketSocketServer>();
2343 server->SetLocal(srvAddr);
2344 (*nodeIt)->AddApplication(server);
2345 server->SetStartTime(Seconds(0)); // now
2346 server->SetStopTime(duration);
2347 server->TraceConnectWithoutContext(
2348 "Rx",
2349 MakeCallback(&MultiLinkMuTxTest::L7Receive, this).Bind(device->GetNode()->GetId()));
2350 }
2351
2352 Simulator::Stop(duration);
2353}
2354
2355void
2357{
2359
2360 // Expected number of packets received by each node (AP, STA 0, STA 1) at application layer
2361 std::array<std::size_t, 3> expectedRxPkts{};
2362
2363 switch (m_muTrafficPattern)
2364 {
2365 case WifiMuTrafficPattern::DL_MU_BAR_BA_SEQUENCE:
2366 case WifiMuTrafficPattern::DL_MU_MU_BAR:
2367 case WifiMuTrafficPattern::DL_MU_AGGR_MU_BAR:
2368 // both STA 0 and STA 1 receive m_nPackets + 3 (sent to trigger BA establishment) packets
2369 expectedRxPkts[1] = m_nPackets + 3;
2370 expectedRxPkts[2] = m_nPackets + 3;
2371 break;
2372 case WifiMuTrafficPattern::UL_MU:
2373 // AP receives m_nPackets + 3 (sent to trigger BA establishment) packets from each station
2374 expectedRxPkts[0] = 2 * (m_nPackets + 3);
2375 break;
2376 }
2377
2379 +expectedRxPkts[0],
2380 "Unexpected number of packets received by the AP");
2382 +expectedRxPkts[1],
2383 "Unexpected number of packets received by STA 0");
2385 +expectedRxPkts[2],
2386 "Unexpected number of packets received by STA 1");
2387
2388 // check that setting the QosTxop::NMaxInflights attribute has the expected effect.
2389 // For DL, for each station we send 2 MPDUs to trigger BA agreement and m_nPackets / 2 MPDUs
2390 // For UL, each station sends 2 MPDUs to trigger BA agreement and m_nPackets / 2 MPDUs
2392 m_inflightCount.size(),
2393 2 * (2 + m_nPackets / 2),
2394 "Did not collect number of simultaneous transmissions for all data frames");
2395
2396 auto nMaxInflight = std::min(m_nMaxInflight, m_staMacs[0]->GetSetupLinkIds().size());
2397 std::size_t maxCount = 0;
2398 for (const auto& [txSeqNoPair, count] : m_inflightCount)
2399 {
2401 nMaxInflight,
2402 "MPDU with seqNo="
2403 << txSeqNoPair.second
2404 << " transmitted simultaneously more times than allowed");
2405 maxCount = std::max(maxCount, count);
2406 }
2407
2409 maxCount,
2410 nMaxInflight,
2411 "Expected that at least one data frame was transmitted simultaneously a number of "
2412 "times equal to the NMaxInflights attribute");
2413
2415}
2416
2436{
2437 public:
2440
2441 protected:
2442 void DoSetup() override;
2443 void DoRun() override;
2444 void Transmit(uint8_t linkId,
2445 std::string context,
2446 WifiConstPsduMap psduMap,
2447 WifiTxVector txVector,
2448 double txPowerW) override;
2449
2450 private:
2451 void StartTraffic() override;
2452
2457
2459 std::size_t m_nQosDataFrames;
2462};
2463
2466 "Check sequence numbers after CTS timeout",
2467 1,
2468 {"{36, 0, BAND_5GHZ, 0}", "{2, 0, BAND_2_4GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"},
2469 {"{36, 0, BAND_5GHZ, 0}", "{2, 0, BAND_2_4GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"}),
2470 m_nQosDataFrames(0),
2471 m_errorModel(CreateObject<ListErrorModel>()),
2472 m_rtsCorrupted(false)
2473{
2474}
2475
2476void
2478{
2479 // Enable RTS/CTS
2480 Config::SetDefault("ns3::WifiRemoteStationManager::RtsCtsThreshold", StringValue("1000"));
2481
2483
2484 // install post reception error model on all STAs affiliated with non-AP MLD
2485 for (std::size_t linkId = 0; linkId < m_staMacs[0]->GetNLinks(); linkId++)
2486 {
2487 m_staMacs[0]->GetWifiPhy(linkId)->SetPostReceptionErrorModel(m_errorModel);
2488 }
2489}
2490
2493{
2494 const Time duration = Seconds(1);
2495
2496 auto client = CreateObject<PacketSocketClient>();
2497 client->SetAttribute("PacketSize", UintegerValue(1000));
2498 client->SetAttribute("MaxPackets", UintegerValue(4));
2499 client->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
2500 client->SetRemote(m_socket);
2501 client->SetStartTime(Seconds(0)); // now
2502 client->SetStopTime(duration);
2503
2504 return client;
2505}
2506
2507void
2509{
2510 const Time duration = Seconds(1);
2511
2512 PacketSocketHelper packetSocket;
2513 packetSocket.Install(m_apMac->GetDevice()->GetNode());
2514 packetSocket.Install(m_staMacs[0]->GetDevice()->GetNode());
2515
2517 m_socket.SetPhysicalAddress(m_staMacs[0]->GetAddress());
2519
2520 // install client application generating 4 packets
2522
2523 // install a server on all nodes
2524 for (auto nodeIt = NodeList::Begin(); nodeIt != NodeList::End(); nodeIt++)
2525 {
2526 Ptr<PacketSocketServer> server = CreateObject<PacketSocketServer>();
2527 server->SetLocal(m_socket);
2528 (*nodeIt)->AddApplication(server);
2529 server->SetStartTime(Seconds(0)); // now
2530 server->SetStopTime(duration);
2531 }
2532}
2533
2534void
2536 std::string context,
2537 WifiConstPsduMap psduMap,
2538 WifiTxVector txVector,
2539 double txPowerW)
2540{
2541 auto psdu = psduMap.begin()->second;
2542
2543 if (psdu->GetHeader(0).IsRts() && !m_rtsCorrupted)
2544 {
2545 m_errorModel->SetList({psdu->GetPacket()->GetUid()});
2546 m_rtsCorrupted = true;
2547 // generate other packets when the first RTS is transmitted
2549 }
2550 else if (psdu->GetHeader(0).IsQosData())
2551 {
2553
2554 if (m_nQosDataFrames == 2)
2555 {
2556 // generate other packets when the second QoS data frame is transmitted
2558 }
2559 }
2560
2561 MultiLinkOperationsTestBase::Transmit(linkId, context, psduMap, txVector, txPowerW);
2562}
2563
2564void
2566{
2569
2570 NS_TEST_EXPECT_MSG_EQ(m_nQosDataFrames, 3, "Unexpected number of transmitted QoS data frames");
2571
2572 std::size_t count{};
2573
2574 for (const auto& txPsdu : m_txPsdus)
2575 {
2576 auto psdu = txPsdu.psduMap.begin()->second;
2577
2578 if (!psdu->GetHeader(0).IsQosData())
2579 {
2580 continue;
2581 }
2582
2583 NS_TEST_EXPECT_MSG_EQ(psdu->GetNMpdus(), 4, "Unexpected number of MPDUs in A-MPDU");
2584
2585 count++;
2586 uint16_t expectedSeqNo{};
2587
2588 switch (count)
2589 {
2590 case 1:
2591 expectedSeqNo = 4;
2592 break;
2593 case 2:
2594 expectedSeqNo = 0;
2595 break;
2596 case 3:
2597 expectedSeqNo = 8;
2598 break;
2599 }
2600
2601 for (const auto& mpdu : *PeekPointer(psdu))
2602 {
2603 NS_TEST_EXPECT_MSG_EQ(mpdu->GetHeader().GetSequenceNumber(),
2604 expectedSeqNo++,
2605 "Unexpected sequence number");
2606 }
2607 }
2608
2610}
2611
2619{
2620 public:
2622};
2623
2625 : TestSuite("wifi-mlo", UNIT)
2626{
2627 using ParamsTuple =
2628 std::tuple<std::vector<std::string>, // non-AP MLD channels
2629 std::vector<std::string>, // AP MLD channels
2630 std::vector<std::pair<uint8_t, uint8_t>>, // (STA link ID, AP link ID) of setup
2631 // links
2632 std::vector<uint8_t>>; // IDs of link that cannot change PHY band
2633
2635
2636 for (const auto& [staChannels, apChannels, setupLinks, fixedPhyBands] :
2637 {// matching channels: setup all links
2638 ParamsTuple({"{36, 0, BAND_5GHZ, 0}", "{2, 0, BAND_2_4GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"},
2639 {"{36, 0, BAND_5GHZ, 0}", "{2, 0, BAND_2_4GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"},
2640 {{0, 0}, {1, 1}, {2, 2}},
2641 {}),
2642 // non-matching channels, matching PHY bands: setup all links
2643 ParamsTuple({"{108, 0, BAND_5GHZ, 0}", "{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}"},
2644 {"{36, 0, BAND_5GHZ, 0}", "{120, 0, BAND_5GHZ, 0}", "{5, 0, BAND_6GHZ, 0}"},
2645 {{1, 0}, {0, 1}, {2, 2}},
2646 {}),
2647 // non-AP MLD switches band on some links to setup 3 links
2648 ParamsTuple({"{2, 0, BAND_2_4GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{36, 0, BAND_5GHZ, 0}"},
2649 {"{36, 0, BAND_5GHZ, 0}", "{9, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
2650 {{2, 0}, {0, 1}, {1, 2}},
2651 {}),
2652 // the first link of the non-AP MLD cannot change PHY band and no AP is operating on
2653 // that band, hence only 2 links are setup
2654 ParamsTuple(
2655 {"{2, 0, BAND_2_4GHZ, 0}", "{36, 0, BAND_5GHZ, 0}", "{8, 20, BAND_2_4GHZ, 0}"},
2656 {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
2657 {{1, 0}, {2, 1}},
2658 {0}),
2659 // the first link of the non-AP MLD cannot change PHY band and no AP is operating on
2660 // that band; the second link of the non-AP MLD cannot change PHY band and there is
2661 // an AP operating on the same channel; hence 2 links are setup
2662 ParamsTuple(
2663 {"{2, 0, BAND_2_4GHZ, 0}", "{36, 0, BAND_5GHZ, 0}", "{8, 20, BAND_2_4GHZ, 0}"},
2664 {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
2665 {{1, 0}, {2, 1}},
2666 {0, 1}),
2667 // the first link of the non-AP MLD cannot change PHY band and no AP is operating on
2668 // that band; the second link of the non-AP MLD cannot change PHY band and there is
2669 // an AP operating on the same channel; the third link of the non-AP MLD cannot
2670 // change PHY band and there is an AP operating on the same band (different channel);
2671 // hence 2 links are setup by switching channel (not band) on the third link
2672 ParamsTuple({"{2, 0, BAND_2_4GHZ, 0}", "{36, 0, BAND_5GHZ, 0}", "{60, 0, BAND_5GHZ, 0}"},
2673 {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
2674 {{1, 0}, {2, 2}},
2675 {0, 1, 2}),
2676 // the first link of the non-AP MLD cannot change PHY band and no AP is operating on
2677 // that band; the second link of the non-AP MLD cannot change PHY band and there is
2678 // an AP operating on the same channel; hence one link only is setup
2679 ParamsTuple({"{2, 0, BAND_2_4GHZ, 0}", "{36, 0, BAND_5GHZ, 0}"},
2680 {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
2681 {{1, 0}},
2682 {0, 1}),
2683 // non-AP MLD has only two STAs and setups two links
2684 ParamsTuple({"{2, 0, BAND_2_4GHZ, 0}", "{36, 0, BAND_5GHZ, 0}"},
2685 {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
2686 {{0, 1}, {1, 0}},
2687 {}),
2688 // single link non-AP STA associates with an AP affiliated with an AP MLD
2689 ParamsTuple({"{120, 0, BAND_5GHZ, 0}"},
2690 {"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
2691 {{0, 2}},
2692 {}),
2693 // a STA affiliated with a non-AP MLD associates with a single link AP
2694 ParamsTuple({"{36, 0, BAND_5GHZ, 0}", "{1, 0, BAND_6GHZ, 0}", "{120, 0, BAND_5GHZ, 0}"},
2695 {"{120, 0, BAND_5GHZ, 0}"},
2696 {{2, 0}},
2697 {})})
2698 {
2699 AddTestCase(new MultiLinkSetupTest(staChannels,
2700 apChannels,
2701 WifiScanType::PASSIVE,
2702 setupLinks,
2703 fixedPhyBands),
2705 AddTestCase(new MultiLinkSetupTest(staChannels,
2706 apChannels,
2707 WifiScanType::ACTIVE,
2708 setupLinks,
2709 fixedPhyBands),
2711
2712 for (const auto& trafficPattern : {WifiTrafficPattern::STA_TO_STA,
2713 WifiTrafficPattern::STA_TO_AP,
2714 WifiTrafficPattern::AP_TO_STA,
2715 WifiTrafficPattern::AP_TO_BCAST,
2716 WifiTrafficPattern::STA_TO_BCAST})
2717 {
2718 // No Block Ack agreement
2719 AddTestCase(new MultiLinkTxTest(trafficPattern,
2720 WifiBaEnabled::NO,
2721 WifiUseBarAfterMissedBa::NO,
2722 1,
2723 staChannels,
2724 apChannels,
2725 fixedPhyBands),
2727 for (const auto& useBarAfterMissedBa :
2728 {WifiUseBarAfterMissedBa::YES, WifiUseBarAfterMissedBa::NO})
2729 {
2730 // Block Ack agreement with nMaxInflight=1
2731 AddTestCase(new MultiLinkTxTest(trafficPattern,
2732 WifiBaEnabled::YES,
2733 useBarAfterMissedBa,
2734 1,
2735 staChannels,
2736 apChannels,
2737 fixedPhyBands),
2739 // Block Ack agreement with nMaxInflight=2
2740 AddTestCase(new MultiLinkTxTest(trafficPattern,
2741 WifiBaEnabled::YES,
2742 useBarAfterMissedBa,
2743 2,
2744 staChannels,
2745 apChannels,
2746 fixedPhyBands),
2748 }
2749 }
2750
2751 for (const auto& muTrafficPattern : {WifiMuTrafficPattern::DL_MU_BAR_BA_SEQUENCE,
2752 WifiMuTrafficPattern::DL_MU_MU_BAR,
2753 WifiMuTrafficPattern::DL_MU_AGGR_MU_BAR,
2754 WifiMuTrafficPattern::UL_MU})
2755 {
2756 for (const auto& useBarAfterMissedBa :
2757 {WifiUseBarAfterMissedBa::YES, WifiUseBarAfterMissedBa::NO})
2758 {
2759 // Block Ack agreement with nMaxInflight=1
2760 AddTestCase(new MultiLinkMuTxTest(muTrafficPattern,
2761 useBarAfterMissedBa,
2762 1,
2763 staChannels,
2764 apChannels,
2765 fixedPhyBands),
2767 // Block Ack agreement with nMaxInflight=2
2768 AddTestCase(new MultiLinkMuTxTest(muTrafficPattern,
2769 useBarAfterMissedBa,
2770 2,
2771 staChannels,
2772 apChannels,
2773 fixedPhyBands),
2775 }
2776 }
2777 }
2778
2780}
2781
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:170
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:942
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
static Mac48Address GetBroadcast()
Implement the header for management frames of type association request.
Definition: mgt-headers.h:161
Implement the header for management frames of type association and reassociation response.
Definition: mgt-headers.h:338
Implement the header for management frames of type beacon.
Definition: mgt-headers.h:516
Implement the header for management frames of type probe response.
Definition: mgt-headers.h:455
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
static Iterator Begin()
Definition: node-list.cc:237
static uint32_t GetNNodes()
Definition: node-list.cc:258
static Iterator End()
Definition: node-list.cc:244
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.
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:78
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 ...
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:568
static void Destroy()
Execute the events scheduled with ScheduleDestroy().
Definition: simulator.cc:140
static Time Now()
Return the current simulation virtual time.
Definition: simulator.cc:199
static void Run()
Run the simulation.
Definition: simulator.cc:176
static void Stop()
Tell the Simulator the calling event should be the last one executed.
Definition: simulator.cc:184
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)
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
@ QUICK
Fast test.
Definition: test.h:1065
void AddTestCase(TestCase *testCase, TestDuration duration=QUICK)
Add an individual child TestCase to this test suite.
Definition: test.cc:301
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
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
Definition: wifi-helper.h:324
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:864
std::optional< Mac48Address > GetMldAddress(const Mac48Address &remoteAddr) const
Definition: wifi-mac.cc:1358
uint8_t GetNLinks() const
Get the number of links (can be greater than 1 for 11be devices only).
Definition: wifi-mac.cc:930
Ptr< WifiPhy > GetWifiPhy(uint8_t linkId=SINGLE_LINK_OP_ID) const
Definition: wifi-mac.cc:978
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:936
Ptr< WifiNetDevice > GetDevice() const
Return the device this PHY is associated with.
Definition: wifi-mac.cc:437
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:543
Ptr< WifiRemoteStationManager > GetWifiRemoteStationManager(uint8_t linkId=0) const
Definition: wifi-mac.cc:910
Mac48Address GetAddress() const
Definition: wifi-mac.cc:450
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
@ DLT_IEEE802_11_RADIO
Include Radiotap link layer information.
Definition: wifi-helper.h:178
void SetPostReceptionErrorModel(const Ptr< ErrorModel > em)
Attach a receive ErrorModel to the WifiPhy.
Definition: wifi-phy.cc:648
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
Definition: wifi-phy.cc:1496
WifiPhyBand GetPhyBand() const
Get the configured Wi-Fi band.
Definition: wifi-phy.cc:1005
const WifiPhyOperatingChannel & GetOperatingChannel() const
Get a const reference to the operating channel.
Definition: wifi-phy.cc:1017
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).
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
#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
WifiScanType
Scan type (active or passive)
Definition: sta-wifi-mac.h:51
@ WIFI_STANDARD_80211be
@ AC_BE
Best Effort.
Definition: qos-utils.h:74
@ AC_VI
Video.
Definition: qos-utils.h:78
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: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:702
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_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
constexpr FrequencyRange WIFI_SPECTRUM_2_4_GHZ
Identifier for the frequency range covering the wifi spectrum in the 2.4 GHz band.
STL namespace.
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.