A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
sta-wifi-mac.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2006, 2009 INRIA
3 * Copyright (c) 2009 MIRKO BANCHI
4 *
5 * SPDX-License-Identifier: GPL-2.0-only
6 *
7 * Authors: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
8 * Mirko Banchi <mk.banchi@gmail.com>
9 */
10
11#include "sta-wifi-mac.h"
12
15#include "mgt-action-headers.h"
16#include "qos-txop.h"
17#include "snr-tag.h"
18#include "wifi-assoc-manager.h"
19#include "wifi-mac-queue.h"
20#include "wifi-net-device.h"
21#include "wifi-phy.h"
22
23#include "ns3/attribute-container.h"
24#include "ns3/eht-configuration.h"
25#include "ns3/emlsr-manager.h"
26#include "ns3/he-configuration.h"
27#include "ns3/ht-configuration.h"
28#include "ns3/log.h"
29#include "ns3/packet.h"
30#include "ns3/pair.h"
31#include "ns3/pointer.h"
32#include "ns3/random-variable-stream.h"
33#include "ns3/shuffle.h"
34#include "ns3/simulator.h"
35#include "ns3/string.h"
36
37#include <iterator>
38#include <numeric>
39#include <sstream>
40
41namespace ns3
42{
43
44NS_LOG_COMPONENT_DEFINE("StaWifiMac");
45
47
50{
51 static TypeId tid =
52 TypeId("ns3::StaWifiMac")
54 .SetGroupName("Wifi")
55 .AddConstructor<StaWifiMac>()
56 .AddAttribute("ProbeRequestTimeout",
57 "The duration to actively probe the channel.",
58 TimeValue(Seconds(0.05)),
61 .AddAttribute("WaitBeaconTimeout",
62 "The duration to dwell on a channel while passively scanning for beacon",
66 .AddAttribute("AssocRequestTimeout",
67 "The interval between two consecutive association request attempts.",
68 TimeValue(Seconds(0.5)),
71 .AddAttribute("MaxMissedBeacons",
72 "Number of beacons which much be consecutively missed before "
73 "we attempt to restart association.",
74 UintegerValue(10),
77 .AddAttribute("EnableScanning",
78 "If false, STA does not perform channel scanning. This may be useful in "
79 "case of static configuration via the static setup helper.",
80 BooleanValue(true),
83 .AddAttribute(
84 "ActiveProbing",
85 "If true, we send probe requests. If false, we don't."
86 "NOTE: if more than one STA in your simulation is using active probing, "
87 "you should enable it at a different simulation time for each STA, "
88 "otherwise all the STAs will start sending probes at the same time resulting in "
89 "collisions. "
90 "See bug 1060 for more info.",
91 BooleanValue(false),
94 .AddAttribute("ProbeDelay",
95 "Delay (in microseconds) to be used prior to transmitting a "
96 "Probe frame during active scanning.",
97 StringValue("ns3::UniformRandomVariable[Min=50.0|Max=250.0]"),
100 .AddAttribute("AssocType",
101 "Type of association performed by this device (provided that it is "
102 "supported by the standard configured for this device, otherwise legacy "
103 "association is performed). By using this attribute, it is possible for "
104 "an EHT single-link device to perform ML setup with an AP MLD and for an "
105 "EHT multi-link device to perform legacy association with an AP MLD.",
107 TypeId::ATTR_CONSTRUCT, // prevent setting after construction
111 "LEGACY",
113 "ML_SETUP"))
114 .AddAttribute(
115 "PowerSaveMode",
116 "Enable/disable power save mode on the given link. The power management mode is "
117 "actually changed when the AP acknowledges a frame sent with the Power Management "
118 "field set to the value corresponding to the requested mode",
119 TypeId::ATTR_GET | TypeId::ATTR_SET, // do not set at construction time
124 .AddAttribute("PmModeSwitchTimeout",
125 "If switching to a new Power Management mode is not completed within "
126 "this amount of time, make another attempt at switching Power "
127 "Management mode.",
128 TimeValue(Seconds(0.1)),
131 .AddTraceSource("Assoc",
132 "Associated with an access point. If this is an MLD that associated "
133 "with an AP MLD, the AP MLD address is provided.",
135 "ns3::Mac48Address::TracedCallback")
136 .AddTraceSource("LinkSetupCompleted",
137 "A link was setup in the context of ML setup with an AP MLD. "
138 "Provides ID of the setup link and AP MAC address",
140 "ns3::StaWifiMac::LinkSetupCallback")
141 .AddTraceSource("DeAssoc",
142 "Association with an access point lost. If this is an MLD "
143 "that disassociated with an AP MLD, the AP MLD address is provided.",
145 "ns3::Mac48Address::TracedCallback")
146 .AddTraceSource("BeaconArrival",
147 "Time of beacons arrival from associated AP",
149 "ns3::Time::TracedCallback")
150 .AddTraceSource("ReceivedBeaconInfo",
151 "Information about every received Beacon frame",
153 "ns3::ApInfo::TracedCallback")
154 .AddTraceSource("EmlsrLinkSwitch",
155 "Trace start/end of EMLSR link switch events. Specifically, this trace "
156 "is fired: (i) when a PHY _operating on a link_ starts switching to "
157 "another link, thus the PHY is disconnected from the previous link; "
158 "(ii) when a PHY is connected to a new link after performing a channel "
159 "switch. This trace provides: the ID of the previous link, in "
160 "case the PHY is disconnected, or the ID of the new link, in case the "
161 "PHY is connected; a pointer to the PHY that switches link; a boolean "
162 "value indicating if the PHY is connected to (true) or disconnected "
163 "from (false) the given link.",
165 "ns3::StaWifiMac::EmlsrLinkSwitchCallback");
166 return tid;
167}
168
171 m_aid(0),
173{
174 NS_LOG_FUNCTION(this);
175
176 // Let the lower layers know that we are acting as a non-AP STA in
177 // an infrastructure BSS.
179}
180
181void
183{
184 NS_LOG_FUNCTION(this);
185 // an EMLSR client must perform ML setup by using its main PHY
187 {
188 auto mainPhyId = m_emlsrManager->GetMainPhyId();
189 auto linkId = GetLinkForPhy(mainPhyId);
190 NS_ASSERT(linkId);
191 m_assocManager->SetAttribute(
192 "AllowedLinks",
193 AttributeContainerValue<UintegerValue>(std::list<uint8_t>{*linkId}));
194 }
195 if (m_emlsrManager)
196 {
197 m_emlsrManager->Initialize();
198 }
202}
203
204void
206{
207 NS_LOG_FUNCTION(this);
208 if (m_assocManager)
209 {
210 m_assocManager->Dispose();
211 }
212 m_assocManager = nullptr;
213 if (m_emlsrManager)
214 {
215 m_emlsrManager->Dispose();
216 }
217 m_emlsrManager = nullptr;
218 for (auto& [phyId, event] : m_emlsrLinkSwitch)
219 {
220 event.Cancel();
221 }
222 m_emlsrLinkSwitch.clear();
224}
225
230
231void
236
241
242std::unique_ptr<WifiMac::LinkEntity>
244{
245 return std::make_unique<StaLinkEntity>();
246}
247
249StaWifiMac::GetLink(uint8_t linkId) const
250{
251 return static_cast<StaLinkEntity&>(WifiMac::GetLink(linkId));
252}
253
255StaWifiMac::GetStaLink(const std::unique_ptr<WifiMac::LinkEntity>& link) const
256{
257 return static_cast<StaLinkEntity&>(*link);
258}
259
260int64_t
262{
263 NS_LOG_FUNCTION(this << stream);
264 m_probeDelay->SetStream(stream);
265 auto currentStream = stream + 1;
266 currentStream += WifiMac::AssignStreams(currentStream);
267 return (currentStream - stream);
268}
269
270void
272{
273 NS_LOG_FUNCTION(this << assocManager);
274 m_assocManager = assocManager;
275 m_assocManager->SetStaWifiMac(this);
276}
277
280{
281 // non-EHT devices can only perform legacy association
283}
284
285void
287{
288 NS_LOG_FUNCTION(this << emlsrManager);
289 m_emlsrManager = emlsrManager;
290 m_emlsrManager->SetWifiMac(this);
291}
292
295{
296 return m_emlsrManager;
297}
298
299uint16_t
301{
302 NS_ASSERT_MSG(IsAssociated(), "This station is not associated to any AP");
303 return m_aid;
304}
305
306void
308{
309 NS_LOG_FUNCTION(this << enable);
310 m_activeProbing = enable;
311 if (m_state == SCANNING)
312 {
313 NS_LOG_DEBUG("STA is still scanning, reset scanning process");
315 }
316}
317
318bool
320{
321 return m_activeProbing;
322}
323
324void
325StaWifiMac::SetWifiPhys(const std::vector<Ptr<WifiPhy>>& phys)
326{
327 NS_LOG_FUNCTION(this);
329 for (auto& phy : phys)
330 {
331 phy->SetCapabilitiesChangedCallback(
333 }
334}
335
337StaWifiMac::GetCurrentChannel(uint8_t linkId) const
338{
339 auto phy = GetWifiPhy(linkId);
340 const auto width = phy->GetOperatingChannel().IsOfdm() ? MHz_u{20} : phy->GetChannelWidth();
341 uint8_t ch = phy->GetPrimaryChannelNumber(width);
342 return {ch, phy->GetPhyBand()};
343}
344
345void
346StaWifiMac::NotifyEmlsrModeChanged(const std::set<uint8_t>& linkIds)
347{
348 std::stringstream ss;
349 if (g_log.IsEnabled(ns3::LOG_FUNCTION))
350 {
351 std::copy(linkIds.cbegin(), linkIds.cend(), std::ostream_iterator<uint16_t>(ss, " "));
352 }
353 NS_LOG_FUNCTION(this << ss.str());
354
355 for (const auto& [linkId, lnk] : GetLinks())
356 {
357 auto& link = GetStaLink(lnk);
358
359 if (linkIds.contains(linkId))
360 {
361 // EMLSR mode enabled
362 link.emlsrEnabled = true;
363 link.pmMode = WIFI_PM_ACTIVE;
364 }
365 else
366 {
367 // EMLSR mode disabled
368 if (link.emlsrEnabled)
369 {
370 link.pmMode = WIFI_PM_POWERSAVE;
371 }
372 link.emlsrEnabled = false;
373 }
374 }
375}
376
377bool
378StaWifiMac::IsEmlsrLink(uint8_t linkId) const
379{
380 return GetLink(linkId).emlsrEnabled;
381}
382
384StaWifiMac::GetProbeRequest(uint8_t linkId) const
385{
387 probe.Get<Ssid>() = GetSsid();
388 auto supportedRates = GetSupportedRates(linkId);
389 probe.Get<SupportedRates>() = supportedRates.rates;
390 probe.Get<ExtendedSupportedRatesIE>() = supportedRates.extendedRates;
391 if (GetWifiPhy(linkId)->GetPhyBand() == WIFI_PHY_BAND_2_4GHZ)
392 {
393 DsssParameterSet params;
394 params.SetCurrentChannel(GetWifiPhy(linkId)->GetChannelNumber());
395 probe.Get<DsssParameterSet>() = params;
396 }
397 if (GetHtSupported(linkId))
398 {
400 probe.Get<HtCapabilities>() = GetHtCapabilities(linkId);
401 }
402 if (GetVhtSupported(linkId))
403 {
404 probe.Get<VhtCapabilities>() = GetVhtCapabilities(linkId);
405 }
406 if (GetHeSupported())
407 {
408 probe.Get<HeCapabilities>() = GetHeCapabilities(linkId);
409 if (Is6GhzBand(linkId))
410 {
412 }
413 }
414 if (GetEhtSupported())
415 {
416 probe.Get<EhtCapabilities>() = GetEhtCapabilities(linkId);
417 }
418 return probe;
419}
420
423 const std::vector<uint8_t>& apLinkIds,
424 std::optional<uint8_t> apMldId) const
425{
426 NS_LOG_FUNCTION(this << linkId << apMldId.has_value());
427 auto req = GetProbeRequest(linkId);
428
430 {
431 NS_LOG_DEBUG("Legacy association, not including Multi-link Element");
432 return req;
433 }
434
435 req.Get<MultiLinkElement>() = GetProbeReqMultiLinkElement(apLinkIds, apMldId);
436 return req;
437}
438
439void
441 uint8_t linkId,
442 const Mac48Address& addr1,
443 const Mac48Address& addr3)
444{
445 NS_LOG_FUNCTION(this << linkId << addr1 << addr3);
447 hdr.SetAddr1(addr1);
449 hdr.SetAddr3(addr3);
450 hdr.SetDsNotFrom();
451 hdr.SetDsNotTo();
452
453 auto packet = Create<Packet>();
454 packet->AddHeader(probeReq);
455
456 if (!GetQosSupported())
457 {
458 GetTxop()->Queue(Create<WifiMpdu>(packet, hdr));
459 }
460 // "A QoS STA that transmits a Management frame determines access category used
461 // for medium access in transmission of the Management frame as follows
462 // (If dot11QMFActivated is false or not present)
463 // — If the Management frame is individually addressed to a non-QoS STA, category
464 // AC_BE should be selected.
465 // — If category AC_BE was not selected by the previous step, category AC_VO
466 // shall be selected." (Sec. 10.2.3.2 of 802.11-2020)
467 else
468 {
469 GetVOQueue()->Queue(Create<WifiMpdu>(packet, hdr));
470 }
471}
472
473std::variant<MgtAssocRequestHeader, MgtReassocRequestHeader>
474StaWifiMac::GetAssociationRequest(bool isReassoc, uint8_t linkId) const
475{
476 NS_LOG_FUNCTION(this << isReassoc << +linkId);
477
478 std::variant<MgtAssocRequestHeader, MgtReassocRequestHeader> mgtFrame;
479
480 if (isReassoc)
481 {
483 reassoc.SetCurrentApAddress(GetBssid(linkId));
484 mgtFrame = std::move(reassoc);
485 }
486 else
487 {
488 mgtFrame = MgtAssocRequestHeader();
489 }
490
491 // lambda to set the fields of the (Re)Association Request
492 auto fill = [&](auto&& frame) {
493 frame.template Get<Ssid>() = GetSsid();
494 auto supportedRates = GetSupportedRates(linkId);
495 frame.template Get<SupportedRates>() = supportedRates.rates;
496 frame.template Get<ExtendedSupportedRatesIE>() = supportedRates.extendedRates;
497 frame.Capabilities() = GetCapabilities(linkId);
498 frame.SetListenInterval(0);
499 if (GetHtSupported(linkId))
500 {
501 frame.template Get<ExtendedCapabilities>() = GetExtendedCapabilities();
502 frame.template Get<HtCapabilities>() = GetHtCapabilities(linkId);
503 }
504 if (GetVhtSupported(linkId))
505 {
506 frame.template Get<VhtCapabilities>() = GetVhtCapabilities(linkId);
507 }
508 if (GetHeSupported())
509 {
510 frame.template Get<HeCapabilities>() = GetHeCapabilities(linkId);
511 if (Is6GhzBand(linkId))
512 {
513 frame.template Get<He6GhzBandCapabilities>() = GetHe6GhzBandCapabilities(linkId);
514 }
515 }
516 if (GetEhtSupported())
517 {
518 frame.template Get<EhtCapabilities>() = GetEhtCapabilities(linkId);
519 }
520 };
521
522 std::visit(fill, mgtFrame);
523 return mgtFrame;
524}
525
527StaWifiMac::GetBasicMultiLinkElement(bool isReassoc, uint8_t linkId) const
528{
529 NS_LOG_FUNCTION(this << isReassoc << +linkId);
530
532 // The Common info field of the Basic Multi-Link element carried in the (Re)Association
533 // Request frame shall include the MLD MAC address, the MLD Capabilities and Operations,
534 // and the EML Capabilities subfields, and shall not include the Link ID Info, the BSS
535 // Parameters Change Count, and the Medium Synchronization Delay Information subfields
536 // (Sec. 35.3.5.4 of 802.11be D2.0)
537 // TODO Add the MLD Capabilities and Operations subfield
538 multiLinkElement.SetMldMacAddress(GetAddress());
539
540 if (m_emlsrManager) // EMLSR Manager is only installed if EMLSR is activated
541 {
542 multiLinkElement.SetEmlsrSupported(true);
543 TimeValue time;
544 m_emlsrManager->GetAttribute("EmlsrPaddingDelay", time);
545 multiLinkElement.SetEmlsrPaddingDelay(time.Get());
546 m_emlsrManager->GetAttribute("EmlsrTransitionDelay", time);
547 multiLinkElement.SetEmlsrTransitionDelay(time.Get());
548 // When the Transition Timeout subfield is included in a frame sent by a non-AP STA
549 // affiliated with a non-AP MLD, the Transition Timeout subfield is reserved
550 // (Section 9.4.2.312.2.3 of 802.11be D2.3)
551 // The Medium Synchronization Delay Information subfield in the Common Info subfield is
552 // not present if the Basic Multi-Link element is sent by a non-AP STA. (Section
553 // 9.4.2.312.2.3 of 802.11be D3.1)
554 }
555
556 // The MLD Capabilities And Operations subfield is present in the Common Info field of the
557 // Basic Multi-Link element carried in Beacon, Probe Response, (Re)Association Request, and
558 // (Re)Association Response frames. (Sec. 9.4.2.312.2.3 of 802.11be D3.1)
559 auto& mldCapabilities = multiLinkElement.GetCommonInfoBasic().m_mldCapabilities;
560 mldCapabilities.emplace();
561 mldCapabilities->maxNSimultaneousLinks = GetNLinks() - 1; // assuming STR for now
562 mldCapabilities->srsSupport = 0;
563
564 auto ehtConfiguration = GetEhtConfiguration();
565 NS_ASSERT(ehtConfiguration);
566
567 mldCapabilities->tidToLinkMappingSupport =
568 static_cast<uint8_t>(ehtConfiguration->m_tidLinkMappingSupport);
569 mldCapabilities->freqSepForStrApMld = 0; // not supported yet
570 mldCapabilities->aarSupport = 0; // not supported yet
571
572 // For each requested link in addition to the link on which the (Re)Association Request
573 // frame is transmitted, the Link Info field of the Basic Multi-Link element carried
574 // in the (Re)Association Request frame shall contain the corresponding Per-STA Profile
575 // subelement(s).
576 for (const auto& [index, link] : GetLinks())
577 {
578 const auto& staLink = GetStaLink(link);
579
580 if (index != linkId && staLink.bssid.has_value())
581 {
582 multiLinkElement.AddPerStaProfileSubelement();
583 auto& perStaProfile = multiLinkElement.GetPerStaProfile(
584 multiLinkElement.GetNPerStaProfileSubelements() - 1);
585 // The Link ID subfield of the STA Control field of the Per-STA Profile subelement
586 // for the corresponding non-AP STA that requests a link for multi-link (re)setup
587 // with the AP MLD is set to the link ID of the AP affiliated with the AP MLD that
588 // is operating on that link. The link ID is obtained during multi-link discovery
589 perStaProfile.SetLinkId(index);
590 // For each Per-STA Profile subelement included in the Link Info field, the
591 // Complete Profile subfield of the STA Control field shall be set to 1
592 perStaProfile.SetCompleteProfile();
593 // The MAC Address Present subfield indicates the presence of the STA MAC Address
594 // subfield in the STA Info field and is set to 1 if the STA MAC Address subfield
595 // is present in the STA Info field; otherwise set to 0. An STA sets this subfield
596 // to 1 when the element carries complete profile.
597 perStaProfile.SetStaMacAddress(staLink.feManager->GetAddress());
598 perStaProfile.SetAssocRequest(GetAssociationRequest(isReassoc, index));
599 }
600 }
601
602 return multiLinkElement;
603}
604
606StaWifiMac::GetProbeReqMultiLinkElement(const std::vector<uint8_t>& apLinkIds,
607 std::optional<uint8_t> apMldId) const
608{
609 // IEEE 802.11be D6.0 9.4.2.321.3
611 if (apMldId.has_value())
612 {
613 mle.SetApMldId(*apMldId);
614 }
615
616 for (const auto apLinkId : apLinkIds)
617 {
619 auto& perStaProfile = mle.GetPerStaProfile(mle.GetNPerStaProfileSubelements() - 1);
620 perStaProfile.SetLinkId(apLinkId);
621 // Current support limited to Complete Profile request per link ID
622 // TODO: Add support for Partial Per-STA Profile request
623 perStaProfile.SetCompleteProfile();
624 };
625
626 return mle;
627}
628
629std::vector<TidToLinkMapping>
631{
632 NS_LOG_FUNCTION(this << apNegSupport);
633
634 auto ehtConfig = GetEhtConfiguration();
635 NS_ASSERT(ehtConfig);
636
637 auto negSupport = ehtConfig->m_tidLinkMappingSupport;
638
640 "Cannot request TID-to-Link Mapping if negotiation is not supported");
641
642 // store the mappings, so that we can enforce them when the AP MLD accepts them
643 m_dlTidLinkMappingInAssocReq = ehtConfig->GetTidLinkMapping(WifiDirection::DOWNLINK);
644 m_ulTidLinkMappingInAssocReq = ehtConfig->GetTidLinkMapping(WifiDirection::UPLINK);
645
649 negSupport == WifiTidToLinkMappingNegSupport::SAME_LINK_SET && !mappingValidForNegType1,
650 "Mapping TIDs to distinct link sets is incompatible with negotiation support of 1");
651
652 if (apNegSupport == WifiTidToLinkMappingNegSupport::SAME_LINK_SET && !mappingValidForNegType1)
653 {
654 // If the TID-to-link Mapping Negotiation Support subfield value received from a peer
655 // MLD is equal to 1, the MLD that initiates a TID-to-link mapping negotiation with the
656 // peer MLD shall send only the TID-to-link Mapping element where all TIDs are mapped to
657 // the same link set (Sec. 35.3.7.1.3 of 802.11be D3.1). We use default mapping to meet
658 // this requirement.
659 NS_LOG_DEBUG("Using default mapping because AP MLD advertised negotiation support of 1");
662 }
663
664 std::vector<TidToLinkMapping> ret(1);
665
666 ret.back().m_control.direction = WifiDirection::DOWNLINK;
667
668 // lambda to fill the last TID-to-Link Mapping IE in the vector to return
669 auto fillIe = [&ret](const auto& mapping) {
670 ret.back().m_control.defaultMapping = mapping.empty();
671
672 for (const auto& [tid, linkSet] : mapping)
673 {
674 // At any point in time, a TID shall always be mapped to at least one setup link both
675 // in DL and UL, which means that a TID-to-link mapping change is only valid and
676 // successful if it will not result in having any TID for which the link set for DL
677 // or UL is made of zero setup links (Sec. 35.3.7.1.1 of 802.11be D3.1)
678 NS_ABORT_MSG_IF(linkSet.empty(), "Cannot map a TID to an empty link set");
679 ret.back().SetLinkMappingOfTid(tid, linkSet);
680 }
681 };
682
684
686 {
687 ret.back().m_control.direction = WifiDirection::BOTH_DIRECTIONS;
688 return ret;
689 }
690
691 ret.emplace_back();
692 ret.back().m_control.direction = WifiDirection::UPLINK;
694
695 return ret;
696}
697
698void
700{
701 // find the link where the (Re)Association Request has to be sent
702 auto it = GetLinks().cbegin();
703 while (it != GetLinks().cend())
704 {
705 if (GetStaLink(it->second).sendAssocReq)
706 {
707 break;
708 }
709 it++;
710 }
711 NS_ABORT_MSG_IF(it == GetLinks().cend(),
712 "No link selected to send the (Re)Association Request");
713 uint8_t linkId = it->first;
714 auto& link = GetLink(linkId);
715 NS_ABORT_MSG_IF(!link.bssid.has_value(),
716 "No BSSID set for the link on which the (Re)Association Request is to be sent");
717
718 NS_LOG_FUNCTION(this << *link.bssid << isReassoc);
719 WifiMacHeader hdr;
721 hdr.SetAddr1(*link.bssid);
722 hdr.SetAddr2(link.feManager->GetAddress());
723 hdr.SetAddr3(*link.bssid);
724 hdr.SetDsNotFrom();
725 hdr.SetDsNotTo();
726 Ptr<Packet> packet = Create<Packet>();
727
728 auto frame = GetAssociationRequest(isReassoc, linkId);
729
730 // include a Multi-Link Element if this device performs ML Setup and the AP is a multi-link
731 // device; if the AP MLD has indicated a support of TID-to-link mapping negotiation, also
732 // include the TID-to-link Mapping element(s)
734 GetWifiRemoteStationManager(linkId)->GetMldAddress(*link.bssid).has_value())
735 {
736 auto addMle = [&](auto&& frame) {
737 frame.template Get<MultiLinkElement>() = GetBasicMultiLinkElement(isReassoc, linkId);
738 };
739 std::visit(addMle, frame);
740
742 if (const auto& mldCapabilities =
743 GetWifiRemoteStationManager(linkId)->GetStationMldCapabilities(*link.bssid);
744 mldCapabilities && (negSupport = static_cast<WifiTidToLinkMappingNegSupport>(
745 mldCapabilities->get().tidToLinkMappingSupport)) >
747 {
748 auto addTlm = [&](auto&& frame) {
749 frame.template Get<TidToLinkMapping>() = GetTidToLinkMappingElements(negSupport);
750 };
751 std::visit(addTlm, frame);
752 }
753 }
754
755 if (!isReassoc)
756 {
757 packet->AddHeader(std::get<MgtAssocRequestHeader>(frame));
758 }
759 else
760 {
761 packet->AddHeader(std::get<MgtReassocRequestHeader>(frame));
762 }
763
764 if (!GetQosSupported())
765 {
766 GetTxop()->Queue(Create<WifiMpdu>(packet, hdr));
767 }
768 // "A QoS STA that transmits a Management frame determines access category used
769 // for medium access in transmission of the Management frame as follows
770 // (If dot11QMFActivated is false or not present)
771 // — If the Management frame is individually addressed to a non-QoS STA, category
772 // AC_BE should be selected.
773 // — If category AC_BE was not selected by the previous step, category AC_VO
774 // shall be selected." (Sec. 10.2.3.2 of 802.11-2020)
775 else if (!GetWifiRemoteStationManager(linkId)->GetQosSupported(*link.bssid))
776 {
777 GetBEQueue()->Queue(Create<WifiMpdu>(packet, hdr));
778 }
779 else
780 {
781 GetVOQueue()->Queue(Create<WifiMpdu>(packet, hdr));
782 }
783
784 if (m_assocRequestEvent.IsPending())
785 {
786 m_assocRequestEvent.Cancel();
787 }
790}
791
792void
794{
795 NS_LOG_FUNCTION(this);
796 switch (m_state)
797 {
798 case ASSOCIATED:
799 return;
800 case SCANNING:
801 /* we have initiated active or passive scanning, continue to wait
802 and gather beacons or probe responses until the scanning timeout
803 */
804 break;
805 case UNASSOCIATED:
806 /* we were associated but we missed a bunch of beacons
807 * so we should assume we are not associated anymore.
808 * We try to initiate a scan now.
809 */
810 m_linkDown();
812 break;
813 case WAIT_ASSOC_RESP:
814 /* we have sent an association request so we do not need to
815 re-send an association request right now. We just need to
816 wait until either assoc-request-timeout or until
817 we get an association response.
818 */
819 case REFUSED:
820 /* we have sent an association request and received a negative
821 association response. We wait until someone restarts an
822 association with a given SSID.
823 */
824 break;
825 }
826}
827
828void
830{
831 NS_LOG_FUNCTION(this);
832 if (!m_enableScanning)
833 {
834 NS_LOG_DEBUG("Scanning disabled");
835 return;
836 }
839
840 WifiScanParams scanParams;
841 scanParams.ssid = GetSsid();
842 for (const auto& [id, link] : GetLinks())
843 {
845 (link->phy->HasFixedPhyBand()) ? WifiScanParams::Channel{0, link->phy->GetPhyBand()}
847
848 scanParams.channelList.push_back(channel);
849 }
850 if (m_activeProbing)
851 {
852 scanParams.type = WifiScanType::ACTIVE;
853 scanParams.probeDelay = MicroSeconds(m_probeDelay->GetValue());
854 scanParams.minChannelTime = scanParams.maxChannelTime = m_probeRequestTimeout;
855 }
856 else
857 {
858 scanParams.type = WifiScanType::PASSIVE;
860 }
861
862 m_assocManager->StartScanning(std::move(scanParams));
863}
864
865void
866StaWifiMac::ScanningTimeout(const std::optional<ApInfo>& bestAp)
867{
868 NS_LOG_FUNCTION(this);
869
870 if (!bestAp.has_value())
871 {
872 NS_LOG_DEBUG("Exhausted list of candidate AP; restart scanning");
874 return;
875 }
876
877 NS_LOG_DEBUG("Attempting to associate with AP: " << *bestAp);
878 ApplyOperationalSettings(bestAp->m_frame, bestAp->m_apAddr, bestAp->m_bssid, bestAp->m_linkId);
879 // reset info on links to setup
880 for (auto& [id, link] : GetLinks())
881 {
882 auto& staLink = GetStaLink(link);
883 staLink.sendAssocReq = false;
884 staLink.bssid = std::nullopt;
885 }
886 // send Association Request on the link where the Beacon/Probe Response was received
887 GetLink(bestAp->m_linkId).sendAssocReq = true;
888 GetLink(bestAp->m_linkId).bssid = bestAp->m_bssid;
889 std::shared_ptr<CommonInfoBasicMle> mleCommonInfo;
890 // update info on links to setup (11be MLDs only)
891 const auto& mle =
892 std::visit([](auto&& frame) { return frame.template Get<MultiLinkElement>(); },
893 bestAp->m_frame);
894 std::map<uint8_t, uint8_t> swapInfo;
895 for (const auto& [localLinkId, apLinkId, bssid] : bestAp->m_setupLinks)
896 {
897 NS_ASSERT_MSG(mle, "We get here only for ML setup");
898 NS_LOG_DEBUG("Setting up link (local ID=" << +localLinkId << ", AP ID=" << +apLinkId
899 << ")");
900 GetLink(localLinkId).bssid = bssid;
901 if (!mleCommonInfo)
902 {
903 mleCommonInfo = std::make_shared<CommonInfoBasicMle>(mle->GetCommonInfoBasic());
904 }
905 GetWifiRemoteStationManager(localLinkId)->AddStationMleCommonInfo(bssid, mleCommonInfo);
906 swapInfo.emplace(localLinkId, apLinkId);
907 }
908
909 SwapLinks(swapInfo);
910
911 // lambda to get beacon interval from Beacon or Probe Response
912 auto getBeaconInterval = [](auto&& frame) {
913 using T = std::decay_t<decltype(frame)>;
914 if constexpr (std::is_same_v<T, MgtBeaconHeader> ||
915 std::is_same_v<T, MgtProbeResponseHeader>)
916 {
917 return MicroSeconds(frame.GetBeaconIntervalUs());
918 }
919 else
920 {
921 NS_ABORT_MSG("Unexpected frame type");
922 return Seconds(0);
923 }
924 };
925 Time beaconInterval = std::visit(getBeaconInterval, bestAp->m_frame);
926 Time delay = beaconInterval * m_maxMissedBeacons;
927 // restart beacon watchdog
929
932}
933
934void
941
942void
944{
945 NS_LOG_FUNCTION(this);
946
948 {
949 if (m_beaconWatchdog.IsPending())
950 {
951 m_beaconWatchdog.Cancel();
952 }
955 this);
956 return;
957 }
958 NS_LOG_DEBUG("beacon missed");
959 // We need to switch to the UNASSOCIATED state. However, if we are receiving a frame, wait
960 // until the RX is completed (otherwise, crashes may occur if we are receiving a MU frame
961 // because its reception requires the STA-ID). We need to check that a PHY is operating on
962 // the given link, because this may (temporarily) not be the case for EMLSR clients.
963 Time delay;
964 for (const auto& [id, link] : GetLinks())
965 {
966 if (link->phy && link->phy->IsStateRx())
967 {
968 delay = std::max(delay, link->phy->GetDelayUntilIdle());
969 }
970 }
972}
973
974void
976{
977 NS_LOG_FUNCTION(this);
978
979 Mac48Address apAddr; // the AP address to trace (MLD address in case of ML setup)
980
981 for (const auto& [id, link] : GetLinks())
982 {
983 auto& bssid = GetStaLink(link).bssid;
984 if (bssid)
985 {
986 apAddr = GetWifiRemoteStationManager(id)->GetMldAddress(*bssid).value_or(*bssid);
987 }
988 bssid = std::nullopt; // link is no longer setup
989 }
990
991 NS_LOG_DEBUG("Set state to UNASSOCIATED and start scanning");
993 // cancel the association request timer (see issue #862)
994 m_assocRequestEvent.Cancel();
995 m_deAssocLogger(apAddr);
996 m_aid = 0; // reset AID
998}
999
1000void
1002{
1003 NS_LOG_FUNCTION(this << delay);
1004
1006 if (Simulator::GetDelayLeft(m_beaconWatchdog) < delay && m_beaconWatchdog.IsExpired())
1007 {
1008 NS_LOG_DEBUG("really restart watchdog.");
1010 }
1011}
1012
1013bool
1015{
1016 return m_state == ASSOCIATED;
1017}
1018
1019bool
1021{
1022 return m_state == WAIT_ASSOC_RESP;
1023}
1024
1025std::set<uint8_t>
1027{
1028 if (!IsAssociated())
1029 {
1030 return {};
1031 }
1032
1033 std::set<uint8_t> linkIds;
1034 for (const auto& [id, link] : GetLinks())
1035 {
1036 if (GetStaLink(link).bssid)
1037 {
1038 linkIds.insert(id);
1039 }
1040 }
1041 return linkIds;
1042}
1043
1046{
1047 for (const auto& [id, link] : GetLinks())
1048 {
1049 if (GetStaLink(link).bssid == remoteAddr)
1050 {
1051 // the remote address is the address of the AP we are associated with;
1052 return link->feManager->GetAddress();
1053 }
1054 }
1055
1056 // the remote address is unknown
1057
1058 if (!IsAssociated())
1059 {
1060 return GetAddress();
1061 }
1062
1063 // if this device has performed ML setup with an AP MLD, return the MLD address of this device
1064 const auto linkIds = GetSetupLinkIds();
1065 NS_ASSERT(!linkIds.empty());
1066 const auto linkId = *linkIds.cbegin(); // a setup link
1067
1068 if (GetLink(linkId).stationManager->GetMldAddress(GetBssid(linkId)))
1069 {
1070 return GetAddress();
1071 }
1072
1073 // return the address of the link used to perform association with the AP
1074 return GetLink(linkId).feManager->GetAddress();
1075}
1076
1077bool
1079{
1080 return IsAssociated();
1081}
1082
1083void
1089
1090void
1092{
1093 NS_LOG_FUNCTION(this << *mpdu << to << from);
1094
1095 auto& hdr = mpdu->GetHeader();
1096
1097 // the Receiver Address (RA) and the Transmitter Address (TA) are the MLD addresses only for
1098 // non-broadcast data frames exchanged between two MLDs
1099 auto linkIds = GetSetupLinkIds();
1100 NS_ASSERT(!linkIds.empty());
1101 uint8_t linkId = *linkIds.begin();
1102 const auto apMldAddr = GetWifiRemoteStationManager(linkId)->GetMldAddress(GetBssid(linkId));
1103
1104 hdr.SetAddr1(apMldAddr.value_or(GetBssid(linkId)));
1105 hdr.SetAddr2(apMldAddr ? GetAddress() : GetFrameExchangeManager(linkId)->GetAddress());
1106 hdr.SetAddr3(to);
1107 hdr.SetDsNotFrom();
1108 hdr.SetDsTo();
1109
1110 auto txop = hdr.IsQosData() ? StaticCast<Txop>(GetQosTxop(hdr.GetQosTid())) : GetTxop();
1111 NS_ASSERT(txop);
1112 txop->Queue(mpdu);
1113}
1114
1115void
1117{
1118 NS_LOG_FUNCTION(this << linkId << reason);
1119
1120 GetMacQueueScheduler()->BlockAllQueues(reason, {linkId});
1121}
1122
1123void
1124StaWifiMac::UnblockTxOnLink(std::set<uint8_t> linkIds, WifiQueueBlockedReason reason)
1125{
1126 // shuffle link IDs not to unblock links always in the same order
1127 std::vector<uint8_t> shuffledLinkIds(linkIds.cbegin(), linkIds.cend());
1128 Shuffle(shuffledLinkIds.begin(), shuffledLinkIds.end(), m_shuffleLinkIdsGen.GetRv());
1129
1130 std::stringstream ss;
1131 if (g_log.IsEnabled(ns3::LOG_FUNCTION))
1132 {
1133 std::copy(shuffledLinkIds.cbegin(),
1134 shuffledLinkIds.cend(),
1135 std::ostream_iterator<uint16_t>(ss, " "));
1136 }
1137 NS_LOG_FUNCTION(this << reason << ss.str());
1138
1139 for (const auto linkId : shuffledLinkIds)
1140 {
1141 std::map<AcIndex, bool> hasFramesToTransmit;
1142 for (const auto& [acIndex, ac] : wifiAcList)
1143 {
1144 // save the status of the AC queues before unblocking the queues
1145 hasFramesToTransmit[acIndex] = GetQosTxop(acIndex)->HasFramesToTransmit(linkId);
1146 }
1147
1148 GetMacQueueScheduler()->UnblockAllQueues(reason, {linkId});
1149
1150 for (const auto& [acIndex, ac] : wifiAcList)
1151 {
1152 // request channel access if needed (schedule now because multiple invocations
1153 // of this method may be done in a loop at the caller)
1155 GetQosTxop(acIndex),
1156 linkId,
1157 hasFramesToTransmit[acIndex],
1158 Txop::CHECK_MEDIUM_BUSY); // generate backoff if medium busy
1159 }
1160 }
1161}
1162
1163void
1165{
1166 NS_LOG_FUNCTION(this << *mpdu << +linkId);
1167 // consider the MAC header of the original MPDU (makes a difference for data frames only)
1168 const WifiMacHeader* hdr = &mpdu->GetOriginal()->GetHeader();
1169 Ptr<const Packet> packet = mpdu->GetPacket();
1170 NS_ASSERT(!hdr->IsCtl());
1172 : GetFrameExchangeManager(linkId)->GetAddress();
1173 if (hdr->GetAddr3() == myAddr)
1174 {
1175 NS_LOG_LOGIC("packet sent by us.");
1176 return;
1177 }
1178 if (hdr->GetAddr1() != myAddr && !hdr->GetAddr1().IsGroup())
1179 {
1180 NS_LOG_LOGIC("packet is not for us");
1181 NotifyRxDrop(packet);
1182 return;
1183 }
1184 if (hdr->IsData())
1185 {
1186 if (!IsAssociated())
1187 {
1188 NS_LOG_LOGIC("Received data frame while not associated: ignore");
1189 NotifyRxDrop(packet);
1190 return;
1191 }
1192 if (!(hdr->IsFromDs() && !hdr->IsToDs()))
1193 {
1194 NS_LOG_LOGIC("Received data frame not from the DS: ignore");
1195 NotifyRxDrop(packet);
1196 return;
1197 }
1198 std::set<Mac48Address> apAddresses; // link addresses of AP
1199 for (auto id : GetSetupLinkIds())
1200 {
1201 apAddresses.insert(GetBssid(id));
1202 }
1203 if (!apAddresses.contains(mpdu->GetHeader().GetAddr2()))
1204 {
1205 NS_LOG_LOGIC("Received data frame not from the BSS we are associated with: ignore");
1206 NotifyRxDrop(packet);
1207 return;
1208 }
1209 if (!hdr->HasData())
1210 {
1211 NS_LOG_LOGIC("Received (QoS) Null Data frame: ignore");
1212 NotifyRxDrop(packet);
1213 return;
1214 }
1215 if (hdr->IsQosData())
1216 {
1217 if (hdr->IsQosAmsdu())
1218 {
1219 NS_ASSERT(apAddresses.contains(mpdu->GetHeader().GetAddr3()));
1221 packet = nullptr;
1222 }
1223 else
1224 {
1225 ForwardUp(packet, hdr->GetAddr3(), hdr->GetAddr1());
1226 }
1227 }
1228 else
1229 {
1230 ForwardUp(packet, hdr->GetAddr3(), hdr->GetAddr1());
1231 }
1232 return;
1233 }
1234
1235 switch (hdr->GetType())
1236 {
1240 // This is a frame aimed at an AP, so we can safely ignore it.
1241 NotifyRxDrop(packet);
1242 break;
1243
1245 ReceiveBeacon(mpdu, linkId);
1246 break;
1247
1249 ReceiveProbeResp(mpdu, linkId);
1250 break;
1251
1254 ReceiveAssocResp(mpdu, linkId);
1255 break;
1256
1258 if (auto [category, action] = WifiActionHeader::Peek(packet);
1259 category == WifiActionHeader::PROTECTED_EHT &&
1260 action.protectedEhtAction ==
1262 {
1263 // this is handled by the EMLSR Manager
1264 break;
1265 }
1266
1267 default:
1268 // Invoke the receive handler of our parent class to deal with any other frames
1269 WifiMac::Receive(mpdu, linkId);
1270 }
1271
1272 if (m_emlsrManager)
1273 {
1274 m_emlsrManager->NotifyMgtFrameReceived(mpdu, linkId);
1275 }
1276}
1277
1278void
1280{
1281 NS_LOG_FUNCTION(this << *mpdu << +linkId);
1282 const WifiMacHeader& hdr = mpdu->GetHeader();
1283 const auto from = hdr.GetAddr2();
1284 NS_ASSERT(hdr.IsBeacon());
1285
1286 NS_LOG_DEBUG("Beacon received");
1287 MgtBeaconHeader beacon;
1288 mpdu->GetPacket()->PeekHeader(beacon);
1289 const auto& capabilities = beacon.Capabilities();
1290 NS_ASSERT(capabilities.IsEss());
1291 bool goodBeacon;
1292 if (IsWaitAssocResp() || IsAssociated())
1293 {
1294 // we have to process this Beacon only if sent by the AP we are associated
1295 // with or from which we are waiting an Association Response frame
1296 auto bssid = GetLink(linkId).bssid;
1297 goodBeacon = bssid.has_value() && (hdr.GetAddr3() == *bssid);
1298 }
1299 else
1300 {
1301 // we retain this Beacon as candidate AP if the supported rates fit the
1302 // configured BSS membership selector
1303 goodBeacon = CheckSupportedRates(beacon, linkId);
1304 }
1305
1306 SnrTag snrTag;
1307 bool found = mpdu->GetPacket()->PeekPacketTag(snrTag);
1308 NS_ASSERT(found);
1309 ApInfo apInfo = {.m_bssid = hdr.GetAddr3(),
1310 .m_apAddr = hdr.GetAddr2(),
1311 .m_snr = snrTag.Get(),
1312 .m_frame = std::move(beacon),
1313 .m_channel = {GetCurrentChannel(linkId)},
1314 .m_linkId = linkId};
1315
1316 if (!m_beaconInfo.IsEmpty())
1317 {
1318 m_beaconInfo(apInfo);
1319 }
1320
1321 RecordCapabilities(beacon, from, linkId);
1322 RecordOperations(beacon, from, linkId);
1323
1324 if (!goodBeacon)
1325 {
1326 NS_LOG_LOGIC("Beacon is not for us");
1327 return;
1328 }
1329 if (m_state == ASSOCIATED)
1330 {
1332 Time delay = MicroSeconds(std::get<MgtBeaconHeader>(apInfo.m_frame).GetBeaconIntervalUs() *
1334 RestartBeaconWatchdog(delay);
1335 ApplyOperationalSettings(apInfo.m_frame, hdr.GetAddr2(), hdr.GetAddr3(), linkId);
1336 }
1337 else
1338 {
1339 NS_LOG_DEBUG("Beacon received from " << hdr.GetAddr2());
1340 m_assocManager->NotifyApInfo(std::move(apInfo));
1341 }
1342}
1343
1344void
1346{
1347 NS_LOG_FUNCTION(this << *mpdu << +linkId);
1348 const WifiMacHeader& hdr = mpdu->GetHeader();
1349 NS_ASSERT(hdr.IsProbeResp());
1350
1351 const auto from = hdr.GetAddr2();
1352 NS_LOG_DEBUG("Probe response received from " << from);
1353 MgtProbeResponseHeader probeResp;
1354 mpdu->GetPacket()->PeekHeader(probeResp);
1355
1356 RecordCapabilities(probeResp, from, linkId);
1357 RecordOperations(probeResp, from, linkId);
1358
1359 if (!CheckSupportedRates(probeResp, linkId))
1360 {
1361 return;
1362 }
1363 SnrTag snrTag;
1364 bool found = mpdu->GetPacket()->PeekPacketTag(snrTag);
1365 NS_ASSERT(found);
1366 m_assocManager->NotifyApInfo(ApInfo{.m_bssid = hdr.GetAddr3(),
1367 .m_apAddr = hdr.GetAddr2(),
1368 .m_snr = snrTag.Get(),
1369 .m_frame = std::move(probeResp),
1370 .m_channel = {GetCurrentChannel(linkId)},
1371 .m_linkId = linkId});
1372}
1373
1374void
1376{
1377 NS_LOG_FUNCTION(this << *mpdu << +linkId);
1378 const WifiMacHeader& hdr = mpdu->GetHeader();
1379 NS_ASSERT(hdr.IsAssocResp() || hdr.IsReassocResp());
1380
1381 MgtAssocResponseHeader assocResp;
1382 mpdu->GetPacket()->PeekHeader(assocResp);
1383
1384 RecordCapabilities(assocResp, hdr.GetAddr2(), linkId);
1385 RecordOperations(assocResp, hdr.GetAddr2(), linkId);
1386
1387 if (m_state != WAIT_ASSOC_RESP)
1388 {
1389 return;
1390 }
1391
1392 if (m_assocRequestEvent.IsPending())
1393 {
1394 m_assocRequestEvent.Cancel();
1395 }
1396
1397 std::optional<Mac48Address> apMldAddress;
1398 if (assocResp.GetStatusCode().IsSuccess())
1399 {
1400 m_aid = assocResp.GetAssociationId();
1401 NS_LOG_DEBUG((hdr.IsReassocResp() ? "reassociation done" : "association completed"));
1402 ApplyOperationalSettings(assocResp, hdr.GetAddr2(), hdr.GetAddr3(), linkId);
1403 NS_ASSERT(GetLink(linkId).bssid.has_value() && *GetLink(linkId).bssid == hdr.GetAddr3());
1404 SetBssid(hdr.GetAddr3(), linkId);
1407 assocResp.Get<MultiLinkElement>().has_value())
1408 {
1409 // this is an ML setup, trace the setup link
1410 m_setupCompleted(linkId, hdr.GetAddr3());
1411 apMldAddress = GetWifiRemoteStationManager(linkId)->GetMldAddress(hdr.GetAddr3());
1412 NS_ASSERT(apMldAddress);
1413
1414 if (const auto& mldCapabilities =
1415 GetWifiRemoteStationManager(linkId)->GetStationMldCapabilities(hdr.GetAddr3());
1416 mldCapabilities && static_cast<WifiTidToLinkMappingNegSupport>(
1417 mldCapabilities->get().tidToLinkMappingSupport) >
1419 {
1420 // the AP MLD supports TID-to-Link Mapping negotiation, hence we included
1421 // TID-to-Link Mapping element(s) in the Association Request.
1422 if (assocResp.Get<TidToLinkMapping>().empty())
1423 {
1424 // The AP MLD did not include a TID-to-Link Mapping element in the Association
1425 // Response, hence it accepted the mapping, which we can now store.
1426 UpdateTidToLinkMapping(*apMldAddress,
1429 UpdateTidToLinkMapping(*apMldAddress,
1432
1433 // Apply the negotiated TID-to-Link Mapping (if any) for UL direction
1435 }
1436 }
1437 }
1438 else
1439 {
1440 m_assocLogger(hdr.GetAddr3());
1441 }
1442 if (!m_linkUp.IsNull())
1443 {
1444 m_linkUp();
1445 }
1446 }
1447 else
1448 {
1449 // If the link on which the (Re)Association Request frame was received cannot be
1450 // accepted by the AP MLD, the AP MLD shall treat the multi-link (re)setup as a
1451 // failure and shall not accept any requested links. If the link on which the
1452 // (Re)Association Request frame was received is accepted by the AP MLD, the
1453 // multi-link (re)setup is successful. (Sec. 35.3.5.1 of 802.11be D3.1)
1454 NS_LOG_DEBUG("association refused");
1456 StartScanning();
1457 return;
1458 }
1459
1460 // create a list of all local Link IDs. IDs are removed as we find a corresponding
1461 // Per-STA Profile Subelements indicating successful association. Links with
1462 // remaining IDs are not setup
1463 std::list<uint8_t> setupLinks;
1464 for (const auto& [id, link] : GetLinks())
1465 {
1466 setupLinks.push_back(id);
1467 }
1468 if (assocResp.GetStatusCode().IsSuccess())
1469 {
1470 setupLinks.remove(linkId);
1471 }
1472
1473 // if a Multi-Link Element is present, this is an ML setup, hence check if we can setup (other)
1474 // links
1475 if (const auto& mle = assocResp.Get<MultiLinkElement>())
1476 {
1477 NS_ABORT_MSG_IF(!GetLink(linkId).bssid.has_value(),
1478 "The link on which the Association Response was received "
1479 "is not a link we requested to setup");
1480 NS_ABORT_MSG_IF(linkId != mle->GetLinkIdInfo(),
1481 "The link ID of the AP that transmitted the Association "
1482 "Response does not match the stored link ID");
1484 mle->GetMldMacAddress(),
1485 "The AP MLD MAC address in the received Multi-Link Element does not "
1486 "match the address stored in the station manager for link "
1487 << +linkId);
1488 // process the Per-STA Profile Subelements in the Multi-Link Element
1489 for (std::size_t elem = 0; elem < mle->GetNPerStaProfileSubelements(); elem++)
1490 {
1491 auto& perStaProfile = mle->GetPerStaProfile(elem);
1492 uint8_t apLinkId = perStaProfile.GetLinkId();
1493 auto it = GetLinks().find(apLinkId);
1494 uint8_t staLinkid = 0;
1495 std::optional<Mac48Address> bssid;
1496 NS_ABORT_MSG_IF(it == GetLinks().cend() ||
1497 !(bssid = GetLink((staLinkid = it->first)).bssid).has_value(),
1498 "Setup for AP link ID " << apLinkId << " was not requested");
1499 NS_ABORT_MSG_IF(*bssid != perStaProfile.GetStaMacAddress(),
1500 "The BSSID in the Per-STA Profile for link ID "
1501 << +staLinkid << " does not match the stored BSSID");
1503 perStaProfile.GetStaMacAddress()) != mle->GetMldMacAddress(),
1504 "The AP MLD MAC address in the received Multi-Link Element does not "
1505 "match the address stored in the station manager for link "
1506 << +staLinkid);
1507 // process the Association Response contained in this Per-STA Profile
1508 MgtAssocResponseHeader assoc = perStaProfile.GetAssocResponse();
1509 RecordCapabilities(assoc, *bssid, staLinkid);
1510 RecordOperations(assoc, *bssid, staLinkid);
1511 if (assoc.GetStatusCode().IsSuccess())
1512 {
1513 NS_ABORT_MSG_IF(m_aid != 0 && m_aid != assoc.GetAssociationId(),
1514 "AID should be the same for all the links");
1515 m_aid = assoc.GetAssociationId();
1516 NS_LOG_DEBUG("Setup on link " << staLinkid << " completed");
1517 ApplyOperationalSettings(assocResp, *bssid, *bssid, staLinkid);
1518 SetBssid(*bssid, staLinkid);
1519 m_setupCompleted(staLinkid, *bssid);
1521 apMldAddress = GetWifiRemoteStationManager(staLinkid)->GetMldAddress(*bssid);
1522 if (!m_linkUp.IsNull())
1523 {
1524 m_linkUp();
1525 }
1526 }
1527 // remove the ID of the link we setup
1528 setupLinks.remove(staLinkid);
1529 }
1530 if (apMldAddress)
1531 {
1532 // this is an ML setup, trace the MLD address of the AP (only once)
1533 m_assocLogger(*apMldAddress);
1534 }
1535 }
1536 // remaining links in setupLinks are not setup and hence must be disabled
1537 for (const auto& id : setupLinks)
1538 {
1539 GetLink(id).bssid = std::nullopt;
1540 GetLink(id).phy->SetOffMode();
1541 }
1542
1543 // the station that associated with the AP may have dissociated and then associated again.
1544 // In this case, the station may store packets from the previous period in which it was
1545 // associated. Have the station restart access if it has packets queued.
1546 for (const auto& [id, link] : GetLinks())
1547 {
1548 if (GetStaLink(link).bssid)
1549 {
1550 if (const auto txop = GetTxop())
1551 {
1552 txop->StartAccessAfterEvent(id,
1555 }
1556 for (const auto& [acIndex, ac] : wifiAcList)
1557 {
1558 if (const auto edca = GetQosTxop(acIndex))
1559 {
1560 edca->StartAccessAfterEvent(id,
1563 }
1564 }
1565 }
1566 }
1567
1569}
1570
1571void
1573{
1574 NS_LOG_FUNCTION(this << linkId);
1575
1576 // STAs operating on setup links may need to transition to a new PM mode after the
1577 // acknowledgement of the Association Response. For this purpose, we connect a callback to
1578 // the PHY TX begin trace to catch the Ack transmitted after the Association Response.
1580 [=, this](WifiConstPsduMap psduMap, WifiTxVector txVector, Watt_u /* txPower */) {
1581 NS_ASSERT_MSG(psduMap.size() == 1 && psduMap.begin()->second->GetNMpdus() == 1 &&
1582 psduMap.begin()->second->GetHeader(0).IsAck(),
1583 "Expected a Normal Ack after Association Response frame");
1584
1585 auto ackDuration =
1586 WifiPhy::CalculateTxDuration(psduMap, txVector, GetLink(linkId).phy->GetPhyBand());
1587
1588 for (const auto& [id, lnk] : GetLinks())
1589 {
1590 auto& link = GetStaLink(lnk);
1591
1592 if (!link.bssid)
1593 {
1594 // link has not been setup
1595 continue;
1596 }
1597
1598 if (id == linkId)
1599 {
1600 /**
1601 * When a link becomes enabled for a non-AP STA that is affiliated with a
1602 * non-AP MLD after successful association with an AP MLD with (Re)Association
1603 * Request/Response frames transmitted on that link [..], the power management
1604 * mode of the non-AP STA, immediately after the acknowledgement of the
1605 * (Re)Association Response frame [..], is active mode.
1606 * (Sec. 35.3.7.1.4 of 802.11be D3.0)
1607 */
1608 // if the user requested this link to be in powersave mode, we have to
1609 // switch PM mode
1610 if (link.pmMode == WIFI_PM_POWERSAVE)
1611 {
1612 Simulator::Schedule(ackDuration,
1614 this,
1615 std::pair<bool, uint8_t>{true, id});
1616 }
1617 link.pmMode = WIFI_PM_ACTIVE;
1618 }
1619 else
1620 {
1621 /**
1622 * When a link becomes enabled for a non-AP STA that is affiliated with a
1623 * non-AP MLD after successful association with an AP MLD with (Re)Association
1624 * Request/Response frames transmitted on another link [..], the power
1625 * management mode of the non-AP STA, immediately after the acknowledgement of
1626 * the (Re)Association Response frame [..], is power save mode, and its power
1627 * state is doze. (Sec. 35.3.7.1.4 of 802.11be D3.0)
1628 */
1629 // if the user requested this link to be in active mode, we have to
1630 // switch PM mode
1631 if (link.pmMode == WIFI_PM_ACTIVE)
1632 {
1633 Simulator::Schedule(ackDuration,
1635 this,
1636 std::pair<bool, uint8_t>{false, id});
1637 }
1638 link.pmMode = WIFI_PM_POWERSAVE;
1639 }
1640 }
1641 });
1642
1643 // connect the callback to the PHY TX begin trace to catch the Ack and disconnect
1644 // after its transmission begins
1645 auto phy = GetLink(linkId).phy;
1646 phy->TraceConnectWithoutContext("PhyTxPsduBegin", cb);
1647 Simulator::Schedule(phy->GetSifs() + NanoSeconds(1),
1648 [=]() { phy->TraceDisconnectWithoutContext("PhyTxPsduBegin", cb); });
1649}
1650
1651bool
1652StaWifiMac::CheckSupportedRates(std::variant<MgtBeaconHeader, MgtProbeResponseHeader> frame,
1653 uint8_t linkId)
1654{
1655 NS_LOG_FUNCTION(this << +linkId);
1656
1657 // lambda to invoke on the current frame variant
1658 auto check = [&](auto&& mgtFrame) -> bool {
1659 // check supported rates
1660 NS_ASSERT(mgtFrame.template Get<SupportedRates>());
1661 const auto rates = AllSupportedRates{*mgtFrame.template Get<SupportedRates>(),
1662 mgtFrame.template Get<ExtendedSupportedRatesIE>()};
1663 for (const auto& selector : GetWifiPhy(linkId)->GetBssMembershipSelectorList())
1664 {
1665 if (!rates.IsBssMembershipSelectorRate(selector))
1666 {
1667 NS_LOG_DEBUG("Supported rates do not fit with the BSS membership selector");
1668 return false;
1669 }
1670 }
1671
1672 return true;
1673 };
1674
1675 return std::visit(check, frame);
1676}
1677
1678void
1679StaWifiMac::RecordOperations(const MgtFrameType& frame, const Mac48Address& from, uint8_t linkId)
1680{
1681 NS_LOG_FUNCTION(this << frame.index() << from << linkId);
1682 auto remoteStationManager = GetWifiRemoteStationManager(linkId);
1683 auto phy = GetWifiPhy(linkId);
1684
1685 // lambda processing Information Elements included in all frame types
1686 auto recordFromOpIes = [&](auto&& frame) {
1687 const auto& edcaParameters = frame.template Get<EdcaParameterSet>();
1688 const auto qosSupported = edcaParameters.has_value();
1689 GetWifiRemoteStationManager(linkId)->SetQosSupport(from, qosSupported);
1690
1691 if (GetHtSupported(linkId))
1692 {
1693 /* HT station */
1694 if (const auto& htOperation = frame.template Get<HtOperation>())
1695 {
1696 remoteStationManager->AddStationHtOperation(from, *htOperation);
1697 }
1698 }
1699
1700 if (GetVhtSupported(linkId))
1701 {
1702 /* VHT station */
1703 if (const auto& vhtOperation = frame.template Get<VhtOperation>())
1704 {
1705 remoteStationManager->AddStationVhtOperation(from, *vhtOperation);
1706 }
1707 }
1708
1709 if (!GetHeSupported())
1710 {
1711 return;
1712 }
1713 /* HE station */
1714 if (const auto& heOperation = frame.template Get<HeOperation>())
1715 {
1716 remoteStationManager->AddStationHeOperation(from, *heOperation);
1717 }
1718
1719 if (!GetEhtSupported())
1720 {
1721 return;
1722 }
1723 /* EHT station */
1724 if (const auto& ehtOperation = frame.template Get<EhtOperation>())
1725 {
1726 remoteStationManager->AddStationEhtOperation(from, *ehtOperation);
1727 }
1728 };
1729
1730 // process Information Elements included in the current frame variant
1731 std::visit(recordFromOpIes, frame);
1732}
1733
1734void
1736 const Mac48Address& apAddr,
1737 const Mac48Address& bssid,
1738 uint8_t linkId)
1739{
1740 NS_LOG_FUNCTION(this << frame.index() << apAddr << bssid << +linkId);
1741
1742 // ERP Information is not present in Association Response frames
1743 const std::optional<ErpInformation>* erpInformation = nullptr;
1744
1745 if (const auto* beacon = std::get_if<MgtBeaconHeader>(&frame))
1746 {
1747 erpInformation = &beacon->Get<ErpInformation>();
1748 }
1749 else if (const auto* probe = std::get_if<MgtProbeResponseHeader>(&frame))
1750 {
1751 erpInformation = &probe->Get<ErpInformation>();
1752 }
1753
1754 // lambda processing Information Elements included in all frame types sent by APs
1755 auto processOtherIes = [&](auto&& frame) {
1756 const auto& capabilities = frame.Capabilities();
1757 bool isShortPreambleEnabled = capabilities.IsShortPreamble();
1758 auto remoteStationManager = GetWifiRemoteStationManager(linkId);
1759 if (erpInformation && erpInformation->has_value() && GetErpSupported(linkId))
1760 {
1761 isShortPreambleEnabled &= !(*erpInformation)->GetBarkerPreambleMode();
1762 if ((*erpInformation)->GetUseProtection() != 0)
1763 {
1764 remoteStationManager->SetUseNonErpProtection(true);
1765 }
1766 else
1767 {
1768 remoteStationManager->SetUseNonErpProtection(false);
1769 }
1770 if (capabilities.IsShortSlotTime() == true)
1771 {
1772 // enable short slot time
1773 GetWifiPhy(linkId)->SetSlot(MicroSeconds(9));
1774 }
1775 else
1776 {
1777 // disable short slot time
1778 GetWifiPhy(linkId)->SetSlot(MicroSeconds(20));
1779 }
1780 }
1781 remoteStationManager->SetShortPreambleEnabled(isShortPreambleEnabled);
1782 remoteStationManager->SetShortSlotTimeEnabled(capabilities.IsShortSlotTime());
1783
1784 if (!GetQosSupported())
1785 {
1786 return;
1787 }
1788 /* QoS station */
1789 const auto& edcaParameters = frame.template Get<EdcaParameterSet>();
1790 if (edcaParameters.has_value())
1791 {
1792 // The value of the TXOP Limit field is specified as an unsigned integer, with the least
1793 // significant octet transmitted first, in units of 32 μs.
1795 edcaParameters->GetBeCWmin(),
1796 edcaParameters->GetBeCWmax(),
1797 edcaParameters->GetBeAifsn(),
1798 32 * MicroSeconds(edcaParameters->GetBeTxopLimit())},
1799 linkId);
1801 edcaParameters->GetBkCWmin(),
1802 edcaParameters->GetBkCWmax(),
1803 edcaParameters->GetBkAifsn(),
1804 32 * MicroSeconds(edcaParameters->GetBkTxopLimit())},
1805 linkId);
1807 edcaParameters->GetViCWmin(),
1808 edcaParameters->GetViCWmax(),
1809 edcaParameters->GetViAifsn(),
1810 32 * MicroSeconds(edcaParameters->GetViTxopLimit())},
1811 linkId);
1813 edcaParameters->GetVoCWmin(),
1814 edcaParameters->GetVoCWmax(),
1815 edcaParameters->GetVoAifsn(),
1816 32 * MicroSeconds(edcaParameters->GetVoTxopLimit())},
1817 linkId);
1818 }
1819
1820 if (GetHtSupported(linkId))
1821 {
1822 /* HT station */
1823 if (const auto& htCapabilities = frame.template Get<HtCapabilities>();
1824 !htCapabilities.has_value())
1825 {
1826 remoteStationManager->RemoveAllSupportedMcs(apAddr);
1827 }
1828 }
1829
1830 if (!GetHeSupported())
1831 {
1832 return;
1833 }
1834 /* HE station */
1835 if (const auto& heOperation = frame.template Get<HeOperation>())
1836 {
1837 GetHeConfiguration()->m_bssColor = heOperation->m_bssColorInfo.m_bssColor;
1838 }
1839
1840 const auto& muEdcaParameters = frame.template Get<MuEdcaParameterSet>();
1841 if (muEdcaParameters.has_value())
1842 {
1844 muEdcaParameters->GetMuCwMin(AC_BE),
1845 muEdcaParameters->GetMuCwMax(AC_BE),
1846 muEdcaParameters->GetMuAifsn(AC_BE),
1847 muEdcaParameters->GetMuEdcaTimer(AC_BE)},
1848 linkId);
1850 muEdcaParameters->GetMuCwMin(AC_BK),
1851 muEdcaParameters->GetMuCwMax(AC_BK),
1852 muEdcaParameters->GetMuAifsn(AC_BK),
1853 muEdcaParameters->GetMuEdcaTimer(AC_BK)},
1854 linkId);
1856 muEdcaParameters->GetMuCwMin(AC_VI),
1857 muEdcaParameters->GetMuCwMax(AC_VI),
1858 muEdcaParameters->GetMuAifsn(AC_VI),
1859 muEdcaParameters->GetMuEdcaTimer(AC_VI)},
1860 linkId);
1862 muEdcaParameters->GetMuCwMin(AC_VO),
1863 muEdcaParameters->GetMuCwMax(AC_VO),
1864 muEdcaParameters->GetMuAifsn(AC_VO),
1865 muEdcaParameters->GetMuEdcaTimer(AC_VO)},
1866 linkId);
1867 }
1868
1869 if (!GetEhtSupported())
1870 {
1871 return;
1872 }
1873 /* EHT station */
1874 if (const auto& mle = frame.template Get<MultiLinkElement>(); mle && m_emlsrManager)
1875 {
1876 if (mle->HasEmlCapabilities())
1877 {
1878 m_emlsrManager->SetTransitionTimeout(mle->GetTransitionTimeout());
1879 }
1880 if (const auto& common = mle->GetCommonInfoBasic(); common.m_mediumSyncDelayInfo)
1881 {
1882 m_emlsrManager->SetMediumSyncDuration(common.GetMediumSyncDelayTimer());
1883 m_emlsrManager->SetMediumSyncOfdmEdThreshold(common.GetMediumSyncOfdmEdThreshold());
1884 m_emlsrManager->SetMediumSyncMaxNTxops(common.GetMediumSyncMaxNTxops());
1885 }
1886 }
1887 };
1888
1889 // process Information Elements included in the current frame variant
1890 std::visit(processOtherIes, frame);
1891}
1892
1893void
1894StaWifiMac::SetPowerSaveMode(const std::pair<bool, uint8_t>& enableLinkIdPair)
1895{
1896 const auto [enable, linkId] = enableLinkIdPair;
1897 NS_LOG_FUNCTION(this << enable << linkId);
1898
1899 auto& link = GetLink(linkId);
1900
1901 if (!IsAssociated())
1902 {
1903 NS_LOG_DEBUG("Not associated yet, record the PM mode to switch to upon association");
1904 link.pmMode = enable ? WIFI_PM_POWERSAVE : WIFI_PM_ACTIVE;
1905 return;
1906 }
1907
1908 if (!link.bssid)
1909 {
1910 NS_LOG_DEBUG("Link " << +linkId << " has not been setup, ignore request");
1911 return;
1912 }
1913
1914 if ((enable && link.pmMode == WIFI_PM_POWERSAVE) || (!enable && link.pmMode == WIFI_PM_ACTIVE))
1915 {
1916 NS_LOG_DEBUG("No PM mode change needed");
1917 return;
1918 }
1919
1921
1922 // reschedule a call to this function to make sure that the PM mode switch
1923 // is eventually completed
1926 this,
1927 enableLinkIdPair);
1928
1929 if (HasFramesToTransmit(linkId))
1930 {
1931 NS_LOG_DEBUG("Next transmitted frame will be sent with PM=" << enable);
1932 return;
1933 }
1934
1935 // No queued frames. Enqueue a Data Null frame to inform the AP of the PM mode change
1937
1938 hdr.SetAddr1(GetBssid(linkId));
1940 hdr.SetAddr3(GetBssid(linkId));
1941 hdr.SetDsNotFrom();
1942 hdr.SetDsTo();
1943 enable ? hdr.SetPowerManagement() : hdr.SetNoPowerManagement();
1944 if (GetQosSupported())
1945 {
1947 }
1948 else
1949 {
1950 m_txop->Queue(Create<WifiMpdu>(Create<Packet>(), hdr));
1951 }
1952}
1953
1955StaWifiMac::GetPmMode(uint8_t linkId) const
1956{
1957 return GetLink(linkId).pmMode;
1958}
1959
1960void
1962{
1963 NS_LOG_FUNCTION(this << *mpdu);
1964
1965 auto linkId = GetLinkIdByAddress(mpdu->GetHeader().GetAddr2());
1966
1967 if (!linkId)
1968 {
1969 // the given MPDU may be the original copy containing MLD addresses and not carrying
1970 // a valid PM bit (which is set on the aliases).
1971 auto linkIds = mpdu->GetInFlightLinkIds();
1972 NS_ASSERT_MSG(!linkIds.empty(),
1973 "The TA of the acked MPDU (" << *mpdu
1974 << ") is not a link "
1975 "address and the MPDU is not inflight");
1976 // in case the ack'ed MPDU is inflight on multiple links, we cannot really know if
1977 // it was received by the AP on all links or only on some links. Hence, we only
1978 // consider the first link ID in the set, given that in the most common case of MPDUs
1979 // that cannot be sent concurrently on multiple links, there will be only one link ID
1980 linkId = *linkIds.begin();
1981 mpdu = GetTxopQueue(mpdu->GetQueueAc())->GetAlias(mpdu, *linkId);
1982 }
1983
1984 auto& link = GetLink(*linkId);
1985 const WifiMacHeader& hdr = mpdu->GetHeader();
1986
1987 // we received an acknowledgment while switching PM mode; the PM mode change is effective now
1988 if (hdr.IsPowerManagement() && link.pmMode == WIFI_PM_SWITCHING_TO_PS)
1989 {
1990 link.pmMode = WIFI_PM_POWERSAVE;
1991 }
1992 else if (!hdr.IsPowerManagement() && link.pmMode == WIFI_PM_SWITCHING_TO_ACTIVE)
1993 {
1994 link.pmMode = WIFI_PM_ACTIVE;
1995 }
1996}
1997
2000{
2001 AllSupportedRates rates;
2002 for (const auto& mode : GetWifiPhy(linkId)->GetModeList())
2003 {
2004 uint64_t modeDataRate = mode.GetDataRate(GetWifiPhy(linkId)->GetChannelWidth());
2005 NS_LOG_DEBUG("Adding supported rate of " << modeDataRate);
2006 rates.AddSupportedRate(modeDataRate);
2007 }
2008 if (GetHtSupported(linkId))
2009 {
2010 for (const auto& selector : GetWifiPhy(linkId)->GetBssMembershipSelectorList())
2011 {
2012 rates.AddBssMembershipSelectorRate(selector);
2013 }
2014 }
2015 return rates;
2016}
2017
2019StaWifiMac::GetCapabilities(uint8_t linkId) const
2020{
2021 CapabilityInformation capabilities;
2022 capabilities.SetShortPreamble(GetWifiPhy(linkId)->GetShortPhyPreambleSupported() ||
2023 GetErpSupported(linkId));
2025 return capabilities;
2026}
2027
2028void
2030{
2031 m_state = value;
2032}
2033
2034void
2035StaWifiMac::SetEdcaParameters(const EdcaParams& params, uint8_t linkId)
2036{
2037 Ptr<QosTxop> edca = GetQosTxop(params.ac);
2038 edca->SetMinCw(params.cwMin, linkId);
2039 edca->SetMaxCw(params.cwMax, linkId);
2040 edca->SetAifsn(params.aifsn, linkId);
2041 edca->SetTxopLimit(params.txopLimit, linkId);
2042}
2043
2044void
2045StaWifiMac::SetMuEdcaParameters(const MuEdcaParams& params, uint8_t linkId)
2046{
2047 Ptr<QosTxop> edca = GetQosTxop(params.ac);
2048 edca->SetMuCwMin(params.cwMin, linkId);
2049 edca->SetMuCwMax(params.cwMax, linkId);
2050 edca->SetMuAifsn(params.aifsn, linkId);
2051 edca->SetMuEdcaTimer(params.muEdcaTimer, linkId);
2052}
2053
2054void
2056{
2057 NS_LOG_FUNCTION(this);
2058 if (IsAssociated())
2059 {
2060 NS_LOG_DEBUG("PHY capabilities changed: send reassociation request");
2063 }
2064}
2065
2066/**
2067 * Initial configuration:
2068 *
2069 * ┌───┬───┬───┐ ┌────┐ ┌───────┐
2070 * Link A │FEM│RSM│CAM│◄──────►│Main├──────►│Channel│
2071 * │ │ │ │ │PHY │ │ A │
2072 * └───┴───┴───┘ └────┘ └───────┘
2073 *
2074 * ┌───┬───┬───┐ ┌────┐ ┌───────┐
2075 * Link B │FEM│RSM│CAM│ │Aux │ │Channel│
2076 * │ │ │ │◄──────►│PHY ├──────►│ B │
2077 * └───┴───┴───┘ └────┘ └───────┘
2078 *
2079 * A link switching/swapping is notified by the EMLSR Manager and the Channel Access Manager
2080 * (CAM) notifies us that a first PHY (i.e., the Main PHY) switches to Channel B. We connect
2081 * the Main PHY to the MAC stack B:
2082 *
2083 *
2084 * ┌───┬───┬───┐ ┌────┐ ┌───────┐
2085 * Link A │FEM│RSM│CAM│ ┌───►│Main├───┐ │Channel│
2086 * │ │ │ │ │ │PHY │ │ │ A │
2087 * └───┴───┴───┘ │ └────┘ │ └───────┘
2088 * │ │
2089 * ┌───┬───┬───┐ │ ┌────┐ │ ┌───────┐
2090 * Link B │FEM│RSM│CAM│◄──┘ │Aux │ └──►│Channel│
2091 * │ │ │ │◄─ ─ ─ ─│PHY ├──────►│ B │
2092 * └───┴───┴───┘INACTIVE└────┘ └───────┘
2093 *
2094 * MAC stack B keeps a PHY listener associated with the Aux PHY, even though it is inactive,
2095 * meaning that the PHY listener will only notify channel switches (no CCA, no RX).
2096 * If the EMLSR Manager requested a link switching, this configuration will be kept until
2097 * further requests. If the EMLSR Manager requested a link swapping, link B's CAM will be
2098 * notified by its (inactive) PHY listener upon the channel switch performed by the Aux PHY.
2099 * In this case, we remove the inactive PHY listener and connect the Aux PHY to MAC stack A:
2100 *
2101 * ┌───┬───┬───┐ ┌────┐ ┌───────┐
2102 * Link A │FEM│RSM│CAM│◄─┐ ┌──►│Main├───┐ │Channel│
2103 * │ │ │ │ │ │ │PHY │ ┌─┼──►│ A │
2104 * └───┴───┴───┘ │ │ └────┘ │ │ └───────┘
2105 * │ │ │ │
2106 * ┌───┬───┬───┐ │ │ ┌────┐ │ │ ┌───────┐
2107 * Link B │FEM│RSM│CAM│◄─┼─┘ │Aux │ │ └──►│Channel│
2108 * │ │ │ │ └────►│PHY ├─┘ │ B │
2109 * └───┴───┴───┘ └────┘ └───────┘
2110 */
2111
2112void
2114{
2115 NS_LOG_FUNCTION(this << phy << linkId << delay.As(Time::US));
2116
2117 // if the PHY that is starting a channel switch was operating on a link (i.e., there is a link,
2118 // other than the new link, that points to the PHY), then it is no longer operating on that
2119 // link and we have to reset the phy pointer of the link.
2120 for (auto& [id, link] : GetLinks())
2121 {
2122 if (link->phy == phy && id != linkId)
2123 {
2124 // we do not get here if the PHY is not operating on any link, which happens if:
2125 // - PHY is an aux PHY to reconnect to its link
2126 // - PHY is an aux PHY that is starting switching to the link previously occupied by the
2127 // main PHY (because the main PHY is now operating on the aux PHY link)
2128 // - PHY is the main PHY that completed the channel switch but connecting it to the link
2129 // was postponed until now (e.g. because the aux PHY on the link was receiving an ICF)
2130 // - PHY is the main PHY that was switching, the switch was interrupted and it is
2131 // now starting switching to another link
2132 link->phy = nullptr;
2133 m_emlsrLinkSwitchLogger(id, phy, false);
2134 }
2135 }
2136
2137 // lambda to connect the PHY to the new link
2138 auto connectPhy = [=, this]() mutable {
2139 auto& newLink = GetLink(linkId);
2140 // The MAC stack associated with the new link uses the given PHY
2141 newLink.phy = phy;
2142 // Setup a PHY listener for the given PHY on the CAM associated with the new link
2143 newLink.channelAccessManager->SetupPhyListener(phy);
2145 if (m_emlsrManager->GetCamStateReset())
2146 {
2147 newLink.channelAccessManager->ResetState();
2148 }
2149 // Disconnect the FEM on the new link from the current PHY
2150 newLink.feManager->ResetPhy();
2151 // Connect the FEM on the new link to the given PHY
2152 newLink.feManager->SetWifiPhy(phy);
2153 // Connect the station manager on the new link to the given PHY
2154 newLink.stationManager->SetupPhy(phy);
2155 // log link switch
2156 m_emlsrLinkSwitchLogger(linkId, phy, true);
2157 };
2158
2159 // cancel any pending event for the given PHY to switch link
2160 CancelEmlsrPhyConnectEvent(phy->GetPhyId());
2161
2162 // connect the PHY to the new link when the channel switch is completed, unless there is a PHY
2163 // operating on the new link that is possibly receiving an ICF, in which case the PHY is
2164 // connected when the frame reception is completed
2165 if (delay.IsStrictlyPositive())
2166 {
2167 auto lambda = [=, this]() mutable {
2168 const auto [maybeIcf, extension] = m_emlsrManager->CheckPossiblyReceivingIcf(linkId);
2169 if (maybeIcf && extension.IsStrictlyPositive())
2170 {
2171 NS_ASSERT_MSG(phy->GetPhyId() == m_emlsrManager->GetMainPhyId(),
2172 "Only the main PHY is expected to move to a link on which another "
2173 "PHY is operating. PHY ID="
2174 << +phy->GetPhyId());
2175 NS_LOG_DEBUG("Connecting main PHY to link " << +linkId << " is postponed by "
2176 << extension.As(Time::US));
2177 NotifySwitchingEmlsrLink(phy, linkId, extension);
2178 }
2179 else
2180 {
2181 connectPhy();
2182 }
2183 };
2184
2185 m_emlsrLinkSwitch.emplace(phy->GetPhyId(), Simulator::Schedule(delay, lambda));
2186 }
2187 else
2188 {
2189 connectPhy();
2190 }
2191}
2192
2193void
2195{
2196 NS_LOG_FUNCTION(this << phyId);
2197 if (auto eventIt = m_emlsrLinkSwitch.find(phyId); eventIt != m_emlsrLinkSwitch.end())
2198 {
2199 eventIt->second.Cancel();
2200 m_emlsrLinkSwitch.erase(eventIt);
2201 }
2202}
2203
2204void
2206{
2207 NS_LOG_FUNCTION(this << +linkId);
2208
2210
2211 if (IsInitialized() && IsAssociated())
2212 {
2213 Disassociated();
2214 }
2215
2216 // notify association manager
2217 m_assocManager->NotifyChannelSwitched(linkId);
2218}
2219
2220std::ostream&
2221operator<<(std::ostream& os, const StaWifiMac::ApInfo& apInfo)
2222{
2223 os << "BSSID=" << apInfo.m_bssid << ", AP addr=" << apInfo.m_apAddr << ", SNR=" << apInfo.m_snr
2224 << ", Channel={" << apInfo.m_channel.number << "," << apInfo.m_channel.band
2225 << "}, Link ID=" << +apInfo.m_linkId << ", Frame=[";
2226 std::visit([&os](auto&& frame) { frame.Print(os); }, apInfo.m_frame);
2227 os << "]";
2228 return os;
2229}
2230
2231} // namespace ns3
A container for one type of attribute.
AttributeValue implementation for Boolean.
Definition boolean.h:26
Base class for Callback class.
Definition callback.h:344
Callback template class.
Definition callback.h:422
void SetShortSlotTime(bool shortSlotTime)
Set the short slot time bit in the capability information field.
void SetShortPreamble(bool shortPreamble)
Set the short preamble bit in the capability information field.
The DSSS Parameter Set.
The IEEE 802.11be EHT Capabilities.
Hold variables of type enum.
Definition enum.h:52
The ErpInformation Information Element.
The Extended Capabilities Information Element.
The Extended Supported Rates Information Element.
The HE 6 GHz Band Capabilities (IEEE 802.11ax-2021 9.4.2.263)
The IEEE 802.11ax HE Capabilities.
The HT Capabilities Information Element.
an EUI-48 address
bool IsGroup() const
static Mac48Address ConvertFrom(const Address &address)
Implement the header for management frames of type association request.
Implement the header for management frames of type association and reassociation response.
StatusCode GetStatusCode()
Return the status code.
uint16_t GetAssociationId() const
Return the association ID.
Implement the header for management frames of type beacon.
Implement the header for management frames of type probe request.
Implement the header for management frames of type probe response.
CapabilityInformation & Capabilities()
Implement the header for management frames of type reassociation request.
void SetCurrentApAddress(Mac48Address currentApAddr)
Set the address of the current access point.
bool TraceConnectWithoutContext(std::string name, const CallbackBase &cb)
Connect a TraceSource to a Callback without a context.
bool IsInitialized() const
Check if the object has been initialized.
Definition object.cc:240
AttributeValue implementation for Pair.
Definition pair.h:54
Smart pointer class similar to boost::intrusive_ptr.
Definition ptr.h:67
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition simulator.h:561
static Time Now()
Return the current simulation virtual time.
Definition simulator.cc:197
static EventId ScheduleNow(FUNC f, Ts &&... args)
Schedule an event to expire Now.
Definition simulator.h:595
static Time GetDelayLeft(const EventId &id)
Get the remaining time until this event will execute.
Definition simulator.cc:206
Introspection did not find any typical Config paths
Definition snr-tag.h:24
double Get() const
Return the SNR value.
Definition snr-tag.cc:79
The IEEE 802.11 SSID Information Element.
Definition ssid.h:25
The Wifi MAC high model for a non-AP STA in a BSS.
std::set< uint8_t > GetSetupLinkIds() const
Get the IDs of the setup links (if any).
void ScanningTimeout(const std::optional< ApInfo > &bestAp)
This method is called after wait beacon timeout or wait probe request timeout has occurred.
Time m_waitBeaconTimeout
wait beacon timeout
int64_t AssignStreams(int64_t stream) override
Assign a fixed random variable stream number to the random variables used by this model.
void SetPowerSaveMode(const std::pair< bool, uint8_t > &enableLinkIdPair)
Enable or disable Power Save mode on the given link.
Ptr< WifiAssocManager > m_assocManager
Association Manager.
void DoCompleteConfig() override
Allow subclasses to complete the configuration of the MAC layer components.
bool m_activeProbing
active probing
void DoInitialize() override
Initialize() implementation.
void SetAssocManager(Ptr< WifiAssocManager > assocManager)
Set the Association Manager.
bool CanForwardPacketsTo(Mac48Address to) const override
Return true if packets can be forwarded to the given destination, false otherwise.
std::unique_ptr< LinkEntity > CreateLinkEntity() const override
Create a LinkEntity object.
void SetState(MacState value)
Set the current MAC state.
Time m_beaconWatchdogEnd
beacon watchdog end
bool m_enableScanning
enable channel scanning
AllSupportedRates GetSupportedRates(uint8_t linkId) const
Return an instance of SupportedRates that contains all rates that we support including HT rates.
void SetEdcaParameters(const EdcaParams &params, uint8_t linkId)
Set the EDCA parameters for the given link.
TracedCallback< Mac48Address > m_deAssocLogger
disassociation logger
MacState
The current MAC state of the STA.
void NotifyChannelSwitching(uint8_t linkId) override
Notify that channel on the given link has been switched.
bool GetActiveProbing() const
Return whether active probing is enabled.
EventId m_beaconWatchdog
beacon watchdog
void PhyCapabilitiesChanged()
Indicate that PHY capabilities have changed.
WifiAssocType m_assocType
type of association
StaLinkEntity & GetStaLink(const std::unique_ptr< WifiMac::LinkEntity > &link) const
Cast the given LinkEntity object to StaLinkEntity.
void ReceiveProbeResp(Ptr< const WifiMpdu > mpdu, uint8_t linkId)
Process the Probe Response frame received on the given link.
void SetPmModeAfterAssociation(uint8_t linkId)
Set the Power Management mode of the setup links after association.
WifiScanParams::Channel GetCurrentChannel(uint8_t linkId) const
Get the current primary20 channel used on the given link as a (channel number, PHY band) pair.
uint16_t GetAssociationId() const
Return the association ID.
void TryToEnsureAssociated()
Try to ensure that we are associated with an AP by taking an appropriate action depending on the curr...
void ReceiveAssocResp(Ptr< const WifiMpdu > mpdu, uint8_t linkId)
Process the (Re)Association Response frame received on the given link.
void NotifySwitchingEmlsrLink(Ptr< WifiPhy > phy, uint8_t linkId, Time delay)
Notify that the given PHY switched channel to operate on another EMLSR link.
std::variant< MgtAssocRequestHeader, MgtReassocRequestHeader > GetAssociationRequest(bool isReassoc, uint8_t linkId) const
Get the (Re)Association Request frame to send on a given link.
static TypeId GetTypeId()
Get the type ID.
MultiLinkElement GetBasicMultiLinkElement(bool isReassoc, uint8_t linkId) const
Return the Basic Multi-Link Element to include in the management frames transmitted on the given link...
void CancelEmlsrPhyConnectEvent(uint8_t phyId)
Cancel any scheduled event for connecting the given PHY to an EMLSR link.
void DoDispose() override
Destructor implementation.
void BlockTxOnLink(uint8_t linkId, WifiQueueBlockedReason reason)
Block transmissions on the given link for the given reason.
std::map< uint8_t, EventId > m_emlsrLinkSwitch
maps PHY ID to the event scheduled to switch the corresponding PHY to a new EMLSR link
void RecordOperations(const MgtFrameType &frame, const Mac48Address &addr, uint8_t linkId)
Update operations information from the given management frame.
StaLinkEntity & GetLink(uint8_t linkId) const
Get a reference to the link associated with the given ID.
uint32_t m_maxMissedBeacons
maximum missed beacons
TracedCallback< uint8_t, Mac48Address > m_setupCompleted
link setup completed logger
TracedCallback< Mac48Address > m_assocLogger
association logger
void SetWifiPhys(const std::vector< Ptr< WifiPhy > > &phys) override
void SetMuEdcaParameters(const MuEdcaParams &params, uint8_t linkId)
Set the MU EDCA parameters for the given link.
void NotifyEmlsrModeChanged(const std::set< uint8_t > &linkIds)
Notify the MAC that EMLSR mode has changed on the given set of links.
bool CheckSupportedRates(std::variant< MgtBeaconHeader, MgtProbeResponseHeader > frame, uint8_t linkId)
Determine whether the supported rates indicated in a given Beacon frame or Probe Response frame fit w...
Mac48Address DoGetLocalAddress(const Mac48Address &remoteAddr) const override
This method is called if this device is an MLD to determine the MAC address of the affiliated STA use...
void RestartBeaconWatchdog(Time delay)
Restarts the beacon timer.
void SetEmlsrManager(Ptr< EmlsrManager > emlsrManager)
Set the EMLSR Manager.
void NotifyDropPacketToEnqueue(Ptr< Packet > packet, Mac48Address to) override
Allow subclasses to take actions when a packet to enqueue has been dropped.
Time m_pmModeSwitchTimeout
PM mode switch timeout.
void Disassociated()
Set the state to unassociated and try to associate again.
Ptr< EmlsrManager > GetEmlsrManager() const
void TxOk(Ptr< const WifiMpdu > mpdu)
Notify that the MPDU we sent was successfully received by the receiver (i.e.
MgtProbeRequestHeader GetProbeRequest(uint8_t linkId) const
Get the frame body of the Probe Request to transmit on the given link.
MgtProbeRequestHeader GetMultiLinkProbeRequest(uint8_t linkId, const std::vector< uint8_t > &apLinkIds, std::optional< uint8_t > apMldId) const
Get the frame body of the Multi-Link Probe Request to transmit on the given link.
void Receive(Ptr< const WifiMpdu > mpdu, uint8_t linkId) override
This method acts as the MacRxMiddle receive callback and is invoked to notify us that a frame has bee...
WifiTidLinkMapping m_ulTidLinkMappingInAssocReq
store the UL TID-to-Link Mapping included in the Association Request frame
WifiPowerManagementMode GetPmMode(uint8_t linkId) const
Ptr< RandomVariableStream > m_probeDelay
RandomVariable used to randomize the time of the first Probe Response on each channel.
TracedCallback< ApInfo > m_beaconInfo
beacon info logger
void MissedBeacons()
This method is called after we have not received a beacon from the AP on any link.
uint16_t m_aid
Association AID.
MacState m_state
MAC state.
bool IsEmlsrLink(uint8_t linkId) const
WifiAssocType GetAssocType() const
void StartScanning()
Start the scanning process which trigger active or passive scanning based on the active probing flag.
std::vector< TidToLinkMapping > GetTidToLinkMappingElements(WifiTidToLinkMappingNegSupport apNegSupport)
TracedCallback< Time > m_beaconArrival
beacon arrival logger
void UnblockTxOnLink(std::set< uint8_t > linkIds, WifiQueueBlockedReason reason)
Unblock transmissions on the given links for the given reason.
void AssocRequestTimeout()
This method is called after the association timeout occurred.
void Enqueue(Ptr< WifiMpdu > mpdu, Mac48Address to, Mac48Address from) override
Ptr< EmlsrManager > m_emlsrManager
EMLSR Manager.
void ApplyOperationalSettings(const MgtFrameType &frame, const Mac48Address &apAddr, const Mac48Address &bssid, uint8_t linkId)
Update operational settings based on associated AP's information provided by the given management fra...
Time m_assocRequestTimeout
association request timeout
void ReceiveBeacon(Ptr< const WifiMpdu > mpdu, uint8_t linkId)
Process the Beacon frame received on the given link.
Time m_probeRequestTimeout
probe request timeout
void SetActiveProbing(bool enable)
Enable or disable active probing.
CapabilityInformation GetCapabilities(uint8_t linkId) const
Return the Capability information for the given link.
bool IsAssociated() const
Return whether we are associated with an AP.
~StaWifiMac() override
bool IsWaitAssocResp() const
Return whether we are waiting for an association response from an AP.
MultiLinkElement GetProbeReqMultiLinkElement(const std::vector< uint8_t > &apLinkIds, std::optional< uint8_t > apMldId) const
Return the Probe Request Multi-Link Element to include in the management frames to transmit.
EventId m_assocRequestEvent
association request event
void EnqueueProbeRequest(const MgtProbeRequestHeader &probeReq, uint8_t linkId, const Mac48Address &addr1=Mac48Address::GetBroadcast(), const Mac48Address &addr3=Mac48Address::GetBroadcast())
Enqueue the given probe request packet for transmission on the given link.
void SendAssociationRequest(bool isReassoc)
Forward an association or reassociation request packet to the DCF.
TracedCallback< uint8_t, Ptr< WifiPhy >, bool > m_emlsrLinkSwitchLogger
EMLSR link switch logger.
WifiTidLinkMapping m_dlTidLinkMappingInAssocReq
store the DL TID-to-Link Mapping included in the Association Request frame
bool IsSuccess() const
Return whether the status code is success.
Hold variables of type string.
Definition string.h:45
The Supported Rates Information Element.
Simulation virtual time values and global simulation resolution.
Definition nstime.h:96
TimeWithUnit As(const Unit unit=Time::AUTO) const
Attach a unit to a Time, to facilitate output in a specific unit.
Definition time.cc:409
bool IsStrictlyPositive() const
Exactly equivalent to t > 0.
Definition nstime.h:342
@ US
microsecond
Definition nstime.h:109
AttributeValue implementation for Time.
Definition nstime.h:1456
Time Get() const
Definition time.cc:524
void StartAccessAfterEvent(uint8_t linkId, bool hadFramesToTransmit, bool checkMediumBusy)
Request channel access on the given link after the occurrence of an event that possibly requires to g...
Definition txop.cc:664
static constexpr bool DIDNT_HAVE_FRAMES_TO_TRANSMIT
no packet available for transmission was in the queue
Definition txop.h:411
static constexpr bool CHECK_MEDIUM_BUSY
generation of backoff (also) depends on the busy/idle state of the medium
Definition txop.h:413
a unique identifier for an interface.
Definition type-id.h:49
@ ATTR_GET
The attribute can be read.
Definition type-id.h:54
@ ATTR_SET
The attribute can be written.
Definition type-id.h:55
@ ATTR_CONSTRUCT
The attribute can be written at construction-time.
Definition type-id.h:56
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition type-id.cc:1001
Hold an unsigned integer type.
Definition uinteger.h:34
The IEEE 802.11ac VHT Capabilities.
static std::pair< CategoryValue, ActionValue > Peek(Ptr< const Packet > pkt)
Peek an Action header from the given packet.
Implements the IEEE 802.11 MAC header.
bool IsQosAmsdu() const
Check if IsQosData() is true and the A-MSDU present bit is set in the QoS control field.
Mac48Address GetAddr3() const
Return the address in the Address 3 field.
bool IsBeacon() const
Return true if the header is a Beacon header.
bool IsAssocResp() const
Return true if the header is an Association Response header.
Mac48Address GetAddr1() const
Return the address in the Address 1 field.
virtual WifiMacType GetType() const
Return the type (WifiMacType)
bool IsCtl() const
Return true if the Type is Control.
void SetDsNotFrom()
Un-set the From DS bit in the Frame Control field.
bool IsProbeResp() const
Return true if the header is a Probe Response header.
void SetAddr1(Mac48Address address)
Fill the Address 1 field with the given address.
virtual void SetType(WifiMacType type, bool resetToDsFromDs=true)
Set Type/Subtype values with the correct values depending on the given type.
Mac48Address GetAddr2() const
Return the address in the Address 2 field.
bool HasData() const
Return true if the header type is DATA and is not DATA_NULL.
bool IsData() const
Return true if the Type is DATA.
bool IsReassocResp() const
Return true if the header is a Reassociation Response header.
void SetDsTo()
Set the To DS bit in the Frame Control field.
void SetAddr2(Mac48Address address)
Fill the Address 2 field with the given address.
bool IsQosData() const
Return true if the Type is DATA and Subtype is one of the possible values for QoS Data.
void SetAddr3(Mac48Address address)
Fill the Address 3 field with the given address.
void SetDsNotTo()
Un-set the To DS bit in the Frame Control field.
bool IsPowerManagement() const
Return if the Power Management bit is set.
void SetPowerManagement()
Set the Power Management bit in the Frame Control field.
void SetNoPowerManagement()
Un-set the Power Management bit in the Frame Control field.
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:1014
Ptr< QosTxop > GetBEQueue() const
Accessor for the AC_BE channel access function.
Definition wifi-mac.cc:652
virtual void NotifyChannelSwitching(uint8_t linkId)
Notify that channel on the given link has been switched.
Definition wifi-mac.cc:701
std::optional< Mac48Address > GetMldAddress(const Mac48Address &remoteAddr) const
Definition wifi-mac.cc:1858
Mac48Address GetBssid(uint8_t linkId) const
Definition wifi-mac.cc:547
Ptr< HeConfiguration > GetHeConfiguration() const
Definition wifi-mac.cc:1980
const std::map< uint8_t, std::unique_ptr< LinkEntity > > & GetLinks() const
Definition wifi-mac.cc:1091
Ptr< Txop > GetTxop() const
Accessor for the Txop object.
Definition wifi-mac.cc:572
VhtCapabilities GetVhtCapabilities(uint8_t linkId) const
Return the VHT capabilities of the device for the given link.
Definition wifi-mac.cc:2260
Callback< void > m_linkDown
Callback when a link is down.
Definition wifi-mac.h:1009
bool GetQosSupported() const
Return whether the device supports QoS.
Definition wifi-mac.cc:1409
Ptr< Txop > m_txop
TXOP used for transmission of frames to non-QoS peers.
Definition wifi-mac.h:1005
Ptr< WifiMacQueueScheduler > GetMacQueueScheduler() const
Get the wifi MAC queue scheduler.
Definition wifi-mac.cc:695
uint8_t GetNLinks() const
Get the number of links (can be greater than 1 for 11be devices only).
Definition wifi-mac.cc:1106
void SwapLinks(std::map< uint8_t, uint8_t > links)
Swap the links based on the information included in the given map.
Definition wifi-mac.cc:1172
void DoInitialize() override
Initialize() implementation.
Definition wifi-mac.cc:418
std::variant< MgtBeaconHeader, MgtProbeResponseHeader, MgtAssocResponseHeader > MgtFrameType
type of the management frames
Definition wifi-mac.h:108
Ssid GetSsid() const
Definition wifi-mac.cc:534
bool GetErpSupported(uint8_t linkId) const
Return whether the device supports ERP on the given link.
Definition wifi-mac.cc:1415
Ptr< QosTxop > GetVOQueue() const
Accessor for the AC_VO channel access function.
Definition wifi-mac.cc:640
void SetTypeOfStation(TypeOfStation type)
This method is invoked by a subclass to specify what type of station it is implementing.
Definition wifi-mac.cc:484
Ptr< WifiPhy > GetWifiPhy(uint8_t linkId=SINGLE_LINK_OP_ID) const
Definition wifi-mac.cc:1377
void RecordCapabilities(const MgtFrameType &frame, const Mac48Address &addr, uint8_t linkId)
Update capabilities information from the given management frame.
Definition wifi-mac.cc:2607
bool GetEhtSupported() const
Return whether the device supports EHT.
Definition wifi-mac.cc:2013
bool GetHeSupported() const
Return whether the device supports HE.
Definition wifi-mac.cc:2007
HtCapabilities GetHtCapabilities(uint8_t linkId) const
Return the HT capabilities of the device for the given link.
Definition wifi-mac.cc:2202
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:1138
virtual bool HasFramesToTransmit(uint8_t linkId)
Check if the MAC has frames to transmit over the given link.
Definition wifi-mac.cc:671
UniformRandomBitGenerator m_shuffleLinkIdsGen
random number generator to shuffle link IDs
Definition wifi-mac.h:1001
void ApplyTidLinkMapping(const Mac48Address &mldAddr, WifiDirection dir)
Apply the TID-to-Link Mapping negotiated with the given MLD for the given direction by properly confi...
Definition wifi-mac.cc:1492
Ptr< EhtConfiguration > GetEhtConfiguration() const
Definition wifi-mac.cc:1986
bool GetVhtSupported(uint8_t linkId) const
Return whether the device supports VHT on the given link.
Definition wifi-mac.cc:1999
virtual int64_t AssignStreams(int64_t stream)
Assign a fixed random variable stream number to the random variables used by this model.
Definition wifi-mac.cc:389
virtual void DeaggregateAmsduAndForward(Ptr< const WifiMpdu > mpdu)
This method can be called to de-aggregate an A-MSDU and forward the constituent packets up the stack.
Definition wifi-mac.cc:1846
void SetBssid(Mac48Address bssid, uint8_t linkId)
Definition wifi-mac.cc:540
Ptr< WifiNetDevice > GetDevice() const
Return the device this PHY is associated with.
Definition wifi-mac.cc:508
void UpdateTidToLinkMapping(const Mac48Address &mldAddr, WifiDirection dir, const WifiTidLinkMapping &mapping)
Update the TID-to-Link Mappings for the given MLD in the given direction based on the given negotiate...
Definition wifi-mac.cc:1269
ExtendedCapabilities GetExtendedCapabilities() const
Return the extended capabilities of the device.
Definition wifi-mac.cc:2194
He6GhzBandCapabilities GetHe6GhzBandCapabilities(uint8_t linkId) const
Return the HE 6GHz band capabilities of the device for the given 6 GHz link.
Definition wifi-mac.cc:2401
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:664
std::optional< uint8_t > GetLinkForPhy(Ptr< const WifiPhy > phy) const
Get the ID of the link (if any) on which the given PHY is operating.
Definition wifi-mac.cc:1151
bool GetShortSlotTimeSupported() const
Definition wifi-mac.cc:1459
void NotifyRxDrop(Ptr< const Packet > packet)
Definition wifi-mac.cc:738
Ptr< WifiRemoteStationManager > GetWifiRemoteStationManager(uint8_t linkId=0) const
Definition wifi-mac.cc:1079
bool GetHtSupported(uint8_t linkId) const
Return whether the device supports HT on the given link.
Definition wifi-mac.cc:1992
void ForwardUp(Ptr< const Packet > packet, Mac48Address from, Mac48Address to)
Forward the packet up to the device.
Definition wifi-mac.cc:1809
bool Is6GhzBand(uint8_t linkId) const
Indicate if a given link is on the 6 GHz band.
Definition wifi-mac.cc:1261
virtual void Receive(Ptr< const WifiMpdu > mpdu, uint8_t linkId)
This method acts as the MacRxMiddle receive callback and is invoked to notify us that a frame has bee...
Definition wifi-mac.cc:1816
Mac48Address GetAddress() const
Definition wifi-mac.cc:521
EhtCapabilities GetEhtCapabilities(uint8_t linkId) const
Return the EHT capabilities of the device for the given link.
Definition wifi-mac.cc:2436
Callback< void > m_linkUp
Callback when a link is up.
Definition wifi-mac.h:1008
LinkEntity & GetLink(uint8_t linkId) const
Get a reference to the link associated with the given ID.
Definition wifi-mac.cc:1097
HeCapabilities GetHeCapabilities(uint8_t linkId) const
Return the HE capabilities of the device for the given link.
Definition wifi-mac.cc:2337
virtual void SetWifiPhys(const std::vector< Ptr< WifiPhy > > &phys)
Definition wifi-mac.cc:1355
Ptr< QosTxop > GetQosTxop(AcIndex ac) const
Accessor for a specified EDCA object.
Definition wifi-mac.cc:618
void DoDispose() override
Destructor implementation.
Definition wifi-mac.cc:442
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
Definition wifi-phy.cc:1568
This class mimics the TXVECTOR which is to be passed to the PHY in order to define the parameters whi...
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition assert.h:55
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition assert.h:75
Ptr< const AttributeChecker > MakeBooleanChecker()
Definition boolean.cc:114
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition boolean.h:70
Ptr< const AttributeAccessor > MakeEnumAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition enum.h:221
Ptr< const AttributeAccessor > MakePairAccessor(T1 a1)
Create an AttributeAccessor for std::pair<>.
Definition pair.h:403
Ptr< AttributeChecker > MakePairChecker(const PairValue< A, B > &value)
Make a PairChecker from a PairValue.
Definition pair.h:272
Ptr< const AttributeAccessor > MakePointerAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition pointer.h:249
Ptr< AttributeChecker > MakePointerChecker()
Create a PointerChecker for a type.
Definition pointer.h:270
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition nstime.h:1457
Ptr< const AttributeChecker > MakeTimeChecker()
Helper to make an unbounded Time checker.
Definition nstime.h:1477
Ptr< const AttributeChecker > MakeUintegerChecker()
Definition uinteger.h:85
Ptr< const AttributeAccessor > MakeUintegerAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition uinteger.h:35
#define NS_ABORT_MSG(msg)
Unconditional abnormal program termination with a message.
Definition abort.h:38
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition abort.h:97
#define NS_ABORT_IF(cond)
Abnormal program termination if a condition is true.
Definition abort.h:65
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:191
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition log.h:257
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition log.h:271
#define NS_LOG_FUNCTION_NOARGS()
Output the name of the function.
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition object-base.h:35
Ptr< T > Create(Ts &&... args)
Create class instances by constructors with varying numbers of arguments and return them by Ptr.
Definition ptr.h:439
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1393
Time NanoSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1405
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1369
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1381
Ptr< const TraceSourceAccessor > MakeTraceSourceAccessor(T a)
Create a TraceSourceAccessor which will control access to the underlying trace source.
WifiPowerManagementMode
Enumeration for power management modes.
WifiQueueBlockedReason
Enumeration of the reasons to block container queues.
WifiAssocType
Type of association performed by this device (provided that it is supported by the standard configure...
@ STA
Definition wifi-mac.h:59
@ WIFI_PM_SWITCHING_TO_ACTIVE
@ WIFI_PM_POWERSAVE
@ WIFI_PM_SWITCHING_TO_PS
@ WIFI_PM_ACTIVE
@ WIFI_PHY_BAND_UNSPECIFIED
Unspecified.
@ WIFI_PHY_BAND_2_4GHZ
The 2.4 GHz band.
@ AC_BE
Best Effort.
Definition qos-utils.h:65
@ AC_VO
Voice.
Definition qos-utils.h:71
@ AC_VI
Video.
Definition qos-utils.h:69
@ AC_BK
Background.
Definition qos-utils.h:67
-style-clang-format
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Callback< R, Args... > MakeCallback(R(T::*memPtr)(Args...), OBJ objPtr)
Build Callbacks for class method members which take varying numbers of arguments and potentially retu...
Definition callback.h:684
WifiTidToLinkMappingNegSupport
TID-to-Link Mapping Negotiation Support.
std::ostream & operator<<(std::ostream &os, const Angles &a)
Definition angles.cc:148
double MHz_u
MHz weak type.
Definition wifi-units.h:31
Ptr< const AttributeChecker > MakeEnumChecker(T v, std::string n, Ts... args)
Make an EnumChecker pre-configured with a set of allowed values by name.
Definition enum.h:179
void Shuffle(RND_ACCESS_ITER first, RND_ACCESS_ITER last, Ptr< UniformRandomVariable > rv)
Shuffle the elements in the range first to last.
Definition shuffle.h:48
@ WIFI_MAC_MGT_PROBE_REQUEST
@ WIFI_MAC_DATA_NULL
@ WIFI_MAC_MGT_BEACON
@ WIFI_MAC_MGT_ACTION
@ WIFI_MAC_MGT_ASSOCIATION_RESPONSE
@ WIFI_MAC_MGT_ASSOCIATION_REQUEST
@ WIFI_MAC_MGT_REASSOCIATION_REQUEST
@ WIFI_MAC_MGT_PROBE_RESPONSE
@ WIFI_MAC_MGT_REASSOCIATION_RESPONSE
bool TidToLinkMappingValidForNegType1(const WifiTidLinkMapping &dlLinkMapping, const WifiTidLinkMapping &ulLinkMapping)
Check if the given TID-to-Link Mappings are valid for a negotiation type of 1.
@ LOG_FUNCTION
Function tracing for non-trivial function calls.
Definition log.h:95
Ptr< T1 > StaticCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:592
std::unordered_map< uint16_t, Ptr< const WifiPsdu > > WifiConstPsduMap
Map of const PSDUs indexed by STA-ID.
Definition wifi-ppdu.h:38
double Watt_u
Watt weak type.
Definition wifi-units.h:25
const std::map< AcIndex, WifiAc > wifiAcList
Map containing the four ACs in increasing order of priority (according to Table 10-1 "UP-to-AC Mappin...
Definition qos-utils.cc:115
Struct containing all supported rates.
void AddBssMembershipSelectorRate(uint64_t bs)
Add a special value to the supported rate set, corresponding to a BSS membership selector.
void AddSupportedRate(uint64_t bs)
Add the given rate to the supported rates.
std::optional< MldCapabilities > m_mldCapabilities
MLD Capabilities.
Struct to hold information regarding observed AP through active/passive scanning.
MgtFrameType m_frame
The body of the management frame used to update AP info.
WifiScanParams::Channel m_channel
The channel the management frame was received on.
Mac48Address m_apAddr
AP MAC address.
uint8_t m_linkId
ID of the link used to communicate with the AP.
Mac48Address m_bssid
BSSID.
double m_snr
SNR in linear scale.
Struct identifying a channel to scan.
WifiPhyBand band
PHY band.
uint16_t number
channel number
Structure holding scan parameters.
std::list< Channel > ChannelList
typedef for a list of channels
std::vector< ChannelList > channelList
list of channels to scan, for each link
Time probeDelay
delay prior to transmitting a Probe Request
WifiScanType type
indicates either active or passive scanning
Time maxChannelTime
maximum time to spend on each channel
Ssid ssid
desired SSID or wildcard SSID
Time minChannelTime
minimum time to spend on each channel