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