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