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/simulator.h"
34#include "ns3/string.h"
35
36#include <iterator>
37#include <numeric>
38#include <sstream>
39
40namespace ns3
41{
42
43NS_LOG_COMPONENT_DEFINE("StaWifiMac");
44
46
47TypeId
49{
50 static TypeId tid =
51 TypeId("ns3::StaWifiMac")
53 .SetGroupName("Wifi")
54 .AddConstructor<StaWifiMac>()
55 .AddAttribute("ProbeRequestTimeout",
56 "The duration to actively probe the channel.",
57 TimeValue(Seconds(0.05)),
60 .AddAttribute("WaitBeaconTimeout",
61 "The duration to dwell on a channel while passively scanning for beacon",
65 .AddAttribute("AssocRequestTimeout",
66 "The interval between two consecutive association request attempts.",
67 TimeValue(Seconds(0.5)),
70 .AddAttribute("MaxMissedBeacons",
71 "Number of beacons which much be consecutively missed before "
72 "we attempt to restart association.",
73 UintegerValue(10),
76 .AddAttribute(
77 "ActiveProbing",
78 "If true, we send probe requests. If false, we don't."
79 "NOTE: if more than one STA in your simulation is using active probing, "
80 "you should enable it at a different simulation time for each STA, "
81 "otherwise all the STAs will start sending probes at the same time resulting in "
82 "collisions. "
83 "See bug 1060 for more info.",
84 BooleanValue(false),
87 .AddAttribute("ProbeDelay",
88 "Delay (in microseconds) to be used prior to transmitting a "
89 "Probe frame during active scanning.",
90 StringValue("ns3::UniformRandomVariable[Min=50.0|Max=250.0]"),
93 .AddAttribute(
94 "PowerSaveMode",
95 "Enable/disable power save mode on the given link. The power management mode is "
96 "actually changed when the AP acknowledges a frame sent with the Power Management "
97 "field set to the value corresponding to the requested mode",
98 TypeId::ATTR_GET | TypeId::ATTR_SET, // do not set at construction time
103 .AddAttribute("PmModeSwitchTimeout",
104 "If switching to a new Power Management mode is not completed within "
105 "this amount of time, make another attempt at switching Power "
106 "Management mode.",
107 TimeValue(Seconds(0.1)),
110 .AddTraceSource("Assoc",
111 "Associated with an access point. If this is an MLD that associated "
112 "with an AP MLD, the AP MLD address is provided.",
114 "ns3::Mac48Address::TracedCallback")
115 .AddTraceSource("LinkSetupCompleted",
116 "A link was setup in the context of ML setup with an AP MLD. "
117 "Provides ID of the setup link and AP MAC address",
119 "ns3::StaWifiMac::LinkSetupCallback")
120 .AddTraceSource("DeAssoc",
121 "Association with an access point lost. If this is an MLD "
122 "that disassociated with an AP MLD, the AP MLD address is provided.",
124 "ns3::Mac48Address::TracedCallback")
125 .AddTraceSource("BeaconArrival",
126 "Time of beacons arrival from associated AP",
128 "ns3::Time::TracedCallback")
129 .AddTraceSource("ReceivedBeaconInfo",
130 "Information about every received Beacon frame",
132 "ns3::ApInfo::TracedCallback")
133 .AddTraceSource("EmlsrLinkSwitch",
134 "Trace start/end of EMLSR link switch events: when a PHY operating on "
135 "a link starts switching, provides the ID of the link and a null "
136 "pointer (indicating no PHY is operating on that link); when a PHY "
137 "completes switching to a link, provides the ID of the link and a "
138 "pointer to the PHY (that is now operating on that link)",
140 "ns3::StaWifiMac::EmlsrLinkSwitchCallback");
141 return tid;
142}
143
145 : m_state(UNASSOCIATED),
146 m_aid(0),
147 m_assocRequestEvent()
148{
149 NS_LOG_FUNCTION(this);
150
151 // Let the lower layers know that we are acting as a non-AP STA in
152 // an infrastructure BSS.
154}
155
156void
158{
159 NS_LOG_FUNCTION(this);
160 // an EMLSR client must perform ML setup by using its main PHY
162 {
163 auto mainPhyId = m_emlsrManager->GetMainPhyId();
164 auto linkId = GetLinkForPhy(mainPhyId);
165 NS_ASSERT(linkId);
166 m_assocManager->SetAttribute(
167 "AllowedLinks",
168 AttributeContainerValue<UintegerValue>(std::list<uint8_t>{*linkId}));
169 }
170 if (m_emlsrManager)
171 {
172 m_emlsrManager->Initialize();
173 }
177}
178
179void
181{
182 NS_LOG_FUNCTION(this);
183 if (m_assocManager)
184 {
185 m_assocManager->Dispose();
186 }
187 m_assocManager = nullptr;
188 if (m_emlsrManager)
189 {
190 m_emlsrManager->Dispose();
191 }
192 m_emlsrManager = nullptr;
193 for (auto& [phyId, event] : m_emlsrLinkSwitch)
194 {
195 event.Cancel();
196 }
197 m_emlsrLinkSwitch.clear();
199}
200
205
206void
211
216
217std::unique_ptr<WifiMac::LinkEntity>
219{
220 return std::make_unique<StaLinkEntity>();
221}
222
224StaWifiMac::GetLink(uint8_t linkId) const
225{
226 return static_cast<StaLinkEntity&>(WifiMac::GetLink(linkId));
227}
228
230StaWifiMac::GetStaLink(const std::unique_ptr<WifiMac::LinkEntity>& link) const
231{
232 return static_cast<StaLinkEntity&>(*link);
233}
234
235int64_t
237{
238 NS_LOG_FUNCTION(this << stream);
239 m_probeDelay->SetStream(stream);
240 auto currentStream = stream + 1;
241 currentStream += WifiMac::AssignStreams(currentStream);
242 return (currentStream - stream);
243}
244
245void
247{
248 NS_LOG_FUNCTION(this << assocManager);
249 m_assocManager = assocManager;
250 m_assocManager->SetStaWifiMac(this);
251}
252
253void
255{
256 NS_LOG_FUNCTION(this << emlsrManager);
257 m_emlsrManager = emlsrManager;
258 m_emlsrManager->SetWifiMac(this);
259}
260
263{
264 return m_emlsrManager;
265}
266
267uint16_t
269{
270 NS_ASSERT_MSG(IsAssociated(), "This station is not associated to any AP");
271 return m_aid;
272}
273
274void
276{
277 NS_LOG_FUNCTION(this << enable);
278 m_activeProbing = enable;
279 if (m_state == SCANNING)
280 {
281 NS_LOG_DEBUG("STA is still scanning, reset scanning process");
283 }
284}
285
286bool
288{
289 return m_activeProbing;
290}
291
292void
293StaWifiMac::SetWifiPhys(const std::vector<Ptr<WifiPhy>>& phys)
294{
295 NS_LOG_FUNCTION(this);
297 for (auto& phy : phys)
298 {
299 phy->SetCapabilitiesChangedCallback(
301 }
302}
303
305StaWifiMac::GetCurrentChannel(uint8_t linkId) const
306{
307 auto phy = GetWifiPhy(linkId);
308 const auto width = phy->GetOperatingChannel().IsOfdm() ? MHz_u{20} : phy->GetChannelWidth();
309 uint8_t ch = phy->GetPrimaryChannelNumber(width);
310 return {ch, phy->GetPhyBand()};
311}
312
313void
314StaWifiMac::NotifyEmlsrModeChanged(const std::set<uint8_t>& linkIds)
315{
316 std::stringstream ss;
317 if (g_log.IsEnabled(ns3::LOG_FUNCTION))
318 {
319 std::copy(linkIds.cbegin(), linkIds.cend(), std::ostream_iterator<uint16_t>(ss, " "));
320 }
321 NS_LOG_FUNCTION(this << ss.str());
322
323 for (const auto& [linkId, lnk] : GetLinks())
324 {
325 auto& link = GetStaLink(lnk);
326
327 if (linkIds.contains(linkId))
328 {
329 // EMLSR mode enabled
330 link.emlsrEnabled = true;
331 link.pmMode = WIFI_PM_ACTIVE;
332 }
333 else
334 {
335 // EMLSR mode disabled
336 if (link.emlsrEnabled)
337 {
338 link.pmMode = WIFI_PM_POWERSAVE;
339 }
340 link.emlsrEnabled = false;
341 }
342 }
343}
344
345bool
346StaWifiMac::IsEmlsrLink(uint8_t linkId) const
347{
348 return GetLink(linkId).emlsrEnabled;
349}
350
352StaWifiMac::GetProbeRequest(uint8_t linkId) const
353{
355 probe.Get<Ssid>() = GetSsid();
356 auto supportedRates = GetSupportedRates(linkId);
357 probe.Get<SupportedRates>() = supportedRates.rates;
358 probe.Get<ExtendedSupportedRatesIE>() = supportedRates.extendedRates;
359 if (GetWifiPhy(linkId)->GetPhyBand() == WIFI_PHY_BAND_2_4GHZ)
360 {
361 DsssParameterSet params;
362 params.SetCurrentChannel(GetWifiPhy(linkId)->GetChannelNumber());
363 probe.Get<DsssParameterSet>() = params;
364 }
365 if (GetHtSupported(linkId))
366 {
368 probe.Get<HtCapabilities>() = GetHtCapabilities(linkId);
369 }
370 if (GetVhtSupported(linkId))
371 {
372 probe.Get<VhtCapabilities>() = GetVhtCapabilities(linkId);
373 }
374 if (GetHeSupported())
375 {
376 probe.Get<HeCapabilities>() = GetHeCapabilities(linkId);
377 if (Is6GhzBand(linkId))
378 {
380 }
381 }
382 if (GetEhtSupported())
383 {
384 probe.Get<EhtCapabilities>() = GetEhtCapabilities(linkId);
385 }
386 return probe;
387}
388
391 const std::vector<uint8_t>& apLinkIds,
392 std::optional<uint8_t> apMldId) const
393{
394 NS_LOG_FUNCTION(this << linkId << apMldId.has_value());
395 auto req = GetProbeRequest(linkId);
396
397 if (GetNLinks() == 1)
398 {
399 // Single link (non-EHT) device
400 NS_LOG_DEBUG("Single link device does not support Multi-link operation, not including "
401 "Multi-link Element");
402 return req;
403 }
404
405 req.Get<MultiLinkElement>() = GetProbeReqMultiLinkElement(apLinkIds, apMldId);
406 return req;
407}
408
409void
411 uint8_t linkId,
412 const Mac48Address& addr1,
413 const Mac48Address& addr3)
414{
415 NS_LOG_FUNCTION(this << linkId << addr1 << addr3);
417 hdr.SetAddr1(addr1);
419 hdr.SetAddr3(addr3);
420 hdr.SetDsNotFrom();
421 hdr.SetDsNotTo();
422
423 auto packet = Create<Packet>();
424 packet->AddHeader(probeReq);
425
426 if (!GetQosSupported())
427 {
428 GetTxop()->Queue(Create<WifiMpdu>(packet, hdr));
429 }
430 // "A QoS STA that transmits a Management frame determines access category used
431 // for medium access in transmission of the Management frame as follows
432 // (If dot11QMFActivated is false or not present)
433 // — If the Management frame is individually addressed to a non-QoS STA, category
434 // AC_BE should be selected.
435 // — If category AC_BE was not selected by the previous step, category AC_VO
436 // shall be selected." (Sec. 10.2.3.2 of 802.11-2020)
437 else
438 {
439 GetVOQueue()->Queue(Create<WifiMpdu>(packet, hdr));
440 }
441}
442
443std::variant<MgtAssocRequestHeader, MgtReassocRequestHeader>
444StaWifiMac::GetAssociationRequest(bool isReassoc, uint8_t linkId) const
445{
446 NS_LOG_FUNCTION(this << isReassoc << +linkId);
447
448 std::variant<MgtAssocRequestHeader, MgtReassocRequestHeader> mgtFrame;
449
450 if (isReassoc)
451 {
453 reassoc.SetCurrentApAddress(GetBssid(linkId));
454 mgtFrame = std::move(reassoc);
455 }
456 else
457 {
458 mgtFrame = MgtAssocRequestHeader();
459 }
460
461 // lambda to set the fields of the (Re)Association Request
462 auto fill = [&](auto&& frame) {
463 frame.template Get<Ssid>() = GetSsid();
464 auto supportedRates = GetSupportedRates(linkId);
465 frame.template Get<SupportedRates>() = supportedRates.rates;
466 frame.template Get<ExtendedSupportedRatesIE>() = supportedRates.extendedRates;
467 frame.Capabilities() = GetCapabilities(linkId);
468 frame.SetListenInterval(0);
469 if (GetHtSupported(linkId))
470 {
471 frame.template Get<ExtendedCapabilities>() = GetExtendedCapabilities();
472 frame.template Get<HtCapabilities>() = GetHtCapabilities(linkId);
473 }
474 if (GetVhtSupported(linkId))
475 {
476 frame.template Get<VhtCapabilities>() = GetVhtCapabilities(linkId);
477 }
478 if (GetHeSupported())
479 {
480 frame.template Get<HeCapabilities>() = GetHeCapabilities(linkId);
481 if (Is6GhzBand(linkId))
482 {
483 frame.template Get<He6GhzBandCapabilities>() = GetHe6GhzBandCapabilities(linkId);
484 }
485 }
486 if (GetEhtSupported())
487 {
488 frame.template Get<EhtCapabilities>() = GetEhtCapabilities(linkId);
489 }
490 };
491
492 std::visit(fill, mgtFrame);
493 return mgtFrame;
494}
495
497StaWifiMac::GetBasicMultiLinkElement(bool isReassoc, uint8_t linkId) const
498{
499 NS_LOG_FUNCTION(this << isReassoc << +linkId);
500
502 // The Common info field of the Basic Multi-Link element carried in the (Re)Association
503 // Request frame shall include the MLD MAC address, the MLD Capabilities and Operations,
504 // and the EML Capabilities subfields, and shall not include the Link ID Info, the BSS
505 // Parameters Change Count, and the Medium Synchronization Delay Information subfields
506 // (Sec. 35.3.5.4 of 802.11be D2.0)
507 // TODO Add the MLD Capabilities and Operations subfield
508 multiLinkElement.SetMldMacAddress(GetAddress());
509
510 if (m_emlsrManager) // EMLSR Manager is only installed if EMLSR is activated
511 {
512 multiLinkElement.SetEmlsrSupported(true);
513 TimeValue time;
514 m_emlsrManager->GetAttribute("EmlsrPaddingDelay", time);
515 multiLinkElement.SetEmlsrPaddingDelay(time.Get());
516 m_emlsrManager->GetAttribute("EmlsrTransitionDelay", time);
517 multiLinkElement.SetEmlsrTransitionDelay(time.Get());
518 // When the Transition Timeout subfield is included in a frame sent by a non-AP STA
519 // affiliated with a non-AP MLD, the Transition Timeout subfield is reserved
520 // (Section 9.4.2.312.2.3 of 802.11be D2.3)
521 // The Medium Synchronization Delay Information subfield in the Common Info subfield is
522 // not present if the Basic Multi-Link element is sent by a non-AP STA. (Section
523 // 9.4.2.312.2.3 of 802.11be D3.1)
524 }
525
526 // The MLD Capabilities And Operations subfield is present in the Common Info field of the
527 // Basic Multi-Link element carried in Beacon, Probe Response, (Re)Association Request, and
528 // (Re)Association Response frames. (Sec. 9.4.2.312.2.3 of 802.11be D3.1)
529 auto& mldCapabilities = multiLinkElement.GetCommonInfoBasic().m_mldCapabilities;
530 mldCapabilities.emplace();
531 mldCapabilities->maxNSimultaneousLinks = GetNLinks() - 1; // assuming STR for now
532 mldCapabilities->srsSupport = 0;
533
534 auto ehtConfiguration = GetEhtConfiguration();
535 NS_ASSERT(ehtConfiguration);
536
537 mldCapabilities->tidToLinkMappingSupport =
538 static_cast<uint8_t>(ehtConfiguration->m_tidLinkMappingSupport);
539 mldCapabilities->freqSepForStrApMld = 0; // not supported yet
540 mldCapabilities->aarSupport = 0; // not supported yet
541
542 // For each requested link in addition to the link on which the (Re)Association Request
543 // frame is transmitted, the Link Info field of the Basic Multi-Link element carried
544 // in the (Re)Association Request frame shall contain the corresponding Per-STA Profile
545 // subelement(s).
546 for (const auto& [index, link] : GetLinks())
547 {
548 const auto& staLink = GetStaLink(link);
549
550 if (index != linkId && staLink.bssid.has_value())
551 {
552 multiLinkElement.AddPerStaProfileSubelement();
553 auto& perStaProfile = multiLinkElement.GetPerStaProfile(
554 multiLinkElement.GetNPerStaProfileSubelements() - 1);
555 // The Link ID subfield of the STA Control field of the Per-STA Profile subelement
556 // for the corresponding non-AP STA that requests a link for multi-link (re)setup
557 // with the AP MLD is set to the link ID of the AP affiliated with the AP MLD that
558 // is operating on that link. The link ID is obtained during multi-link discovery
559 perStaProfile.SetLinkId(index);
560 // For each Per-STA Profile subelement included in the Link Info field, the
561 // Complete Profile subfield of the STA Control field shall be set to 1
562 perStaProfile.SetCompleteProfile();
563 // The MAC Address Present subfield indicates the presence of the STA MAC Address
564 // subfield in the STA Info field and is set to 1 if the STA MAC Address subfield
565 // is present in the STA Info field; otherwise set to 0. An STA sets this subfield
566 // to 1 when the element carries complete profile.
567 perStaProfile.SetStaMacAddress(staLink.feManager->GetAddress());
568 perStaProfile.SetAssocRequest(GetAssociationRequest(isReassoc, index));
569 }
570 }
571
572 return multiLinkElement;
573}
574
576StaWifiMac::GetProbeReqMultiLinkElement(const std::vector<uint8_t>& apLinkIds,
577 std::optional<uint8_t> apMldId) const
578{
579 // IEEE 802.11be D6.0 9.4.2.321.3
581 if (apMldId.has_value())
582 {
583 mle.SetApMldId(*apMldId);
584 }
585
586 for (const auto apLinkId : apLinkIds)
587 {
589 auto& perStaProfile = mle.GetPerStaProfile(mle.GetNPerStaProfileSubelements() - 1);
590 perStaProfile.SetLinkId(apLinkId);
591 // Current support limited to Complete Profile request per link ID
592 // TODO: Add support for Partial Per-STA Profile request
593 perStaProfile.SetCompleteProfile();
594 };
595
596 return mle;
597}
598
599std::vector<TidToLinkMapping>
601{
602 NS_LOG_FUNCTION(this << apNegSupport);
603
604 auto ehtConfig = GetEhtConfiguration();
605 NS_ASSERT(ehtConfig);
606
607 auto negSupport = ehtConfig->m_tidLinkMappingSupport;
608
610 "Cannot request TID-to-Link Mapping if negotiation is not supported");
611
612 // store the mappings, so that we can enforce them when the AP MLD accepts them
613 m_dlTidLinkMappingInAssocReq = ehtConfig->GetTidLinkMapping(WifiDirection::DOWNLINK);
614 m_ulTidLinkMappingInAssocReq = ehtConfig->GetTidLinkMapping(WifiDirection::UPLINK);
615
619 negSupport == WifiTidToLinkMappingNegSupport::SAME_LINK_SET && !mappingValidForNegType1,
620 "Mapping TIDs to distinct link sets is incompatible with negotiation support of 1");
621
622 if (apNegSupport == WifiTidToLinkMappingNegSupport::SAME_LINK_SET && !mappingValidForNegType1)
623 {
624 // If the TID-to-link Mapping Negotiation Support subfield value received from a peer
625 // MLD is equal to 1, the MLD that initiates a TID-to-link mapping negotiation with the
626 // peer MLD shall send only the TID-to-link Mapping element where all TIDs are mapped to
627 // the same link set (Sec. 35.3.7.1.3 of 802.11be D3.1). We use default mapping to meet
628 // this requirement.
629 NS_LOG_DEBUG("Using default mapping because AP MLD advertised negotiation support of 1");
632 }
633
634 std::vector<TidToLinkMapping> ret(1);
635
636 ret.back().m_control.direction = WifiDirection::DOWNLINK;
637
638 // lambda to fill the last TID-to-Link Mapping IE in the vector to return
639 auto fillIe = [&ret](const auto& mapping) {
640 ret.back().m_control.defaultMapping = mapping.empty();
641
642 for (const auto& [tid, linkSet] : mapping)
643 {
644 // At any point in time, a TID shall always be mapped to at least one setup link both
645 // in DL and UL, which means that a TID-to-link mapping change is only valid and
646 // successful if it will not result in having any TID for which the link set for DL
647 // or UL is made of zero setup links (Sec. 35.3.7.1.1 of 802.11be D3.1)
648 NS_ABORT_MSG_IF(linkSet.empty(), "Cannot map a TID to an empty link set");
649 ret.back().SetLinkMappingOfTid(tid, linkSet);
650 }
651 };
652
654
656 {
657 ret.back().m_control.direction = WifiDirection::BOTH_DIRECTIONS;
658 return ret;
659 }
660
661 ret.emplace_back();
662 ret.back().m_control.direction = WifiDirection::UPLINK;
664
665 return ret;
666}
667
668void
670{
671 // find the link where the (Re)Association Request has to be sent
672 auto it = GetLinks().cbegin();
673 while (it != GetLinks().cend())
674 {
675 if (GetStaLink(it->second).sendAssocReq)
676 {
677 break;
678 }
679 it++;
680 }
681 NS_ABORT_MSG_IF(it == GetLinks().cend(),
682 "No link selected to send the (Re)Association Request");
683 uint8_t linkId = it->first;
684 auto& link = GetLink(linkId);
685 NS_ABORT_MSG_IF(!link.bssid.has_value(),
686 "No BSSID set for the link on which the (Re)Association Request is to be sent");
687
688 NS_LOG_FUNCTION(this << *link.bssid << isReassoc);
689 WifiMacHeader hdr;
691 hdr.SetAddr1(*link.bssid);
692 hdr.SetAddr2(link.feManager->GetAddress());
693 hdr.SetAddr3(*link.bssid);
694 hdr.SetDsNotFrom();
695 hdr.SetDsNotTo();
696 Ptr<Packet> packet = Create<Packet>();
697
698 auto frame = GetAssociationRequest(isReassoc, linkId);
699
700 // include a Multi-Link Element if this device has multiple links (independently
701 // of how many links will be setup) and the AP is a multi-link device;
702 // if the AP MLD has indicated a support of TID-to-link mapping negotiation, also
703 // include the TID-to-link Mapping element(s)
704 if (GetNLinks() > 1 &&
705 GetWifiRemoteStationManager(linkId)->GetMldAddress(*link.bssid).has_value())
706 {
707 auto addMle = [&](auto&& frame) {
708 frame.template Get<MultiLinkElement>() = GetBasicMultiLinkElement(isReassoc, linkId);
709 };
710 std::visit(addMle, frame);
711
713 if (const auto& mldCapabilities =
714 GetWifiRemoteStationManager(linkId)->GetStationMldCapabilities(*link.bssid);
715 mldCapabilities && (negSupport = static_cast<WifiTidToLinkMappingNegSupport>(
716 mldCapabilities->get().tidToLinkMappingSupport)) >
718 {
719 auto addTlm = [&](auto&& frame) {
720 frame.template Get<TidToLinkMapping>() = GetTidToLinkMappingElements(negSupport);
721 };
722 std::visit(addTlm, frame);
723 }
724 }
725
726 if (!isReassoc)
727 {
728 packet->AddHeader(std::get<MgtAssocRequestHeader>(frame));
729 }
730 else
731 {
732 packet->AddHeader(std::get<MgtReassocRequestHeader>(frame));
733 }
734
735 if (!GetQosSupported())
736 {
737 GetTxop()->Queue(Create<WifiMpdu>(packet, hdr));
738 }
739 // "A QoS STA that transmits a Management frame determines access category used
740 // for medium access in transmission of the Management frame as follows
741 // (If dot11QMFActivated is false or not present)
742 // — If the Management frame is individually addressed to a non-QoS STA, category
743 // AC_BE should be selected.
744 // — If category AC_BE was not selected by the previous step, category AC_VO
745 // shall be selected." (Sec. 10.2.3.2 of 802.11-2020)
746 else if (!GetWifiRemoteStationManager(linkId)->GetQosSupported(*link.bssid))
747 {
748 GetBEQueue()->Queue(Create<WifiMpdu>(packet, hdr));
749 }
750 else
751 {
752 GetVOQueue()->Queue(Create<WifiMpdu>(packet, hdr));
753 }
754
756 {
758 }
761}
762
763void
765{
766 NS_LOG_FUNCTION(this);
767 switch (m_state)
768 {
769 case ASSOCIATED:
770 return;
771 case SCANNING:
772 /* we have initiated active or passive scanning, continue to wait
773 and gather beacons or probe responses until the scanning timeout
774 */
775 break;
776 case UNASSOCIATED:
777 /* we were associated but we missed a bunch of beacons
778 * so we should assume we are not associated anymore.
779 * We try to initiate a scan now.
780 */
781 m_linkDown();
783 break;
784 case WAIT_ASSOC_RESP:
785 /* we have sent an association request so we do not need to
786 re-send an association request right now. We just need to
787 wait until either assoc-request-timeout or until
788 we get an association response.
789 */
790 case REFUSED:
791 /* we have sent an association request and received a negative
792 association response. We wait until someone restarts an
793 association with a given SSID.
794 */
795 break;
796 }
797}
798
799void
801{
802 NS_LOG_FUNCTION(this);
805
806 WifiScanParams scanParams;
807 scanParams.ssid = GetSsid();
808 for (const auto& [id, link] : GetLinks())
809 {
811 (link->phy->HasFixedPhyBand()) ? WifiScanParams::Channel{0, link->phy->GetPhyBand()}
813
814 scanParams.channelList.push_back(channel);
815 }
816 if (m_activeProbing)
817 {
818 scanParams.type = WifiScanType::ACTIVE;
820 scanParams.minChannelTime = scanParams.maxChannelTime = m_probeRequestTimeout;
821 }
822 else
823 {
824 scanParams.type = WifiScanType::PASSIVE;
826 }
827
828 m_assocManager->StartScanning(std::move(scanParams));
829}
830
831void
832StaWifiMac::ScanningTimeout(const std::optional<ApInfo>& bestAp)
833{
834 NS_LOG_FUNCTION(this);
835
836 if (!bestAp.has_value())
837 {
838 NS_LOG_DEBUG("Exhausted list of candidate AP; restart scanning");
840 return;
841 }
842
843 NS_LOG_DEBUG("Attempting to associate with AP: " << *bestAp);
844 UpdateApInfo(bestAp->m_frame, bestAp->m_apAddr, bestAp->m_bssid, bestAp->m_linkId);
845 // reset info on links to setup
846 for (auto& [id, link] : GetLinks())
847 {
848 auto& staLink = GetStaLink(link);
849 staLink.sendAssocReq = false;
850 staLink.bssid = std::nullopt;
851 }
852 // send Association Request on the link where the Beacon/Probe Response was received
853 GetLink(bestAp->m_linkId).sendAssocReq = true;
854 GetLink(bestAp->m_linkId).bssid = bestAp->m_bssid;
855 std::shared_ptr<CommonInfoBasicMle> mleCommonInfo;
856 // update info on links to setup (11be MLDs only)
857 const auto& mle =
858 std::visit([](auto&& frame) { return frame.template Get<MultiLinkElement>(); },
859 bestAp->m_frame);
860 std::map<uint8_t, uint8_t> swapInfo;
861 for (const auto& [localLinkId, apLinkId, bssid] : bestAp->m_setupLinks)
862 {
863 NS_ASSERT_MSG(mle, "We get here only for ML setup");
864 NS_LOG_DEBUG("Setting up link (local ID=" << +localLinkId << ", AP ID=" << +apLinkId
865 << ")");
866 GetLink(localLinkId).bssid = bssid;
867 if (!mleCommonInfo)
868 {
869 mleCommonInfo = std::make_shared<CommonInfoBasicMle>(mle->GetCommonInfoBasic());
870 }
871 GetWifiRemoteStationManager(localLinkId)->AddStationMleCommonInfo(bssid, mleCommonInfo);
872 swapInfo.emplace(localLinkId, apLinkId);
873 }
874
875 SwapLinks(swapInfo);
876
877 // lambda to get beacon interval from Beacon or Probe Response
878 auto getBeaconInterval = [](auto&& frame) {
879 using T = std::decay_t<decltype(frame)>;
880 if constexpr (std::is_same_v<T, MgtBeaconHeader> ||
881 std::is_same_v<T, MgtProbeResponseHeader>)
882 {
883 return MicroSeconds(frame.GetBeaconIntervalUs());
884 }
885 else
886 {
887 NS_ABORT_MSG("Unexpected frame type");
888 return Seconds(0);
889 }
890 };
891 Time beaconInterval = std::visit(getBeaconInterval, bestAp->m_frame);
892 Time delay = beaconInterval * m_maxMissedBeacons;
893 // restart beacon watchdog
895
898}
899
900void
907
908void
910{
911 NS_LOG_FUNCTION(this);
912
914 {
916 {
918 }
921 this);
922 return;
923 }
924 NS_LOG_DEBUG("beacon missed");
925 // We need to switch to the UNASSOCIATED state. However, if we are receiving a frame, wait
926 // until the RX is completed (otherwise, crashes may occur if we are receiving a MU frame
927 // because its reception requires the STA-ID). We need to check that a PHY is operating on
928 // the given link, because this may (temporarily) not be the case for EMLSR clients.
929 Time delay;
930 for (const auto& [id, link] : GetLinks())
931 {
932 if (link->phy && link->phy->IsStateRx())
933 {
934 delay = std::max(delay, link->phy->GetDelayUntilIdle());
935 }
936 }
938}
939
940void
942{
943 NS_LOG_FUNCTION(this);
944
945 Mac48Address apAddr; // the AP address to trace (MLD address in case of ML setup)
946
947 for (const auto& [id, link] : GetLinks())
948 {
949 auto& bssid = GetStaLink(link).bssid;
950 if (bssid)
951 {
952 apAddr = GetWifiRemoteStationManager(id)->GetMldAddress(*bssid).value_or(*bssid);
953 }
954 bssid = std::nullopt; // link is no longer setup
955 }
956
957 NS_LOG_DEBUG("Set state to UNASSOCIATED and start scanning");
959 // cancel the association request timer (see issue #862)
961 m_deAssocLogger(apAddr);
962 m_aid = 0; // reset AID
964}
965
966void
968{
969 NS_LOG_FUNCTION(this << delay);
970
973 {
974 NS_LOG_DEBUG("really restart watchdog.");
976 }
977}
978
979bool
981{
982 return m_state == ASSOCIATED;
983}
984
985bool
987{
988 return m_state == WAIT_ASSOC_RESP;
989}
990
991std::set<uint8_t>
993{
994 if (!IsAssociated())
995 {
996 return {};
997 }
998
999 std::set<uint8_t> linkIds;
1000 for (const auto& [id, link] : GetLinks())
1001 {
1002 if (GetStaLink(link).bssid)
1003 {
1004 linkIds.insert(id);
1005 }
1006 }
1007 return linkIds;
1008}
1009
1012{
1013 for (const auto& [id, link] : GetLinks())
1014 {
1015 if (GetStaLink(link).bssid == remoteAddr)
1016 {
1017 // the remote address is the address of the (single link) AP we are associated with;
1018 return link->feManager->GetAddress();
1019 }
1020 }
1021
1022 // the remote address is unknown
1023 return GetAddress();
1024}
1025
1026bool
1028{
1029 return IsAssociated();
1030}
1031
1032void
1038
1039void
1041{
1042 NS_LOG_FUNCTION(this << *mpdu << to << from);
1043
1044 auto& hdr = mpdu->GetHeader();
1045
1046 // the Receiver Address (RA) and the Transmitter Address (TA) are the MLD addresses only for
1047 // non-broadcast data frames exchanged between two MLDs
1048 auto linkIds = GetSetupLinkIds();
1049 NS_ASSERT(!linkIds.empty());
1050 uint8_t linkId = *linkIds.begin();
1051 const auto apMldAddr = GetWifiRemoteStationManager(linkId)->GetMldAddress(GetBssid(linkId));
1052
1053 hdr.SetAddr1(apMldAddr.value_or(GetBssid(linkId)));
1054 hdr.SetAddr2(apMldAddr ? GetAddress() : GetFrameExchangeManager(linkId)->GetAddress());
1055 hdr.SetAddr3(to);
1056 hdr.SetDsNotFrom();
1057 hdr.SetDsTo();
1058
1059 auto txop = hdr.IsQosData() ? StaticCast<Txop>(GetQosTxop(hdr.GetQosTid())) : GetTxop();
1060 NS_ASSERT(txop);
1061 txop->Queue(mpdu);
1062}
1063
1064void
1066{
1067 NS_LOG_FUNCTION(this << linkId << reason);
1068
1069 auto bssid = GetBssid(linkId);
1070 auto apAddress = GetWifiRemoteStationManager(linkId)->GetMldAddress(bssid).value_or(bssid);
1071
1072 BlockUnicastTxOnLinks(reason, apAddress, {linkId});
1073 // the only type of broadcast frames that a non-AP STA can send are management frames
1074 for (const auto& [acIndex, ac] : wifiAcList)
1075 {
1076 GetMacQueueScheduler()->BlockQueues(reason,
1077 acIndex,
1080 GetFrameExchangeManager(linkId)->GetAddress(),
1081 {},
1082 {linkId});
1083 }
1084}
1085
1086void
1087StaWifiMac::UnblockTxOnLink(std::set<uint8_t> linkIds, WifiQueueBlockedReason reason)
1088{
1089 std::stringstream ss;
1090 std::copy(linkIds.cbegin(), linkIds.cend(), std::ostream_iterator<uint16_t>(ss, " "));
1091 NS_LOG_FUNCTION(this << ss.str() << reason);
1092
1093 const auto linkId = *linkIds.cbegin();
1094 const auto bssid = GetBssid(linkId);
1095 const auto apAddress =
1096 GetWifiRemoteStationManager(linkId)->GetMldAddress(bssid).value_or(bssid);
1097
1098 UnblockUnicastTxOnLinks(reason, apAddress, linkIds);
1099 // the only type of broadcast frames that a non-AP STA can send are management frames
1100 for (const auto& [acIndex, ac] : wifiAcList)
1101 {
1102 GetMacQueueScheduler()->UnblockQueues(reason,
1103 acIndex,
1106 GetFrameExchangeManager(linkId)->GetAddress(),
1107 {},
1108 linkIds);
1109 }
1110}
1111
1112void
1114{
1115 NS_LOG_FUNCTION(this << *mpdu << +linkId);
1116 // consider the MAC header of the original MPDU (makes a difference for data frames only)
1117 const WifiMacHeader* hdr = &mpdu->GetOriginal()->GetHeader();
1118 Ptr<const Packet> packet = mpdu->GetPacket();
1119 NS_ASSERT(!hdr->IsCtl());
1121 : GetFrameExchangeManager(linkId)->GetAddress();
1122 if (hdr->GetAddr3() == myAddr)
1123 {
1124 NS_LOG_LOGIC("packet sent by us.");
1125 return;
1126 }
1127 if (hdr->GetAddr1() != myAddr && !hdr->GetAddr1().IsGroup())
1128 {
1129 NS_LOG_LOGIC("packet is not for us");
1130 NotifyRxDrop(packet);
1131 return;
1132 }
1133 if (hdr->IsData())
1134 {
1135 if (!IsAssociated())
1136 {
1137 NS_LOG_LOGIC("Received data frame while not associated: ignore");
1138 NotifyRxDrop(packet);
1139 return;
1140 }
1141 if (!(hdr->IsFromDs() && !hdr->IsToDs()))
1142 {
1143 NS_LOG_LOGIC("Received data frame not from the DS: ignore");
1144 NotifyRxDrop(packet);
1145 return;
1146 }
1147 std::set<Mac48Address> apAddresses; // link addresses of AP
1148 for (auto id : GetSetupLinkIds())
1149 {
1150 apAddresses.insert(GetBssid(id));
1151 }
1152 if (!apAddresses.contains(mpdu->GetHeader().GetAddr2()))
1153 {
1154 NS_LOG_LOGIC("Received data frame not from the BSS we are associated with: ignore");
1155 NotifyRxDrop(packet);
1156 return;
1157 }
1158 if (!hdr->HasData())
1159 {
1160 NS_LOG_LOGIC("Received (QoS) Null Data frame: ignore");
1161 NotifyRxDrop(packet);
1162 return;
1163 }
1164 if (hdr->IsQosData())
1165 {
1166 if (hdr->IsQosAmsdu())
1167 {
1168 NS_ASSERT(apAddresses.contains(mpdu->GetHeader().GetAddr3()));
1170 packet = nullptr;
1171 }
1172 else
1173 {
1174 ForwardUp(packet, hdr->GetAddr3(), hdr->GetAddr1());
1175 }
1176 }
1177 else
1178 {
1179 ForwardUp(packet, hdr->GetAddr3(), hdr->GetAddr1());
1180 }
1181 return;
1182 }
1183
1184 switch (hdr->GetType())
1185 {
1189 // This is a frame aimed at an AP, so we can safely ignore it.
1190 NotifyRxDrop(packet);
1191 break;
1192
1194 ReceiveBeacon(mpdu, linkId);
1195 break;
1196
1198 ReceiveProbeResp(mpdu, linkId);
1199 break;
1200
1203 ReceiveAssocResp(mpdu, linkId);
1204 break;
1205
1207 if (auto [category, action] = WifiActionHeader::Peek(packet);
1208 category == WifiActionHeader::PROTECTED_EHT &&
1209 action.protectedEhtAction ==
1211 {
1212 // this is handled by the EMLSR Manager
1213 break;
1214 }
1215
1216 default:
1217 // Invoke the receive handler of our parent class to deal with any other frames
1218 WifiMac::Receive(mpdu, linkId);
1219 }
1220
1221 if (m_emlsrManager)
1222 {
1223 m_emlsrManager->NotifyMgtFrameReceived(mpdu, linkId);
1224 }
1225}
1226
1227void
1229{
1230 NS_LOG_FUNCTION(this << *mpdu << +linkId);
1231 const WifiMacHeader& hdr = mpdu->GetHeader();
1232 NS_ASSERT(hdr.IsBeacon());
1233
1234 NS_LOG_DEBUG("Beacon received");
1235 MgtBeaconHeader beacon;
1236 mpdu->GetPacket()->PeekHeader(beacon);
1237 const auto& capabilities = beacon.Capabilities();
1238 NS_ASSERT(capabilities.IsEss());
1239 bool goodBeacon;
1240 if (IsWaitAssocResp() || IsAssociated())
1241 {
1242 // we have to process this Beacon only if sent by the AP we are associated
1243 // with or from which we are waiting an Association Response frame
1244 auto bssid = GetLink(linkId).bssid;
1245 goodBeacon = bssid.has_value() && (hdr.GetAddr3() == *bssid);
1246 }
1247 else
1248 {
1249 // we retain this Beacon as candidate AP if the supported rates fit the
1250 // configured BSS membership selector
1251 goodBeacon = CheckSupportedRates(beacon, linkId);
1252 }
1253
1254 SnrTag snrTag;
1255 bool found = mpdu->GetPacket()->PeekPacketTag(snrTag);
1256 NS_ASSERT(found);
1257 ApInfo apInfo = {.m_bssid = hdr.GetAddr3(),
1258 .m_apAddr = hdr.GetAddr2(),
1259 .m_snr = snrTag.Get(),
1260 .m_frame = std::move(beacon),
1261 .m_channel = {GetCurrentChannel(linkId)},
1262 .m_linkId = linkId};
1263
1264 if (!m_beaconInfo.IsEmpty())
1265 {
1266 m_beaconInfo(apInfo);
1267 }
1268
1269 if (!goodBeacon)
1270 {
1271 NS_LOG_LOGIC("Beacon is not for us");
1272 return;
1273 }
1274 if (m_state == ASSOCIATED)
1275 {
1277 Time delay = MicroSeconds(std::get<MgtBeaconHeader>(apInfo.m_frame).GetBeaconIntervalUs() *
1279 RestartBeaconWatchdog(delay);
1280 UpdateApInfo(apInfo.m_frame, hdr.GetAddr2(), hdr.GetAddr3(), linkId);
1281 }
1282 else
1283 {
1284 NS_LOG_DEBUG("Beacon received from " << hdr.GetAddr2());
1285 m_assocManager->NotifyApInfo(std::move(apInfo));
1286 }
1287}
1288
1289void
1291{
1292 NS_LOG_FUNCTION(this << *mpdu << +linkId);
1293 const WifiMacHeader& hdr = mpdu->GetHeader();
1294 NS_ASSERT(hdr.IsProbeResp());
1295
1296 NS_LOG_DEBUG("Probe response received from " << hdr.GetAddr2());
1297 MgtProbeResponseHeader probeResp;
1298 mpdu->GetPacket()->PeekHeader(probeResp);
1299 if (!CheckSupportedRates(probeResp, linkId))
1300 {
1301 return;
1302 }
1303 SnrTag snrTag;
1304 bool found = mpdu->GetPacket()->PeekPacketTag(snrTag);
1305 NS_ASSERT(found);
1306 m_assocManager->NotifyApInfo(ApInfo{.m_bssid = hdr.GetAddr3(),
1307 .m_apAddr = hdr.GetAddr2(),
1308 .m_snr = snrTag.Get(),
1309 .m_frame = std::move(probeResp),
1310 .m_channel = {GetCurrentChannel(linkId)},
1311 .m_linkId = linkId});
1312}
1313
1314void
1316{
1317 NS_LOG_FUNCTION(this << *mpdu << +linkId);
1318 const WifiMacHeader& hdr = mpdu->GetHeader();
1319 NS_ASSERT(hdr.IsAssocResp() || hdr.IsReassocResp());
1320
1321 if (m_state != WAIT_ASSOC_RESP)
1322 {
1323 return;
1324 }
1325
1326 std::optional<Mac48Address> apMldAddress;
1327 MgtAssocResponseHeader assocResp;
1328 mpdu->GetPacket()->PeekHeader(assocResp);
1330 {
1332 }
1333 if (assocResp.GetStatusCode().IsSuccess())
1334 {
1335 m_aid = assocResp.GetAssociationId();
1336 NS_LOG_DEBUG((hdr.IsReassocResp() ? "reassociation done" : "association completed"));
1337 UpdateApInfo(assocResp, hdr.GetAddr2(), hdr.GetAddr3(), linkId);
1338 NS_ASSERT(GetLink(linkId).bssid.has_value() && *GetLink(linkId).bssid == hdr.GetAddr3());
1339 SetBssid(hdr.GetAddr3(), linkId);
1341 if ((GetNLinks() > 1) && assocResp.Get<MultiLinkElement>().has_value())
1342 {
1343 // this is an ML setup, trace the setup link
1344 m_setupCompleted(linkId, hdr.GetAddr3());
1345 apMldAddress = GetWifiRemoteStationManager(linkId)->GetMldAddress(hdr.GetAddr3());
1346 NS_ASSERT(apMldAddress);
1347
1348 if (const auto& mldCapabilities =
1349 GetWifiRemoteStationManager(linkId)->GetStationMldCapabilities(hdr.GetAddr3());
1350 mldCapabilities && static_cast<WifiTidToLinkMappingNegSupport>(
1351 mldCapabilities->get().tidToLinkMappingSupport) >
1353 {
1354 // the AP MLD supports TID-to-Link Mapping negotiation, hence we included
1355 // TID-to-Link Mapping element(s) in the Association Request.
1356 if (assocResp.Get<TidToLinkMapping>().empty())
1357 {
1358 // The AP MLD did not include a TID-to-Link Mapping element in the Association
1359 // Response, hence it accepted the mapping, which we can now store.
1360 UpdateTidToLinkMapping(*apMldAddress,
1363 UpdateTidToLinkMapping(*apMldAddress,
1366
1367 // Apply the negotiated TID-to-Link Mapping (if any) for UL direction
1369 }
1370 }
1371 }
1372 else
1373 {
1374 m_assocLogger(hdr.GetAddr3());
1375 }
1376 if (!m_linkUp.IsNull())
1377 {
1378 m_linkUp();
1379 }
1380 }
1381 else
1382 {
1383 // If the link on which the (Re)Association Request frame was received cannot be
1384 // accepted by the AP MLD, the AP MLD shall treat the multi-link (re)setup as a
1385 // failure and shall not accept any requested links. If the link on which the
1386 // (Re)Association Request frame was received is accepted by the AP MLD, the
1387 // multi-link (re)setup is successful. (Sec. 35.3.5.1 of 802.11be D3.1)
1388 NS_LOG_DEBUG("association refused");
1390 StartScanning();
1391 return;
1392 }
1393
1394 // if this is an MLD, check if we can setup (other) links
1395 if (GetNLinks() > 1)
1396 {
1397 // create a list of all local Link IDs. IDs are removed as we find a corresponding
1398 // Per-STA Profile Subelements indicating successful association. Links with
1399 // remaining IDs are not setup
1400 std::list<uint8_t> setupLinks;
1401 for (const auto& [id, link] : GetLinks())
1402 {
1403 setupLinks.push_back(id);
1404 }
1405 if (assocResp.GetStatusCode().IsSuccess())
1406 {
1407 setupLinks.remove(linkId);
1408 }
1409
1410 // if a Multi-Link Element is present, check its content
1411 if (const auto& mle = assocResp.Get<MultiLinkElement>())
1412 {
1413 NS_ABORT_MSG_IF(!GetLink(linkId).bssid.has_value(),
1414 "The link on which the Association Response was received "
1415 "is not a link we requested to setup");
1416 NS_ABORT_MSG_IF(linkId != mle->GetLinkIdInfo(),
1417 "The link ID of the AP that transmitted the Association "
1418 "Response does not match the stored link ID");
1420 mle->GetMldMacAddress(),
1421 "The AP MLD MAC address in the received Multi-Link Element does not "
1422 "match the address stored in the station manager for link "
1423 << +linkId);
1424 // process the Per-STA Profile Subelements in the Multi-Link Element
1425 for (std::size_t elem = 0; elem < mle->GetNPerStaProfileSubelements(); elem++)
1426 {
1427 auto& perStaProfile = mle->GetPerStaProfile(elem);
1428 uint8_t apLinkId = perStaProfile.GetLinkId();
1429 auto it = GetLinks().find(apLinkId);
1430 uint8_t staLinkid = 0;
1431 std::optional<Mac48Address> bssid;
1432 NS_ABORT_MSG_IF(it == GetLinks().cend() ||
1433 !(bssid = GetLink((staLinkid = it->first)).bssid).has_value(),
1434 "Setup for AP link ID " << apLinkId << " was not requested");
1435 NS_ABORT_MSG_IF(*bssid != perStaProfile.GetStaMacAddress(),
1436 "The BSSID in the Per-STA Profile for link ID "
1437 << +staLinkid << " does not match the stored BSSID");
1440 perStaProfile.GetStaMacAddress()) != mle->GetMldMacAddress(),
1441 "The AP MLD MAC address in the received Multi-Link Element does not "
1442 "match the address stored in the station manager for link "
1443 << +staLinkid);
1444 // process the Association Response contained in this Per-STA Profile
1445 MgtAssocResponseHeader assoc = perStaProfile.GetAssocResponse();
1446 if (assoc.GetStatusCode().IsSuccess())
1447 {
1448 NS_ABORT_MSG_IF(m_aid != 0 && m_aid != assoc.GetAssociationId(),
1449 "AID should be the same for all the links");
1450 m_aid = assoc.GetAssociationId();
1451 NS_LOG_DEBUG("Setup on link " << staLinkid << " completed");
1452 UpdateApInfo(assoc, *bssid, *bssid, staLinkid);
1453 SetBssid(*bssid, staLinkid);
1454 m_setupCompleted(staLinkid, *bssid);
1456 apMldAddress = GetWifiRemoteStationManager(staLinkid)->GetMldAddress(*bssid);
1457 if (!m_linkUp.IsNull())
1458 {
1459 m_linkUp();
1460 }
1461 }
1462 // remove the ID of the link we setup
1463 setupLinks.remove(staLinkid);
1464 }
1465 }
1466 // remaining links in setupLinks are not setup and hence must be disabled
1467 for (const auto& id : setupLinks)
1468 {
1469 GetLink(id).bssid = std::nullopt;
1470 GetLink(id).phy->SetOffMode();
1471 }
1472 if (apMldAddress)
1473 {
1474 // this is an ML setup, trace the MLD address of the AP (only once)
1475 m_assocLogger(*apMldAddress);
1476 }
1477 }
1478
1479 // the station that associated with the AP may have dissociated and then associated again.
1480 // In this case, the station may store packets from the previous period in which it was
1481 // associated. Have the station restart access if it has packets queued.
1482 for (const auto& [id, link] : GetLinks())
1483 {
1484 if (GetStaLink(link).bssid)
1485 {
1486 if (const auto txop = GetTxop())
1487 {
1488 txop->StartAccessAfterEvent(id,
1491 }
1492 for (const auto& [acIndex, ac] : wifiAcList)
1493 {
1494 if (const auto edca = GetQosTxop(acIndex))
1495 {
1496 edca->StartAccessAfterEvent(id,
1499 }
1500 }
1501 }
1502 }
1503
1505}
1506
1507void
1509{
1510 NS_LOG_FUNCTION(this << linkId);
1511
1512 // STAs operating on setup links may need to transition to a new PM mode after the
1513 // acknowledgement of the Association Response. For this purpose, we connect a callback to
1514 // the PHY TX begin trace to catch the Ack transmitted after the Association Response.
1516 [=, this](WifiConstPsduMap psduMap, WifiTxVector txVector, Watt_u /* txPower */) {
1517 NS_ASSERT_MSG(psduMap.size() == 1 && psduMap.begin()->second->GetNMpdus() == 1 &&
1518 psduMap.begin()->second->GetHeader(0).IsAck(),
1519 "Expected a Normal Ack after Association Response frame");
1520
1521 auto ackDuration =
1522 WifiPhy::CalculateTxDuration(psduMap, txVector, GetLink(linkId).phy->GetPhyBand());
1523
1524 for (const auto& [id, lnk] : GetLinks())
1525 {
1526 auto& link = GetStaLink(lnk);
1527
1528 if (!link.bssid)
1529 {
1530 // link has not been setup
1531 continue;
1532 }
1533
1534 if (id == linkId)
1535 {
1536 /**
1537 * When a link becomes enabled for a non-AP STA that is affiliated with a
1538 * non-AP MLD after successful association with an AP MLD with (Re)Association
1539 * Request/Response frames transmitted on that link [..], the power management
1540 * mode of the non-AP STA, immediately after the acknowledgement of the
1541 * (Re)Association Response frame [..], is active mode.
1542 * (Sec. 35.3.7.1.4 of 802.11be D3.0)
1543 */
1544 // if the user requested this link to be in powersave mode, we have to
1545 // switch PM mode
1546 if (link.pmMode == WIFI_PM_POWERSAVE)
1547 {
1548 Simulator::Schedule(ackDuration,
1550 this,
1551 std::pair<bool, uint8_t>{true, id});
1552 }
1553 link.pmMode = WIFI_PM_ACTIVE;
1554 }
1555 else
1556 {
1557 /**
1558 * When a link becomes enabled for a non-AP STA that is affiliated with a
1559 * non-AP MLD after successful association with an AP MLD with (Re)Association
1560 * Request/Response frames transmitted on another link [..], the power
1561 * management mode of the non-AP STA, immediately after the acknowledgement of
1562 * the (Re)Association Response frame [..], is power save mode, and its power
1563 * state is doze. (Sec. 35.3.7.1.4 of 802.11be D3.0)
1564 */
1565 // if the user requested this link to be in active mode, we have to
1566 // switch PM mode
1567 if (link.pmMode == WIFI_PM_ACTIVE)
1568 {
1569 Simulator::Schedule(ackDuration,
1571 this,
1572 std::pair<bool, uint8_t>{false, id});
1573 }
1574 link.pmMode = WIFI_PM_POWERSAVE;
1575 }
1576 }
1577 });
1578
1579 // connect the callback to the PHY TX begin trace to catch the Ack and disconnect
1580 // after its transmission begins
1581 auto phy = GetLink(linkId).phy;
1582 phy->TraceConnectWithoutContext("PhyTxPsduBegin", cb);
1583 Simulator::Schedule(phy->GetSifs() + NanoSeconds(1),
1584 [=]() { phy->TraceDisconnectWithoutContext("PhyTxPsduBegin", cb); });
1585}
1586
1587bool
1588StaWifiMac::CheckSupportedRates(std::variant<MgtBeaconHeader, MgtProbeResponseHeader> frame,
1589 uint8_t linkId)
1590{
1591 NS_LOG_FUNCTION(this << +linkId);
1592
1593 // lambda to invoke on the current frame variant
1594 auto check = [&](auto&& mgtFrame) -> bool {
1595 // check supported rates
1596 NS_ASSERT(mgtFrame.template Get<SupportedRates>());
1597 const auto rates = AllSupportedRates{*mgtFrame.template Get<SupportedRates>(),
1598 mgtFrame.template Get<ExtendedSupportedRatesIE>()};
1599 for (const auto& selector : GetWifiPhy(linkId)->GetBssMembershipSelectorList())
1600 {
1601 if (!rates.IsBssMembershipSelectorRate(selector))
1602 {
1603 NS_LOG_DEBUG("Supported rates do not fit with the BSS membership selector");
1604 return false;
1605 }
1606 }
1607
1608 return true;
1609 };
1610
1611 return std::visit(check, frame);
1612}
1613
1614void
1616 const Mac48Address& apAddr,
1617 const Mac48Address& bssid,
1618 uint8_t linkId)
1619{
1620 NS_LOG_FUNCTION(this << frame.index() << apAddr << bssid << +linkId);
1621
1622 // ERP Information is not present in Association Response frames
1623 const std::optional<ErpInformation>* erpInformation = nullptr;
1624
1625 if (const auto* beacon = std::get_if<MgtBeaconHeader>(&frame))
1626 {
1627 erpInformation = &beacon->Get<ErpInformation>();
1628 }
1629 else if (const auto* probe = std::get_if<MgtProbeResponseHeader>(&frame))
1630 {
1631 erpInformation = &probe->Get<ErpInformation>();
1632 }
1633
1634 // lambda processing Information Elements included in all frame types
1635 auto commonOps = [&](auto&& frame) {
1636 const auto& capabilities = frame.Capabilities();
1637 NS_ASSERT(frame.template Get<SupportedRates>());
1638 const auto rates = AllSupportedRates{*frame.template Get<SupportedRates>(),
1639 frame.template Get<ExtendedSupportedRatesIE>()};
1640 for (const auto& mode : GetWifiPhy(linkId)->GetModeList())
1641 {
1642 if (rates.IsSupportedRate(mode.GetDataRate(GetWifiPhy(linkId)->GetChannelWidth())))
1643 {
1644 GetWifiRemoteStationManager(linkId)->AddSupportedMode(apAddr, mode);
1645 if (rates.IsBasicRate(mode.GetDataRate(GetWifiPhy(linkId)->GetChannelWidth())))
1646 {
1647 GetWifiRemoteStationManager(linkId)->AddBasicMode(mode);
1648 }
1649 }
1650 }
1651
1652 bool isShortPreambleEnabled = capabilities.IsShortPreamble();
1653 if (erpInformation && erpInformation->has_value() && GetErpSupported(linkId))
1654 {
1655 isShortPreambleEnabled &= !(*erpInformation)->GetBarkerPreambleMode();
1656 if ((*erpInformation)->GetUseProtection() != 0)
1657 {
1658 GetWifiRemoteStationManager(linkId)->SetUseNonErpProtection(true);
1659 }
1660 else
1661 {
1662 GetWifiRemoteStationManager(linkId)->SetUseNonErpProtection(false);
1663 }
1664 if (capabilities.IsShortSlotTime() == true)
1665 {
1666 // enable short slot time
1667 GetWifiPhy(linkId)->SetSlot(MicroSeconds(9));
1668 }
1669 else
1670 {
1671 // disable short slot time
1672 GetWifiPhy(linkId)->SetSlot(MicroSeconds(20));
1673 }
1674 }
1675 GetWifiRemoteStationManager(linkId)->SetShortPreambleEnabled(isShortPreambleEnabled);
1676 GetWifiRemoteStationManager(linkId)->SetShortSlotTimeEnabled(
1677 capabilities.IsShortSlotTime());
1678
1679 if (!GetQosSupported())
1680 {
1681 return;
1682 }
1683 /* QoS station */
1684 bool qosSupported = false;
1685 const auto& edcaParameters = frame.template Get<EdcaParameterSet>();
1686 if (edcaParameters.has_value())
1687 {
1688 qosSupported = true;
1689 // The value of the TXOP Limit field is specified as an unsigned integer, with the least
1690 // significant octet transmitted first, in units of 32 μs.
1692 edcaParameters->GetBeCWmin(),
1693 edcaParameters->GetBeCWmax(),
1694 edcaParameters->GetBeAifsn(),
1695 32 * MicroSeconds(edcaParameters->GetBeTxopLimit())},
1696 linkId);
1698 edcaParameters->GetBkCWmin(),
1699 edcaParameters->GetBkCWmax(),
1700 edcaParameters->GetBkAifsn(),
1701 32 * MicroSeconds(edcaParameters->GetBkTxopLimit())},
1702 linkId);
1704 edcaParameters->GetViCWmin(),
1705 edcaParameters->GetViCWmax(),
1706 edcaParameters->GetViAifsn(),
1707 32 * MicroSeconds(edcaParameters->GetViTxopLimit())},
1708 linkId);
1710 edcaParameters->GetVoCWmin(),
1711 edcaParameters->GetVoCWmax(),
1712 edcaParameters->GetVoAifsn(),
1713 32 * MicroSeconds(edcaParameters->GetVoTxopLimit())},
1714 linkId);
1715 }
1716 GetWifiRemoteStationManager(linkId)->SetQosSupport(apAddr, qosSupported);
1717
1718 if (GetHtSupported(linkId))
1719 {
1720 /* HT station */
1721 if (const auto& htCapabilities = frame.template Get<HtCapabilities>();
1722 htCapabilities.has_value())
1723 {
1724 GetWifiRemoteStationManager(linkId)->AddStationHtCapabilities(apAddr,
1725 *htCapabilities);
1726 }
1727 else
1728 {
1729 GetWifiRemoteStationManager(linkId)->RemoveAllSupportedMcs(apAddr);
1730 }
1731 // TODO: process ExtendedCapabilities
1732 // ExtendedCapabilities extendedCapabilities = frame.GetExtendedCapabilities ();
1733 }
1734
1735 // we do not return if VHT is not supported because HE STAs operating in
1736 // the 2.4 GHz band do not support VHT
1737 if (GetVhtSupported(linkId))
1738 {
1739 const auto& vhtCapabilities = frame.template Get<VhtCapabilities>();
1740 // we will always fill in RxHighestSupportedLgiDataRate field at TX, so this can be used
1741 // to check whether it supports VHT
1742 if (vhtCapabilities.has_value() &&
1743 vhtCapabilities->GetRxHighestSupportedLgiDataRate() > 0)
1744 {
1745 GetWifiRemoteStationManager(linkId)->AddStationVhtCapabilities(apAddr,
1746 *vhtCapabilities);
1747 // const auto& vhtOperation = frame.GetVhtOperation ();
1748 for (const auto& mcs : GetWifiPhy(linkId)->GetMcsList(WIFI_MOD_CLASS_VHT))
1749 {
1750 if (vhtCapabilities->IsSupportedRxMcs(mcs.GetMcsValue()))
1751 {
1752 GetWifiRemoteStationManager(linkId)->AddSupportedMcs(apAddr, mcs);
1753 }
1754 }
1755 }
1756 }
1757
1758 if (!GetHeSupported())
1759 {
1760 return;
1761 }
1762 /* HE station */
1763 const auto& heCapabilities = frame.template Get<HeCapabilities>();
1764 if (heCapabilities.has_value() && heCapabilities->GetSupportedMcsAndNss() != 0)
1765 {
1766 GetWifiRemoteStationManager(linkId)->AddStationHeCapabilities(apAddr, *heCapabilities);
1767 for (const auto& mcs : GetWifiPhy(linkId)->GetMcsList(WIFI_MOD_CLASS_HE))
1768 {
1769 if (heCapabilities->IsSupportedRxMcs(mcs.GetMcsValue()))
1770 {
1771 GetWifiRemoteStationManager(linkId)->AddSupportedMcs(apAddr, mcs);
1772 }
1773 }
1774 if (const auto& heOperation = frame.template Get<HeOperation>();
1775 heOperation.has_value())
1776 {
1777 GetHeConfiguration()->m_bssColor = heOperation->m_bssColorInfo.m_bssColor;
1778 }
1779 }
1780
1781 const auto& muEdcaParameters = frame.template Get<MuEdcaParameterSet>();
1782 if (muEdcaParameters.has_value())
1783 {
1785 muEdcaParameters->GetMuCwMin(AC_BE),
1786 muEdcaParameters->GetMuCwMax(AC_BE),
1787 muEdcaParameters->GetMuAifsn(AC_BE),
1788 muEdcaParameters->GetMuEdcaTimer(AC_BE)},
1789 linkId);
1791 muEdcaParameters->GetMuCwMin(AC_BK),
1792 muEdcaParameters->GetMuCwMax(AC_BK),
1793 muEdcaParameters->GetMuAifsn(AC_BK),
1794 muEdcaParameters->GetMuEdcaTimer(AC_BK)},
1795 linkId);
1797 muEdcaParameters->GetMuCwMin(AC_VI),
1798 muEdcaParameters->GetMuCwMax(AC_VI),
1799 muEdcaParameters->GetMuAifsn(AC_VI),
1800 muEdcaParameters->GetMuEdcaTimer(AC_VI)},
1801 linkId);
1803 muEdcaParameters->GetMuCwMin(AC_VO),
1804 muEdcaParameters->GetMuCwMax(AC_VO),
1805 muEdcaParameters->GetMuAifsn(AC_VO),
1806 muEdcaParameters->GetMuEdcaTimer(AC_VO)},
1807 linkId);
1808 }
1809
1810 if (Is6GhzBand(linkId))
1811 {
1812 if (const auto& he6GhzCapabilities = frame.template Get<He6GhzBandCapabilities>())
1813 {
1814 GetWifiRemoteStationManager(linkId)->AddStationHe6GhzCapabilities(
1815 apAddr,
1816 *he6GhzCapabilities);
1817 }
1818 }
1819
1820 if (!GetEhtSupported())
1821 {
1822 return;
1823 }
1824 /* EHT station */
1825 const auto& ehtCapabilities = frame.template Get<EhtCapabilities>();
1826 // TODO: once we support non constant rate managers, we should add checks here whether EHT
1827 // is supported by the peer
1828 GetWifiRemoteStationManager(linkId)->AddStationEhtCapabilities(apAddr, *ehtCapabilities);
1829
1830 if (const auto& mle = frame.template Get<MultiLinkElement>(); mle && m_emlsrManager)
1831 {
1832 if (mle->HasEmlCapabilities())
1833 {
1834 m_emlsrManager->SetTransitionTimeout(mle->GetTransitionTimeout());
1835 }
1836 if (const auto& common = mle->GetCommonInfoBasic(); common.m_mediumSyncDelayInfo)
1837 {
1838 m_emlsrManager->SetMediumSyncDuration(common.GetMediumSyncDelayTimer());
1839 m_emlsrManager->SetMediumSyncOfdmEdThreshold(common.GetMediumSyncOfdmEdThreshold());
1840 m_emlsrManager->SetMediumSyncMaxNTxops(common.GetMediumSyncMaxNTxops());
1841 }
1842 }
1843 };
1844
1845 // process Information Elements included in the current frame variant
1846 std::visit(commonOps, frame);
1847}
1848
1849void
1850StaWifiMac::SetPowerSaveMode(const std::pair<bool, uint8_t>& enableLinkIdPair)
1851{
1852 const auto [enable, linkId] = enableLinkIdPair;
1853 NS_LOG_FUNCTION(this << enable << linkId);
1854
1855 auto& link = GetLink(linkId);
1856
1857 if (!IsAssociated())
1858 {
1859 NS_LOG_DEBUG("Not associated yet, record the PM mode to switch to upon association");
1860 link.pmMode = enable ? WIFI_PM_POWERSAVE : WIFI_PM_ACTIVE;
1861 return;
1862 }
1863
1864 if (!link.bssid)
1865 {
1866 NS_LOG_DEBUG("Link " << +linkId << " has not been setup, ignore request");
1867 return;
1868 }
1869
1870 if ((enable && link.pmMode == WIFI_PM_POWERSAVE) || (!enable && link.pmMode == WIFI_PM_ACTIVE))
1871 {
1872 NS_LOG_DEBUG("No PM mode change needed");
1873 return;
1874 }
1875
1877
1878 // reschedule a call to this function to make sure that the PM mode switch
1879 // is eventually completed
1882 this,
1883 enableLinkIdPair);
1884
1885 if (HasFramesToTransmit(linkId))
1886 {
1887 NS_LOG_DEBUG("Next transmitted frame will be sent with PM=" << enable);
1888 return;
1889 }
1890
1891 // No queued frames. Enqueue a Data Null frame to inform the AP of the PM mode change
1893
1894 hdr.SetAddr1(GetBssid(linkId));
1896 hdr.SetAddr3(GetBssid(linkId));
1897 hdr.SetDsNotFrom();
1898 hdr.SetDsTo();
1899 enable ? hdr.SetPowerManagement() : hdr.SetNoPowerManagement();
1900 if (GetQosSupported())
1901 {
1903 }
1904 else
1905 {
1907 }
1908}
1909
1911StaWifiMac::GetPmMode(uint8_t linkId) const
1912{
1913 return GetLink(linkId).pmMode;
1914}
1915
1916void
1918{
1919 NS_LOG_FUNCTION(this << *mpdu);
1920
1921 auto linkId = GetLinkIdByAddress(mpdu->GetHeader().GetAddr2());
1922
1923 if (!linkId)
1924 {
1925 // the given MPDU may be the original copy containing MLD addresses and not carrying
1926 // a valid PM bit (which is set on the aliases).
1927 auto linkIds = mpdu->GetInFlightLinkIds();
1928 NS_ASSERT_MSG(!linkIds.empty(),
1929 "The TA of the acked MPDU (" << *mpdu
1930 << ") is not a link "
1931 "address and the MPDU is not inflight");
1932 // in case the ack'ed MPDU is inflight on multiple links, we cannot really know if
1933 // it was received by the AP on all links or only on some links. Hence, we only
1934 // consider the first link ID in the set, given that in the most common case of MPDUs
1935 // that cannot be sent concurrently on multiple links, there will be only one link ID
1936 linkId = *linkIds.begin();
1937 mpdu = GetTxopQueue(mpdu->GetQueueAc())->GetAlias(mpdu, *linkId);
1938 }
1939
1940 auto& link = GetLink(*linkId);
1941 const WifiMacHeader& hdr = mpdu->GetHeader();
1942
1943 // we received an acknowledgment while switching PM mode; the PM mode change is effective now
1944 if (hdr.IsPowerManagement() && link.pmMode == WIFI_PM_SWITCHING_TO_PS)
1945 {
1946 link.pmMode = WIFI_PM_POWERSAVE;
1947 }
1948 else if (!hdr.IsPowerManagement() && link.pmMode == WIFI_PM_SWITCHING_TO_ACTIVE)
1949 {
1950 link.pmMode = WIFI_PM_ACTIVE;
1951 }
1952}
1953
1956{
1957 AllSupportedRates rates;
1958 for (const auto& mode : GetWifiPhy(linkId)->GetModeList())
1959 {
1960 uint64_t modeDataRate = mode.GetDataRate(GetWifiPhy(linkId)->GetChannelWidth());
1961 NS_LOG_DEBUG("Adding supported rate of " << modeDataRate);
1962 rates.AddSupportedRate(modeDataRate);
1963 }
1964 if (GetHtSupported(linkId))
1965 {
1966 for (const auto& selector : GetWifiPhy(linkId)->GetBssMembershipSelectorList())
1967 {
1968 rates.AddBssMembershipSelectorRate(selector);
1969 }
1970 }
1971 return rates;
1972}
1973
1975StaWifiMac::GetCapabilities(uint8_t linkId) const
1976{
1977 CapabilityInformation capabilities;
1978 capabilities.SetShortPreamble(GetWifiPhy(linkId)->GetShortPhyPreambleSupported() ||
1979 GetErpSupported(linkId));
1981 return capabilities;
1982}
1983
1984void
1986{
1987 m_state = value;
1988}
1989
1990void
1991StaWifiMac::SetEdcaParameters(const EdcaParams& params, uint8_t linkId)
1992{
1993 Ptr<QosTxop> edca = GetQosTxop(params.ac);
1994 edca->SetMinCw(params.cwMin, linkId);
1995 edca->SetMaxCw(params.cwMax, linkId);
1996 edca->SetAifsn(params.aifsn, linkId);
1997 edca->SetTxopLimit(params.txopLimit, linkId);
1998}
1999
2000void
2001StaWifiMac::SetMuEdcaParameters(const MuEdcaParams& params, uint8_t linkId)
2002{
2003 Ptr<QosTxop> edca = GetQosTxop(params.ac);
2004 edca->SetMuCwMin(params.cwMin, linkId);
2005 edca->SetMuCwMax(params.cwMax, linkId);
2006 edca->SetMuAifsn(params.aifsn, linkId);
2007 edca->SetMuEdcaTimer(params.muEdcaTimer, linkId);
2008}
2009
2010void
2012{
2013 NS_LOG_FUNCTION(this);
2014 if (IsAssociated())
2015 {
2016 NS_LOG_DEBUG("PHY capabilities changed: send reassociation request");
2019 }
2020}
2021
2022/**
2023 * Initial configuration:
2024 *
2025 * ┌───┬───┬───┐ ┌────┐ ┌───────┐
2026 * Link A │FEM│RSM│CAM│◄──────►│Main├──────►│Channel│
2027 * │ │ │ │ │PHY │ │ A │
2028 * └───┴───┴───┘ └────┘ └───────┘
2029 *
2030 * ┌───┬───┬───┐ ┌────┐ ┌───────┐
2031 * Link B │FEM│RSM│CAM│ │Aux │ │Channel│
2032 * │ │ │ │◄──────►│PHY ├──────►│ B │
2033 * └───┴───┴───┘ └────┘ └───────┘
2034 *
2035 * A link switching/swapping is notified by the EMLSR Manager and the Channel Access Manager
2036 * (CAM) notifies us that a first PHY (i.e., the Main PHY) switches to Channel B. We connect
2037 * the Main PHY to the MAC stack B:
2038 *
2039 *
2040 * ┌───┬───┬───┐ ┌────┐ ┌───────┐
2041 * Link A │FEM│RSM│CAM│ ┌───►│Main├───┐ │Channel│
2042 * │ │ │ │ │ │PHY │ │ │ A │
2043 * └───┴───┴───┘ │ └────┘ │ └───────┘
2044 * │ │
2045 * ┌───┬───┬───┐ │ ┌────┐ │ ┌───────┐
2046 * Link B │FEM│RSM│CAM│◄──┘ │Aux │ └──►│Channel│
2047 * │ │ │ │◄─ ─ ─ ─│PHY ├──────►│ B │
2048 * └───┴───┴───┘INACTIVE└────┘ └───────┘
2049 *
2050 * MAC stack B keeps a PHY listener associated with the Aux PHY, even though it is inactive,
2051 * meaning that the PHY listener will only notify channel switches (no CCA, no RX).
2052 * If the EMLSR Manager requested a link switching, this configuration will be kept until
2053 * further requests. If the EMLSR Manager requested a link swapping, link B's CAM will be
2054 * notified by its (inactive) PHY listener upon the channel switch performed by the Aux PHY.
2055 * In this case, we remove the inactive PHY listener and connect the Aux PHY to MAC stack A:
2056 *
2057 * ┌───┬───┬───┐ ┌────┐ ┌───────┐
2058 * Link A │FEM│RSM│CAM│◄─┐ ┌──►│Main├───┐ │Channel│
2059 * │ │ │ │ │ │ │PHY │ ┌─┼──►│ A │
2060 * └───┴───┴───┘ │ │ └────┘ │ │ └───────┘
2061 * │ │ │ │
2062 * ┌───┬───┬───┐ │ │ ┌────┐ │ │ ┌───────┐
2063 * Link B │FEM│RSM│CAM│◄─┼─┘ │Aux │ │ └──►│Channel│
2064 * │ │ │ │ └────►│PHY ├─┘ │ B │
2065 * └───┴───┴───┘ └────┘ └───────┘
2066 */
2067
2068void
2070{
2071 NS_LOG_FUNCTION(this << phy << linkId << delay.As(Time::US));
2072
2073 // If the PHY is switching channel to operate on another link, then it is no longer operating
2074 // on the current link. If any link (other than the current link) points to the PHY that is
2075 // switching channel, reset the phy pointer of the link
2076 for (auto& [id, link] : GetLinks())
2077 {
2078 if (link->phy == phy && id != linkId)
2079 {
2080 link->phy = nullptr;
2081 m_emlsrLinkSwitchLogger(id, nullptr);
2082 }
2083 }
2084
2085 // lambda to connect the PHY to the new link
2086 auto connectPhy = [=, this]() mutable {
2087 auto& newLink = GetLink(linkId);
2088 // The MAC stack associated with the new link uses the given PHY
2089 newLink.phy = phy;
2090 // Setup a PHY listener for the given PHY on the CAM associated with the new link
2091 newLink.channelAccessManager->SetupPhyListener(phy);
2093 if (m_emlsrManager->GetCamStateReset())
2094 {
2095 newLink.channelAccessManager->ResetState();
2096 }
2097 // Disconnect the FEM on the new link from the current PHY
2098 newLink.feManager->ResetPhy();
2099 // Connect the FEM on the new link to the given PHY
2100 newLink.feManager->SetWifiPhy(phy);
2101 // Connect the station manager on the new link to the given PHY
2102 newLink.stationManager->SetupPhy(phy);
2103 // log link switch
2104 m_emlsrLinkSwitchLogger(linkId, phy);
2105 };
2106
2107 // cancel any pending event for the given PHY to switch link
2108 CancelEmlsrPhyConnectEvent(phy->GetPhyId());
2109
2110 // connect the PHY to the new link when the channel switch is completed, unless there is a PHY
2111 // operating on the new link that is possibly receiving an ICF, in which case the PHY is
2112 // connected when the frame reception is completed
2113 if (delay.IsStrictlyPositive())
2114 {
2115 auto lambda = [=, this]() mutable {
2116 const auto [maybeIcf, extension] = m_emlsrManager->CheckPossiblyReceivingIcf(linkId);
2117 if (maybeIcf && extension.IsStrictlyPositive())
2118 {
2119 NS_ASSERT_MSG(phy->GetPhyId() == m_emlsrManager->GetMainPhyId(),
2120 "Only the main PHY is expected to move to a link on which another "
2121 "PHY is operating. PHY ID="
2122 << +phy->GetPhyId());
2123 NS_LOG_DEBUG("Connecting main PHY to link " << +linkId << " is postponed by "
2124 << extension.As(Time::US));
2125 NotifySwitchingEmlsrLink(phy, linkId, extension);
2126 }
2127 else
2128 {
2129 connectPhy();
2130 }
2131 };
2132
2133 m_emlsrLinkSwitch.emplace(phy->GetPhyId(), Simulator::Schedule(delay, lambda));
2134 }
2135 else
2136 {
2137 connectPhy();
2138 }
2139}
2140
2141void
2143{
2144 NS_LOG_FUNCTION(this << phyId);
2145 if (auto eventIt = m_emlsrLinkSwitch.find(phyId); eventIt != m_emlsrLinkSwitch.end())
2146 {
2147 eventIt->second.Cancel();
2148 m_emlsrLinkSwitch.erase(eventIt);
2149 }
2150}
2151
2152void
2154{
2155 NS_LOG_FUNCTION(this << +linkId);
2156
2158
2159 if (IsInitialized() && IsAssociated())
2160 {
2161 Disassociated();
2162 }
2163
2164 // notify association manager
2165 m_assocManager->NotifyChannelSwitched(linkId);
2166}
2167
2168std::ostream&
2169operator<<(std::ostream& os, const StaWifiMac::ApInfo& apInfo)
2170{
2171 os << "BSSID=" << apInfo.m_bssid << ", AP addr=" << apInfo.m_apAddr << ", SNR=" << apInfo.m_snr
2172 << ", Channel={" << apInfo.m_channel.number << "," << apInfo.m_channel.band
2173 << "}, Link ID=" << +apInfo.m_linkId << ", Frame=[";
2174 std::visit([&os](auto&& frame) { frame.Print(os); }, apInfo.m_frame);
2175 os << "]";
2176 return os;
2177}
2178
2179} // namespace ns3
A container for one type of attribute.
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.
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)
static Mac48Address GetBroadcast()
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.
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:560
static Time Now()
Return the current simulation virtual time.
Definition simulator.cc:197
static Time GetDelayLeft(const EventId &id)
Get the remaining time until this event will execute.
Definition simulator.cc:206
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.
TracedCallback< uint8_t, Ptr< WifiPhy > > m_emlsrLinkSwitchLogger
EMLSR link switch logger.
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.
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
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
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 UpdateApInfo(const MgtFrameType &frame, const Mac48Address &apAddr, const Mac48Address &bssid, uint8_t linkId)
Update associated AP's information from the given management frame (Beacon, Probe Response or Associa...
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.
std::variant< MgtBeaconHeader, MgtProbeResponseHeader, MgtAssocResponseHeader > MgtFrameType
type of the management frames used to get info about APs
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.
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:404
bool IsStrictlyPositive() const
Exactly equivalent to t > 0.
Definition nstime.h:340
@ US
microsecond
Definition nstime.h:107
Time Get() const
Definition time.cc:519
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:635
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
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition type-id.cc:1001
Hold an unsigned integer type.
Definition uinteger.h:34
The IEEE 802.11ac VHT Capabilities.
static std::pair< CategoryValue, ActionValue > Peek(Ptr< const Packet > pkt)
Peek an Action header from the given packet.
Implements the IEEE 802.11 MAC header.
bool IsQosAmsdu() const
Check if IsQosData() is true and the A-MSDU present bit is set in the QoS control field.
Mac48Address GetAddr3() const
Return the address in the Address 3 field.
bool IsBeacon() const
Return true if the header is a Beacon header.
bool IsAssocResp() const
Return true if the header is an Association Response header.
Mac48Address GetAddr1() const
Return the address in the Address 1 field.
virtual WifiMacType GetType() const
Return the type (WifiMacType)
bool IsCtl() const
Return true if the Type is Control.
void SetDsNotFrom()
Un-set the From DS bit in the Frame Control field.
bool IsProbeResp() const
Return true if the header is a Probe Response header.
void SetAddr1(Mac48Address address)
Fill the Address 1 field with the given address.
virtual void SetType(WifiMacType type, bool resetToDsFromDs=true)
Set Type/Subtype values with the correct values depending on the given type.
Mac48Address GetAddr2() const
Return the address in the Address 2 field.
bool HasData() const
Return true if the header type is DATA and is not DATA_NULL.
bool IsData() const
Return true if the Type is DATA.
bool IsReassocResp() const
Return true if the header is a Reassociation Response header.
void SetDsTo()
Set the To DS bit in the Frame Control field.
void SetAddr2(Mac48Address address)
Fill the Address 2 field with the given address.
bool IsQosData() const
Return true if the Type is DATA and Subtype is one of the possible values for QoS Data.
void SetAddr3(Mac48Address address)
Fill the Address 3 field with the given address.
void SetDsNotTo()
Un-set the To DS bit in the Frame Control field.
bool IsPowerManagement() const
Return if the Power Management bit is set.
void SetPowerManagement()
Set the Power Management bit in the Frame Control field.
void SetNoPowerManagement()
Un-set the Power Management bit in the Frame Control field.
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:2261
Callback< void > m_linkDown
Callback when a link is down.
Definition wifi-mac.h:974
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:970
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
void UnblockUnicastTxOnLinks(WifiQueueBlockedReason reason, const Mac48Address &address, const std::set< uint8_t > &linkIds)
Unblock the transmission on the given links of all unicast frames addressed to the station with the g...
Definition wifi-mac.cc:1620
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 BlockUnicastTxOnLinks(WifiQueueBlockedReason reason, const Mac48Address &address, const std::set< uint8_t > &linkIds)
Block the transmission on the given links of all unicast frames addressed to the station with the giv...
Definition wifi-mac.cc:1574
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
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:2397
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:2432
Callback< void > m_linkUp
Callback when a link is up.
Definition wifi-mac.h:973
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:2341
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:1402
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
Definition wifi-phy.cc:1587
void SetSlot(Time slot)
Set the slot duration for this PHY.
Definition wifi-phy.cc:835
MHz_u GetChannelWidth() const
Definition wifi-phy.cc:1099
void SetOffMode()
Put in off mode.
Definition wifi-phy.cc:1463
std::list< WifiMode > GetMcsList() const
The WifiPhy::GetMcsList() method is used (e.g., by a WifiRemoteStationManager) to determine the set o...
Definition wifi-phy.cc:2131
std::list< WifiMode > GetModeList() const
The WifiPhy::GetModeList() method is used (e.g., by a WifiRemoteStationManager) to determine the set ...
Definition wifi-phy.cc:2082
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 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
#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:1368
Time NanoSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1380
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1344
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1356
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.
@ 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.
@ WIFI_MOD_CLASS_VHT
VHT (Clause 22)
@ WIFI_MOD_CLASS_HE
HE (Clause 27)
@ 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.
Ptr< const AttributeChecker > MakeBooleanChecker()
Definition boolean.cc:113
Ptr< const AttributeChecker > MakeUintegerChecker()
Definition uinteger.h:85
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Definition nstime.h:1432
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
Ptr< const AttributeAccessor > MakeUintegerAccessor(T1 a1)
Definition uinteger.h:35
WifiTidToLinkMappingNegSupport
TID-to-Link Mapping Negotiation Support.
std::ostream & operator<<(std::ostream &os, const Angles &a)
Definition angles.cc:148
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
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Definition boolean.h:70
std::unordered_map< uint16_t, Ptr< const WifiPsdu > > WifiConstPsduMap
Map of const PSDUs indexed by STA-ID.
Definition wifi-ppdu.h:38
Ptr< const AttributeChecker > MakeTimeChecker()
Helper to make an unbounded Time checker.
Definition nstime.h:1452
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