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