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 "power-save-manager.h"
17#include "qos-txop.h"
18#include "snr-tag.h"
19#include "wifi-assoc-manager.h"
20#include "wifi-mac-queue.h"
21#include "wifi-net-device.h"
22#include "wifi-phy.h"
23
24#include "ns3/attribute-container.h"
25#include "ns3/eht-configuration.h"
26#include "ns3/emlsr-manager.h"
27#include "ns3/he-configuration.h"
28#include "ns3/ht-configuration.h"
29#include "ns3/log.h"
30#include "ns3/packet.h"
31#include "ns3/pair.h"
32#include "ns3/pointer.h"
33#include "ns3/random-variable-stream.h"
34#include "ns3/shuffle.h"
35#include "ns3/simulator.h"
36#include "ns3/string.h"
37
38#include <iterator>
39#include <numeric>
40#include <sstream>
41
42namespace ns3
43{
44
45NS_LOG_COMPONENT_DEFINE("StaWifiMac");
46
48
51{
52 static TypeId tid =
53 TypeId("ns3::StaWifiMac")
55 .SetGroupName("Wifi")
56 .AddConstructor<StaWifiMac>()
57 .AddAttribute("ProbeRequestTimeout",
58 "The duration to actively probe the channel.",
59 TimeValue(Seconds(0.05)),
62 .AddAttribute("WaitBeaconTimeout",
63 "The duration to dwell on a channel while passively scanning for beacon",
67 .AddAttribute("AssocRequestTimeout",
68 "The interval between two consecutive association request attempts.",
69 TimeValue(Seconds(0.5)),
72 .AddAttribute("MaxMissedBeacons",
73 "Number of beacons which much be consecutively missed before "
74 "we attempt to restart association.",
75 UintegerValue(10),
78 .AddAttribute("EnableScanning",
79 "If false, STA does not perform channel scanning. This may be useful in "
80 "case of static configuration via the static setup helper.",
81 BooleanValue(true),
84 .AddAttribute(
85 "ActiveProbing",
86 "If true, we send probe requests. If false, we don't."
87 "NOTE: if more than one STA in your simulation is using active probing, "
88 "you should enable it at a different simulation time for each STA, "
89 "otherwise all the STAs will start sending probes at the same time resulting in "
90 "collisions. "
91 "See bug 1060 for more info.",
92 BooleanValue(false),
95 .AddAttribute("ProbeDelay",
96 "Delay (in microseconds) to be used prior to transmitting a "
97 "Probe frame during active scanning.",
98 StringValue("ns3::UniformRandomVariable[Min=50.0|Max=250.0]"),
101 .AddAttribute("AssocType",
102 "Type of association performed by this device (provided that it is "
103 "supported by the standard configured for this device, otherwise legacy "
104 "association is performed). By using this attribute, it is possible for "
105 "an EHT single-link device to perform ML setup with an AP MLD and for an "
106 "EHT multi-link device to perform legacy association with an AP MLD.",
108 TypeId::ATTR_CONSTRUCT, // prevent setting after construction
112 "LEGACY",
114 "ML_SETUP"))
115 .AddAttribute("PowerSaveManager",
116 "The Power Save manager object.",
117 PointerValue(),
120 // NS_DEPRECATED_3_48
121 .AddAttribute(
122 "PowerSaveMode",
123 "Enable/disable power save mode on the given link. The power management mode is "
124 "actually changed when the AP acknowledges a frame sent with the Power Management "
125 "field set to the value corresponding to the requested mode",
126 TypeId::ATTR_GET | TypeId::ATTR_SET, // do not set at construction time
132 "Use PowerSaveManager::PowerSaveMode instead")
133 .AddAttribute("PmModeSwitchTimeout",
134 "If switching to a new Power Management mode is not completed within "
135 "this amount of time, make another attempt at switching Power "
136 "Management mode.",
137 TimeValue(Seconds(0.1)),
140 .AddTraceSource("Assoc",
141 "Associated with an access point. If this is an MLD that associated "
142 "with an AP MLD, the AP MLD address is provided.",
144 "ns3::Mac48Address::TracedCallback")
145 .AddTraceSource("LinkSetupCompleted",
146 "A link was setup in the context of ML setup with an AP MLD. "
147 "Provides ID of the setup link and AP MAC address",
149 "ns3::StaWifiMac::LinkSetupCallback")
150 .AddTraceSource("DeAssoc",
151 "Association with an access point lost. If this is an MLD "
152 "that disassociated with an AP MLD, the AP MLD address is provided.",
154 "ns3::Mac48Address::TracedCallback")
155 .AddTraceSource("BeaconArrival",
156 "Time of beacons arrival from associated AP",
158 "ns3::Time::TracedCallback")
159 .AddTraceSource("ReceivedBeaconInfo",
160 "Information about every received Beacon frame",
162 "ns3::ApInfo::TracedCallback")
163 .AddTraceSource("EmlsrLinkSwitch",
164 "Trace start/end of EMLSR link switch events. Specifically, this trace "
165 "is fired: (i) when a PHY _operating on a link_ starts switching to "
166 "another link, thus the PHY is disconnected from the previous link; "
167 "(ii) when a PHY is connected to a new link after performing a channel "
168 "switch. This trace provides: the ID of the previous link, in "
169 "case the PHY is disconnected, or the ID of the new link, in case the "
170 "PHY is connected; a pointer to the PHY that switches link; a boolean "
171 "value indicating if the PHY is connected to (true) or disconnected "
172 "from (false) the given link.",
174 "ns3::StaWifiMac::EmlsrLinkSwitchCallback");
175 return tid;
176}
177
180 m_aid(0),
182{
183 NS_LOG_FUNCTION(this);
184
185 // Let the lower layers know that we are acting as a non-AP STA in
186 // an infrastructure BSS.
188}
189
190void
192{
193 NS_LOG_FUNCTION(this);
194 // an EMLSR client must perform ML setup by using its main PHY
196 {
197 auto mainPhyId = m_emlsrManager->GetMainPhyId();
198 auto linkId = GetLinkForPhy(mainPhyId);
199 NS_ASSERT(linkId);
200 m_assocManager->SetAttribute(
201 "AllowedLinks",
202 AttributeContainerValue<UintegerValue>(std::list<uint8_t>{*linkId}));
203 }
204 if (m_emlsrManager)
205 {
206 m_emlsrManager->Initialize();
207 }
209 {
210 m_powerSaveManager->Initialize();
211 }
215}
216
217void
219{
220 NS_LOG_FUNCTION(this);
221 if (m_assocManager)
222 {
223 m_assocManager->Dispose();
224 }
225 m_assocManager = nullptr;
227 {
228 m_powerSaveManager->Dispose();
229 }
230 m_powerSaveManager = nullptr;
231 if (m_emlsrManager)
232 {
233 m_emlsrManager->Dispose();
234 }
235 m_emlsrManager = nullptr;
236 for (auto& [phyId, event] : m_emlsrLinkSwitch)
237 {
238 event.Cancel();
239 }
240 m_emlsrLinkSwitch.clear();
242}
243
248
249void
254
259
260std::unique_ptr<WifiMac::LinkEntity>
262{
263 return std::make_unique<StaLinkEntity>();
264}
265
267StaWifiMac::GetLink(uint8_t linkId) const
268{
269 return static_cast<StaLinkEntity&>(WifiMac::GetLink(linkId));
270}
271
273StaWifiMac::GetStaLink(const std::unique_ptr<WifiMac::LinkEntity>& link) const
274{
275 return static_cast<StaLinkEntity&>(*link);
276}
277
278int64_t
280{
281 NS_LOG_FUNCTION(this << stream);
282 m_probeDelay->SetStream(stream);
283 auto currentStream = stream + 1;
284 currentStream += WifiMac::AssignStreams(currentStream);
285 return (currentStream - stream);
286}
287
288void
290{
291 NS_LOG_FUNCTION(this << assocManager);
292 m_assocManager = assocManager;
293 m_assocManager->SetStaWifiMac(this);
294}
295
298{
299 // non-EHT devices can only perform legacy association
301}
302
303void
305{
306 NS_LOG_FUNCTION(this << powerSaveManager);
307 m_powerSaveManager = powerSaveManager;
308 m_powerSaveManager->SetWifiMac(this);
309}
310
316
317void
319{
320 NS_LOG_FUNCTION(this << emlsrManager);
321 m_emlsrManager = emlsrManager;
322 m_emlsrManager->SetWifiMac(this);
323}
324
327{
328 return m_emlsrManager;
329}
330
331uint16_t
333{
334 NS_ASSERT_MSG(IsAssociated(), "This station is not associated to any AP");
335 return m_aid;
336}
337
338void
340{
341 NS_LOG_FUNCTION(this << enable);
342 m_activeProbing = enable;
343 if (m_state == SCANNING)
344 {
345 NS_LOG_DEBUG("STA is still scanning, reset scanning process");
347 }
348}
349
350bool
352{
353 return m_activeProbing;
354}
355
356void
357StaWifiMac::SetWifiPhys(const std::vector<Ptr<WifiPhy>>& phys)
358{
359 NS_LOG_FUNCTION(this);
361 for (auto& phy : phys)
362 {
363 phy->SetCapabilitiesChangedCallback(
365 }
366}
367
369StaWifiMac::GetCurrentChannel(uint8_t linkId) const
370{
371 auto phy = GetWifiPhy(linkId);
372 const auto width = phy->GetOperatingChannel().IsOfdm() ? MHz_u{20} : phy->GetChannelWidth();
373 uint8_t ch = phy->GetPrimaryChannelNumber(width);
374 return {ch, phy->GetPhyBand()};
375}
376
377void
378StaWifiMac::NotifyEmlsrModeChanged(uint8_t txLinkId, const std::set<uint8_t>& linkIds)
379{
380 std::stringstream ss;
381 if (g_log.IsEnabled(ns3::LOG_FUNCTION))
382 {
383 std::copy(linkIds.cbegin(), linkIds.cend(), std::ostream_iterator<uint16_t>(ss, " "));
384 }
385 NS_LOG_FUNCTION(this << txLinkId << ss.str());
386
387 for (const auto& [linkId, lnk] : GetLinks())
388 {
389 auto& link = GetStaLink(lnk);
390
391 if (!linkIds.empty())
392 {
393 // enabling EMLSR mode on EMLSR links
394 /**
395 * 802.11be D7.0 Sec. 35.3.17
396 * When a non-AP MLD [...] intends to enable the EMLSR mode on the EMLSR link(s), then:
397 * - The non-AP MLD shall operate in the EMLSR mode on the EMLSR link(s) and the other
398 * non-AP STA(s) affiliated with the non-AP MLD operating on the corresponding EMLSR
399 * link(s), which did not transmit the EML Operating Mode Notification frame, shall
400 * transition to active mode without being required to transmit a frame with the Power
401 * Management subfield set to 0
402 */
403 const auto enabled = linkIds.contains(linkId);
404 if (enabled)
405 {
406 link.pmMode = WIFI_PM_ACTIVE;
407 }
408 link.emlsrEnabled = enabled;
409 }
410 else
411 {
412 // disabling EMLSR mode
413 /**
414 * 802.11be D7.0 Sec. 35.3.17
415 * When a non-AP MLD [...] intends to disable the EMLSR mode, then:
416 * - The non-AP MLD shall disable the EMLSR mode and the other non-AP STA(s) affiliated
417 * with the non-AP MLD operating on the corresponding EMLSR link(s), which did not
418 * transmit the EML Operating Mode Notification frame, shall transition to power save
419 * mode without being required to transmit a frame with the Power Management subfield
420 * set to 1
421 */
422 if (linkId != txLinkId && link.emlsrEnabled)
423 {
424 link.pmMode = WIFI_PM_POWERSAVE;
425 }
426 link.emlsrEnabled = false;
427 }
428 }
429}
430
431bool
432StaWifiMac::IsEmlsrLink(uint8_t linkId) const
433{
434 return GetLink(linkId).emlsrEnabled;
435}
436
438StaWifiMac::GetProbeRequest(uint8_t linkId) const
439{
441 probe.Get<Ssid>() = GetSsid();
442 auto supportedRates = GetSupportedRates(linkId);
443 probe.Get<SupportedRates>() = supportedRates.rates;
444 probe.Get<ExtendedSupportedRatesIE>() = supportedRates.extendedRates;
445 if (GetWifiPhy(linkId)->GetPhyBand() == WIFI_PHY_BAND_2_4GHZ)
446 {
447 DsssParameterSet params;
448 params.SetCurrentChannel(GetWifiPhy(linkId)->GetChannelNumber());
449 probe.Get<DsssParameterSet>() = params;
450 }
451 if (GetHtSupported(linkId))
452 {
454 probe.Get<HtCapabilities>() = GetHtCapabilities(linkId);
455 }
456 if (GetVhtSupported(linkId))
457 {
458 probe.Get<VhtCapabilities>() = GetVhtCapabilities(linkId);
459 }
460 if (GetHeSupported())
461 {
462 probe.Get<HeCapabilities>() = GetHeCapabilities(linkId);
463 if (Is6GhzBand(linkId))
464 {
466 }
467 }
468 if (GetEhtSupported())
469 {
470 probe.Get<EhtCapabilities>() = GetEhtCapabilities(linkId);
471 }
472 return probe;
473}
474
477 const std::vector<uint8_t>& apLinkIds,
478 std::optional<uint8_t> apMldId) const
479{
480 NS_LOG_FUNCTION(this << linkId << apMldId.has_value());
481 auto req = GetProbeRequest(linkId);
482
484 {
485 NS_LOG_DEBUG("Legacy association, not including Multi-link Element");
486 return req;
487 }
488
489 req.Get<MultiLinkElement>() = GetProbeReqMultiLinkElement(apLinkIds, apMldId);
490 return req;
491}
492
493void
495 uint8_t linkId,
496 const Mac48Address& addr1,
497 const Mac48Address& addr3)
498{
499 NS_LOG_FUNCTION(this << linkId << addr1 << addr3);
501 hdr.SetAddr1(addr1);
503 hdr.SetAddr3(addr3);
504 hdr.SetDsNotFrom();
505 hdr.SetDsNotTo();
506
507 auto packet = Create<Packet>();
508 packet->AddHeader(probeReq);
509
510 if (!GetQosSupported())
511 {
512 GetTxop()->Queue(Create<WifiMpdu>(packet, hdr));
513 }
514 // "A QoS STA that transmits a Management frame determines access category used
515 // for medium access in transmission of the Management frame as follows
516 // (If dot11QMFActivated is false or not present)
517 // — If the Management frame is individually addressed to a non-QoS STA, category
518 // AC_BE should be selected.
519 // — If category AC_BE was not selected by the previous step, category AC_VO
520 // shall be selected." (Sec. 10.2.3.2 of 802.11-2020)
521 else
522 {
523 GetVOQueue()->Queue(Create<WifiMpdu>(packet, hdr));
524 }
525}
526
527void
529{
530 NS_LOG_FUNCTION(this << linkId);
531
533 psPoll.SetDsNotFrom();
534 psPoll.SetDsTo();
535 psPoll.SetNoRetry();
536 psPoll.SetNoMoreFragments();
537 psPoll.SetPowerManagement();
538 // The Duration/ID field contains the AID value assigned to the STA transmitting the frame by
539 // the AP in the (Re)Association Response frame that established that STA’s current association,
540 // with the two MSBs set to 1 (Sec. 9.3.1.5.2 of 802.11-2020)
541 psPoll.SetId(GetAssociationId() | 0xC000);
542 auto fem = GetLink(linkId).feManager;
543 psPoll.SetAddr1(fem->GetBssid());
544 psPoll.SetAddr2(fem->GetAddress());
545
546 // A non-S1G STA shall send PS-Poll frames using the access category AC_BE. This reduces the
547 // likelihood of collisions following a Beacon frame. (802.11-2020 Section 10.2.3.2)
549 txop->Queue(Create<WifiMpdu>(Create<Packet>(), psPoll));
550}
551
552std::variant<MgtAssocRequestHeader, MgtReassocRequestHeader>
553StaWifiMac::GetAssociationRequest(bool isReassoc, uint8_t linkId) const
554{
555 NS_LOG_FUNCTION(this << isReassoc << +linkId);
556
557 std::variant<MgtAssocRequestHeader, MgtReassocRequestHeader> mgtFrame;
558
559 if (isReassoc)
560 {
562 reassoc.m_currentApAddr = GetBssid(linkId);
563 mgtFrame = std::move(reassoc);
564 }
565 else
566 {
567 mgtFrame = MgtAssocRequestHeader();
568 }
569
570 // lambda to set the fields of the (Re)Association Request
571 auto fill = [&](auto&& frame) {
572 frame.template Get<Ssid>() = GetSsid();
573 auto supportedRates = GetSupportedRates(linkId);
574 frame.template Get<SupportedRates>() = supportedRates.rates;
575 frame.template Get<ExtendedSupportedRatesIE>() = supportedRates.extendedRates;
576 frame.m_capability = GetCapabilities(linkId);
577 frame.m_listenInterval = m_powerSaveManager ? m_powerSaveManager->GetListenInterval() : 1;
578 if (GetHtSupported(linkId))
579 {
580 frame.template Get<ExtendedCapabilities>() = GetExtendedCapabilities();
581 frame.template Get<HtCapabilities>() = GetHtCapabilities(linkId);
582 }
583 if (GetVhtSupported(linkId))
584 {
585 frame.template Get<VhtCapabilities>() = GetVhtCapabilities(linkId);
586 }
587 if (GetHeSupported())
588 {
589 frame.template Get<HeCapabilities>() = GetHeCapabilities(linkId);
590 if (Is6GhzBand(linkId))
591 {
592 frame.template Get<He6GhzBandCapabilities>() = GetHe6GhzBandCapabilities(linkId);
593 }
594 }
595 if (GetEhtSupported())
596 {
597 frame.template Get<EhtCapabilities>() = GetEhtCapabilities(linkId);
598 }
599 };
600
601 std::visit(fill, mgtFrame);
602 return mgtFrame;
603}
604
606StaWifiMac::GetBasicMultiLinkElement(bool isReassoc, uint8_t linkId) const
607{
608 NS_LOG_FUNCTION(this << isReassoc << +linkId);
609
611 // The Common info field of the Basic Multi-Link element carried in the (Re)Association
612 // Request frame shall include the MLD MAC address, the MLD Capabilities and Operations,
613 // and the EML Capabilities subfields, and shall not include the Link ID Info, the BSS
614 // Parameters Change Count, and the Medium Synchronization Delay Information subfields
615 // (Sec. 35.3.5.4 of 802.11be D2.0)
616 // TODO Add the MLD Capabilities and Operations subfield
617 multiLinkElement.SetMldMacAddress(GetAddress());
618
619 if (m_emlsrManager) // EMLSR Manager is only installed if EMLSR is activated
620 {
621 multiLinkElement.SetEmlsrSupported(true);
622 TimeValue time;
623 m_emlsrManager->GetAttribute("EmlsrPaddingDelay", time);
624 multiLinkElement.SetEmlsrPaddingDelay(time.Get());
625 m_emlsrManager->GetAttribute("EmlsrTransitionDelay", time);
626 multiLinkElement.SetEmlsrTransitionDelay(time.Get());
627 // When the Transition Timeout subfield is included in a frame sent by a non-AP STA
628 // affiliated with a non-AP MLD, the Transition Timeout subfield is reserved
629 // (Section 9.4.2.312.2.3 of 802.11be D2.3)
630 // The Medium Synchronization Delay Information subfield in the Common Info subfield is
631 // not present if the Basic Multi-Link element is sent by a non-AP STA. (Section
632 // 9.4.2.312.2.3 of 802.11be D3.1)
633 }
634
635 // The MLD Capabilities And Operations subfield is present in the Common Info field of the
636 // Basic Multi-Link element carried in Beacon, Probe Response, (Re)Association Request, and
637 // (Re)Association Response frames. (Sec. 9.4.2.312.2.3 of 802.11be D3.1)
638 auto& mldCapabilities = multiLinkElement.GetCommonInfoBasic().m_mldCapabilities;
639 mldCapabilities.emplace();
640 mldCapabilities->maxNSimultaneousLinks = GetNLinks() - 1; // assuming STR for now
641 mldCapabilities->srsSupport = 0;
642
643 auto ehtConfiguration = GetEhtConfiguration();
644 NS_ASSERT(ehtConfiguration);
645
646 mldCapabilities->tidToLinkMappingSupport =
647 static_cast<uint8_t>(ehtConfiguration->m_tidLinkMappingSupport);
648 mldCapabilities->freqSepForStrApMld = 0; // not supported yet
649 mldCapabilities->aarSupport = 0; // not supported yet
650
651 // For each requested link in addition to the link on which the (Re)Association Request
652 // frame is transmitted, the Link Info field of the Basic Multi-Link element carried
653 // in the (Re)Association Request frame shall contain the corresponding Per-STA Profile
654 // subelement(s).
655 for (const auto& [index, link] : GetLinks())
656 {
657 const auto& staLink = GetStaLink(link);
658
659 if (index != linkId && staLink.bssid.has_value())
660 {
661 multiLinkElement.AddPerStaProfileSubelement();
662 auto& perStaProfile = multiLinkElement.GetPerStaProfile(
663 multiLinkElement.GetNPerStaProfileSubelements() - 1);
664 // The Link ID subfield of the STA Control field of the Per-STA Profile subelement
665 // for the corresponding non-AP STA that requests a link for multi-link (re)setup
666 // with the AP MLD is set to the link ID of the AP affiliated with the AP MLD that
667 // is operating on that link. The link ID is obtained during multi-link discovery
668 perStaProfile.SetLinkId(index);
669 // For each Per-STA Profile subelement included in the Link Info field, the
670 // Complete Profile subfield of the STA Control field shall be set to 1
671 perStaProfile.SetCompleteProfile();
672 // The MAC Address Present subfield indicates the presence of the STA MAC Address
673 // subfield in the STA Info field and is set to 1 if the STA MAC Address subfield
674 // is present in the STA Info field; otherwise set to 0. An STA sets this subfield
675 // to 1 when the element carries complete profile.
676 perStaProfile.SetStaMacAddress(staLink.feManager->GetAddress());
677 perStaProfile.SetAssocRequest(GetAssociationRequest(isReassoc, index));
678 }
679 }
680
681 return multiLinkElement;
682}
683
685StaWifiMac::GetProbeReqMultiLinkElement(const std::vector<uint8_t>& apLinkIds,
686 std::optional<uint8_t> apMldId) const
687{
688 // IEEE 802.11be D6.0 9.4.2.321.3
690 if (apMldId.has_value())
691 {
692 mle.SetApMldId(*apMldId);
693 }
694
695 for (const auto apLinkId : apLinkIds)
696 {
698 auto& perStaProfile = mle.GetPerStaProfile(mle.GetNPerStaProfileSubelements() - 1);
699 perStaProfile.SetLinkId(apLinkId);
700 // Current support limited to Complete Profile request per link ID
701 // TODO: Add support for Partial Per-STA Profile request
702 perStaProfile.SetCompleteProfile();
703 };
704
705 return mle;
706}
707
708std::vector<TidToLinkMapping>
710{
711 NS_LOG_FUNCTION(this << apNegSupport);
712
713 auto ehtConfig = GetEhtConfiguration();
714 NS_ASSERT(ehtConfig);
715
716 auto negSupport = ehtConfig->m_tidLinkMappingSupport;
717
719 "Cannot request TID-to-Link Mapping if negotiation is not supported");
720
721 // store the mappings, so that we can enforce them when the AP MLD accepts them
722 m_dlTidLinkMappingInAssocReq = ehtConfig->GetTidLinkMapping(WifiDirection::DOWNLINK);
723 m_ulTidLinkMappingInAssocReq = ehtConfig->GetTidLinkMapping(WifiDirection::UPLINK);
724
728 negSupport == WifiTidToLinkMappingNegSupport::SAME_LINK_SET && !mappingValidForNegType1,
729 "Mapping TIDs to distinct link sets is incompatible with negotiation support of 1");
730
731 if (apNegSupport == WifiTidToLinkMappingNegSupport::SAME_LINK_SET && !mappingValidForNegType1)
732 {
733 // If the TID-to-link Mapping Negotiation Support subfield value received from a peer
734 // MLD is equal to 1, the MLD that initiates a TID-to-link mapping negotiation with the
735 // peer MLD shall send only the TID-to-link Mapping element where all TIDs are mapped to
736 // the same link set (Sec. 35.3.7.1.3 of 802.11be D3.1). We use default mapping to meet
737 // this requirement.
738 NS_LOG_DEBUG("Using default mapping because AP MLD advertised negotiation support of 1");
741 }
742
743 std::vector<TidToLinkMapping> ret(1);
744
745 ret.back().m_control.direction = WifiDirection::DOWNLINK;
746
747 // lambda to fill the last TID-to-Link Mapping IE in the vector to return
748 auto fillIe = [&ret](const auto& mapping) {
749 ret.back().m_control.defaultMapping = mapping.empty();
750
751 for (const auto& [tid, linkSet] : mapping)
752 {
753 // At any point in time, a TID shall always be mapped to at least one setup link both
754 // in DL and UL, which means that a TID-to-link mapping change is only valid and
755 // successful if it will not result in having any TID for which the link set for DL
756 // or UL is made of zero setup links (Sec. 35.3.7.1.1 of 802.11be D3.1)
757 NS_ABORT_MSG_IF(linkSet.empty(), "Cannot map a TID to an empty link set");
758 ret.back().SetLinkMappingOfTid(tid, linkSet);
759 }
760 };
761
763
765 {
766 ret.back().m_control.direction = WifiDirection::BOTH_DIRECTIONS;
767 return ret;
768 }
769
770 ret.emplace_back();
771 ret.back().m_control.direction = WifiDirection::UPLINK;
773
774 return ret;
775}
776
777void
779{
780 // find the link where the (Re)Association Request has to be sent
781 auto it = GetLinks().cbegin();
782 while (it != GetLinks().cend())
783 {
784 if (GetStaLink(it->second).sendAssocReq)
785 {
786 break;
787 }
788 it++;
789 }
790 NS_ABORT_MSG_IF(it == GetLinks().cend(),
791 "No link selected to send the (Re)Association Request");
792 uint8_t linkId = it->first;
793 auto& link = GetLink(linkId);
794 NS_ABORT_MSG_IF(!link.bssid.has_value(),
795 "No BSSID set for the link on which the (Re)Association Request is to be sent");
796
797 NS_LOG_FUNCTION(this << *link.bssid << isReassoc);
798 WifiMacHeader hdr;
800 hdr.SetAddr1(*link.bssid);
801 hdr.SetAddr2(link.feManager->GetAddress());
802 hdr.SetAddr3(*link.bssid);
803 hdr.SetDsNotFrom();
804 hdr.SetDsNotTo();
805 Ptr<Packet> packet = Create<Packet>();
806
807 auto frame = GetAssociationRequest(isReassoc, linkId);
808
809 // include a Multi-Link Element if this device performs ML Setup and the AP is a multi-link
810 // device; if the AP MLD has indicated a support of TID-to-link mapping negotiation, also
811 // include the TID-to-link Mapping element(s)
813 GetWifiRemoteStationManager(linkId)->GetMldAddress(*link.bssid).has_value())
814 {
815 auto addMle = [&](auto&& frame) {
816 frame.template Get<MultiLinkElement>() = GetBasicMultiLinkElement(isReassoc, linkId);
817 };
818 std::visit(addMle, frame);
819
821 if (const auto& mldCapabilities =
822 GetWifiRemoteStationManager(linkId)->GetStationMldCapabilities(*link.bssid);
823 mldCapabilities && (negSupport = static_cast<WifiTidToLinkMappingNegSupport>(
824 mldCapabilities->get().tidToLinkMappingSupport)) >
826 {
827 auto addTlm = [&](auto&& frame) {
828 frame.template Get<TidToLinkMapping>() = GetTidToLinkMappingElements(negSupport);
829 };
830 std::visit(addTlm, frame);
831 }
832 }
833
834 if (!isReassoc)
835 {
836 packet->AddHeader(std::get<MgtAssocRequestHeader>(frame));
837 }
838 else
839 {
840 packet->AddHeader(std::get<MgtReassocRequestHeader>(frame));
841 }
842
843 if (!GetQosSupported())
844 {
845 GetTxop()->Queue(Create<WifiMpdu>(packet, hdr));
846 }
847 // "A QoS STA that transmits a Management frame determines access category used
848 // for medium access in transmission of the Management frame as follows
849 // (If dot11QMFActivated is false or not present)
850 // — If the Management frame is individually addressed to a non-QoS STA, category
851 // AC_BE should be selected.
852 // — If category AC_BE was not selected by the previous step, category AC_VO
853 // shall be selected." (Sec. 10.2.3.2 of 802.11-2020)
854 else if (!GetWifiRemoteStationManager(linkId)->GetQosSupported(*link.bssid))
855 {
856 GetBEQueue()->Queue(Create<WifiMpdu>(packet, hdr));
857 }
858 else
859 {
860 GetVOQueue()->Queue(Create<WifiMpdu>(packet, hdr));
861 }
862
863 if (m_assocRequestEvent.IsPending())
864 {
865 m_assocRequestEvent.Cancel();
866 }
869}
870
871void
873{
874 NS_LOG_FUNCTION(this);
875 switch (m_state)
876 {
877 case ASSOCIATED:
878 return;
879 case SCANNING:
880 /* we have initiated active or passive scanning, continue to wait
881 and gather beacons or probe responses until the scanning timeout
882 */
883 break;
884 case UNASSOCIATED:
885 /* we were associated but we missed a bunch of beacons
886 * so we should assume we are not associated anymore.
887 * We try to initiate a scan now.
888 */
889 m_linkDown();
891 break;
892 case WAIT_ASSOC_RESP:
893 /* we have sent an association request so we do not need to
894 re-send an association request right now. We just need to
895 wait until either assoc-request-timeout or until
896 we get an association response.
897 */
898 case REFUSED:
899 /* we have sent an association request and received a negative
900 association response. We wait until someone restarts an
901 association with a given SSID.
902 */
903 break;
904 }
905}
906
907void
909{
910 NS_LOG_FUNCTION(this);
911 if (!m_enableScanning)
912 {
913 NS_LOG_DEBUG("Scanning disabled");
914 return;
915 }
918
919 WifiScanParams scanParams;
920 scanParams.ssid = GetSsid();
921 for (const auto& [id, link] : GetLinks())
922 {
924 (link->phy->HasFixedPhyBand()) ? WifiScanParams::Channel{0, link->phy->GetPhyBand()}
926
927 scanParams.channelList.push_back(channel);
928 }
929 if (m_activeProbing)
930 {
931 scanParams.type = WifiScanType::ACTIVE;
932 scanParams.probeDelay = MicroSeconds(m_probeDelay->GetValue());
933 scanParams.minChannelTime = scanParams.maxChannelTime = m_probeRequestTimeout;
934 }
935 else
936 {
937 scanParams.type = WifiScanType::PASSIVE;
939 }
940
941 m_assocManager->StartScanning(std::move(scanParams));
942}
943
944void
945StaWifiMac::ScanningTimeout(const std::optional<ApInfo>& bestAp)
946{
947 NS_LOG_FUNCTION(this);
948
949 if (!bestAp.has_value())
950 {
951 NS_LOG_DEBUG("Exhausted list of candidate AP; restart scanning");
953 return;
954 }
955
956 NS_LOG_DEBUG("Attempting to associate with AP: " << *bestAp);
957 ApplyOperationalSettings(bestAp->m_frame, bestAp->m_apAddr, bestAp->m_bssid, bestAp->m_linkId);
958 // reset info on links to setup
959 for (auto& [id, link] : GetLinks())
960 {
961 auto& staLink = GetStaLink(link);
962 staLink.sendAssocReq = false;
963 staLink.bssid = std::nullopt;
964 }
965 // send Association Request on the link where the Beacon/Probe Response was received
966 GetLink(bestAp->m_linkId).sendAssocReq = true;
967 GetLink(bestAp->m_linkId).bssid = bestAp->m_bssid;
968 std::shared_ptr<CommonInfoBasicMle> mleCommonInfo;
969 // update info on links to setup (11be MLDs only)
970 const auto& mle =
971 std::visit([](auto&& frame) { return frame.template Get<MultiLinkElement>(); },
972 bestAp->m_frame);
973 std::map<uint8_t, uint8_t> swapInfo;
974 for (const auto& [localLinkId, apLinkId, bssid] : bestAp->m_setupLinks)
975 {
976 NS_ASSERT_MSG(mle, "We get here only for ML setup");
977 NS_LOG_DEBUG("Setting up link (local ID=" << +localLinkId << ", AP ID=" << +apLinkId
978 << ")");
979 GetLink(localLinkId).bssid = bssid;
980 if (!mleCommonInfo)
981 {
982 mleCommonInfo = std::make_shared<CommonInfoBasicMle>(mle->GetCommonInfoBasic());
983 }
984 GetWifiRemoteStationManager(localLinkId)->AddStationMleCommonInfo(bssid, mleCommonInfo);
985 swapInfo.emplace(localLinkId, apLinkId);
986 }
987
988 SwapLinks(swapInfo);
989
990 // lambda to get beacon interval from Beacon or Probe Response
991 auto getBeaconIntervalAndTimestamp = [](auto&& frame) {
992 using T = std::decay_t<decltype(frame)>;
993 if constexpr (std::is_same_v<T, MgtBeaconHeader> ||
994 std::is_same_v<T, MgtProbeResponseHeader>)
995 {
996 return std::make_pair(MicroSeconds(frame.m_beaconInterval),
997 MicroSeconds(frame.GetTimestamp()));
998 }
999 else
1000 {
1001 NS_ABORT_MSG("Unexpected frame type");
1002 return std::make_pair(Seconds(0), Seconds(0));
1003 }
1004 };
1005 const auto [beaconInterval, timestamp] =
1006 std::visit(getBeaconIntervalAndTimestamp, bestAp->m_frame);
1007 Time delay = beaconInterval * m_maxMissedBeacons;
1008 // restart beacon watchdog
1009 RestartBeaconWatchdog(delay);
1010
1012 {
1013 m_powerSaveManager->NotifyBeaconIntervalAndTimestamp(beaconInterval,
1014 timestamp,
1015 bestAp->m_linkId);
1016 }
1017
1020}
1021
1022void
1029
1030void
1032{
1033 NS_LOG_FUNCTION(this);
1034
1036 {
1037 if (m_beaconWatchdog.IsPending())
1038 {
1039 m_beaconWatchdog.Cancel();
1040 }
1043 this);
1044 return;
1045 }
1046 NS_LOG_DEBUG("beacon missed");
1047 // We need to switch to the UNASSOCIATED state. However, if we are receiving a frame, wait
1048 // until the RX is completed (otherwise, crashes may occur if we are receiving a MU frame
1049 // because its reception requires the STA-ID). We need to check that a PHY is operating on
1050 // the given link, because this may (temporarily) not be the case for EMLSR clients.
1051 Time delay;
1052 for (const auto& [id, link] : GetLinks())
1053 {
1054 if (link->phy && link->phy->IsStateRx())
1055 {
1056 delay = std::max(delay, link->phy->GetDelayUntilIdle());
1057 }
1058 }
1060}
1061
1062void
1064{
1065 NS_LOG_FUNCTION(this);
1066
1067 Mac48Address apAddr; // the AP address to trace (MLD address in case of ML setup)
1068
1069 for (const auto& [id, link] : GetLinks())
1070 {
1071 auto& bssid = GetStaLink(link).bssid;
1072 if (bssid)
1073 {
1074 apAddr = GetWifiRemoteStationManager(id)->GetMldAddress(*bssid).value_or(*bssid);
1075 }
1076 bssid = std::nullopt; // link is no longer setup
1077 }
1078
1079 NS_LOG_DEBUG("Set state to UNASSOCIATED and start scanning");
1081 // cancel the association request timer (see issue #862)
1082 m_assocRequestEvent.Cancel();
1083 m_deAssocLogger(apAddr);
1084 m_aid = 0; // reset AID
1086 {
1087 m_powerSaveManager->NotifyDisassociation();
1088 }
1090}
1091
1092void
1094{
1095 NS_LOG_FUNCTION(this << delay);
1096
1098 if (Simulator::GetDelayLeft(m_beaconWatchdog) < delay && m_beaconWatchdog.IsExpired())
1099 {
1100 NS_LOG_DEBUG("really restart watchdog.");
1102 }
1103}
1104
1105bool
1107{
1108 return m_state == ASSOCIATED;
1109}
1110
1111bool
1113{
1114 return m_state == WAIT_ASSOC_RESP;
1115}
1116
1117std::set<uint8_t>
1119{
1120 if (!IsAssociated())
1121 {
1122 return {};
1123 }
1124
1125 std::set<uint8_t> linkIds;
1126 for (const auto& [id, link] : GetLinks())
1127 {
1128 if (GetStaLink(link).bssid)
1129 {
1130 linkIds.insert(id);
1131 }
1132 }
1133 return linkIds;
1134}
1135
1138{
1139 for (const auto& [id, link] : GetLinks())
1140 {
1141 if (GetStaLink(link).bssid == remoteAddr)
1142 {
1143 // the remote address is the address of the AP we are associated with;
1144 return link->feManager->GetAddress();
1145 }
1146 }
1147
1148 // the remote address is unknown
1149
1150 if (!IsAssociated())
1151 {
1152 return GetAddress();
1153 }
1154
1155 // if this device has performed ML setup with an AP MLD, return the MLD address of this device
1156 const auto linkIds = GetSetupLinkIds();
1157 NS_ASSERT(!linkIds.empty());
1158 const auto linkId = *linkIds.cbegin(); // a setup link
1159
1160 if (GetLink(linkId).stationManager->GetMldAddress(GetBssid(linkId)))
1161 {
1162 return GetAddress();
1163 }
1164
1165 // return the address of the link used to perform association with the AP
1166 return GetLink(linkId).feManager->GetAddress();
1167}
1168
1169bool
1171{
1172 return IsAssociated();
1173}
1174
1175void
1181
1182void
1184{
1185 NS_LOG_FUNCTION(this << *mpdu << to << from);
1186
1187 auto& hdr = mpdu->GetHeader();
1188
1189 // the Receiver Address (RA) and the Transmitter Address (TA) are the MLD addresses only for
1190 // non-broadcast data frames exchanged between two MLDs
1191 auto linkIds = GetSetupLinkIds();
1192 NS_ASSERT(!linkIds.empty());
1193 uint8_t linkId = *linkIds.begin();
1194 const auto apMldAddr = GetWifiRemoteStationManager(linkId)->GetMldAddress(GetBssid(linkId));
1195
1196 hdr.SetAddr1(apMldAddr.value_or(GetBssid(linkId)));
1197 hdr.SetAddr2(apMldAddr ? GetAddress() : GetFrameExchangeManager(linkId)->GetAddress());
1198 hdr.SetAddr3(to);
1199 hdr.SetDsNotFrom();
1200 hdr.SetDsTo();
1201
1202 auto txop = hdr.IsQosData() ? StaticCast<Txop>(GetQosTxop(hdr.GetQosTid())) : GetTxop();
1203 NS_ASSERT(txop);
1204 txop->Queue(mpdu);
1205}
1206
1207void
1209{
1210 NS_LOG_FUNCTION(this << linkId << reason);
1211
1212 GetMacQueueScheduler()->BlockAllQueues(reason, {linkId});
1213}
1214
1215void
1216StaWifiMac::UnblockTxOnLink(std::set<uint8_t> linkIds, WifiQueueBlockedReason reason)
1217{
1218 // shuffle link IDs not to unblock links always in the same order
1219 std::vector<uint8_t> shuffledLinkIds(linkIds.cbegin(), linkIds.cend());
1220 Shuffle(shuffledLinkIds.begin(), shuffledLinkIds.end(), m_shuffleLinkIdsGen.GetRv());
1221
1222 std::stringstream ss;
1223 if (g_log.IsEnabled(ns3::LOG_FUNCTION))
1224 {
1225 std::copy(shuffledLinkIds.cbegin(),
1226 shuffledLinkIds.cend(),
1227 std::ostream_iterator<uint16_t>(ss, " "));
1228 }
1229 NS_LOG_FUNCTION(this << reason << ss.str());
1230
1231 for (const auto linkId : shuffledLinkIds)
1232 {
1233 std::map<AcIndex, bool> hasFramesToTransmit;
1234 for (const auto& [acIndex, ac] : wifiAcList)
1235 {
1236 // save the status of the AC queues before unblocking the queues
1237 hasFramesToTransmit[acIndex] = GetQosTxop(acIndex)->HasFramesToTransmit(linkId);
1238 }
1239
1240 GetMacQueueScheduler()->UnblockAllQueues(reason, {linkId});
1241
1242 for (const auto& [acIndex, ac] : wifiAcList)
1243 {
1244 // request channel access if needed (schedule now because multiple invocations
1245 // of this method may be done in a loop at the caller)
1247 GetQosTxop(acIndex),
1248 linkId,
1249 hasFramesToTransmit[acIndex],
1250 Txop::CHECK_MEDIUM_BUSY); // generate backoff if medium busy
1251 }
1252 }
1253}
1254
1255void
1257{
1258 NS_LOG_FUNCTION(this << *mpdu << +linkId);
1259 // consider the MAC header of the original MPDU (makes a difference for data frames only)
1260 const WifiMacHeader* hdr = &mpdu->GetOriginal()->GetHeader();
1261 Ptr<const Packet> packet = mpdu->GetPacket();
1262 NS_ASSERT(!hdr->IsCtl());
1264 : GetFrameExchangeManager(linkId)->GetAddress();
1265 if (hdr->GetAddr3() == myAddr)
1266 {
1267 NS_LOG_LOGIC("packet sent by us.");
1268 return;
1269 }
1270 if (hdr->GetAddr1() != myAddr && !hdr->GetAddr1().IsGroup())
1271 {
1272 NS_LOG_LOGIC("packet is not for us");
1273 NotifyRxDrop(packet);
1274 return;
1275 }
1276 if (hdr->IsData())
1277 {
1278 if (!IsAssociated())
1279 {
1280 NS_LOG_LOGIC("Received data frame while not associated: ignore");
1281 NotifyRxDrop(packet);
1282 return;
1283 }
1284 if (!(hdr->IsFromDs() && !hdr->IsToDs()))
1285 {
1286 NS_LOG_LOGIC("Received data frame not from the DS: ignore");
1287 NotifyRxDrop(packet);
1288 return;
1289 }
1290 std::set<Mac48Address> apAddresses; // link addresses of AP
1291 for (auto id : GetSetupLinkIds())
1292 {
1293 apAddresses.insert(GetBssid(id));
1294 }
1295 if (!apAddresses.contains(mpdu->GetHeader().GetAddr2()))
1296 {
1297 NS_LOG_LOGIC("Received data frame not from the BSS we are associated with: ignore");
1298 NotifyRxDrop(packet);
1299 return;
1300 }
1301 if (!hdr->HasData())
1302 {
1303 NS_LOG_LOGIC("Received (QoS) Null Data frame: ignore");
1304 NotifyRxDrop(packet);
1305 return;
1306 }
1307 if (m_powerSaveManager && hdr->GetAddr1().IsGroup())
1308 {
1309 m_powerSaveManager->NotifyReceivedGroupcast(mpdu, linkId);
1310 }
1311 if (hdr->IsQosData())
1312 {
1313 if (hdr->IsQosAmsdu())
1314 {
1315 NS_ASSERT(apAddresses.contains(mpdu->GetHeader().GetAddr3()));
1317 packet = nullptr;
1318 }
1319 else
1320 {
1321 ForwardUp(packet, hdr->GetAddr3(), hdr->GetAddr1());
1322 }
1323 }
1324 else
1325 {
1326 ForwardUp(packet, hdr->GetAddr3(), hdr->GetAddr1());
1327 }
1328 return;
1329 }
1330
1331 switch (hdr->GetType())
1332 {
1336 // This is a frame aimed at an AP, so we can safely ignore it.
1337 NotifyRxDrop(packet);
1338 break;
1339
1341 ReceiveBeacon(mpdu, linkId);
1342 break;
1343
1345 ReceiveProbeResp(mpdu, linkId);
1346 break;
1347
1350 ReceiveAssocResp(mpdu, linkId);
1351 break;
1352
1354 if (auto [category, action] = WifiActionHeader::Peek(packet);
1355 category == WifiActionHeader::PROTECTED_EHT &&
1356 action.protectedEhtAction ==
1358 {
1359 // this is handled by the EMLSR Manager
1360 break;
1361 }
1362
1363 default:
1364 // Invoke the receive handler of our parent class to deal with any other frames
1365 WifiMac::Receive(mpdu, linkId);
1366 }
1367
1368 if (m_emlsrManager)
1369 {
1370 m_emlsrManager->NotifyMgtFrameReceived(mpdu, linkId);
1371 }
1372}
1373
1374void
1376{
1377 NS_LOG_FUNCTION(this << *mpdu << +linkId);
1378 const WifiMacHeader& hdr = mpdu->GetHeader();
1379 const auto from = hdr.GetAddr2();
1380 NS_ASSERT(hdr.IsBeacon());
1381
1382 NS_LOG_DEBUG("Beacon received");
1383 MgtBeaconHeader beacon;
1384 mpdu->GetPacket()->PeekHeader(beacon);
1385 const auto& capabilities = beacon.m_capability;
1386 NS_ASSERT(capabilities.IsEss());
1387 bool goodBeacon;
1388 if (IsWaitAssocResp() || IsAssociated())
1389 {
1390 // we have to process this Beacon only if sent by the AP we are associated
1391 // with or from which we are waiting an Association Response frame
1392 auto bssid = GetLink(linkId).bssid;
1393 goodBeacon = bssid.has_value() && (hdr.GetAddr3() == *bssid);
1394 }
1395 else
1396 {
1397 // we retain this Beacon as candidate AP if the supported rates fit the
1398 // configured BSS membership selector
1399 goodBeacon = CheckSupportedRates(beacon, linkId);
1400 }
1401
1402 SnrTag snrTag;
1403 bool found = mpdu->GetPacket()->PeekPacketTag(snrTag);
1404 NS_ASSERT(found);
1405 ApInfo apInfo = {.m_bssid = hdr.GetAddr3(),
1406 .m_apAddr = hdr.GetAddr2(),
1407 .m_snr = snrTag.Get(),
1408 .m_frame = std::move(beacon),
1409 .m_channel = {GetCurrentChannel(linkId)},
1410 .m_linkId = linkId};
1411
1412 if (!m_beaconInfo.IsEmpty())
1413 {
1414 m_beaconInfo(apInfo);
1415 }
1416
1417 RecordCapabilities(beacon, from, linkId);
1418 RecordOperations(beacon, from, linkId);
1419
1420 if (!goodBeacon)
1421 {
1422 NS_LOG_LOGIC("Beacon is not for us");
1423 return;
1424 }
1425 if (m_state == ASSOCIATED)
1426 {
1428 Time delay = MicroSeconds(std::get<MgtBeaconHeader>(apInfo.m_frame).m_beaconInterval *
1430 RestartBeaconWatchdog(delay);
1431 ApplyOperationalSettings(apInfo.m_frame, hdr.GetAddr2(), hdr.GetAddr3(), linkId);
1433 {
1434 m_powerSaveManager->NotifyReceivedBeacon(mpdu, linkId);
1435 }
1436 }
1437 else
1438 {
1439 NS_LOG_DEBUG("Beacon received from " << hdr.GetAddr2());
1440 m_assocManager->NotifyApInfo(std::move(apInfo));
1441 }
1442}
1443
1444void
1446{
1447 NS_LOG_FUNCTION(this << *mpdu << +linkId);
1448 const WifiMacHeader& hdr = mpdu->GetHeader();
1449 NS_ASSERT(hdr.IsProbeResp());
1450
1451 const auto from = hdr.GetAddr2();
1452 NS_LOG_DEBUG("Probe response received from " << from);
1453 MgtProbeResponseHeader probeResp;
1454 mpdu->GetPacket()->PeekHeader(probeResp);
1455
1456 RecordCapabilities(probeResp, from, linkId);
1457 RecordOperations(probeResp, from, linkId);
1458
1459 if (!CheckSupportedRates(probeResp, linkId))
1460 {
1461 return;
1462 }
1463 SnrTag snrTag;
1464 bool found = mpdu->GetPacket()->PeekPacketTag(snrTag);
1465 NS_ASSERT(found);
1466 m_assocManager->NotifyApInfo(ApInfo{.m_bssid = hdr.GetAddr3(),
1467 .m_apAddr = hdr.GetAddr2(),
1468 .m_snr = snrTag.Get(),
1469 .m_frame = std::move(probeResp),
1470 .m_channel = {GetCurrentChannel(linkId)},
1471 .m_linkId = linkId});
1472}
1473
1474void
1476{
1477 NS_LOG_FUNCTION(this << *mpdu << +linkId);
1478 const WifiMacHeader& hdr = mpdu->GetHeader();
1479 NS_ASSERT(hdr.IsAssocResp() || hdr.IsReassocResp());
1480
1481 MgtAssocResponseHeader assocResp;
1482 mpdu->GetPacket()->PeekHeader(assocResp);
1483
1484 RecordCapabilities(assocResp, hdr.GetAddr2(), linkId);
1485 RecordOperations(assocResp, hdr.GetAddr2(), linkId);
1486
1487 if (m_state != WAIT_ASSOC_RESP)
1488 {
1489 return;
1490 }
1491
1492 if (m_assocRequestEvent.IsPending())
1493 {
1494 m_assocRequestEvent.Cancel();
1495 }
1496
1497 std::optional<Mac48Address> apMldAddress;
1498 if (assocResp.m_statusCode.IsSuccess())
1499 {
1500 m_aid = assocResp.m_aid;
1501 NS_LOG_DEBUG((hdr.IsReassocResp() ? "reassociation done" : "association completed"));
1502 ApplyOperationalSettings(assocResp, hdr.GetAddr2(), hdr.GetAddr3(), linkId);
1503 NS_ASSERT(GetLink(linkId).bssid.has_value() && *GetLink(linkId).bssid == hdr.GetAddr3());
1504 SetBssid(hdr.GetAddr3(), linkId);
1507 assocResp.Get<MultiLinkElement>().has_value())
1508 {
1509 // this is an ML setup, trace the setup link
1510 m_setupCompleted(linkId, hdr.GetAddr3());
1511 apMldAddress = GetWifiRemoteStationManager(linkId)->GetMldAddress(hdr.GetAddr3());
1512 NS_ASSERT(apMldAddress);
1513
1514 if (const auto& mldCapabilities =
1515 GetWifiRemoteStationManager(linkId)->GetStationMldCapabilities(hdr.GetAddr3());
1516 mldCapabilities && static_cast<WifiTidToLinkMappingNegSupport>(
1517 mldCapabilities->get().tidToLinkMappingSupport) >
1519 {
1520 // the AP MLD supports TID-to-Link Mapping negotiation, hence we included
1521 // TID-to-Link Mapping element(s) in the Association Request.
1522 if (assocResp.Get<TidToLinkMapping>().empty())
1523 {
1524 // The AP MLD did not include a TID-to-Link Mapping element in the Association
1525 // Response, hence it accepted the mapping, which we can now store.
1526 UpdateTidToLinkMapping(*apMldAddress,
1529 UpdateTidToLinkMapping(*apMldAddress,
1532
1533 // Apply the negotiated TID-to-Link Mapping (if any) for UL direction
1535 }
1536 }
1537 }
1538 else
1539 {
1540 m_assocLogger(hdr.GetAddr3());
1541 }
1542 if (!m_linkUp.IsNull())
1543 {
1544 m_linkUp();
1545 }
1546 }
1547 else
1548 {
1549 // If the link on which the (Re)Association Request frame was received cannot be
1550 // accepted by the AP MLD, the AP MLD shall treat the multi-link (re)setup as a
1551 // failure and shall not accept any requested links. If the link on which the
1552 // (Re)Association Request frame was received is accepted by the AP MLD, the
1553 // multi-link (re)setup is successful. (Sec. 35.3.5.1 of 802.11be D3.1)
1554 NS_LOG_DEBUG("association refused");
1556 StartScanning();
1557 return;
1558 }
1559
1560 // create a list of all local Link IDs. IDs are removed as we find a corresponding
1561 // Per-STA Profile Subelements indicating successful association. Links with
1562 // remaining IDs are not setup
1563 std::list<uint8_t> setupLinks;
1564 for (const auto& [id, link] : GetLinks())
1565 {
1566 setupLinks.push_back(id);
1567 }
1568 if (assocResp.m_statusCode.IsSuccess())
1569 {
1570 setupLinks.remove(linkId);
1571 }
1572
1573 // if a Multi-Link Element is present, this is an ML setup, hence check if we can setup (other)
1574 // links
1575 if (const auto& mle = assocResp.Get<MultiLinkElement>())
1576 {
1577 NS_ABORT_MSG_IF(!GetLink(linkId).bssid.has_value(),
1578 "The link on which the Association Response was received "
1579 "is not a link we requested to setup");
1580 NS_ABORT_MSG_IF(linkId != mle->GetLinkIdInfo(),
1581 "The link ID of the AP that transmitted the Association "
1582 "Response does not match the stored link ID");
1584 mle->GetMldMacAddress(),
1585 "The AP MLD MAC address in the received Multi-Link Element does not "
1586 "match the address stored in the station manager for link "
1587 << +linkId);
1588 // process the Per-STA Profile Subelements in the Multi-Link Element
1589 for (std::size_t elem = 0; elem < mle->GetNPerStaProfileSubelements(); elem++)
1590 {
1591 auto& perStaProfile = mle->GetPerStaProfile(elem);
1592 uint8_t apLinkId = perStaProfile.GetLinkId();
1593 auto it = GetLinks().find(apLinkId);
1594 uint8_t staLinkid = 0;
1595 std::optional<Mac48Address> bssid;
1596 NS_ABORT_MSG_IF(it == GetLinks().cend() ||
1597 !(bssid = GetLink((staLinkid = it->first)).bssid).has_value(),
1598 "Setup for AP link ID " << apLinkId << " was not requested");
1599 NS_ABORT_MSG_IF(*bssid != perStaProfile.GetStaMacAddress(),
1600 "The BSSID in the Per-STA Profile for link ID "
1601 << +staLinkid << " does not match the stored BSSID");
1603 perStaProfile.GetStaMacAddress()) != mle->GetMldMacAddress(),
1604 "The AP MLD MAC address in the received Multi-Link Element does not "
1605 "match the address stored in the station manager for link "
1606 << +staLinkid);
1607 // process the Association Response contained in this Per-STA Profile
1608 MgtAssocResponseHeader assoc = perStaProfile.GetAssocResponse();
1609 RecordCapabilities(assoc, *bssid, staLinkid);
1610 RecordOperations(assoc, *bssid, staLinkid);
1611 if (assoc.m_statusCode.IsSuccess())
1612 {
1613 NS_ABORT_MSG_IF(m_aid != 0 && m_aid != assoc.m_aid,
1614 "AID should be the same for all the links");
1615 m_aid = assoc.m_aid;
1616 NS_LOG_DEBUG("Setup on link " << staLinkid << " completed");
1617 ApplyOperationalSettings(assocResp, *bssid, *bssid, staLinkid);
1618 SetBssid(*bssid, staLinkid);
1619 m_setupCompleted(staLinkid, *bssid);
1621 apMldAddress = GetWifiRemoteStationManager(staLinkid)->GetMldAddress(*bssid);
1622 if (!m_linkUp.IsNull())
1623 {
1624 m_linkUp();
1625 }
1626 }
1627 // remove the ID of the link we setup
1628 setupLinks.remove(staLinkid);
1629 }
1630 if (apMldAddress)
1631 {
1632 // this is an ML setup, trace the MLD address of the AP (only once)
1633 m_assocLogger(*apMldAddress);
1634 }
1635 }
1636 // remaining links in setupLinks are not setup and hence must be disabled
1637 for (const auto& id : setupLinks)
1638 {
1639 GetLink(id).bssid = std::nullopt;
1640 GetLink(id).phy->SetOffMode();
1641 }
1642
1643 // the station that associated with the AP may have dissociated and then associated again.
1644 // In this case, the station may store packets from the previous period in which it was
1645 // associated. Have the station restart access if it has packets queued.
1646 for (const auto& [id, link] : GetLinks())
1647 {
1648 if (GetStaLink(link).bssid)
1649 {
1650 if (const auto txop = GetTxop())
1651 {
1652 txop->StartAccessAfterEvent(id,
1655 }
1656 for (const auto& [acIndex, ac] : wifiAcList)
1657 {
1658 if (const auto edca = GetQosTxop(acIndex))
1659 {
1660 edca->StartAccessAfterEvent(id,
1663 }
1664 }
1665 }
1666 }
1667
1669}
1670
1671void
1673{
1675 {
1676 m_powerSaveManager->NotifyReceivedFrameAfterPsPoll(mpdu, linkId);
1677 }
1678}
1679
1680void
1682{
1684 {
1685 m_powerSaveManager->NotifyRequestAccess(txop, linkId);
1686 }
1687}
1688
1689void
1691{
1693 {
1694 m_powerSaveManager->NotifyChannelReleased(txop, linkId);
1695 }
1696}
1697
1698void
1700{
1701 NS_LOG_FUNCTION(this << linkId);
1702
1703 // STAs operating on setup links may need to transition to a new PM mode after the
1704 // acknowledgement of the Association Response. For this purpose, we connect a callback to
1705 // the PHY TX begin trace to catch the Ack transmitted after the Association Response.
1707 [=, this](WifiConstPsduMap psduMap, WifiTxVector txVector, Watt_u /* txPower */) {
1708 NS_ASSERT_MSG(psduMap.size() == 1 && psduMap.begin()->second->GetNMpdus() == 1 &&
1709 psduMap.begin()->second->GetHeader(0).IsAck(),
1710 "Expected a Normal Ack after Association Response frame");
1711
1712 auto ackDuration =
1713 WifiPhy::CalculateTxDuration(psduMap, txVector, GetLink(linkId).phy->GetPhyBand());
1714
1715 for (const auto& [id, lnk] : GetLinks())
1716 {
1717 auto& link = GetStaLink(lnk);
1718
1719 if (!link.bssid)
1720 {
1721 // link has not been setup
1722 continue;
1723 }
1724
1725 if (id == linkId)
1726 {
1727 /**
1728 * When a link becomes enabled for a non-AP STA that is affiliated with a
1729 * non-AP MLD after successful association with an AP MLD with (Re)Association
1730 * Request/Response frames transmitted on that link [..], the power management
1731 * mode of the non-AP STA, immediately after the acknowledgement of the
1732 * (Re)Association Response frame [..], is active mode.
1733 * (Sec. 35.3.7.1.4 of 802.11be D3.0)
1734 */
1735 // if the user requested this link to be in powersave mode, we have to
1736 // switch PM mode
1737 if (link.pmMode == WIFI_PM_POWERSAVE)
1738 {
1739 Simulator::Schedule(ackDuration,
1741 this,
1742 std::pair<bool, uint8_t>{true, id});
1743 }
1744 link.pmMode = WIFI_PM_ACTIVE;
1745 }
1746 else
1747 {
1748 /**
1749 * When a link becomes enabled for a non-AP STA that is affiliated with a
1750 * non-AP MLD after successful association with an AP MLD with (Re)Association
1751 * Request/Response frames transmitted on another link [..], the power
1752 * management mode of the non-AP STA, immediately after the acknowledgement of
1753 * the (Re)Association Response frame [..], is power save mode, and its power
1754 * state is doze. (Sec. 35.3.7.1.4 of 802.11be D3.0)
1755 */
1756 // if the user requested this link to be in active mode, we have to
1757 // switch PM mode
1758 if (link.pmMode == WIFI_PM_ACTIVE)
1759 {
1760 Simulator::Schedule(ackDuration,
1762 this,
1763 std::pair<bool, uint8_t>{false, id});
1764 }
1765 link.pmMode = WIFI_PM_POWERSAVE;
1766 }
1767 }
1768
1770 {
1771 Simulator::Schedule(ackDuration,
1774 }
1775 });
1776
1777 // connect the callback to the PHY TX begin trace to catch the Ack and disconnect
1778 // after its transmission begins
1779 auto phy = GetLink(linkId).phy;
1780 phy->TraceConnectWithoutContext("PhyTxPsduBegin", cb);
1781 Simulator::Schedule(phy->GetSifs() + NanoSeconds(1),
1782 [=]() { phy->TraceDisconnectWithoutContext("PhyTxPsduBegin", cb); });
1783}
1784
1785bool
1786StaWifiMac::CheckSupportedRates(std::variant<MgtBeaconHeader, MgtProbeResponseHeader> frame,
1787 uint8_t linkId)
1788{
1789 NS_LOG_FUNCTION(this << +linkId);
1790
1791 // lambda to invoke on the current frame variant
1792 auto check = [&](auto&& mgtFrame) -> bool {
1793 // check supported rates
1794 NS_ASSERT(mgtFrame.template Get<SupportedRates>());
1795 const auto rates = AllSupportedRates{*mgtFrame.template Get<SupportedRates>(),
1796 mgtFrame.template Get<ExtendedSupportedRatesIE>()};
1797 for (const auto& selector : GetWifiPhy(linkId)->GetBssMembershipSelectorList())
1798 {
1799 if (!rates.IsBssMembershipSelectorRate(selector))
1800 {
1801 NS_LOG_DEBUG("Supported rates do not fit with the BSS membership selector");
1802 return false;
1803 }
1804 }
1805
1806 return true;
1807 };
1808
1809 return std::visit(check, frame);
1810}
1811
1812void
1813StaWifiMac::RecordOperations(const MgtFrameType& frame, const Mac48Address& from, uint8_t linkId)
1814{
1815 NS_LOG_FUNCTION(this << frame.index() << from << linkId);
1816 auto remoteStationManager = GetWifiRemoteStationManager(linkId);
1817 auto phy = GetWifiPhy(linkId);
1818
1819 // lambda processing Information Elements included in all frame types
1820 auto recordFromOpIes = [&](auto&& frame) {
1821 const auto& edcaParameters = frame.template Get<EdcaParameterSet>();
1822 const auto qosSupported = edcaParameters.has_value();
1823 GetWifiRemoteStationManager(linkId)->SetQosSupport(from, qosSupported);
1824
1825 if (GetHtSupported(linkId))
1826 {
1827 /* HT station */
1828 if (const auto& htOperation = frame.template Get<HtOperation>())
1829 {
1830 remoteStationManager->AddStationHtOperation(from, *htOperation);
1831 }
1832 }
1833
1834 if (GetVhtSupported(linkId))
1835 {
1836 /* VHT station */
1837 if (const auto& vhtOperation = frame.template Get<VhtOperation>())
1838 {
1839 remoteStationManager->AddStationVhtOperation(from, *vhtOperation);
1840 }
1841 }
1842
1843 if (!GetHeSupported())
1844 {
1845 return;
1846 }
1847 /* HE station */
1848 if (const auto& heOperation = frame.template Get<HeOperation>())
1849 {
1850 remoteStationManager->AddStationHeOperation(from, *heOperation);
1851 }
1852
1853 if (!GetEhtSupported())
1854 {
1855 return;
1856 }
1857 /* EHT station */
1858 if (const auto& ehtOperation = frame.template Get<EhtOperation>())
1859 {
1860 remoteStationManager->AddStationEhtOperation(from, *ehtOperation);
1861 }
1862 };
1863
1864 // process Information Elements included in the current frame variant
1865 std::visit(recordFromOpIes, frame);
1866}
1867
1868void
1870 const Mac48Address& apAddr,
1871 const Mac48Address& bssid,
1872 uint8_t linkId)
1873{
1874 NS_LOG_FUNCTION(this << frame.index() << apAddr << bssid << +linkId);
1875
1876 // ERP Information is not present in Association Response frames
1877 const std::optional<ErpInformation>* erpInformation = nullptr;
1878
1879 if (const auto* beacon = std::get_if<MgtBeaconHeader>(&frame))
1880 {
1881 erpInformation = &beacon->Get<ErpInformation>();
1882 }
1883 else if (const auto* probe = std::get_if<MgtProbeResponseHeader>(&frame))
1884 {
1885 erpInformation = &probe->Get<ErpInformation>();
1886 }
1887
1888 // lambda processing Information Elements included in all frame types sent by APs
1889 auto processOtherIes = [&](auto&& frame) {
1890 const auto& capabilities = frame.m_capability;
1891 bool isShortPreambleEnabled = capabilities.IsShortPreamble();
1892 auto remoteStationManager = GetWifiRemoteStationManager(linkId);
1893 if (erpInformation && erpInformation->has_value() && GetErpSupported(linkId))
1894 {
1895 isShortPreambleEnabled &= !(*erpInformation)->GetBarkerPreambleMode();
1896 if ((*erpInformation)->GetUseProtection() != 0)
1897 {
1898 remoteStationManager->SetUseNonErpProtection(true);
1899 }
1900 else
1901 {
1902 remoteStationManager->SetUseNonErpProtection(false);
1903 }
1904 if (capabilities.IsShortSlotTime() == true)
1905 {
1906 // enable short slot time
1907 GetWifiPhy(linkId)->SetSlot(MicroSeconds(9));
1908 }
1909 else
1910 {
1911 // disable short slot time
1912 GetWifiPhy(linkId)->SetSlot(MicroSeconds(20));
1913 }
1914 }
1915 remoteStationManager->SetShortPreambleEnabled(isShortPreambleEnabled);
1916 remoteStationManager->SetShortSlotTimeEnabled(capabilities.IsShortSlotTime());
1917
1918 if (!GetQosSupported())
1919 {
1920 return;
1921 }
1922 /* QoS station */
1923 const auto& edcaParameters = frame.template Get<EdcaParameterSet>();
1924 if (edcaParameters.has_value())
1925 {
1926 // The value of the TXOP Limit field is specified as an unsigned integer, with the least
1927 // significant octet transmitted first, in units of 32 μs.
1929 edcaParameters->GetBeCWmin(),
1930 edcaParameters->GetBeCWmax(),
1931 edcaParameters->GetBeAifsn(),
1932 32 * MicroSeconds(edcaParameters->GetBeTxopLimit())},
1933 linkId);
1935 edcaParameters->GetBkCWmin(),
1936 edcaParameters->GetBkCWmax(),
1937 edcaParameters->GetBkAifsn(),
1938 32 * MicroSeconds(edcaParameters->GetBkTxopLimit())},
1939 linkId);
1941 edcaParameters->GetViCWmin(),
1942 edcaParameters->GetViCWmax(),
1943 edcaParameters->GetViAifsn(),
1944 32 * MicroSeconds(edcaParameters->GetViTxopLimit())},
1945 linkId);
1947 edcaParameters->GetVoCWmin(),
1948 edcaParameters->GetVoCWmax(),
1949 edcaParameters->GetVoAifsn(),
1950 32 * MicroSeconds(edcaParameters->GetVoTxopLimit())},
1951 linkId);
1952 }
1953
1954 if (GetHtSupported(linkId))
1955 {
1956 /* HT station */
1957 if (const auto& htCapabilities = frame.template Get<HtCapabilities>();
1958 !htCapabilities.has_value())
1959 {
1960 remoteStationManager->RemoveAllSupportedMcs(apAddr);
1961 }
1962 }
1963
1964 if (!GetHeSupported())
1965 {
1966 return;
1967 }
1968 /* HE station */
1969 if (const auto& heOperation = frame.template Get<HeOperation>())
1970 {
1971 GetHeConfiguration()->m_bssColor = heOperation->m_bssColorInfo.m_bssColor;
1972 }
1973
1974 const auto& muEdcaParameters = frame.template Get<MuEdcaParameterSet>();
1975 if (muEdcaParameters.has_value())
1976 {
1978 muEdcaParameters->GetMuCwMin(AC_BE),
1979 muEdcaParameters->GetMuCwMax(AC_BE),
1980 muEdcaParameters->GetMuAifsn(AC_BE),
1981 muEdcaParameters->GetMuEdcaTimer(AC_BE)},
1982 linkId);
1984 muEdcaParameters->GetMuCwMin(AC_BK),
1985 muEdcaParameters->GetMuCwMax(AC_BK),
1986 muEdcaParameters->GetMuAifsn(AC_BK),
1987 muEdcaParameters->GetMuEdcaTimer(AC_BK)},
1988 linkId);
1990 muEdcaParameters->GetMuCwMin(AC_VI),
1991 muEdcaParameters->GetMuCwMax(AC_VI),
1992 muEdcaParameters->GetMuAifsn(AC_VI),
1993 muEdcaParameters->GetMuEdcaTimer(AC_VI)},
1994 linkId);
1996 muEdcaParameters->GetMuCwMin(AC_VO),
1997 muEdcaParameters->GetMuCwMax(AC_VO),
1998 muEdcaParameters->GetMuAifsn(AC_VO),
1999 muEdcaParameters->GetMuEdcaTimer(AC_VO)},
2000 linkId);
2001 }
2002
2003 if (!GetEhtSupported())
2004 {
2005 return;
2006 }
2007 /* EHT station */
2008 if (const auto& mle = frame.template Get<MultiLinkElement>(); mle && m_emlsrManager)
2009 {
2010 if (mle->HasEmlCapabilities())
2011 {
2012 m_emlsrManager->SetTransitionTimeout(mle->GetTransitionTimeout());
2013 }
2014 if (const auto& common = mle->GetCommonInfoBasic(); common.m_mediumSyncDelayInfo)
2015 {
2016 m_emlsrManager->SetMediumSyncDuration(common.GetMediumSyncDelayTimer());
2017 m_emlsrManager->SetMediumSyncOfdmEdThreshold(common.GetMediumSyncOfdmEdThreshold());
2018 m_emlsrManager->SetMediumSyncMaxNTxops(common.GetMediumSyncMaxNTxops());
2019 }
2020 }
2021 };
2022
2023 // process Information Elements included in the current frame variant
2024 std::visit(processOtherIes, frame);
2025}
2026
2027void
2028StaWifiMac::SetPowerSaveMode(const std::pair<bool, uint8_t>& enableLinkIdPair)
2029{
2030 const auto [enable, linkId] = enableLinkIdPair;
2031 NS_LOG_FUNCTION(this << enable << linkId);
2032
2033 auto& link = GetLink(linkId);
2034
2035 if (!IsAssociated())
2036 {
2037 NS_LOG_DEBUG("Not associated yet, record the PM mode to switch to upon association");
2038 link.pmMode = enable ? WIFI_PM_POWERSAVE : WIFI_PM_ACTIVE;
2039 return;
2040 }
2041
2042 if (!link.bssid)
2043 {
2044 NS_LOG_DEBUG("Link " << +linkId << " has not been setup, ignore request");
2045 return;
2046 }
2047
2048 if ((enable && link.pmMode == WIFI_PM_POWERSAVE) || (!enable && link.pmMode == WIFI_PM_ACTIVE))
2049 {
2050 NS_LOG_DEBUG("No PM mode change needed");
2051 return;
2052 }
2053
2055
2056 // reschedule a call to this function to make sure that the PM mode switch
2057 // is eventually completed
2060 this,
2061 enableLinkIdPair);
2062
2063 if (HasFramesToTransmit(linkId))
2064 {
2065 NS_LOG_DEBUG("Next transmitted frame will be sent with PM=" << enable);
2066 return;
2067 }
2068
2069 // No queued frames. Enqueue a Data Null frame to inform the AP of the PM mode change
2071
2072 hdr.SetAddr1(GetBssid(linkId));
2074 hdr.SetAddr3(GetBssid(linkId));
2075 hdr.SetDsNotFrom();
2076 hdr.SetDsTo();
2077 enable ? hdr.SetPowerManagement() : hdr.SetNoPowerManagement();
2078 if (GetQosSupported())
2079 {
2081 }
2082 else
2083 {
2084 m_txop->Queue(Create<WifiMpdu>(Create<Packet>(), hdr));
2085 }
2086}
2087
2089StaWifiMac::GetPmMode(uint8_t linkId) const
2090{
2091 return GetLink(linkId).pmMode;
2092}
2093
2094void
2096{
2097 NS_LOG_FUNCTION(this << *mpdu);
2098
2099 auto linkId = GetLinkIdByAddress(mpdu->GetHeader().GetAddr2());
2100
2101 if (!linkId)
2102 {
2103 // the given MPDU may be the original copy containing MLD addresses and not carrying
2104 // a valid PM bit (which is set on the aliases).
2105 auto linkIds = mpdu->GetInFlightLinkIds();
2106 NS_ASSERT_MSG(!linkIds.empty(),
2107 "The TA of the acked MPDU (" << *mpdu
2108 << ") is not a link "
2109 "address and the MPDU is not inflight");
2110 // in case the ack'ed MPDU is inflight on multiple links, we cannot really know if
2111 // it was received by the AP on all links or only on some links. Hence, we only
2112 // consider the first link ID in the set, given that in the most common case of MPDUs
2113 // that cannot be sent concurrently on multiple links, there will be only one link ID
2114 linkId = *linkIds.begin();
2115 mpdu = GetTxopQueue(mpdu->GetQueueAc())->GetAlias(mpdu, *linkId);
2116 }
2117
2118 auto& link = GetLink(*linkId);
2119 const WifiMacHeader& hdr = mpdu->GetHeader();
2120
2121 // we received an acknowledgment while switching PM mode; the PM mode change is effective now
2122 if (hdr.IsPowerManagement() && link.pmMode == WIFI_PM_SWITCHING_TO_PS)
2123 {
2124 link.pmMode = WIFI_PM_POWERSAVE;
2126 {
2127 m_powerSaveManager->NotifyPmModeChanged(link.pmMode, *linkId);
2128 }
2129 }
2130 else if (!hdr.IsPowerManagement() && link.pmMode == WIFI_PM_SWITCHING_TO_ACTIVE)
2131 {
2132 link.pmMode = WIFI_PM_ACTIVE;
2134 {
2135 m_powerSaveManager->NotifyPmModeChanged(link.pmMode, *linkId);
2136 }
2137 }
2138}
2139
2142{
2143 AllSupportedRates rates;
2144 for (const auto& mode : GetWifiPhy(linkId)->GetModeList())
2145 {
2146 uint64_t modeDataRate = mode.GetDataRate(GetWifiPhy(linkId)->GetChannelWidth());
2147 NS_LOG_DEBUG("Adding supported rate of " << modeDataRate);
2148 rates.AddSupportedRate(modeDataRate);
2149 }
2150 if (GetHtSupported(linkId))
2151 {
2152 for (const auto& selector : GetWifiPhy(linkId)->GetBssMembershipSelectorList())
2153 {
2154 rates.AddBssMembershipSelectorRate(selector);
2155 }
2156 }
2157 return rates;
2158}
2159
2161StaWifiMac::GetCapabilities(uint8_t linkId) const
2162{
2163 CapabilityInformation capabilities;
2164 capabilities.SetShortPreamble(GetWifiPhy(linkId)->GetShortPhyPreambleSupported() ||
2165 GetErpSupported(linkId));
2167 return capabilities;
2168}
2169
2170void
2172{
2173 m_state = value;
2174}
2175
2176void
2177StaWifiMac::SetEdcaParameters(const EdcaParams& params, uint8_t linkId)
2178{
2179 Ptr<QosTxop> edca = GetQosTxop(params.ac);
2180 edca->SetMinCw(params.cwMin, linkId);
2181 edca->SetMaxCw(params.cwMax, linkId);
2182 edca->SetAifsn(params.aifsn, linkId);
2183 edca->SetTxopLimit(params.txopLimit, linkId);
2184}
2185
2186void
2187StaWifiMac::SetMuEdcaParameters(const MuEdcaParams& params, uint8_t linkId)
2188{
2189 Ptr<QosTxop> edca = GetQosTxop(params.ac);
2190 edca->SetMuCwMin(params.cwMin, linkId);
2191 edca->SetMuCwMax(params.cwMax, linkId);
2192 edca->SetMuAifsn(params.aifsn, linkId);
2193 edca->SetMuEdcaTimer(params.muEdcaTimer, linkId);
2194}
2195
2196void
2198{
2199 NS_LOG_FUNCTION(this);
2200 if (IsAssociated())
2201 {
2202 NS_LOG_DEBUG("PHY capabilities changed: send reassociation request");
2205 }
2206}
2207
2208/**
2209 * Initial configuration:
2210 *
2211 * ┌───┬───┬───┐ ┌────┐ ┌───────┐
2212 * Link A │FEM│RSM│CAM│◄──────►│Main├──────►│Channel│
2213 * │ │ │ │ │PHY │ │ A │
2214 * └───┴───┴───┘ └────┘ └───────┘
2215 *
2216 * ┌───┬───┬───┐ ┌────┐ ┌───────┐
2217 * Link B │FEM│RSM│CAM│ │Aux │ │Channel│
2218 * │ │ │ │◄──────►│PHY ├──────►│ B │
2219 * └───┴───┴───┘ └────┘ └───────┘
2220 *
2221 * A link switching/swapping is notified by the EMLSR Manager and the Channel Access Manager
2222 * (CAM) notifies us that a first PHY (i.e., the Main PHY) switches to Channel B. We connect
2223 * the Main PHY to the MAC stack B:
2224 *
2225 *
2226 * ┌───┬───┬───┐ ┌────┐ ┌───────┐
2227 * Link A │FEM│RSM│CAM│ ┌───►│Main├───┐ │Channel│
2228 * │ │ │ │ │ │PHY │ │ │ A │
2229 * └───┴───┴───┘ │ └────┘ │ └───────┘
2230 * │ │
2231 * ┌───┬───┬───┐ │ ┌────┐ │ ┌───────┐
2232 * Link B │FEM│RSM│CAM│◄──┘ │Aux │ └──►│Channel│
2233 * │ │ │ │◄─ ─ ─ ─│PHY ├──────►│ B │
2234 * └───┴───┴───┘INACTIVE└────┘ └───────┘
2235 *
2236 * MAC stack B keeps a PHY listener associated with the Aux PHY, even though it is inactive,
2237 * meaning that the PHY listener will only notify channel switches (no CCA, no RX).
2238 * If the EMLSR Manager requested a link switching, this configuration will be kept until
2239 * further requests. If the EMLSR Manager requested a link swapping, link B's CAM will be
2240 * notified by its (inactive) PHY listener upon the channel switch performed by the Aux PHY.
2241 * In this case, we remove the inactive PHY listener and connect the Aux PHY to MAC stack A:
2242 *
2243 * ┌───┬───┬───┐ ┌────┐ ┌───────┐
2244 * Link A │FEM│RSM│CAM│◄─┐ ┌──►│Main├───┐ │Channel│
2245 * │ │ │ │ │ │ │PHY │ ┌─┼──►│ A │
2246 * └───┴───┴───┘ │ │ └────┘ │ │ └───────┘
2247 * │ │ │ │
2248 * ┌───┬───┬───┐ │ │ ┌────┐ │ │ ┌───────┐
2249 * Link B │FEM│RSM│CAM│◄─┼─┘ │Aux │ │ └──►│Channel│
2250 * │ │ │ │ └────►│PHY ├─┘ │ B │
2251 * └───┴───┴───┘ └────┘ └───────┘
2252 */
2253
2254void
2256{
2257 NS_LOG_FUNCTION(this << phy << linkId << delay.As(Time::US));
2258
2259 // if the PHY that is starting a channel switch was operating on a link (i.e., there is a link,
2260 // other than the new link, that points to the PHY), then it is no longer operating on that
2261 // link and we have to reset the phy pointer of the link.
2262 for (auto& [id, link] : GetLinks())
2263 {
2264 if (link->phy == phy && id != linkId)
2265 {
2266 // we do not get here if the PHY is not operating on any link, which happens if:
2267 // - PHY is an aux PHY to reconnect to its link
2268 // - PHY is an aux PHY that is starting switching to the link previously occupied by the
2269 // main PHY (because the main PHY is now operating on the aux PHY link)
2270 // - PHY is the main PHY that completed the channel switch but connecting it to the link
2271 // was postponed until now (e.g. because the aux PHY on the link was receiving an ICF)
2272 // - PHY is the main PHY that was switching, the switch was interrupted and it is
2273 // now starting switching to another link
2274 link->phy = nullptr;
2275 m_emlsrLinkSwitchLogger(id, phy, false);
2276 }
2277 }
2278
2279 // lambda to connect the PHY to the new link
2280 auto connectPhy = [=, this]() mutable {
2281 auto& newLink = GetLink(linkId);
2282 // The MAC stack associated with the new link uses the given PHY
2283 newLink.phy = phy;
2284 // Setup a PHY listener for the given PHY on the CAM associated with the new link
2285 newLink.channelAccessManager->SetupPhyListener(phy);
2287 if (m_emlsrManager->GetCamStateReset())
2288 {
2289 newLink.channelAccessManager->ResetState();
2290 }
2291 // Disconnect the FEM on the new link from the current PHY
2292 newLink.feManager->ResetPhy();
2293 // Connect the FEM on the new link to the given PHY
2294 newLink.feManager->SetWifiPhy(phy);
2295 // Connect the station manager on the new link to the given PHY
2296 newLink.stationManager->SetupPhy(phy);
2297 // log link switch
2298 m_emlsrLinkSwitchLogger(linkId, phy, true);
2299 };
2300
2301 // cancel any pending event for the given PHY to switch link
2302 CancelEmlsrPhyConnectEvent(phy->GetPhyId());
2303
2304 // connect the PHY to the new link when the channel switch is completed, unless there is a PHY
2305 // operating on the new link that is possibly receiving an ICF, in which case the PHY is
2306 // connected when the frame reception is completed
2307 if (delay.IsStrictlyPositive())
2308 {
2309 auto lambda = [=, this]() mutable {
2310 const auto [maybeIcf, extension] = m_emlsrManager->CheckPossiblyReceivingIcf(linkId);
2311 if (maybeIcf && extension.IsStrictlyPositive())
2312 {
2313 NS_ASSERT_MSG(phy->GetPhyId() == m_emlsrManager->GetMainPhyId(),
2314 "Only the main PHY is expected to move to a link on which another "
2315 "PHY is operating. PHY ID="
2316 << +phy->GetPhyId());
2317 NS_LOG_DEBUG("Connecting main PHY to link " << +linkId << " is postponed by "
2318 << extension.As(Time::US));
2319 NotifySwitchingEmlsrLink(phy, linkId, extension);
2320 }
2321 else
2322 {
2323 connectPhy();
2324 }
2325 };
2326
2327 m_emlsrLinkSwitch.emplace(phy->GetPhyId(), Simulator::Schedule(delay, lambda));
2328 }
2329 else
2330 {
2331 connectPhy();
2332 }
2333}
2334
2335void
2337{
2338 NS_LOG_FUNCTION(this << phyId);
2339 if (auto eventIt = m_emlsrLinkSwitch.find(phyId); eventIt != m_emlsrLinkSwitch.end())
2340 {
2341 eventIt->second.Cancel();
2342 m_emlsrLinkSwitch.erase(eventIt);
2343 }
2344}
2345
2346void
2348{
2349 NS_LOG_FUNCTION(this << +linkId);
2350
2352
2353 if (IsInitialized() && IsAssociated())
2354 {
2355 Disassociated();
2356 }
2357
2358 // notify association manager
2359 m_assocManager->NotifyChannelSwitched(linkId);
2360}
2361
2362std::ostream&
2363operator<<(std::ostream& os, const StaWifiMac::ApInfo& apInfo)
2364{
2365 os << "BSSID=" << apInfo.m_bssid << ", AP addr=" << apInfo.m_apAddr << ", SNR=" << apInfo.m_snr
2366 << ", Channel={" << apInfo.m_channel.number << "," << apInfo.m_channel.band
2367 << "}, Link ID=" << +apInfo.m_linkId << ", Frame=[";
2368 std::visit([&os](auto&& frame) { frame.Print(os); }, apInfo.m_frame);
2369 os << "]";
2370 return os;
2371}
2372
2373std::ostream&
2374operator<<(std::ostream& os, WifiPowerManagementMode pmMode)
2375{
2376 switch (pmMode)
2377 {
2378 case WIFI_PM_ACTIVE:
2379 return (os << "PM_ACTIVE");
2381 return (os << "PM_SWITCHING_TO_PS");
2382 case WIFI_PM_POWERSAVE:
2383 return (os << "PM_POWERSAVE");
2385 return (os << "PM_SWITCHING_TO_ACTIVE");
2386 default:
2387 return (os << "INVALID");
2388 }
2389}
2390
2391} // 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:347
Callback template class.
Definition callback.h:428
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 m_statusCode
Status code.
Implement the header for management frames of type beacon.
CapabilityInformation m_capability
Capability information.
Implement the header for management frames of type probe request.
Implement the header for management frames of type probe response.
Implement the header for management frames of type reassociation request.
Mac48Address m_currentApAddr
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:237
AttributeValue implementation for Pair.
Definition pair.h:54
AttributeValue implementation for Pointer.
Definition pointer.h:37
void NotifyAssocCompleted()
Notify that the non-AP STA/MLD has completed association with an AP.
Smart pointer class similar to boost::intrusive_ptr.
Definition ptr.h:70
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition simulator.h:580
static Time Now()
Return the current simulation virtual time.
Definition simulator.cc:191
static EventId ScheduleNow(FUNC f, Ts &&... args)
Schedule an event to expire Now.
Definition simulator.h:614
static Time GetDelayLeft(const EventId &id)
Get the remaining time until this event will execute.
Definition simulator.cc:200
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.
void NotifyEmlsrModeChanged(uint8_t txLinkId, const std::set< uint8_t > &linkIds)
Notify the MAC that EMLSR mode has changed on the given set of links.
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.
void SetPowerSaveManager(Ptr< PowerSaveManager > powerSaveManager)
Set the Power Save Manager.
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 NotifyChannelReleased(Ptr< Txop > txop, uint8_t linkId) override
Notify that the given TXOP has released the channel on the given link.
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.
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.
Ptr< PowerSaveManager > GetPowerSaveManager() const
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.
void EnqueuePsPoll(uint8_t linkId)
Enqueue a PS-Poll frame to be sent 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 NotifyReceivedFrameAfterPsPoll(Ptr< const WifiMpdu > mpdu, uint8_t linkId)
Notify the reception of a frame in response to a PS-Poll frame 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 NotifyRequestAccess(Ptr< Txop > txop, uint8_t linkId) override
Notify that the given TXOP is requesting channel access on the given link.
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.
Ptr< PowerSaveManager > m_powerSaveManager
Power Save Manager.
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:95
TimeWithUnit As(const Unit unit=Time::AUTO) const
Attach a unit to a Time, to facilitate output in a specific unit.
Definition time.cc:408
bool IsStrictlyPositive() const
Exactly equivalent to t > 0.
Definition nstime.h:341
@ US
microsecond
Definition nstime.h:108
AttributeValue implementation for Time.
Definition nstime.h:1375
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:50
@ ATTR_GET
The attribute can be read.
Definition type-id.h:55
@ ATTR_SET
The attribute can be written.
Definition type-id.h:56
@ ATTR_CONSTRUCT
The attribute can be written at construction-time.
Definition type-id.h:57
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition type-id.cc:999
@ DEPRECATED
Attribute or trace source is deprecated; user is warned.
Definition type-id.h:66
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).
void SetNoMoreFragments()
Un-set the More Fragment bit in the Frame Control Field.
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.
void SetId(uint16_t id)
Set the Duration/ID field with the given ID.
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 SetNoRetry()
Un-set the Retry 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:1034
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:711
std::optional< Mac48Address > GetMldAddress(const Mac48Address &remoteAddr) const
Definition wifi-mac.cc:1878
Mac48Address GetBssid(uint8_t linkId) const
Definition wifi-mac.cc:547
Ptr< HeConfiguration > GetHeConfiguration() const
Definition wifi-mac.cc:2000
const std::map< uint8_t, std::unique_ptr< LinkEntity > > & GetLinks() const
Definition wifi-mac.cc:1111
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:2280
Callback< void > m_linkDown
Callback when a link is down.
Definition wifi-mac.h:1035
bool GetQosSupported() const
Return whether the device supports QoS.
Definition wifi-mac.cc:1429
Ptr< Txop > m_txop
TXOP used for transmission of frames to non-QoS peers.
Definition wifi-mac.h:1031
Ptr< WifiMacQueueScheduler > GetMacQueueScheduler() const
Get the wifi MAC queue scheduler.
Definition wifi-mac.cc:705
uint8_t GetNLinks() const
Get the number of links (can be greater than 1 for 11be devices only).
Definition wifi-mac.cc:1126
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:1192
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:1435
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:1397
void RecordCapabilities(const MgtFrameType &frame, const Mac48Address &addr, uint8_t linkId)
Update capabilities information from the given management frame.
Definition wifi-mac.cc:2627
bool GetEhtSupported() const
Return whether the device supports EHT.
Definition wifi-mac.cc:2033
bool GetHeSupported() const
Return whether the device supports HE.
Definition wifi-mac.cc:2027
HtCapabilities GetHtCapabilities(uint8_t linkId) const
Return the HT capabilities of the device for the given link.
Definition wifi-mac.cc:2222
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:1158
virtual bool HasFramesToTransmit(uint8_t linkId)
Check if the MAC has frames to transmit over the given link.
Definition wifi-mac.cc:681
UniformRandomBitGenerator m_shuffleLinkIdsGen
random number generator to shuffle link IDs
Definition wifi-mac.h:1027
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:1512
Ptr< EhtConfiguration > GetEhtConfiguration() const
Definition wifi-mac.cc:2006
bool GetVhtSupported(uint8_t linkId) const
Return whether the device supports VHT on the given link.
Definition wifi-mac.cc:2019
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:1866
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:1289
ExtendedCapabilities GetExtendedCapabilities() const
Return the extended capabilities of the device.
Definition wifi-mac.cc:2214
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:2421
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:1171
bool GetShortSlotTimeSupported() const
Definition wifi-mac.cc:1479
void NotifyRxDrop(Ptr< const Packet > packet)
Definition wifi-mac.cc:748
Ptr< WifiRemoteStationManager > GetWifiRemoteStationManager(uint8_t linkId=0) const
Definition wifi-mac.cc:1099
bool GetHtSupported(uint8_t linkId) const
Return whether the device supports HT on the given link.
Definition wifi-mac.cc:2012
void ForwardUp(Ptr< const Packet > packet, Mac48Address from, Mac48Address to)
Forward the packet up to the device.
Definition wifi-mac.cc:1829
bool Is6GhzBand(uint8_t linkId) const
Indicate if a given link is on the 6 GHz band.
Definition wifi-mac.cc:1281
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:1836
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:2456
Callback< void > m_linkUp
Callback when a link is up.
Definition wifi-mac.h:1034
LinkEntity & GetLink(uint8_t linkId) const
Get a reference to the link associated with the given ID.
Definition wifi-mac.cc:1117
HeCapabilities GetHeCapabilities(uint8_t linkId) const
Return the HE capabilities of the device for the given link.
Definition wifi-mac.cc:2357
virtual void SetWifiPhys(const std::vector< Ptr< WifiPhy > > &phys)
Definition wifi-mac.cc:1375
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:1574
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:113
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:223
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:250
Ptr< AttributeChecker > MakePointerChecker()
Create a PointerChecker for a type.
Definition pointer.h:273
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:1376
Ptr< const AttributeChecker > MakeTimeChecker()
Helper to make an unbounded Time checker.
Definition nstime.h:1396
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
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:690
#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:194
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition log.h:260
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition log.h:274
#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:454
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1307
Time NanoSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1324
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1273
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1290
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:66
@ AC_VO
Voice.
Definition qos-utils.h:72
@ AC_VI
Video.
Definition qos-utils.h:70
@ AC_BK
Background.
Definition qos-utils.h:68
-style-clang-format
Every class exported by the ns3 library is enclosed in the ns3 namespace.
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:181
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_CTL_PSPOLL
@ 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:98
Ptr< T1 > StaticCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:612
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