A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
ap-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 "ap-wifi-mac.h"
12
15#include "mac-rx-middle.h"
16#include "mac-tx-middle.h"
17#include "mgt-action-headers.h"
18#include "mgt-headers.h"
19#include "msdu-aggregator.h"
20#include "qos-txop.h"
23#include "wifi-mac-queue.h"
24#include "wifi-net-device.h"
25#include "wifi-phy.h"
26
27#include "ns3/ap-emlsr-manager.h"
28#include "ns3/eht-configuration.h"
29#include "ns3/eht-frame-exchange-manager.h"
30#include "ns3/he-configuration.h"
31#include "ns3/ht-configuration.h"
32#include "ns3/log.h"
33#include "ns3/multi-link-element.h"
34#include "ns3/packet.h"
35#include "ns3/pointer.h"
36#include "ns3/random-variable-stream.h"
37#include "ns3/simulator.h"
38#include "ns3/string.h"
39
40namespace ns3
41{
42
43NS_LOG_COMPONENT_DEFINE("ApWifiMac");
44
46
47TypeId
49{
50 static TypeId tid =
51 TypeId("ns3::ApWifiMac")
53 .SetGroupName("Wifi")
54 .AddConstructor<ApWifiMac>()
55 .AddAttribute(
56 "BeaconInterval",
57 "Delay between two beacons",
58 TimeValue(MicroSeconds(102400)),
61 .AddAttribute("BeaconJitter",
62 "A uniform random variable to cause the initial beacon starting time "
63 "(after simulation time 0) "
64 "to be distributed between 0 and the BeaconInterval.",
65 StringValue("ns3::UniformRandomVariable"),
68 .AddAttribute("EnableBeaconJitter",
69 "If beacons are enabled, whether to jitter the initial send event.",
70 BooleanValue(true),
73 .AddAttribute("BeaconGeneration",
74 "Whether or not beacons are generated.",
75 BooleanValue(true),
78 .AddAttribute("FdBeaconInterval6GHz",
79 "Time between a Beacon frame and a FILS Discovery (FD) frame or between "
80 "two FD frames to be sent on a 6GHz link. A value of zero disables the "
81 "transmission of FD frames.",
82 TimeValue(Time{0}),
85 .AddAttribute("FdBeaconIntervalNon6GHz",
86 "Time between a Beacon frame and a FILS Discovery (FD) frame or between "
87 "two FD frames to be sent on a non-6GHz link. A value of zero disables "
88 "the transmission of FD frames.",
89 TimeValue(Time{0}),
92 .AddAttribute("SendUnsolProbeResp",
93 "Send unsolicited broadcast Probe Response instead of FILS Discovery",
94 BooleanValue(false),
97 .AddAttribute("EnableNonErpProtection",
98 "Whether or not protection mechanism should be used when non-ERP STAs "
99 "are present within the BSS."
100 "This parameter is only used when ERP is supported by the AP.",
101 BooleanValue(true),
104 .AddAttribute("BsrLifetime",
105 "Lifetime of Buffer Status Reports received from stations.",
109 .AddAttribute(
110 "CwMinsForSta",
111 "The CW min values that the AP advertises in EDCA Parameter Set elements and the "
112 "associated stations will use. The value of this attribute is an AC-indexed map "
113 "containing the CW min values for given ACs for all the links (sorted in "
114 "increasing order of link ID). If no values are provided for an AC, the same "
115 "values used by the AP are advertised. In case a string is used to set this "
116 "attribute, the string shall contain the pairs separated by a semicolon (;); "
117 "in every pair, the AC index and the list of values are separated by a blank "
118 "space, and the values of a list are separated by a comma (,) without spaces. "
119 "E.g. \"BE 31,31,31; VI 15,15,15\" defines the CW min values for AC BE and AC VI "
120 "for an AP MLD having three links.",
121 StringValue(""),
125 .AddAttribute(
126 "CwMaxsForSta",
127 "The CW max values that the AP advertises in EDCA Parameter Set elements and the "
128 "associated stations will use. The value of this attribute is an AC-indexed map "
129 "containing the CW max values for given ACs for all the links (sorted in "
130 "increasing order of link ID). If no values are provided for an AC, the same "
131 "values used by the AP are advertised. In case a string is used to set this "
132 "attribute, the string shall contain the pairs separated by a semicolon (;); "
133 "in every pair, the AC index and the list of values are separated by a blank "
134 "space, and the values of a list are separated by a comma (,) without spaces. "
135 "E.g. \"BE 31,31,31; VI 15,15,15\" defines the CW max values for AC BE and AC VI "
136 "for an AP MLD having three links.",
137 StringValue(""),
141 .AddAttribute(
142 "AifsnsForSta",
143 "The AIFSN values that the AP advertises in EDCA Parameter Set elements and the "
144 "associated stations will use. The value of this attribute is an AC-indexed map "
145 "containing the AIFSN values for given ACs for all the links (sorted in "
146 "increasing order of link ID). If no values are provided for an AC, the same "
147 "values used by the AP are advertised. In case a string is used to set this "
148 "attribute, the string shall contain the pairs separated by a semicolon (;); "
149 "in every pair, the AC index and the list of values are separated by a blank "
150 "space, and the values of a list are separated by a comma (,) without spaces. "
151 "E.g. \"BE 3,3,3; VI 2,2,2\" defines the AIFSN values for AC BE and AC VI "
152 "for an AP MLD having three links.",
153 StringValue(""),
157 .AddAttribute(
158 "TxopLimitsForSta",
159 "The TXOP limit values that the AP advertises in EDCA Parameter Set elements and "
160 "the associated stations will use. The value of this attribute is an AC-indexed "
161 "map containing the TXOP limit values for given ACs for all the links (sorted in "
162 "increasing order of link ID). If no values are provided for an AC, the same "
163 "values used by the AP are advertised. In case a string is used to set this "
164 "attribute, the string shall contain the pairs separated by a semicolon (;); "
165 "in every pair, the AC index and the list of values are separated by a blank "
166 "space, and the values of a list are separated by a comma (,) without spaces. "
167 "E.g. \"BE 3200us,3200us,3200us; VI 2400us,2400us,2400us\" defines the TXOP limit "
168 "values for AC BE and AC VI for an AP MLD having three links.",
169 StringValue(""),
173 .AddTraceSource("AssociatedSta",
174 "A station associated with this access point.",
176 "ns3::ApWifiMac::AssociationCallback")
177 .AddTraceSource("DeAssociatedSta",
178 "A station lost association with this access point.",
180 "ns3::ApWifiMac::AssociationCallback");
181 return tid;
182}
183
184template <class T>
195
204
206 : m_enableBeaconGeneration(false)
207{
208 NS_LOG_FUNCTION(this);
211
212 // Let the lower layers know that we are acting as an AP.
214}
215
220
221void
223{
224 NS_LOG_FUNCTION(this);
226 m_beaconTxop = nullptr;
229 {
230 m_apEmlsrManager->Dispose();
231 }
232 m_apEmlsrManager = nullptr;
234}
235
241
242std::unique_ptr<WifiMac::LinkEntity>
244{
245 return std::make_unique<ApLinkEntity>();
246}
247
249ApWifiMac::GetLink(uint8_t linkId) const
250{
251 return static_cast<ApLinkEntity&>(WifiMac::GetLink(linkId));
252}
253
254void
256{
257 NS_LOG_FUNCTION(this << apEmlsrManager);
258 m_apEmlsrManager = apEmlsrManager;
259 m_apEmlsrManager->SetWifiMac(this);
260}
261
264{
265 return m_apEmlsrManager;
266}
267
268void
270{
271 NS_LOG_FUNCTION(this);
273 // DCF behavior may be edited here; the default is PIFS access with zero backoff
274 m_beaconTxop->SetAifsns(std::vector<uint8_t>(GetNLinks(), 1));
275 m_beaconTxop->SetMinCws(std::vector<uint32_t>(GetNLinks(), 0));
276 m_beaconTxop->SetMaxCws(std::vector<uint32_t>(GetNLinks(), 0));
277 for (uint8_t linkId = 0; linkId < GetNLinks(); linkId++)
278 {
280 }
281}
282
285{
286 if (ac == AC_BEACON)
287 {
289 }
290 return WifiMac::GetTxopQueue(ac);
291}
292
293void
295{
296 NS_LOG_FUNCTION(this << enable);
297 for (uint8_t linkId = 0; linkId < GetNLinks(); ++linkId)
298 {
299 if (!enable)
300 {
301 GetLink(linkId).beaconEvent.Cancel();
302 }
303 else if (!m_enableBeaconGeneration)
304 {
305 GetLink(linkId).beaconEvent =
307 }
308 }
310}
311
312Time
314{
315 NS_LOG_FUNCTION(this);
316 return m_beaconInterval;
317}
318
319void
321{
322 NS_LOG_FUNCTION(this << &linkUp);
324
325 // The approach taken here is that, from the point of view of an AP,
326 // the link is always up, so we immediately invoke the callback if
327 // one is set
328 linkUp();
329}
330
331void
333{
334 NS_LOG_FUNCTION(this << interval);
335 if ((interval.GetMicroSeconds() % 1024) != 0)
336 {
337 NS_FATAL_ERROR("beacon interval should be multiple of 1024us (802.11 time unit), see IEEE "
338 "Std. 802.11-2012");
339 }
340 if (interval.GetMicroSeconds() > (1024 * 65535))
341 {
343 "beacon interval should be smaller then or equal to 65535 * 1024us (802.11 time unit)");
344 }
345 m_beaconInterval = interval;
346}
347
348int64_t
350{
351 NS_LOG_FUNCTION(this << stream);
352 m_beaconJitter->SetStream(stream);
353 auto currentStream = stream + 1;
354 currentStream += m_beaconTxop->AssignStreams(currentStream);
355 currentStream += WifiMac::AssignStreams(currentStream);
356 return (currentStream - stream);
357}
358
359void
361{
362 NS_LOG_FUNCTION(this << +linkId);
363 auto& link = GetLink(linkId);
364 if (GetErpSupported(linkId) && GetShortSlotTimeSupported() && (link.numNonErpStations == 0))
365 {
366 for (const auto& sta : link.staList)
367 {
368 if (!GetWifiRemoteStationManager(linkId)->GetShortSlotTimeSupported(sta.second))
369 {
370 link.shortSlotTimeEnabled = false;
371 return;
372 }
373 }
374 link.shortSlotTimeEnabled = true;
375 }
376 else
377 {
378 link.shortSlotTimeEnabled = false;
379 }
380}
381
382void
384{
385 NS_LOG_FUNCTION(this << +linkId);
386 auto& link = GetLink(linkId);
387 if (GetErpSupported(linkId) && GetWifiPhy(linkId)->GetShortPhyPreambleSupported())
388 {
389 for (const auto& sta : link.staList)
390 {
391 if (!GetWifiRemoteStationManager(linkId)->GetErpOfdmSupported(sta.second) ||
392 !GetWifiRemoteStationManager(linkId)->GetShortPreambleSupported(sta.second))
393 {
394 link.shortPreambleEnabled = false;
395 return;
396 }
397 }
398 link.shortPreambleEnabled = true;
399 }
400 else
401 {
402 link.shortPreambleEnabled = false;
403 }
404}
405
406bool
408{
409 return (to.IsGroup() || IsAssociated(to));
410}
411
412void
414{
415 NS_LOG_FUNCTION(this << *mpdu << to << from);
416
417 std::list<Mac48Address> addr2Set;
418 if (to.IsGroup())
419 {
420 // broadcast frames are transmitted on all the links
421 for (uint8_t linkId = 0; linkId < GetNLinks(); linkId++)
422 {
423 addr2Set.push_back(GetFrameExchangeManager(linkId)->GetAddress());
424 }
425 }
426 else
427 {
428 // the Transmitter Address (TA) is the MLD address only for non-broadcast data frames
429 // exchanged between two MLDs
430 addr2Set = {GetAddress()};
431 auto linkId = IsAssociated(to);
432 NS_ASSERT_MSG(linkId, "Station " << to << "is not associated, cannot send it a frame");
433 if (GetNLinks() == 1 || !GetWifiRemoteStationManager(*linkId)->GetMldAddress(to))
434 {
435 addr2Set = {GetFrameExchangeManager(*linkId)->GetAddress()};
436 }
437 }
438
439 for (auto addr2 = addr2Set.cbegin(); addr2 != addr2Set.cend(); ++addr2)
440 {
441 auto& hdr = mpdu->GetHeader();
442
443 hdr.SetAddr1(to);
444 hdr.SetAddr2(*addr2);
445 hdr.SetAddr3(from);
446 hdr.SetDsFrom();
447 hdr.SetDsNotTo();
448
449 auto txop = hdr.IsQosData() ? StaticCast<Txop>(GetQosTxop(hdr.GetQosTid())) : GetTxop();
450 NS_ASSERT(txop);
451 txop->Queue(mpdu);
452
453 // create another MPDU if needed
454 if (std::next(addr2) != addr2Set.cend())
455 {
456 mpdu = Create<WifiMpdu>(mpdu->GetPacket()->Copy(), hdr);
457 }
458 }
459}
460
461bool
463{
464 NS_LOG_FUNCTION(this);
465 return true;
466}
467
469ApWifiMac::GetSupportedRates(uint8_t linkId) const
470{
471 NS_LOG_FUNCTION(this << +linkId);
472 AllSupportedRates rates;
473 // Send the set of supported rates and make sure that we indicate
474 // the Basic Rate set in this set of supported rates.
475 for (const auto& mode : GetWifiPhy(linkId)->GetModeList())
476 {
477 uint64_t modeDataRate = mode.GetDataRate(GetWifiPhy(linkId)->GetChannelWidth());
478 NS_LOG_DEBUG("Adding supported rate of " << modeDataRate);
479 rates.AddSupportedRate(modeDataRate);
480 // Add rates that are part of the BSSBasicRateSet (manufacturer dependent!)
481 // here we choose to add the mandatory rates to the BSSBasicRateSet,
482 // except for 802.11b where we assume that only the non HR-DSSS rates are part of the
483 // BSSBasicRateSet
484 if (mode.IsMandatory() && (mode.GetModulationClass() != WIFI_MOD_CLASS_HR_DSSS))
485 {
486 NS_LOG_DEBUG("Adding basic mode " << mode.GetUniqueName());
487 GetWifiRemoteStationManager(linkId)->AddBasicMode(mode);
488 }
489 }
490 // set the basic rates
491 for (uint8_t j = 0; j < GetWifiRemoteStationManager(linkId)->GetNBasicModes(); j++)
492 {
493 WifiMode mode = GetWifiRemoteStationManager(linkId)->GetBasicMode(j);
494 uint64_t modeDataRate = mode.GetDataRate(GetWifiPhy(linkId)->GetChannelWidth());
495 NS_LOG_DEBUG("Setting basic rate " << mode.GetUniqueName());
496 rates.SetBasicRate(modeDataRate);
497 }
498 // If it is a HT AP, then add the BSSMembershipSelectorSet
499 // The standard says that the BSSMembershipSelectorSet
500 // must have its MSB set to 1 (must be treated as a Basic Rate)
501 // Also the standard mentioned that at least 1 element should be included in the SupportedRates
502 // the rest can be in the ExtendedSupportedRates
503 if (GetHtSupported(linkId))
504 {
505 for (const auto& selector : GetWifiPhy(linkId)->GetBssMembershipSelectorList())
506 {
507 rates.AddBssMembershipSelectorRate(selector);
508 }
509 }
510 return rates;
511}
512
515{
516 NS_LOG_FUNCTION(this << +linkId);
518 DsssParameterSet dsssParameters;
519 dsssParameters.SetCurrentChannel(GetWifiPhy(linkId)->GetChannelNumber());
520 return dsssParameters;
521}
522
524ApWifiMac::GetCapabilities(uint8_t linkId) const
525{
526 NS_LOG_FUNCTION(this << +linkId);
527 CapabilityInformation capabilities;
528 capabilities.SetShortPreamble(GetLink(linkId).shortPreambleEnabled);
529 capabilities.SetShortSlotTime(GetLink(linkId).shortSlotTimeEnabled);
530 capabilities.SetEss();
531 return capabilities;
532}
533
535ApWifiMac::GetErpInformation(uint8_t linkId) const
536{
537 NS_LOG_FUNCTION(this << +linkId);
538 NS_ASSERT(GetErpSupported(linkId));
539 ErpInformation information;
540
541 information.SetNonErpPresent(GetLink(linkId).numNonErpStations > 0);
542 information.SetUseProtection(GetUseNonErpProtection(linkId));
543 if (GetLink(linkId).shortPreambleEnabled)
544 {
545 information.SetBarkerPreambleMode(0);
546 }
547 else
548 {
549 information.SetBarkerPreambleMode(1);
550 }
551
552 return information;
553}
554
557{
558 NS_LOG_FUNCTION(this << +linkId);
560 EdcaParameterSet edcaParameters;
561
562 Ptr<QosTxop> edca;
563 Time txopLimit;
564
565 edca = GetQosTxop(AC_BE);
566 edcaParameters.SetBeAci(0);
567 edcaParameters.SetBeCWmin(m_cwMinsForSta.contains(AC_BE) ? m_cwMinsForSta.at(AC_BE).at(linkId)
568 : edca->GetMinCw(linkId));
569 edcaParameters.SetBeCWmax(m_cwMaxsForSta.contains(AC_BE) ? m_cwMaxsForSta.at(AC_BE).at(linkId)
570 : edca->GetMaxCw(linkId));
571 edcaParameters.SetBeAifsn(m_aifsnsForSta.contains(AC_BE) ? m_aifsnsForSta.at(AC_BE).at(linkId)
572 : edca->GetAifsn(linkId));
573 txopLimit = m_txopLimitsForSta.contains(AC_BE) ? m_txopLimitsForSta.at(AC_BE).at(linkId)
574 : edca->GetTxopLimit(linkId);
575 edcaParameters.SetBeTxopLimit(static_cast<uint16_t>(txopLimit.GetMicroSeconds() / 32));
576
577 edca = GetQosTxop(AC_BK);
578 edcaParameters.SetBkAci(1);
579 edcaParameters.SetBkCWmin(m_cwMinsForSta.contains(AC_BK) ? m_cwMinsForSta.at(AC_BK).at(linkId)
580 : edca->GetMinCw(linkId));
581 edcaParameters.SetBkCWmax(m_cwMaxsForSta.contains(AC_BK) ? m_cwMaxsForSta.at(AC_BK).at(linkId)
582 : edca->GetMaxCw(linkId));
583 edcaParameters.SetBkAifsn(m_aifsnsForSta.contains(AC_BK) ? m_aifsnsForSta.at(AC_BK).at(linkId)
584 : edca->GetAifsn(linkId));
585 txopLimit = m_txopLimitsForSta.contains(AC_BK) ? m_txopLimitsForSta.at(AC_BK).at(linkId)
586 : edca->GetTxopLimit(linkId);
587 edcaParameters.SetBkTxopLimit(static_cast<uint16_t>(txopLimit.GetMicroSeconds() / 32));
588
589 edca = GetQosTxop(AC_VI);
590 edcaParameters.SetViAci(2);
591 edcaParameters.SetViCWmin(m_cwMinsForSta.contains(AC_VI) ? m_cwMinsForSta.at(AC_VI).at(linkId)
592 : edca->GetMinCw(linkId));
593 edcaParameters.SetViCWmax(m_cwMaxsForSta.contains(AC_VI) ? m_cwMaxsForSta.at(AC_VI).at(linkId)
594 : edca->GetMaxCw(linkId));
595 edcaParameters.SetViAifsn(m_aifsnsForSta.contains(AC_VI) ? m_aifsnsForSta.at(AC_VI).at(linkId)
596 : edca->GetAifsn(linkId));
597 txopLimit = m_txopLimitsForSta.contains(AC_VI) ? m_txopLimitsForSta.at(AC_VI).at(linkId)
598 : edca->GetTxopLimit(linkId);
599 edcaParameters.SetViTxopLimit(static_cast<uint16_t>(txopLimit.GetMicroSeconds() / 32));
600
601 edca = GetQosTxop(AC_VO);
602 edcaParameters.SetVoAci(3);
603 edcaParameters.SetVoCWmin(m_cwMinsForSta.contains(AC_VO) ? m_cwMinsForSta.at(AC_VO).at(linkId)
604 : edca->GetMinCw(linkId));
605 edcaParameters.SetVoCWmax(m_cwMaxsForSta.contains(AC_VO) ? m_cwMaxsForSta.at(AC_VO).at(linkId)
606 : edca->GetMaxCw(linkId));
607 edcaParameters.SetVoAifsn(m_aifsnsForSta.contains(AC_VO) ? m_aifsnsForSta.at(AC_VO).at(linkId)
608 : edca->GetAifsn(linkId));
609 txopLimit = m_txopLimitsForSta.contains(AC_VO) ? m_txopLimitsForSta.at(AC_VO).at(linkId)
610 : edca->GetTxopLimit(linkId);
611 edcaParameters.SetVoTxopLimit(static_cast<uint16_t>(txopLimit.GetMicroSeconds() / 32));
612
613 edcaParameters.SetQosInfo(0);
614
615 return edcaParameters;
616}
617
618std::optional<MuEdcaParameterSet>
620{
621 NS_LOG_FUNCTION(this);
623
624 Ptr<HeConfiguration> heConfiguration = GetHeConfiguration();
625 NS_ASSERT(heConfiguration);
626
627 MuEdcaParameterSet muEdcaParameters;
628 muEdcaParameters.SetQosInfo(0);
629
630 UintegerValue uintegerValue;
631 TimeValue timeValue;
632
633 heConfiguration->GetAttribute("MuBeAifsn", uintegerValue);
634 muEdcaParameters.SetMuAifsn(AC_BE, uintegerValue.Get());
635 heConfiguration->GetAttribute("MuBeCwMin", uintegerValue);
636 muEdcaParameters.SetMuCwMin(AC_BE, uintegerValue.Get());
637 heConfiguration->GetAttribute("MuBeCwMax", uintegerValue);
638 muEdcaParameters.SetMuCwMax(AC_BE, uintegerValue.Get());
639 heConfiguration->GetAttribute("BeMuEdcaTimer", timeValue);
640 muEdcaParameters.SetMuEdcaTimer(AC_BE, timeValue.Get());
641
642 heConfiguration->GetAttribute("MuBkAifsn", uintegerValue);
643 muEdcaParameters.SetMuAifsn(AC_BK, uintegerValue.Get());
644 heConfiguration->GetAttribute("MuBkCwMin", uintegerValue);
645 muEdcaParameters.SetMuCwMin(AC_BK, uintegerValue.Get());
646 heConfiguration->GetAttribute("MuBkCwMax", uintegerValue);
647 muEdcaParameters.SetMuCwMax(AC_BK, uintegerValue.Get());
648 heConfiguration->GetAttribute("BkMuEdcaTimer", timeValue);
649 muEdcaParameters.SetMuEdcaTimer(AC_BK, timeValue.Get());
650
651 heConfiguration->GetAttribute("MuViAifsn", uintegerValue);
652 muEdcaParameters.SetMuAifsn(AC_VI, uintegerValue.Get());
653 heConfiguration->GetAttribute("MuViCwMin", uintegerValue);
654 muEdcaParameters.SetMuCwMin(AC_VI, uintegerValue.Get());
655 heConfiguration->GetAttribute("MuViCwMax", uintegerValue);
656 muEdcaParameters.SetMuCwMax(AC_VI, uintegerValue.Get());
657 heConfiguration->GetAttribute("ViMuEdcaTimer", timeValue);
658 muEdcaParameters.SetMuEdcaTimer(AC_VI, timeValue.Get());
659
660 heConfiguration->GetAttribute("MuVoAifsn", uintegerValue);
661 muEdcaParameters.SetMuAifsn(AC_VO, uintegerValue.Get());
662 heConfiguration->GetAttribute("MuVoCwMin", uintegerValue);
663 muEdcaParameters.SetMuCwMin(AC_VO, uintegerValue.Get());
664 heConfiguration->GetAttribute("MuVoCwMax", uintegerValue);
665 muEdcaParameters.SetMuCwMax(AC_VO, uintegerValue.Get());
666 heConfiguration->GetAttribute("VoMuEdcaTimer", timeValue);
667 muEdcaParameters.SetMuEdcaTimer(AC_VO, timeValue.Get());
668
669 // The timers of the MU EDCA Parameter Set must be either all zero or all
670 // non-zero. The information element is advertised if all timers are non-zero
671 auto timerNotNull = [&muEdcaParameters](uint8_t aci) {
672 return !muEdcaParameters.GetMuEdcaTimer(aci).IsZero();
673 };
674 auto aci = {0, 1, 2, 3};
675 if (std::all_of(aci.begin(), aci.end(), timerNotNull))
676 {
677 return muEdcaParameters;
678 }
679
680 NS_ABORT_MSG_UNLESS(std::none_of(aci.begin(), aci.end(), timerNotNull),
681 "MU EDCA Timers must be all zero if the IE is not advertised.");
682
683 return std::nullopt;
684}
685
686std::optional<ReducedNeighborReport>
688{
689 NS_LOG_FUNCTION(this << +linkId);
690
691 if (GetNLinks() <= 1)
692 {
693 return std::nullopt;
694 }
695
698
699 for (uint8_t index = 0; index < GetNLinks(); ++index)
700 {
701 if (index != linkId) // all links but the one used to send this Beacon frame
702 {
703 rnr.AddNbrApInfoField();
704 std::size_t nbrId = rnr.GetNNbrApInfoFields() - 1;
706 rnr.AddTbttInformationField(nbrId);
707 rnr.SetBssid(nbrId, 0, GetLink(index).feManager->GetAddress());
708 rnr.SetShortSsid(nbrId, 0, 0);
709 rnr.SetBssParameters(nbrId, 0, 0);
710 rnr.SetPsd20MHz(nbrId, 0, 0);
711 rnr.SetMldParameters(nbrId, 0, 0, index, 0);
712 }
713 }
714 return rnr;
715}
716
718ApWifiMac::GetMultiLinkElement(uint8_t linkId, WifiMacType frameType, const Mac48Address& to)
719{
720 NS_LOG_FUNCTION(this << +linkId << frameType << to);
721 NS_ABORT_IF(GetNLinks() == 1);
722
725 mle.SetLinkIdInfo(linkId);
727
728 auto ehtConfiguration = GetEhtConfiguration();
729 NS_ASSERT(ehtConfiguration);
730
731 if (BooleanValue emlsrActivated;
732 ehtConfiguration->GetAttributeFailSafe("EmlsrActivated", emlsrActivated) &&
733 emlsrActivated.Get())
734 {
735 mle.SetEmlsrSupported(true);
736 // When the EMLSR Padding Delay subfield is included in a frame sent by an AP affiliated
737 // with an AP MLD, the EMLSR Padding Delay subfield is reserved.
738 // When the EMLSR Transition Delay subfield is included in a frame sent by an AP affiliated
739 // with an AP MLD, the EMLSR Transition Delay subfield is reserved. (Sec. 9.4.2.312.2.3
740 // of 802.11be D2.3)
741 TimeValue time;
742 ehtConfiguration->GetAttribute("TransitionTimeout", time);
743 mle.SetTransitionTimeout(time.Get());
744
745 // An AP affiliated with an AP MLD may include the Medium Synchronization Delay Information
746 // subfield in the Common Info field of the Basic Multi-Link element carried in transmitted
747 // (Re)Association Response or Multi-Link Probe Response frames to provide medium
748 // synchronization information used by the AP MLD. (Section 35.3.16.8.2 of 802.11be D3.1)
749 if (frameType == WIFI_MAC_MGT_ASSOCIATION_RESPONSE)
750 {
751 auto& commonInfo = mle.GetCommonInfoBasic();
752
753 ehtConfiguration->GetAttribute("MediumSyncDuration", time);
754 commonInfo.SetMediumSyncDelayTimer(time.Get());
755
756 IntegerValue ofdmEdThres;
757 ehtConfiguration->GetAttribute("MsdOfdmEdThreshold", ofdmEdThres);
758 commonInfo.SetMediumSyncOfdmEdThreshold(ofdmEdThres.Get());
759
760 UintegerValue maxNTxops;
761 ehtConfiguration->GetAttribute("MsdMaxNTxops", maxNTxops);
762 commonInfo.SetMediumSyncMaxNTxops(maxNTxops.Get());
763 }
764 }
765
766 // The MLD Capabilities And Operations subfield is present in the Common Info field of the
767 // Basic Multi-Link element carried in Beacon, Probe Response, (Re)Association Request, and
768 // (Re)Association Response frames. (Sec. 9.4.2.312.2.3 of 802.11be D3.1)
769 if (frameType == WIFI_MAC_MGT_BEACON || frameType == WIFI_MAC_MGT_PROBE_RESPONSE ||
773 {
774 auto& mldCapabilities = mle.GetCommonInfoBasic().m_mldCapabilities;
775 mldCapabilities.emplace();
776 mldCapabilities->maxNSimultaneousLinks = GetNLinks() - 1; // assuming STR for now
777 mldCapabilities->srsSupport = 0;
779 ehtConfiguration->GetAttributeFailSafe("TidToLinkMappingNegSupport", negSupport);
780 mldCapabilities->tidToLinkMappingSupport = static_cast<uint8_t>(negSupport.Get());
781 mldCapabilities->freqSepForStrApMld = 0; // not supported yet
782 mldCapabilities->aarSupport = 0; // not supported yet
783 }
784
785 // if the Multi-Link Element is being inserted in a (Re)Association Response frame
786 // and the remote station is affiliated with an MLD, try multi-link setup
787 if (auto staMldAddress = GetWifiRemoteStationManager(linkId)->GetMldAddress(to);
788 (frameType == WIFI_MAC_MGT_ASSOCIATION_RESPONSE ||
790 staMldAddress.has_value())
791 {
792 for (uint8_t i = 0; i < GetNLinks(); i++)
793 {
794 auto remoteStationManager = GetWifiRemoteStationManager(i);
795 if (auto staAddress = remoteStationManager->GetAffiliatedStaAddress(*staMldAddress);
796 i != linkId && staAddress.has_value() &&
797 (remoteStationManager->IsWaitAssocTxOk(*staAddress) ||
798 remoteStationManager->IsAssocRefused(*staAddress)))
799 {
800 // For each requested link in addition to the link on which the
801 // (Re)Association Response frame is transmitted, the Link Info field
802 // of the Basic Multi-Link element carried in the (Re)Association
803 // Response frame shall contain the corresponding Per-STA Profile
804 // subelement(s) (Sec. 35.3.5.4 of 802.11be D2.0)
806 auto& perStaProfile = mle.GetPerStaProfile(mle.GetNPerStaProfileSubelements() - 1);
807 // The Link ID subfield of the STA Control field of the Per-STA Profile
808 // subelement for the AP corresponding to a link is set to the link ID
809 // of the AP affiliated with the AP MLD that is operating on that link.
810 perStaProfile.SetLinkId(i);
811 perStaProfile.SetCompleteProfile();
812 // For each Per-STA Profile subelement included in the Link Info field,
813 // the Complete Profile subfield of the STA Control field shall be set to 1
814 perStaProfile.SetStaMacAddress(GetFrameExchangeManager(i)->GetAddress());
815 perStaProfile.SetAssocResponse(GetAssocResp(*staAddress, i));
816 }
817 }
818 }
819
820 return mle;
821}
822
824ApWifiMac::GetHtOperation(uint8_t linkId) const
825{
826 NS_LOG_FUNCTION(this << +linkId);
827 NS_ASSERT(GetHtSupported(linkId));
828 HtOperation operation;
829 auto phy = GetWifiPhy(linkId);
830 auto remoteStationManager = GetWifiRemoteStationManager(linkId);
831
832 operation.SetPrimaryChannel(phy->GetPrimaryChannelNumber(20));
833 operation.SetRifsMode(false);
834 operation.SetNonGfHtStasPresent(true);
835 if (phy->GetChannelWidth() > 20)
836 {
837 operation.SetSecondaryChannelOffset(1);
838 operation.SetStaChannelWidth(1);
839 }
840 if (GetLink(linkId).numNonHtStations == 0)
841 {
843 }
844 else
845 {
847 }
848 uint64_t maxSupportedRate = 0; // in bit/s
849 for (const auto& mcs : phy->GetMcsList(WIFI_MOD_CLASS_HT))
850 {
851 uint8_t nss = (mcs.GetMcsValue() / 8) + 1;
852 NS_ASSERT(nss > 0 && nss < 5);
853 uint64_t dataRate = mcs.GetDataRate(
854 phy->GetChannelWidth(),
855 NanoSeconds(GetHtConfiguration()->GetShortGuardIntervalSupported() ? 400 : 800),
856 nss);
857 if (dataRate > maxSupportedRate)
858 {
859 maxSupportedRate = dataRate;
860 NS_LOG_DEBUG("Updating maxSupportedRate to " << maxSupportedRate);
861 }
862 }
863 uint8_t maxSpatialStream = phy->GetMaxSupportedTxSpatialStreams();
864 auto mcsList = phy->GetMcsList(WIFI_MOD_CLASS_HT);
865 uint8_t nMcs = mcsList.size();
866 for (const auto& sta : GetLink(linkId).staList)
867 {
868 if (remoteStationManager->GetHtSupported(sta.second) ||
869 remoteStationManager->GetStationHe6GhzCapabilities(sta.second))
870 {
871 uint64_t maxSupportedRateByHtSta = 0; // in bit/s
872 auto itMcs = mcsList.begin();
873 for (uint8_t j = 0;
874 j < (std::min(nMcs, remoteStationManager->GetNMcsSupported(sta.second)));
875 j++)
876 {
877 WifiMode mcs = *itMcs++;
878 uint8_t nss = (mcs.GetMcsValue() / 8) + 1;
879 NS_ASSERT(nss > 0 && nss < 5);
880 uint64_t dataRate = mcs.GetDataRate(
881 remoteStationManager->GetChannelWidthSupported(sta.second),
882 NanoSeconds(remoteStationManager->GetShortGuardIntervalSupported(sta.second)
883 ? 400
884 : 800),
885 nss);
886 if (dataRate > maxSupportedRateByHtSta)
887 {
888 maxSupportedRateByHtSta = dataRate;
889 }
890 }
891 if (maxSupportedRateByHtSta < maxSupportedRate)
892 {
893 maxSupportedRate = maxSupportedRateByHtSta;
894 }
895 if (remoteStationManager->GetNMcsSupported(sta.second) < nMcs)
896 {
897 nMcs = remoteStationManager->GetNMcsSupported(sta.second);
898 }
899 if (remoteStationManager->GetNumberOfSupportedStreams(sta.second) < maxSpatialStream)
900 {
901 maxSpatialStream = remoteStationManager->GetNumberOfSupportedStreams(sta.second);
902 }
903 }
904 }
906 static_cast<uint16_t>(maxSupportedRate / 1e6)); // in Mbit/s
907 operation.SetTxMcsSetDefined(nMcs > 0);
908 operation.SetTxMaxNSpatialStreams(maxSpatialStream);
909 // To be filled in once supported
910 operation.SetObssNonHtStasPresent(0);
911 operation.SetDualBeacon(0);
912 operation.SetDualCtsProtection(0);
913 operation.SetStbcBeacon(0);
915 operation.SetPcoActive(0);
916 operation.SetPhase(0);
917 operation.SetRxMcsBitmask(0);
918 operation.SetTxRxMcsSetUnequal(0);
919 operation.SetTxUnequalModulation(0);
920
921 return operation;
922}
923
925ApWifiMac::GetVhtOperation(uint8_t linkId) const
926{
927 NS_LOG_FUNCTION(this << +linkId);
928 NS_ASSERT(GetVhtSupported(linkId));
929 VhtOperation operation;
930 auto phy = GetWifiPhy(linkId);
931 auto remoteStationManager = GetWifiRemoteStationManager(linkId);
932
933 const auto bssBandwidth = phy->GetChannelWidth();
934 // Set to 0 for 20 MHz or 40 MHz BSS bandwidth.
935 // Set to 1 for 80 MHz, 160 MHz or 80+80 MHz BSS bandwidth.
936 operation.SetChannelWidth((bssBandwidth > 40) ? 1 : 0);
937 // For 20, 40, or 80 MHz BSS bandwidth, indicates the channel center frequency
938 // index for the 20, 40, or 80 MHz channel on which the VHT BSS operates.
939 // For 160 MHz BSS bandwidth and the Channel Width subfield equal to 1,
940 // indicates the channel center frequency index of the 80 MHz channel
941 // segment that contains the primary channel.
942 // For 80+80 MHz BSS bandwidth and the Channel Width subfield equal to 1 or 3,
943 // indicates the channel center frequency index for the primary 80 MHz channel of the VHT BSS.
945 (bssBandwidth == 160) ? phy->GetPrimaryChannelNumber(80) : phy->GetChannelNumber());
946 // For a 20, 40, or 80 MHz BSS bandwidth, this subfield is set to 0.
947 // For a 160 MHz BSS bandwidth and the Channel Width subfield equal to 1,
948 // indicates the channel center frequency index of the 160 MHz channel on
949 // which the VHT BSS operates.
950 // For an 80+80 MHz BSS bandwidth and the Channel Width subfield equal to 1 or 3,
951 // indicates the channel center frequency index of the secondary 80 MHz channel of the VHT BSS.
952 const auto& operatingChannel = phy->GetOperatingChannel();
953 const auto is80Plus80 =
954 operatingChannel.GetWidthType() == WifiChannelWidthType::CW_80_PLUS_80MHZ;
956 (bssBandwidth == 160) ? is80Plus80 ? operatingChannel.GetNumber(1) : phy->GetChannelNumber()
957 : 0);
958 uint8_t maxSpatialStream = phy->GetMaxSupportedRxSpatialStreams();
959 for (const auto& sta : GetLink(linkId).staList)
960 {
961 if (remoteStationManager->GetVhtSupported(sta.second))
962 {
963 if (remoteStationManager->GetNumberOfSupportedStreams(sta.second) < maxSpatialStream)
964 {
965 maxSpatialStream = remoteStationManager->GetNumberOfSupportedStreams(sta.second);
966 }
967 }
968 }
969 for (uint8_t nss = 1; nss <= maxSpatialStream; nss++)
970 {
971 uint8_t maxMcs =
972 9; // TBD: hardcode to 9 for now since we assume all MCS values are supported
973 operation.SetMaxVhtMcsPerNss(nss, maxMcs);
974 }
975
976 return operation;
977}
978
980ApWifiMac::GetHeOperation(uint8_t linkId) const
981{
982 NS_LOG_FUNCTION(this << +linkId);
984 HeOperation operation;
985 auto remoteStationManager = GetWifiRemoteStationManager(linkId);
986
987 uint8_t maxSpatialStream = GetWifiPhy(linkId)->GetMaxSupportedRxSpatialStreams();
988 for (const auto& sta : GetLink(linkId).staList)
989 {
990 if (remoteStationManager->GetHeSupported(sta.second))
991 {
992 if (remoteStationManager->GetNumberOfSupportedStreams(sta.second) < maxSpatialStream)
993 {
994 maxSpatialStream = remoteStationManager->GetNumberOfSupportedStreams(sta.second);
995 }
996 }
997 }
998 for (uint8_t nss = 1; nss <= maxSpatialStream; nss++)
999 {
1000 operation.SetMaxHeMcsPerNss(
1001 nss,
1002 11); // TBD: hardcode to 11 for now since we assume all MCS values are supported
1003 }
1004 operation.m_bssColorInfo.m_bssColor = GetHeConfiguration()->GetBssColor();
1005
1006 if (auto phy = GetWifiPhy(linkId); phy && phy->GetPhyBand() == WIFI_PHY_BAND_6GHZ)
1007 {
1009 const auto bw = phy->GetChannelWidth();
1010 const auto ch = phy->GetOperatingChannel();
1011 op6Ghz.m_chWid = (bw == 20) ? 0 : (bw == 40) ? 1 : (bw == 80) ? 2 : 3;
1012 op6Ghz.m_primCh = ch.GetPrimaryChannelNumber(20, WIFI_STANDARD_80211ax);
1013 op6Ghz.m_chCntrFreqSeg0 =
1014 (bw == 160) ? ch.GetPrimaryChannelNumber(80, WIFI_STANDARD_80211ax) : ch.GetNumber();
1015 // TODO: for 80+80 MHz channels, set this field to the secondary 80 MHz segment number
1016 op6Ghz.m_chCntrFreqSeg1 = (bw == 160) ? ch.GetNumber() : 0;
1017
1018 operation.m_6GHzOpInfo = op6Ghz;
1019 }
1020
1021 return operation;
1022}
1023
1025ApWifiMac::GetEhtOperation(uint8_t linkId) const
1026{
1027 NS_LOG_FUNCTION(this << +linkId);
1029 EhtOperation operation;
1030 auto remoteStationManager = GetWifiRemoteStationManager(linkId);
1031
1032 auto maxSpatialStream = GetWifiPhy(linkId)->GetMaxSupportedRxSpatialStreams();
1033 for (const auto& sta : GetLink(linkId).staList)
1034 {
1035 if (remoteStationManager->GetEhtSupported(sta.second))
1036 {
1037 if (remoteStationManager->GetNumberOfSupportedStreams(sta.second) < maxSpatialStream)
1038 {
1039 maxSpatialStream = remoteStationManager->GetNumberOfSupportedStreams(sta.second);
1040 }
1041 }
1042 }
1043 operation.SetMaxRxNss(maxSpatialStream, 0, WIFI_EHT_MAX_MCS_INDEX);
1044 operation.SetMaxTxNss(maxSpatialStream, 0, WIFI_EHT_MAX_MCS_INDEX);
1045 return operation;
1046}
1047
1048void
1050{
1051 NS_LOG_FUNCTION(this << to << +linkId);
1052 WifiMacHeader hdr;
1054 hdr.SetAddr1(to);
1055 hdr.SetAddr2(GetLink(linkId).feManager->GetAddress());
1056 hdr.SetAddr3(GetLink(linkId).feManager->GetAddress());
1057 hdr.SetDsNotFrom();
1058 hdr.SetDsNotTo();
1059 Ptr<Packet> packet = Create<Packet>();
1061 probe.Get<Ssid>() = GetSsid();
1062 auto supportedRates = GetSupportedRates(linkId);
1063 probe.Get<SupportedRates>() = supportedRates.rates;
1064 probe.Get<ExtendedSupportedRatesIE>() = supportedRates.extendedRates;
1065 probe.SetBeaconIntervalUs(GetBeaconInterval().GetMicroSeconds());
1066 probe.Capabilities() = GetCapabilities(linkId);
1067 GetWifiRemoteStationManager(linkId)->SetShortPreambleEnabled(
1068 GetLink(linkId).shortPreambleEnabled);
1069 GetWifiRemoteStationManager(linkId)->SetShortSlotTimeEnabled(
1070 GetLink(linkId).shortSlotTimeEnabled);
1071 if (GetDsssSupported(linkId))
1072 {
1073 probe.Get<DsssParameterSet>() = GetDsssParameterSet(linkId);
1074 }
1075 if (GetErpSupported(linkId))
1076 {
1077 probe.Get<ErpInformation>() = GetErpInformation(linkId);
1078 }
1079 if (GetQosSupported())
1080 {
1081 probe.Get<EdcaParameterSet>() = GetEdcaParameterSet(linkId);
1082 }
1083 if (GetHtSupported(linkId))
1084 {
1086 probe.Get<HtCapabilities>() = GetHtCapabilities(linkId);
1087 probe.Get<HtOperation>() = GetHtOperation(linkId);
1088 }
1089 if (GetVhtSupported(linkId))
1090 {
1091 probe.Get<VhtCapabilities>() = GetVhtCapabilities(linkId);
1092 probe.Get<VhtOperation>() = GetVhtOperation(linkId);
1093 }
1094 if (GetHeSupported())
1095 {
1096 probe.Get<HeCapabilities>() = GetHeCapabilities(linkId);
1097 probe.Get<HeOperation>() = GetHeOperation(linkId);
1098 if (auto muEdcaParameterSet = GetMuEdcaParameterSet())
1099 {
1100 probe.Get<MuEdcaParameterSet>() = std::move(*muEdcaParameterSet);
1101 }
1102 if (Is6GhzBand(linkId))
1103 {
1105 }
1106 }
1107 if (GetEhtSupported())
1108 {
1109 probe.Get<EhtCapabilities>() = GetEhtCapabilities(linkId);
1110 probe.Get<EhtOperation>() = GetEhtOperation(linkId);
1111
1112 if (GetNLinks() > 1)
1113 {
1114 /*
1115 * If an AP is affiliated with an AP MLD and does not correspond to a nontransmitted
1116 * BSSID, then the Beacon and Probe Response frames transmitted by the AP shall
1117 * include a TBTT Information field in a Reduced Neighbor Report element with the
1118 * TBTT Information Length field set to 16 or higher, for each of the other APs
1119 * (if any) affiliated with the same AP MLD. (Sec. 35.3.4.1 of 802.11be D2.1.1)
1120 */
1121 if (auto rnr = GetReducedNeighborReport(linkId); rnr.has_value())
1122 {
1123 probe.Get<ReducedNeighborReport>() = std::move(*rnr);
1124 }
1125 /*
1126 * If an AP affiliated with an AP MLD is not in a multiple BSSID set [..], the AP
1127 * shall include, in a Beacon frame or a Probe Response frame, which is not a
1128 * Multi-Link probe response, only the Common Info field of the Basic Multi-Link
1129 * element for the AP MLD unless conditions in 35.3.11 (Multi-link procedures for
1130 * channel switching, extended channel switching, and channel quieting) are
1131 * satisfied. (Sec. 35.3.4.4 of 802.11be D2.1.1)
1132 */
1133 probe.Get<MultiLinkElement>() =
1135 }
1136 }
1137 packet->AddHeader(probe);
1138
1139 if (!GetQosSupported())
1140 {
1141 GetTxop()->Queue(Create<WifiMpdu>(packet, hdr));
1142 }
1143 // "A QoS STA that transmits a Management frame determines access category used
1144 // for medium access in transmission of the Management frame as follows
1145 // (If dot11QMFActivated is false or not present)
1146 // — If the Management frame is individually addressed to a non-QoS STA, category
1147 // AC_BE should be selected.
1148 // — If category AC_BE was not selected by the previous step, category AC_VO
1149 // shall be selected." (Sec. 10.2.3.2 of 802.11-2020)
1150 else if (!GetWifiRemoteStationManager(linkId)->GetQosSupported(to))
1151 {
1152 GetBEQueue()->Queue(Create<WifiMpdu>(packet, hdr));
1153 }
1154 else
1155 {
1156 GetVOQueue()->Queue(Create<WifiMpdu>(packet, hdr));
1157 }
1158}
1159
1162{
1164 StatusCode code;
1165 auto remoteStationManager = GetWifiRemoteStationManager(linkId);
1166 if (remoteStationManager->IsWaitAssocTxOk(to))
1167 {
1168 code.SetSuccess();
1169 }
1170 else
1171 {
1172 NS_ABORT_IF(!remoteStationManager->IsAssocRefused(to));
1173 // reset state
1174 remoteStationManager->RecordDisassociated(to);
1175 code.SetFailure();
1176 }
1177 auto supportedRates = GetSupportedRates(linkId);
1178 assoc.Get<SupportedRates>() = supportedRates.rates;
1179 assoc.Get<ExtendedSupportedRatesIE>() = supportedRates.extendedRates;
1180 assoc.SetStatusCode(code);
1181 assoc.Capabilities() = GetCapabilities(linkId);
1182 if (GetQosSupported())
1183 {
1184 assoc.Get<EdcaParameterSet>() = GetEdcaParameterSet(linkId);
1185 }
1186 if (GetHtSupported(linkId))
1187 {
1189 assoc.Get<HtCapabilities>() = GetHtCapabilities(linkId);
1190 assoc.Get<HtOperation>() = GetHtOperation(linkId);
1191 }
1192 if (GetVhtSupported(linkId))
1193 {
1194 assoc.Get<VhtCapabilities>() = GetVhtCapabilities(linkId);
1195 assoc.Get<VhtOperation>() = GetVhtOperation(linkId);
1196 }
1197 if (GetHeSupported())
1198 {
1199 assoc.Get<HeCapabilities>() = GetHeCapabilities(linkId);
1200 assoc.Get<HeOperation>() = GetHeOperation(linkId);
1201 if (auto muEdcaParameterSet = GetMuEdcaParameterSet(); muEdcaParameterSet.has_value())
1202 {
1203 assoc.Get<MuEdcaParameterSet>() = std::move(*muEdcaParameterSet);
1204 }
1205 if (Is6GhzBand(linkId))
1206 {
1208 }
1209 }
1210 if (GetEhtSupported())
1211 {
1212 assoc.Get<EhtCapabilities>() = GetEhtCapabilities(linkId);
1213 assoc.Get<EhtOperation>() = GetEhtOperation(linkId);
1214 // The AP MLD that accepts the requested TID-to-link mapping shall not include in the
1215 // (Re)Association Response frame the TID-to-link Mapping element.
1216 // (Sec. 35.3.7.1.8 of 802.11be D3.1).
1217 // For now, we assume that AP MLDs always accept requested TID-to-link mappings.
1218 }
1219 return assoc;
1220}
1221
1224 const Mac48Address& to,
1225 uint8_t linkId)
1226{
1227 // find all the links to setup (i.e., those for which status code is success)
1228 std::map<uint8_t /* link ID */, Mac48Address> linkIdStaAddrMap;
1229
1230 if (assoc.GetStatusCode().IsSuccess())
1231 {
1232 linkIdStaAddrMap[linkId] = to;
1233 }
1234
1235 if (const auto& mle = assoc.Get<MultiLinkElement>())
1236 {
1237 const auto staMldAddress = GetWifiRemoteStationManager(linkId)->GetMldAddress(to);
1238 NS_ABORT_MSG_IF(!staMldAddress.has_value(),
1239 "Sending a Multi-Link Element to a single link device");
1240 for (std::size_t idx = 0; idx < mle->GetNPerStaProfileSubelements(); idx++)
1241 {
1242 auto& perStaProfile = mle->GetPerStaProfile(idx);
1243 if (perStaProfile.HasAssocResponse() &&
1244 perStaProfile.GetAssocResponse().GetStatusCode().IsSuccess())
1245 {
1246 uint8_t otherLinkId = perStaProfile.GetLinkId();
1247 auto staAddress = GetWifiRemoteStationManager(otherLinkId)
1248 ->GetAffiliatedStaAddress(*staMldAddress);
1249 NS_ABORT_MSG_IF(!staAddress.has_value(),
1250 "No STA to associate with on link " << +otherLinkId);
1251 const auto [it, inserted] = linkIdStaAddrMap.insert({otherLinkId, *staAddress});
1252 NS_ABORT_MSG_IF(!inserted,
1253 "More than one Association Response to MLD "
1254 << *staMldAddress << " on link ID " << +otherLinkId);
1255 }
1256 }
1257 }
1258
1259 return linkIdStaAddrMap;
1260}
1261
1262void
1264{
1265 if (linkIdStaAddrMap.empty())
1266 {
1267 // no link to setup, nothing to do
1268 return;
1269 }
1270
1271 const auto& [linkId, staAddr] = *linkIdStaAddrMap.cbegin();
1272 const auto addr = GetWifiRemoteStationManager(linkId)->GetMldAddress(staAddr).value_or(staAddr);
1273
1274 // check if an AID is already allocated to the device that is associating
1275 std::set<uint16_t> aids;
1276
1277 for (const auto& [id, link] : GetLinks())
1278 {
1279 if (const auto aid = link->stationManager->GetAssociationId(addr); aid != SU_STA_ID)
1280 {
1281 aids.insert(aid);
1282 }
1283 }
1284
1285 NS_ABORT_MSG_IF(aids.size() > 1, addr << " cannot have more than one AID assigned");
1286
1287 const auto aid = aids.empty() ? GetNextAssociationId() : *aids.cbegin();
1288
1289 // store the MLD or link address in the AID-to-address map
1290 const auto [it, inserted] = m_aidToMldOrLinkAddress.emplace(aid, addr);
1291
1292 NS_ABORT_MSG_IF(!inserted, "AID " << aid << " already present, cannot be assigned to " << addr);
1293
1294 for (const auto& [id, staAddr] : linkIdStaAddrMap)
1295 {
1296 auto& link = GetLink(id);
1297
1298 if (const auto [it, inserted] = link.staList.emplace(aid, staAddr); inserted)
1299 {
1300 // the STA on this link had no AID assigned
1301 m_assocLogger(aid, staAddr);
1302 link.stationManager->SetAssociationId(staAddr, aid);
1303
1304 if (link.stationManager->GetDsssSupported(staAddr) &&
1305 !link.stationManager->GetErpOfdmSupported(staAddr))
1306 {
1307 link.numNonErpStations++;
1308 }
1309 if (!link.stationManager->GetHtSupported(staAddr) &&
1310 !link.stationManager->GetStationHe6GhzCapabilities(staAddr))
1311 {
1312 link.numNonHtStations++;
1313 }
1316 }
1317 else
1318 {
1319 // the STA on this link had an AID assigned
1320 NS_ABORT_MSG_IF(it->first != aid,
1321 "AID " << it->first << " already assigned to " << staAddr
1322 << ", could not assign " << aid);
1323 }
1324 }
1325
1326 // set the AID in all the Association Responses. NOTE that the Association
1327 // Responses included in the Per-STA Profile Subelements of the Multi-Link
1328 // Element must not contain the AID field. We set the AID field in such
1329 // Association Responses anyway, in order to ease future implementation of
1330 // the inheritance mechanism.
1331 if (assoc.GetStatusCode().IsSuccess())
1332 {
1333 assoc.SetAssociationId(aid);
1334 }
1335 if (const auto& mle = assoc.Get<MultiLinkElement>())
1336 {
1337 for (std::size_t idx = 0; idx < mle->GetNPerStaProfileSubelements(); idx++)
1338 {
1339 if (const auto& perStaProfile = mle->GetPerStaProfile(idx);
1340 perStaProfile.HasAssocResponse() &&
1341 perStaProfile.GetAssocResponse().GetStatusCode().IsSuccess())
1342 {
1343 perStaProfile.GetAssocResponse().SetAssociationId(aid);
1344 }
1345 }
1346 }
1347}
1348
1349void
1350ApWifiMac::SendAssocResp(Mac48Address to, bool isReassoc, uint8_t linkId)
1351{
1352 NS_LOG_FUNCTION(this << to << isReassoc << +linkId);
1353 WifiMacHeader hdr;
1356 hdr.SetAddr1(to);
1357 hdr.SetAddr2(GetFrameExchangeManager(linkId)->GetAddress());
1358 hdr.SetAddr3(GetFrameExchangeManager(linkId)->GetAddress());
1359 hdr.SetDsNotFrom();
1360 hdr.SetDsNotTo();
1361
1362 MgtAssocResponseHeader assoc = GetAssocResp(to, linkId);
1363
1364 // The AP that is affiliated with the AP MLD and that responds to an (Re)Association
1365 // Request frame that carries a Basic Multi-Link element shall include a Basic
1366 // Multi-Link element in the (Re)Association Response frame that it transmits
1367 // (Sec. 35.3.5.4 of 802.11be D2.0)
1368 // If the STA included a Multi-Link Element in the (Re)Association Request, we
1369 // stored its MLD address in the remote station manager
1370 if (GetNLinks() > 1 && GetWifiRemoteStationManager(linkId)->GetMldAddress(to).has_value())
1371 {
1372 assoc.Get<MultiLinkElement>() = GetMultiLinkElement(linkId, hdr.GetType(), to);
1373 }
1374
1375 auto linkIdStaAddrMap = GetLinkIdStaAddrMap(assoc, to, linkId);
1376 SetAid(assoc, linkIdStaAddrMap);
1377
1378 Ptr<Packet> packet = Create<Packet>();
1379 packet->AddHeader(assoc);
1380
1381 if (!GetQosSupported())
1382 {
1383 GetTxop()->Queue(Create<WifiMpdu>(packet, hdr));
1384 }
1385 // "A QoS STA that transmits a Management frame determines access category used
1386 // for medium access in transmission of the Management frame as follows
1387 // (If dot11QMFActivated is false or not present)
1388 // — If the Management frame is individually addressed to a non-QoS STA, category
1389 // AC_BE should be selected.
1390 // — If category AC_BE was not selected by the previous step, category AC_VO
1391 // shall be selected." (Sec. 10.2.3.2 of 802.11-2020)
1392 else if (!GetWifiRemoteStationManager(linkId)->GetQosSupported(to))
1393 {
1394 GetBEQueue()->Queue(Create<WifiMpdu>(packet, hdr));
1395 }
1396 else
1397 {
1398 GetVOQueue()->Queue(Create<WifiMpdu>(packet, hdr));
1399 }
1400}
1401
1402void
1404{
1405 NS_LOG_FUNCTION(this << +linkId);
1406 auto& link = GetLink(linkId);
1407 WifiMacHeader hdr;
1410 hdr.SetAddr2(link.feManager->GetAddress());
1411 hdr.SetAddr3(link.feManager->GetAddress());
1412 hdr.SetDsNotFrom();
1413 hdr.SetDsNotTo();
1414 Ptr<Packet> packet = Create<Packet>();
1415 MgtBeaconHeader beacon;
1416 beacon.Get<Ssid>() = GetSsid();
1417 auto supportedRates = GetSupportedRates(linkId);
1418 beacon.Get<SupportedRates>() = supportedRates.rates;
1419 beacon.Get<ExtendedSupportedRatesIE>() = supportedRates.extendedRates;
1420 beacon.SetBeaconIntervalUs(GetBeaconInterval().GetMicroSeconds());
1421 beacon.Capabilities() = GetCapabilities(linkId);
1422 GetWifiRemoteStationManager(linkId)->SetShortPreambleEnabled(link.shortPreambleEnabled);
1423 GetWifiRemoteStationManager(linkId)->SetShortSlotTimeEnabled(link.shortSlotTimeEnabled);
1424 if (GetDsssSupported(linkId))
1425 {
1426 beacon.Get<DsssParameterSet>() = GetDsssParameterSet(linkId);
1427 }
1428 if (GetErpSupported(linkId))
1429 {
1430 beacon.Get<ErpInformation>() = GetErpInformation(linkId);
1431 }
1432 if (GetQosSupported())
1433 {
1434 beacon.Get<EdcaParameterSet>() = GetEdcaParameterSet(linkId);
1435 }
1436 if (GetHtSupported(linkId))
1437 {
1439 beacon.Get<HtCapabilities>() = GetHtCapabilities(linkId);
1440 beacon.Get<HtOperation>() = GetHtOperation(linkId);
1441 }
1442 if (GetVhtSupported(linkId))
1443 {
1444 beacon.Get<VhtCapabilities>() = GetVhtCapabilities(linkId);
1445 beacon.Get<VhtOperation>() = GetVhtOperation(linkId);
1446 }
1447 if (GetHeSupported())
1448 {
1449 beacon.Get<HeCapabilities>() = GetHeCapabilities(linkId);
1450 beacon.Get<HeOperation>() = GetHeOperation(linkId);
1451 if (auto muEdcaParameterSet = GetMuEdcaParameterSet(); muEdcaParameterSet.has_value())
1452 {
1453 beacon.Get<MuEdcaParameterSet>() = std::move(*muEdcaParameterSet);
1454 }
1455 if (Is6GhzBand(linkId))
1456 {
1457 beacon.Get<He6GhzBandCapabilities>() = GetHe6GhzBandCapabilities(linkId);
1458 }
1459 }
1460 if (GetEhtSupported())
1461 {
1462 beacon.Get<EhtCapabilities>() = GetEhtCapabilities(linkId);
1463 beacon.Get<EhtOperation>() = GetEhtOperation(linkId);
1464
1465 if (GetNLinks() > 1)
1466 {
1467 /*
1468 * If an AP is affiliated with an AP MLD and does not correspond to a nontransmitted
1469 * BSSID, then the Beacon and Probe Response frames transmitted by the AP shall
1470 * include a TBTT Information field in a Reduced Neighbor Report element with the
1471 * TBTT Information Length field set to 16 or higher, for each of the other APs
1472 * (if any) affiliated with the same AP MLD. (Sec. 35.3.4.1 of 802.11be D2.1.1)
1473 */
1474 if (auto rnr = GetReducedNeighborReport(linkId); rnr.has_value())
1475 {
1476 beacon.Get<ReducedNeighborReport>() = std::move(*rnr);
1477 }
1478 /*
1479 * If an AP affiliated with an AP MLD is not in a multiple BSSID set [..], the AP
1480 * shall include, in a Beacon frame or a Probe Response frame, which is not a
1481 * Multi-Link probe response, only the Common Info field of the Basic Multi-Link
1482 * element for the AP MLD unless conditions in 35.3.11 (Multi-link procedures for
1483 * channel switching, extended channel switching, and channel quieting) are
1484 * satisfied. (Sec. 35.3.4.4 of 802.11be D2.1.1)
1485 */
1487 }
1488 }
1489 packet->AddHeader(beacon);
1490
1491 NS_LOG_INFO("Generating beacon from " << link.feManager->GetAddress() << " linkID " << +linkId);
1492 // The beacon has it's own special queue, so we load it in there
1493 m_beaconTxop->Queue(Create<WifiMpdu>(packet, hdr));
1494 link.beaconEvent =
1496
1498
1499 // If a STA that does not support Short Slot Time associates,
1500 // the AP shall use long slot time beginning at the first Beacon
1501 // subsequent to the association of the long slot time STA.
1502 if (GetErpSupported(linkId))
1503 {
1504 if (link.shortSlotTimeEnabled)
1505 {
1506 // Enable short slot time
1507 GetWifiPhy(linkId)->SetSlot(MicroSeconds(9));
1508 }
1509 else
1510 {
1511 // Disable short slot time
1512 GetWifiPhy(linkId)->SetSlot(MicroSeconds(20));
1513 }
1514 }
1515}
1516
1518ApWifiMac::GetFilsDiscovery(uint8_t linkId) const
1519{
1522 auto& link = GetLink(linkId);
1523 hdr.SetAddr2(link.feManager->GetAddress());
1524 hdr.SetAddr3(link.feManager->GetAddress());
1525 hdr.SetDsNotFrom();
1526 hdr.SetDsNotTo();
1527
1528 WifiActionHeader actionHdr;
1530 action.publicAction = WifiActionHeader::FILS_DISCOVERY;
1531 actionHdr.SetAction(WifiActionHeader::PUBLIC, action);
1532
1533 FilsDiscHeader fils;
1534 fils.SetSsid(GetSsid().PeekString());
1535 fils.m_beaconInt = (m_beaconInterval / WIFI_TU).GetHigh();
1536
1538 fils.m_fdCap->SetOpChannelWidth(link.phy->GetChannelWidth());
1539 fils.m_fdCap->SetMaxNss(std::min(link.phy->GetMaxSupportedTxSpatialStreams(),
1540 link.phy->GetMaxSupportedRxSpatialStreams()));
1541 fils.m_fdCap->SetStandard(link.phy->GetStandard());
1542
1543 fils.SetLengthSubfield();
1544 fils.m_rnr = GetReducedNeighborReport(linkId);
1545
1546 auto packet = Create<Packet>();
1547 packet->AddHeader(fils);
1548 packet->AddHeader(actionHdr);
1549
1550 return Create<WifiMpdu>(packet, hdr);
1551}
1552
1553void
1555{
1556 NS_LOG_FUNCTION(this << linkId);
1557 auto phy = GetLink(linkId).phy;
1558
1559 auto fdBeaconInterval = (phy->GetPhyBand() == WIFI_PHY_BAND_6GHZ) ? m_fdBeaconInterval6GHz
1561
1562 if (!fdBeaconInterval.IsStrictlyPositive())
1563 {
1564 NS_LOG_DEBUG("Sending FILS Discovery/unsolicited Probe Response disabled");
1565 return;
1566 }
1567
1568 // Schedule FD or unsolicited Probe Response frames (IEEE Std 802.11ax-2021 26.17.2.3.2)
1569 for (uint8_t count = 1; count < (m_beaconInterval / fdBeaconInterval).GetHigh(); ++count)
1570 {
1572 {
1573 Simulator::Schedule(fdBeaconInterval * count,
1575 this,
1577 linkId);
1578 }
1579 else
1580 {
1581 Simulator::Schedule(fdBeaconInterval * count,
1582 [=, this]() { m_beaconTxop->Queue(GetFilsDiscovery(linkId)); });
1583 }
1584 }
1585}
1586
1587void
1589{
1590 NS_LOG_FUNCTION(this << *mpdu);
1591 const WifiMacHeader& hdr = mpdu->GetHeader();
1592
1593 if (hdr.IsAssocResp() || hdr.IsReassocResp())
1594 {
1595 auto linkId = GetLinkIdByAddress(hdr.GetAddr2());
1596 NS_ABORT_MSG_IF(!linkId.has_value(), "No link ID matching the TA");
1597
1598 if (GetWifiRemoteStationManager(*linkId)->IsWaitAssocTxOk(hdr.GetAddr1()))
1599 {
1600 NS_LOG_DEBUG("AP=" << hdr.GetAddr2() << " associated with STA=" << hdr.GetAddr1());
1601 GetWifiRemoteStationManager(*linkId)->RecordGotAssocTxOk(hdr.GetAddr1());
1602 }
1603
1604 if (auto staMldAddress =
1605 GetWifiRemoteStationManager(*linkId)->GetMldAddress(hdr.GetAddr1());
1606 staMldAddress.has_value())
1607 {
1608 /**
1609 * The STA is affiliated with an MLD. From Sec. 35.3.7.1.4 of 802.11be D3.0:
1610 * When a link becomes enabled for a non-AP STA that is affiliated with a non-AP MLD
1611 * after successful association with an AP MLD with (Re)Association Request/Response
1612 * frames transmitted on another link [...], the power management mode of the non-AP
1613 * STA, immediately after the acknowledgement of the (Re)Association Response frame
1614 * [...], is power save mode, and its power state is doze.
1615 *
1616 * Thus, STAs operating on all the links but the link used to establish association
1617 * transition to power save mode.
1618 */
1619 for (uint8_t i = 0; i < GetNLinks(); i++)
1620 {
1621 auto stationManager = GetWifiRemoteStationManager(i);
1622 if (auto staAddress = stationManager->GetAffiliatedStaAddress(*staMldAddress);
1623 staAddress.has_value() && i != *linkId &&
1624 stationManager->IsWaitAssocTxOk(*staAddress))
1625 {
1627 << " associated with STA=" << *staAddress);
1628 stationManager->RecordGotAssocTxOk(*staAddress);
1629 StaSwitchingToPsMode(*staAddress, i);
1630 }
1631 }
1632
1633 // Apply the negotiated TID-to-Link Mapping (if any) for DL direction
1635 }
1636 }
1637 else if (hdr.IsAction())
1638 {
1639 if (auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket());
1640 category == WifiActionHeader::PROTECTED_EHT &&
1641 action.protectedEhtAction ==
1643 {
1644 // the EMLSR client acknowledged the EML Operating Mode Notification frame;
1645 // we can stop the timer and enforce the configuration deriving from the
1646 // EML Notification frame sent by the EMLSR client
1647 if (auto eventIt = m_transitionTimeoutEvents.find(hdr.GetAddr1());
1648 eventIt != m_transitionTimeoutEvents.cend() && eventIt->second.IsPending())
1649 {
1650 // no need to wait until the expiration of the transition timeout
1651 eventIt->second.PeekEventImpl()->Invoke();
1652 eventIt->second.Cancel();
1653 }
1654 }
1655 }
1656}
1657
1658void
1660{
1661 NS_LOG_FUNCTION(this << +timeoutReason << *mpdu);
1662 const WifiMacHeader& hdr = mpdu->GetHeader();
1663
1664 if (hdr.IsAssocResp() || hdr.IsReassocResp())
1665 {
1666 auto linkId = GetLinkIdByAddress(hdr.GetAddr2());
1667 NS_ABORT_MSG_IF(!linkId.has_value(), "No link ID matching the TA");
1668
1669 if (GetWifiRemoteStationManager(*linkId)->IsWaitAssocTxOk(hdr.GetAddr1()))
1670 {
1671 NS_LOG_DEBUG("AP=" << hdr.GetAddr2()
1672 << " association failed with STA=" << hdr.GetAddr1());
1673 GetWifiRemoteStationManager(*linkId)->RecordGotAssocTxFailed(hdr.GetAddr1());
1674 }
1675
1676 if (auto staMldAddress =
1677 GetWifiRemoteStationManager(*linkId)->GetMldAddress(hdr.GetAddr1());
1678 staMldAddress.has_value())
1679 {
1680 // the STA is affiliated with an MLD
1681 for (uint8_t i = 0; i < GetNLinks(); i++)
1682 {
1683 auto stationManager = GetWifiRemoteStationManager(i);
1684 if (auto staAddress = stationManager->GetAffiliatedStaAddress(*staMldAddress);
1685 staAddress.has_value() && i != *linkId &&
1686 stationManager->IsWaitAssocTxOk(*staAddress))
1687 {
1689 << " association failed with STA=" << *staAddress);
1690 stationManager->RecordGotAssocTxFailed(*staAddress);
1691 }
1692 }
1693 }
1694 }
1695}
1696
1697void
1699{
1700 NS_LOG_FUNCTION(this << *mpdu << linkId);
1701
1702 Mac48Address staAddr = mpdu->GetHeader().GetAddr2();
1703 bool staInPsMode = GetWifiRemoteStationManager(linkId)->IsInPsMode(staAddr);
1704
1705 if (!staInPsMode && mpdu->GetHeader().IsPowerManagement())
1706 {
1707 // the sending STA is switching to Power Save mode
1708 StaSwitchingToPsMode(staAddr, linkId);
1709 }
1710 else if (staInPsMode && !mpdu->GetHeader().IsPowerManagement())
1711 {
1712 // the sending STA is switching back to Active mode
1714 }
1715}
1716
1717void
1718ApWifiMac::StaSwitchingToPsMode(const Mac48Address& staAddr, uint8_t linkId)
1719{
1720 NS_LOG_FUNCTION(this << staAddr << linkId);
1721
1722 GetWifiRemoteStationManager(linkId)->SetPsMode(staAddr, true);
1723
1724 // Block frames addressed to the STA in PS mode
1725 NS_LOG_DEBUG("Block destination " << staAddr << " on link " << +linkId);
1726 auto staMldAddr = GetWifiRemoteStationManager(linkId)->GetMldAddress(staAddr).value_or(staAddr);
1728}
1729
1730void
1732{
1733 NS_LOG_FUNCTION(this << staAddr << linkId);
1734
1735 GetWifiRemoteStationManager(linkId)->SetPsMode(staAddr, false);
1736
1737 if (GetWifiRemoteStationManager(linkId)->IsAssociated(staAddr))
1738 {
1739 // the station is still associated, unblock its frames
1740 NS_LOG_DEBUG("Unblock destination " << staAddr << " on link " << +linkId);
1741 auto staMldAddr =
1742 GetWifiRemoteStationManager(linkId)->GetMldAddress(staAddr).value_or(staAddr);
1744 }
1745}
1746
1747std::optional<uint8_t>
1749{
1750 for (uint8_t linkId = 0; linkId < GetNLinks(); linkId++)
1751 {
1752 if (GetWifiRemoteStationManager(linkId)->IsAssociated(address))
1753 {
1754 return linkId;
1755 }
1756 }
1757 NS_LOG_DEBUG(address << " is not associated");
1758 return std::nullopt;
1759}
1760
1763{
1764 auto linkId = IsAssociated(remoteAddr);
1765 NS_ASSERT_MSG(linkId, remoteAddr << " is not associated");
1766 return GetFrameExchangeManager(*linkId)->GetAddress();
1767}
1768
1769std::optional<Mac48Address>
1771{
1772 if (const auto staIt = m_aidToMldOrLinkAddress.find(aid);
1773 staIt != m_aidToMldOrLinkAddress.cend())
1774 {
1775 return staIt->second;
1776 }
1777 return std::nullopt;
1778}
1779
1780void
1782{
1783 NS_LOG_FUNCTION(this << *mpdu << +linkId);
1784 // consider the MAC header of the original MPDU (makes a difference for data frames only)
1785 const WifiMacHeader* hdr = &mpdu->GetOriginal()->GetHeader();
1786 Ptr<const Packet> packet = mpdu->GetPacket();
1787 Mac48Address from = hdr->GetAddr2();
1788 if (hdr->IsData())
1789 {
1790 std::optional<uint8_t> apLinkId;
1791 if (!hdr->IsFromDs() && hdr->IsToDs() &&
1792 (apLinkId = IsAssociated(mpdu->GetHeader().GetAddr2())) &&
1793 mpdu->GetHeader().GetAddr1() == GetFrameExchangeManager(*apLinkId)->GetAddress())
1794 {
1795 // this MPDU is being acknowledged by the AP, so we can process
1796 // the Power Management flag
1797 ProcessPowerManagementFlag(mpdu, *apLinkId);
1798
1799 Mac48Address to = hdr->GetAddr3();
1800 // Address3 can be our MLD address (e.g., this is an MPDU containing a single MSDU
1801 // addressed to us) or a BSSID (e.g., this is an MPDU containing an A-MSDU)
1802 if (to == GetAddress() ||
1803 (hdr->IsQosData() && hdr->IsQosAmsdu() && to == mpdu->GetHeader().GetAddr1()))
1804 {
1805 NS_LOG_DEBUG("frame for me from=" << from);
1806 if (hdr->IsQosData())
1807 {
1808 if (hdr->IsQosAmsdu())
1809 {
1810 NS_LOG_DEBUG("Received A-MSDU from=" << from
1811 << ", size=" << packet->GetSize());
1813 packet = nullptr;
1814 }
1815 else if (hdr->HasData())
1816 {
1817 ForwardUp(packet, from, GetAddress());
1818 }
1819 }
1820 else if (hdr->HasData())
1821 {
1822 ForwardUp(packet, from, GetAddress());
1823 }
1824 }
1825 else if (to.IsGroup() || IsAssociated(to))
1826 {
1827 NS_LOG_DEBUG("forwarding frame from=" << from << ", to=" << to);
1828 Ptr<Packet> copy = packet->Copy();
1829
1830 // If the frame we are forwarding is of type QoS Data,
1831 // then we need to preserve the UP in the QoS control
1832 // header...
1833 if (hdr->IsQosData())
1834 {
1835 WifiMac::Enqueue(copy, to, from, hdr->GetQosTid());
1836 }
1837 else
1838 {
1839 WifiMac::Enqueue(copy, to, from);
1840 }
1841 ForwardUp(packet, from, to);
1842 }
1843 else if (hdr->HasData())
1844 {
1845 ForwardUp(packet, from, to);
1846 }
1847 }
1848 // NOLINTBEGIN(bugprone-branch-clone)
1849 else if (hdr->IsFromDs() && hdr->IsToDs())
1850 {
1851 // this is an AP-to-AP frame
1852 // we ignore for now.
1853 NotifyRxDrop(packet);
1854 }
1855 // NOLINTEND(bugprone-branch-clone)
1856 else
1857 {
1858 // we can ignore these frames since
1859 // they are not targeted at the AP
1860 NotifyRxDrop(packet);
1861 }
1862 return;
1863 }
1864 else if (hdr->IsMgt())
1865 {
1866 if (hdr->GetAddr1() == GetFrameExchangeManager(linkId)->GetAddress() &&
1868 {
1869 // this MPDU is being acknowledged by the AP, so we can process
1870 // the Power Management flag
1871 ProcessPowerManagementFlag(mpdu, linkId);
1872 }
1873 if (hdr->IsProbeReq() && (hdr->GetAddr1().IsGroup() ||
1874 hdr->GetAddr1() == GetFrameExchangeManager(linkId)->GetAddress()))
1875 {
1876 // In the case where the Address 1 field contains a group address, the
1877 // Address 3 field also is validated to verify that the group addressed
1878 // frame originated from a STA in the BSS of which the receiving STA is
1879 // a member (Section 9.3.3.1 of 802.11-2020)
1880 if (hdr->GetAddr1().IsGroup() && !hdr->GetAddr3().IsBroadcast() &&
1881 hdr->GetAddr3() != GetFrameExchangeManager(linkId)->GetAddress())
1882 {
1883 // not addressed to us
1884 return;
1885 }
1886 MgtProbeRequestHeader probeRequestHeader;
1887 packet->PeekHeader(probeRequestHeader);
1888 const auto& ssid = probeRequestHeader.Get<Ssid>();
1889 if (ssid == GetSsid() || ssid->IsBroadcast())
1890 {
1891 NS_LOG_DEBUG("Probe request received from " << from << ": send probe response");
1892 SendProbeResp(from, linkId);
1893 }
1894 return;
1895 }
1896 else if (hdr->GetAddr1() == GetFrameExchangeManager(linkId)->GetAddress())
1897 {
1898 switch (hdr->GetType())
1899 {
1902 NS_LOG_DEBUG(((hdr->IsAssocReq()) ? "Association" : "Reassociation")
1903 << " request received from " << from
1904 << ((GetNLinks() > 1) ? " on link ID " + std::to_string(linkId) : ""));
1905
1906 MgtAssocRequestHeader assocReq;
1907 MgtReassocRequestHeader reassocReq;
1908 AssocReqRefVariant frame = assocReq;
1909 if (hdr->IsAssocReq())
1910 {
1911 packet->PeekHeader(assocReq);
1912 }
1913 else
1914 {
1915 packet->PeekHeader(reassocReq);
1916 frame = reassocReq;
1917 }
1918 if (ReceiveAssocRequest(frame, from, linkId) && GetNLinks() > 1)
1919 {
1920 ParseReportedStaInfo(frame, from, linkId);
1921 }
1922 SendAssocResp(hdr->GetAddr2(), hdr->IsReassocReq(), linkId);
1923 return;
1924 }
1926 NS_LOG_DEBUG("Disassociation received from " << from);
1927 GetWifiRemoteStationManager(linkId)->RecordDisassociated(from);
1928 auto& staList = GetLink(linkId).staList;
1929 for (auto it = staList.begin(); it != staList.end(); ++it)
1930 {
1931 if (it->second == from)
1932 {
1933 staList.erase(it);
1934 m_deAssocLogger(it->first, it->second);
1935 if (GetWifiRemoteStationManager(linkId)->GetDsssSupported(from) &&
1936 !GetWifiRemoteStationManager(linkId)->GetErpOfdmSupported(from))
1937 {
1938 GetLink(linkId).numNonErpStations--;
1939 }
1940 if (!GetWifiRemoteStationManager(linkId)->GetHtSupported(from) &&
1941 !GetWifiRemoteStationManager(linkId)->GetStationHe6GhzCapabilities(
1942 from))
1943 {
1944 GetLink(linkId).numNonHtStations--;
1945 }
1949 break;
1950 }
1951 }
1952 return;
1953 }
1954 case WIFI_MAC_MGT_ACTION: {
1955 auto pkt = mpdu->GetPacket()->Copy();
1956 auto [category, action] = WifiActionHeader::Remove(pkt);
1957 if (category == WifiActionHeader::PROTECTED_EHT &&
1958 action.protectedEhtAction ==
1960 IsAssociated(hdr->GetAddr2()))
1961 {
1962 // received an EML Operating Mode Notification frame from an associated station
1963 MgtEmlOmn frame;
1964 pkt->RemoveHeader(frame);
1965 ReceiveEmlOmn(frame, hdr->GetAddr2(), linkId);
1966 return;
1967 }
1968 break;
1969 }
1970 default:;
1971 // do nothing
1972 }
1973 }
1974 }
1975
1976 // Invoke the receive handler of our parent class to deal with any other frames
1977 WifiMac::Receive(Create<WifiMpdu>(packet, *hdr), linkId);
1978}
1979
1980bool
1982 const Mac48Address& from,
1983 uint8_t linkId)
1984{
1985 NS_LOG_FUNCTION(this << from << +linkId);
1986
1987 auto remoteStationManager = GetWifiRemoteStationManager(linkId);
1988
1989 auto failure = [&](const std::string& msg) -> bool {
1990 NS_LOG_DEBUG("Association Request from " << from << " refused: " << msg);
1991 remoteStationManager->RecordAssocRefused(from);
1992 return false;
1993 };
1994
1995 // lambda to process received (Re)Association Request
1996 auto recvAssocRequest = [&](auto&& frameRefWrapper) -> bool {
1997 const auto& frame = frameRefWrapper.get();
1998
1999 // first, verify that the the station's supported
2000 // rate set is compatible with our Basic Rate set
2001 const CapabilityInformation& capabilities = frame.Capabilities();
2002 remoteStationManager->AddSupportedPhyPreamble(from, capabilities.IsShortPreamble());
2003 NS_ASSERT(frame.template Get<SupportedRates>());
2004 const auto rates = AllSupportedRates{*frame.template Get<SupportedRates>(),
2005 frame.template Get<ExtendedSupportedRatesIE>()};
2006
2007 if (rates.GetNRates() == 0)
2008 {
2009 return failure("STA's supported rate set not compatible with our Basic Rate set");
2010 }
2011
2012 if (GetHtSupported(linkId))
2013 {
2014 // check whether the HT STA supports all MCSs in Basic MCS Set
2015 const auto& htCapabilities = frame.template Get<HtCapabilities>();
2016 if (htCapabilities.has_value() && htCapabilities->IsSupportedMcs(0))
2017 {
2018 for (uint8_t i = 0; i < remoteStationManager->GetNBasicMcs(); i++)
2019 {
2020 WifiMode mcs = remoteStationManager->GetBasicMcs(i);
2021 if (!htCapabilities->IsSupportedMcs(mcs.GetMcsValue()))
2022 {
2023 return failure("HT STA does not support all MCSs in Basic MCS Set");
2024 }
2025 }
2026 }
2027 }
2028 if (GetVhtSupported(linkId))
2029 {
2030 // check whether the VHT STA supports all MCSs in Basic MCS Set
2031 const auto& vhtCapabilities = frame.template Get<VhtCapabilities>();
2032 if (vhtCapabilities.has_value() && vhtCapabilities->GetVhtCapabilitiesInfo() != 0)
2033 {
2034 for (uint8_t i = 0; i < remoteStationManager->GetNBasicMcs(); i++)
2035 {
2036 WifiMode mcs = remoteStationManager->GetBasicMcs(i);
2037 if (!vhtCapabilities->IsSupportedTxMcs(mcs.GetMcsValue()))
2038 {
2039 return failure("VHT STA does not support all MCSs in Basic MCS Set");
2040 }
2041 }
2042 }
2043 }
2044 if (GetHeSupported())
2045 {
2046 // check whether the HE STA supports all MCSs in Basic MCS Set
2047 const auto& heCapabilities = frame.template Get<HeCapabilities>();
2048 if (heCapabilities.has_value() && heCapabilities->GetSupportedMcsAndNss() != 0)
2049 {
2050 for (uint8_t i = 0; i < remoteStationManager->GetNBasicMcs(); i++)
2051 {
2052 WifiMode mcs = remoteStationManager->GetBasicMcs(i);
2053 if (!heCapabilities->IsSupportedTxMcs(mcs.GetMcsValue()))
2054 {
2055 return failure("HE STA does not support all MCSs in Basic MCS Set");
2056 }
2057 }
2058 }
2059 if (Is6GhzBand(linkId))
2060 {
2061 if (const auto& he6GhzCapabilities = frame.template Get<He6GhzBandCapabilities>())
2062 {
2063 remoteStationManager->AddStationHe6GhzCapabilities(from, *he6GhzCapabilities);
2064 }
2065 }
2066 }
2067 if (GetEhtSupported())
2068 {
2069 // TODO check whether the EHT STA supports all MCSs in Basic MCS Set
2070 auto ehtConfig = GetEhtConfiguration();
2071 NS_ASSERT(ehtConfig);
2072
2073 if (const auto& tidLinkMapping = frame.template Get<TidToLinkMapping>();
2074 !tidLinkMapping.empty())
2075 {
2076 // non-AP MLD included TID-to-Link Mapping IE(s) in the Association Request.
2077 // We refuse association if we do not support TID-to-Link mapping negotiation
2078 // or the non-AP MLD included more than two TID-to-Link Mapping IEs
2079 // or we support negotiation type 1 but TIDs are mapped onto distinct link sets
2080 // or there is some TID that is not mapped to any link
2081 // or the direction(s) is/are not set properly
2082 if (tidLinkMapping.size() > 2)
2083 {
2084 return failure("More than two TID-to-Link Mapping IEs");
2085 }
2086
2087 // if only one Tid-to-Link Mapping element is present, it must be valid for
2088 // both directions
2089 bool bothDirIfOneTlm =
2090 tidLinkMapping.size() != 1 ||
2091 tidLinkMapping[0].m_control.direction == WifiDirection::BOTH_DIRECTIONS;
2092 // An MLD that includes two TID-To-Link Mapping elements in a (Re)Association
2093 // Request frame or a (Re)Association Response frame shall set the Direction
2094 // subfield in one of the TID-To-Link Mapping elements to 0 and the Direction
2095 // subfield in the other TID-To- Link Mapping element to 1.
2096 // (Sec. 35.3.7.1.8 of 802.11be D3.1)
2097 bool distinctDirsIfTwoTlms =
2098 tidLinkMapping.size() != 2 ||
2099 (tidLinkMapping[0].m_control.direction != WifiDirection::BOTH_DIRECTIONS &&
2100 tidLinkMapping[1].m_control.direction != WifiDirection::BOTH_DIRECTIONS &&
2101 tidLinkMapping[0].m_control.direction !=
2102 tidLinkMapping[1].m_control.direction);
2103
2104 if (!bothDirIfOneTlm || !distinctDirsIfTwoTlms)
2105 {
2106 return failure("Incorrect directions in TID-to-Link Mapping IEs");
2107 }
2108
2110 ehtConfig->GetAttributeFailSafe("TidToLinkMappingNegSupport", negSupport);
2111
2113 {
2114 return failure("TID-to-Link Mapping negotiation not supported");
2115 }
2116
2117 auto getMapping = [](const TidToLinkMapping& tlmIe, WifiTidLinkMapping& mapping) {
2118 if (tlmIe.m_control.defaultMapping)
2119 {
2120 return;
2121 }
2122 for (uint8_t tid = 0; tid < 8; tid++)
2123 {
2124 if (auto linkSet = tlmIe.GetLinkMappingOfTid(tid); !linkSet.empty())
2125 {
2126 mapping.emplace(tid, std::move(linkSet));
2127 }
2128 }
2129 };
2130
2131 WifiTidLinkMapping dlMapping;
2132 WifiTidLinkMapping ulMapping;
2133
2134 switch (tidLinkMapping[0].m_control.direction)
2135 {
2137 getMapping(tidLinkMapping.at(0), dlMapping);
2138 ulMapping = dlMapping;
2139 break;
2141 getMapping(tidLinkMapping.at(0), dlMapping);
2142 getMapping(tidLinkMapping.at(1), ulMapping);
2143 break;
2145 getMapping(tidLinkMapping.at(0), ulMapping);
2146 getMapping(tidLinkMapping.at(1), dlMapping);
2147 break;
2148 }
2149
2151 !TidToLinkMappingValidForNegType1(dlMapping, ulMapping))
2152 {
2153 return failure("Mapping TIDs to distinct link sets is incompatible with "
2154 "negotiation support of 1");
2155 }
2156
2157 // otherwise, we accept the TID-to-link Mapping and store it
2158 const auto& mle = frame.template Get<MultiLinkElement>();
2159 NS_ASSERT_MSG(mle,
2160 "Multi-Link Element not present in an Association Request including "
2161 "TID-to-Link Mapping element(s)");
2162 auto mldAddr = mle->GetMldMacAddress();
2163
2164 // The requested link mappings are valid and can be accepted; store them.
2166 UpdateTidToLinkMapping(mldAddr, WifiDirection::UPLINK, ulMapping);
2167 }
2168 }
2169
2170 // The association request from the station can be accepted.
2171 // Record all its supported modes in its associated WifiRemoteStation
2172 auto phy = GetWifiPhy(linkId);
2173
2174 for (const auto& mode : phy->GetModeList())
2175 {
2176 if (rates.IsSupportedRate(mode.GetDataRate(phy->GetChannelWidth())))
2177 {
2178 remoteStationManager->AddSupportedMode(from, mode);
2179 }
2180 }
2181 if (GetErpSupported(linkId) && remoteStationManager->GetErpOfdmSupported(from) &&
2182 capabilities.IsShortSlotTime())
2183 {
2184 remoteStationManager->AddSupportedErpSlotTime(from, true);
2185 }
2186 if (GetHtSupported(linkId))
2187 {
2188 const auto& htCapabilities = frame.template Get<HtCapabilities>();
2189 if (htCapabilities.has_value() && htCapabilities->IsSupportedMcs(0))
2190 {
2191 remoteStationManager->AddStationHtCapabilities(from, *htCapabilities);
2192 }
2193 // const ExtendedCapabilities& extendedCapabilities = frame.GetExtendedCapabilities();
2194 // TODO: to be completed
2195 }
2196 if (GetVhtSupported(linkId))
2197 {
2198 const auto& vhtCapabilities = frame.template Get<VhtCapabilities>();
2199 // we will always fill in RxHighestSupportedLgiDataRate field at TX, so this can be used
2200 // to check whether it supports VHT
2201 if (vhtCapabilities.has_value() &&
2202 vhtCapabilities->GetRxHighestSupportedLgiDataRate() > 0)
2203 {
2204 remoteStationManager->AddStationVhtCapabilities(from, *vhtCapabilities);
2205 for (const auto& mcs : phy->GetMcsList(WIFI_MOD_CLASS_VHT))
2206 {
2207 if (vhtCapabilities->IsSupportedTxMcs(mcs.GetMcsValue()))
2208 {
2209 remoteStationManager->AddSupportedMcs(from, mcs);
2210 // here should add a control to add basic MCS when it is implemented
2211 }
2212 }
2213 }
2214 }
2215 if (GetHeSupported())
2216 {
2217 const auto& heCapabilities = frame.template Get<HeCapabilities>();
2218 if (heCapabilities.has_value() && heCapabilities->GetSupportedMcsAndNss() != 0)
2219 {
2220 remoteStationManager->AddStationHeCapabilities(from, *heCapabilities);
2221 for (const auto& mcs : phy->GetMcsList(WIFI_MOD_CLASS_HE))
2222 {
2223 if (heCapabilities->IsSupportedTxMcs(mcs.GetMcsValue()))
2224 {
2225 remoteStationManager->AddSupportedMcs(from, mcs);
2226 // here should add a control to add basic MCS when it is implemented
2227 }
2228 }
2229 }
2230 }
2231 if (GetEhtSupported())
2232 {
2233 if (const auto& ehtCapabilities = frame.template Get<EhtCapabilities>())
2234 {
2235 remoteStationManager->AddStationEhtCapabilities(from, *ehtCapabilities);
2236 }
2237 for (const auto& mcs : phy->GetMcsList(WIFI_MOD_CLASS_EHT))
2238 {
2239 // TODO: Add check whether MCS is supported from the capabilities
2240 remoteStationManager->AddSupportedMcs(from, mcs);
2241 // here should add a control to add basic MCS when it is implemented
2242 }
2243 }
2244
2245 NS_LOG_DEBUG("Association Request from " << from << " accepted");
2246 remoteStationManager->RecordWaitAssocTxOk(from);
2247 return true;
2248 };
2249
2250 return std::visit(recvAssocRequest, assoc);
2251}
2252
2253void
2255{
2256 NS_LOG_FUNCTION(this << from << +linkId);
2257
2258 // lambda to process received Multi-Link Element
2259 auto recvMle = [&](auto&& frame) {
2260 const auto& mle = frame.get().template Get<MultiLinkElement>();
2261
2262 if (!mle.has_value())
2263 {
2264 return;
2265 }
2266
2267 auto mleCommonInfo = std::make_shared<CommonInfoBasicMle>(mle->GetCommonInfoBasic());
2268 GetWifiRemoteStationManager(linkId)->AddStationMleCommonInfo(from, mleCommonInfo);
2269
2270 for (std::size_t i = 0; i < mle->GetNPerStaProfileSubelements(); i++)
2271 {
2272 auto& perStaProfile = mle->GetPerStaProfile(i);
2273 if (!perStaProfile.HasStaMacAddress())
2274 {
2275 NS_LOG_DEBUG("[i=" << i
2276 << "] Cannot setup a link if the STA MAC address is missing");
2277 continue;
2278 }
2279 uint8_t newLinkId = perStaProfile.GetLinkId();
2280 if (newLinkId == linkId || newLinkId >= GetNLinks())
2281 {
2282 NS_LOG_DEBUG("[i=" << i << "] Link ID " << newLinkId << " not valid");
2283 continue;
2284 }
2285 if (!perStaProfile.HasAssocRequest() && !perStaProfile.HasReassocRequest())
2286 {
2287 NS_LOG_DEBUG("[i=" << i << "] No (Re)Association Request frame body present");
2288 continue;
2289 }
2290
2291 ReceiveAssocRequest(perStaProfile.GetAssocRequest(),
2292 perStaProfile.GetStaMacAddress(),
2293 newLinkId);
2294 GetWifiRemoteStationManager(newLinkId)->AddStationMleCommonInfo(
2295 perStaProfile.GetStaMacAddress(),
2296 mleCommonInfo);
2297 }
2298 };
2299
2300 std::visit(recvMle, assoc);
2301}
2302
2303void
2304ApWifiMac::ReceiveEmlOmn(MgtEmlOmn& frame, const Mac48Address& sender, uint8_t linkId)
2305{
2306 NS_LOG_FUNCTION(this << frame << sender << linkId);
2307
2308 auto ehtConfiguration = GetEhtConfiguration();
2309
2310 if (BooleanValue emlsrActivated;
2311 !ehtConfiguration ||
2312 !ehtConfiguration->GetAttributeFailSafe("EmlsrActivated", emlsrActivated) ||
2313 !emlsrActivated.Get())
2314 {
2316 "Received an EML Operating Mode Notification frame but EMLSR is not activated");
2317 return;
2318 }
2319
2321 {
2323 auto emlCapabilities =
2324 GetWifiRemoteStationManager(linkId)->GetStationEmlCapabilities(sender);
2325 NS_ASSERT_MSG(emlCapabilities, "EML Capabilities not stored for STA " << sender);
2326
2327 // update values stored in remote station manager
2328 emlCapabilities->get().emlsrPaddingDelay = frame.m_emlsrParamUpdate->paddingDelay;
2329 emlCapabilities->get().emlsrTransitionDelay = frame.m_emlsrParamUpdate->transitionDelay;
2330 }
2331
2332 auto mldAddress = GetWifiRemoteStationManager(linkId)->GetMldAddress(sender);
2333 NS_ASSERT_MSG(mldAddress, "No MLD address stored for STA " << sender);
2334 auto emlsrLinks =
2335 frame.m_emlControl.emlsrMode == 1 ? frame.GetLinkBitmap() : std::list<uint8_t>{};
2336
2337 // The AP MLD has to consider the changes carried by the received EML Notification frame
2338 // as effective at the same time as the non-AP MLD. Therefore, we need to start a time
2339 // when the transmission of the Ack following the received EML Notification frame is
2340 // completed. For this purpose, we connect a callback to the PHY TX begin trace to catch
2341 // the Ack transmitted after the EML Notification frame.
2343 [=, this](WifiConstPsduMap psduMap, WifiTxVector txVector, Watt_u /* txPower */) {
2344 NS_ASSERT_MSG(psduMap.size() == 1 && psduMap.begin()->second->GetNMpdus() == 1 &&
2345 psduMap.begin()->second->GetHeader(0).IsAck(),
2346 "Expected a Normal Ack after EML Notification frame");
2347
2348 auto ackDuration =
2349 WifiPhy::CalculateTxDuration(psduMap, txVector, GetLink(linkId).phy->GetPhyBand());
2350
2351 TimeValue transitionTimeout;
2352 ehtConfiguration->GetAttribute("TransitionTimeout", transitionTimeout);
2353
2355 Simulator::Schedule(ackDuration + transitionTimeout.Get(), [=, this]() {
2356 for (uint8_t id = 0; id < GetNLinks(); id++)
2357 {
2358 auto linkAddress =
2359 GetWifiRemoteStationManager(id)->GetAffiliatedStaAddress(*mldAddress);
2360 if (!linkAddress)
2361 {
2362 // this link has not been setup by the non-AP MLD
2363 continue;
2364 }
2365
2366 if (!emlsrLinks.empty())
2367 {
2368 // the non-AP MLD is enabling EMLSR mode
2369 /**
2370 * After the successful transmission of the EML Operating Mode
2371 * Notification frame by the non-AP STA affiliated with the non-AP MLD,
2372 * the non-AP MLD shall operate in the EMLSR mode and the other non-AP
2373 * STAs operating on the corresponding EMLSR links shall transition to
2374 * active mode after the transition delay indicated in the Transition
2375 * Timeout subfield in the EML Capabilities subfield of the Basic
2376 * Multi-Link element or immediately after receiving an EML Operating
2377 * Mode Notification frame from one of the APs operating on the EMLSR
2378 * links and affiliated with the AP MLD (Sec. 35.3.17 of 802.11be D3.0)
2379 */
2380 auto enabled = std::find(emlsrLinks.cbegin(), emlsrLinks.cend(), id) !=
2381 emlsrLinks.cend();
2382 if (enabled)
2383 {
2384 StaSwitchingToActiveModeOrDeassociated(*linkAddress, id);
2385 }
2386 GetWifiRemoteStationManager(id)->SetEmlsrEnabled(*linkAddress, enabled);
2387 }
2388 else
2389 {
2390 // the non-AP MLD is disabling EMLSR mode
2391 /**
2392 * After the successful transmission of the EML Operating Mode
2393 * Notification frame by the non-AP STA affiliated with the non-AP MLD,
2394 * the non-AP MLD shall disable the EMLSR mode and the other non-AP
2395 * STAs operating on the corresponding EMLSR links shall transition to
2396 * power save mode after the transition delay indicated in the
2397 * Transition Timeout subfield in the EML Capabilities subfield of the
2398 * Basic Multi-Link element or immediately after receiving an EML
2399 * Operating Mode Notification frame from one of the APs operating on
2400 * the EMLSR links and affiliated with the AP MLD. (Sec. 35.3.17 of
2401 * 802.11be D3.0)
2402 */
2403 if (id != linkId &&
2404 GetWifiRemoteStationManager(id)->GetEmlsrEnabled(*linkAddress))
2405 {
2406 StaSwitchingToPsMode(*linkAddress, id);
2407 }
2408 GetWifiRemoteStationManager(id)->SetEmlsrEnabled(*linkAddress, false);
2409 }
2410 }
2411 });
2412 });
2413
2414 // connect the callback to the PHY TX begin trace to catch the Ack and disconnect
2415 // after its transmission begins
2416 auto phy = GetLink(linkId).phy;
2417 phy->TraceConnectWithoutContext("PhyTxPsduBegin", cb);
2418 Simulator::Schedule(phy->GetSifs() + NanoSeconds(1),
2419 [=]() { phy->TraceDisconnectWithoutContext("PhyTxPsduBegin", cb); });
2420
2421 // An AP MLD with dot11EHTEMLSROptionActivated equal to true sets the EMLSR Mode subfield
2422 // to the value obtained from the EMLSR Mode subfield of the received EML Operating Mode
2423 // Notification frame. (Sec. 9.6.35.8 of 802.11be D3.0)
2424
2425 // When included in a frame sent by an AP affiliated with an AP MLD, the EMLSR Parameter
2426 // Update Control subfield is set to 0. (Sec. 9.6.35.8 of 802.11be D3.0)
2427 frame.m_emlControl.emlsrParamUpdateCtrl = 0;
2428
2429 // An AP MLD with dot11EHTEMLSROptionImplemented equal to true sets the EMLSR Link Bitmap
2430 // subfield to the value obtained from the EMLSR Link Bitmap subfield of the received
2431 // EML Operating Mode Notification frame. (Sec. 9.6.35.8 of 802.11be D3.0)
2432
2433 // The EMLSR Parameter Update field [..] is present if [..] the Action frame is sent by
2434 // a non-AP STA affiliated with a non-AP MLD (Sec. 9.6.35.8 of 802.11be D3.0)
2435 frame.m_emlsrParamUpdate.reset();
2436
2438 ehtFem->SendEmlOmn(sender, frame);
2439}
2440
2441void
2443{
2444 NS_LOG_FUNCTION(this << *mpdu);
2445 for (auto& i : *PeekPointer(mpdu))
2446 {
2447 auto from = i.second.GetSourceAddr();
2448 auto to = i.second.GetDestinationAddr();
2449
2450 if (to.IsGroup() || IsAssociated(to))
2451 {
2452 NS_LOG_DEBUG("forwarding QoS frame from=" << from << ", to=" << to);
2453 WifiMac::Enqueue(i.first->Copy(), to, from, mpdu->GetHeader().GetQosTid());
2454 }
2455
2456 ForwardUp(i.first, from, to);
2457 }
2458}
2459
2460void
2462{
2463 NS_LOG_FUNCTION(this);
2465
2466 for (uint8_t linkId = 0; linkId < GetNLinks(); ++linkId)
2467 {
2468 GetLink(linkId).beaconEvent.Cancel();
2470 {
2471 uint64_t jitterUs =
2473 ? static_cast<uint64_t>(m_beaconJitter->GetValue(0, 1) *
2475 : 0);
2476 NS_LOG_DEBUG("Scheduling initial beacon for access point "
2477 << GetAddress() << " at time " << jitterUs << "us");
2480 this,
2481 linkId);
2482 }
2485 }
2486
2491}
2492
2493bool
2495{
2496 bool useProtection = (GetLink(linkId).numNonErpStations > 0) && m_enableNonErpProtection;
2497 GetWifiRemoteStationManager(linkId)->SetUseNonErpProtection(useProtection);
2498 return useProtection;
2499}
2500
2501uint16_t
2503{
2504 const auto& links = GetLinks();
2505
2506 // Return the first AID value between 1 and 2007 that is free for all the links
2507 for (uint16_t nextAid = 1; nextAid <= 2007; ++nextAid)
2508 {
2509 if (std::none_of(links.cbegin(), links.cend(), [&](auto&& idLinkPair) {
2510 return GetStaList(idLinkPair.first).contains(nextAid);
2511 }))
2512 {
2513 return nextAid;
2514 }
2515 }
2516 NS_FATAL_ERROR("No free association ID available!");
2517 return 0;
2518}
2519
2520const std::map<uint16_t, Mac48Address>&
2521ApWifiMac::GetStaList(uint8_t linkId) const
2522{
2523 return GetLink(linkId).staList;
2524}
2525
2526uint16_t
2528{
2529 return GetWifiRemoteStationManager(linkId)->GetAssociationId(addr);
2530}
2531
2532uint8_t
2533ApWifiMac::GetBufferStatus(uint8_t tid, Mac48Address address) const
2534{
2535 auto it = m_bufferStatus.find(WifiAddressTidPair(address, tid));
2536 if (it == m_bufferStatus.end() || it->second.timestamp + m_bsrLifetime < Simulator::Now())
2537 {
2538 return 255;
2539 }
2540 return it->second.value;
2541}
2542
2543void
2544ApWifiMac::SetBufferStatus(uint8_t tid, Mac48Address address, uint8_t size)
2545{
2546 if (size == 255)
2547 {
2548 // no point in storing an unspecified size
2549 m_bufferStatus.erase(WifiAddressTidPair(address, tid));
2550 }
2551 else
2552 {
2553 m_bufferStatus[WifiAddressTidPair(address, tid)] = {size, Simulator::Now()};
2554 }
2555}
2556
2557uint8_t
2559{
2560 uint8_t maxSize = 0;
2561 bool found = false;
2562
2563 for (uint8_t tid = 0; tid < 8; tid++)
2564 {
2565 uint8_t size = GetBufferStatus(tid, address);
2566 if (size != 255)
2567 {
2568 maxSize = std::max(maxSize, size);
2569 found = true;
2570 }
2571 }
2572
2573 if (found)
2574 {
2575 return maxSize;
2576 }
2577 return 255;
2578}
2579
2580} // namespace ns3
Wi-Fi AP state machine.
Definition ap-wifi-mac.h:63
void SendAssocResp(Mac48Address to, bool isReassoc, uint8_t linkId)
Forward an association or a reassociation response packet to the DCF/EDCA.
uint16_t GetAssociationId(Mac48Address addr, uint8_t linkId) const
std::unique_ptr< LinkEntity > CreateLinkEntity() const override
Create a LinkEntity object.
Ptr< Txop > m_beaconTxop
Dedicated Txop for beacons.
void SetBeaconGeneration(bool enable)
Enable or disable beacon generation of the AP.
void ParseReportedStaInfo(const AssocReqRefVariant &assoc, Mac48Address from, uint8_t linkId)
Given a (Re)Association Request frame body containing a Multi-Link Element, check if a link can be se...
void UpdateShortSlotTimeEnabled(uint8_t linkId)
Update whether short slot time should be enabled or not in the BSS corresponding to the given link.
void DoCompleteConfig() override
Allow subclasses to complete the configuration of the MAC layer components.
const std::map< uint16_t, Mac48Address > & GetStaList(uint8_t linkId) const
Get a const reference to the map of associated stations on the given link.
void DoDispose() override
Destructor implementation.
void SetBeaconInterval(Time interval)
bool ReceiveAssocRequest(const AssocReqRefVariant &assoc, const Mac48Address &from, uint8_t linkId)
Check whether the supported rate set included in the received (Re)Association Request frame is compat...
std::map< uint8_t, Mac48Address > LinkIdStaAddrMap
Map of (link ID, remote STA address) of the links to setup.
std::map< Mac48Address, EventId > m_transitionTimeoutEvents
transition timeout events running for EMLSR clients
UintAccessParamsMap m_cwMaxsForSta
Per-AC CW max values to advertise to stations.
void ScheduleFilsDiscOrUnsolProbeRespFrames(uint8_t linkId)
Schedule the transmission of FILS Discovery frames or unsolicited Probe Response frames on the given ...
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...
CapabilityInformation GetCapabilities(uint8_t linkId) const
Return the Capability information of the current AP for the given link.
Ptr< ApEmlsrManager > m_apEmlsrManager
AP EMLSR Manager.
Ptr< UniformRandomVariable > m_beaconJitter
UniformRandomVariable used to randomize the time of the first beacon.
bool CanForwardPacketsTo(Mac48Address to) const override
Return true if packets can be forwarded to the given destination, false otherwise.
bool m_enableNonErpProtection
Flag whether protection mechanism is used or not when non-ERP STAs are present within the BSS.
EdcaParameterSet GetEdcaParameterSet(uint8_t linkId) const
Return the EDCA Parameter Set of the current AP for the given link.
void StaSwitchingToActiveModeOrDeassociated(const Mac48Address &staAddr, uint8_t linkId)
Perform the necessary actions when a given station deassociates or switches from powersave mode to ac...
MultiLinkElement GetMultiLinkElement(uint8_t linkId, WifiMacType frameType, const Mac48Address &to=Mac48Address::GetBroadcast())
Return the Multi-Link Element that the current AP includes in the management frames of the given type...
HtOperation GetHtOperation(uint8_t linkId) const
Return the HT operation of the current AP for the given link.
std::optional< Mac48Address > GetMldOrLinkAddressByAid(uint16_t aid) const
void UpdateShortPreambleEnabled(uint8_t linkId)
Update whether short preamble should be enabled or not in the BSS corresponding to the given link.
uint16_t GetNextAssociationId() const
void TxOk(Ptr< const WifiMpdu > mpdu)
The packet we sent was successfully received by the receiver (i.e.
Time m_fdBeaconIntervalNon6GHz
Time elapsing between a beacon and FILS Discovery (FD) frame or between two FD frames on 2....
std::map< uint16_t, Mac48Address > m_aidToMldOrLinkAddress
Maps AIDs to MLD addresses (for MLDs) or link addresses (in case of single link devices)
TracedCallback< uint16_t, Mac48Address > m_deAssocLogger
deassociation logger
void Enqueue(Ptr< WifiMpdu > mpdu, Mac48Address to, Mac48Address from) override
LinkIdStaAddrMap GetLinkIdStaAddrMap(MgtAssocResponseHeader &assoc, const Mac48Address &to, uint8_t linkId)
Get a map of (link ID, remote STA address) of the links to setup.
void SetAid(MgtAssocResponseHeader &assoc, const LinkIdStaAddrMap &linkIdStaAddrMap)
Set the AID field of the given Association Response frame.
static Ptr< const AttributeChecker > GetTimeAccessParamsChecker()
Get a checker for the TxopLimitsForSta attribute, which can be used to deserialize an ACI-indexed map...
bool m_enableBeaconGeneration
Flag whether beacons are being generated.
Time m_beaconInterval
Beacon interval.
bool m_enableBeaconJitter
Flag whether the first beacon should be generated at random time.
std::unordered_map< WifiAddressTidPair, BsrType, WifiAddressTidHash > m_bufferStatus
Per (MAC address, TID) buffer status reports.
void SendProbeResp(Mac48Address to, uint8_t linkId)
Send a Probe Response in response to a Probe Request received from the STA with the given address on ...
DsssParameterSet GetDsssParameterSet(uint8_t linkId) const
Return the DSSS Parameter Set that we support on the given link.
TracedCallback< uint16_t, Mac48Address > m_assocLogger
association logger
Time GetBeaconInterval() const
static TypeId GetTypeId()
Get the type ID.
std::optional< ReducedNeighborReport > GetReducedNeighborReport(uint8_t linkId) const
Return the Reduced Neighbor Report (RNR) element that the current AP sends on the given link,...
void ReceiveEmlOmn(MgtEmlOmn &frame, const Mac48Address &sender, uint8_t linkId)
Take necessary actions upon receiving the given EML Operating Mode Notification frame from the given ...
Ptr< ApEmlsrManager > GetApEmlsrManager() const
Time m_fdBeaconInterval6GHz
Time elapsing between a beacon and FILS Discovery (FD) frame or between two FD frames on 6GHz links.
uint8_t GetMaxBufferStatus(Mac48Address address) const
Return the maximum among the values of the Queue Size subfield of the last QoS Data or QoS Null frame...
Time m_bsrLifetime
Lifetime of Buffer Status Reports.
ApLinkEntity & GetLink(uint8_t linkId) const
Get a reference to the link associated with the given ID.
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...
uint8_t GetBufferStatus(uint8_t tid, Mac48Address address) const
Return the value of the Queue Size subfield of the last QoS Data or QoS Null frame received from the ...
EhtOperation GetEhtOperation(uint8_t linkId) const
Return the EHT operation of the current AP for the given link.
bool m_sendUnsolProbeResp
send unsolicited Probe Response instead of FILS Discovery
ErpInformation GetErpInformation(uint8_t linkId) const
Return the ERP information of the current AP for the given link.
void SetLinkUpCallback(Callback< void > linkUp) override
VhtOperation GetVhtOperation(uint8_t linkId) const
Return the VHT operation of the current AP for the given link.
~ApWifiMac() override
void TxFailed(WifiMacDropReason timeoutReason, Ptr< const WifiMpdu > mpdu)
The packet we sent was successfully received by the receiver (i.e.
HeOperation GetHeOperation(uint8_t linkId) const
Return the HE operation of the current AP for the given link.
void SetBufferStatus(uint8_t tid, Mac48Address address, uint8_t size)
Store the value of the Queue Size subfield of the last QoS Data or QoS Null frame received from the s...
TimeAccessParamsMap m_txopLimitsForSta
Per-AC TXOP limits values to advertise to stations.
int64_t AssignStreams(int64_t stream) override
Assign a fixed random variable stream number to the random variables used by this model.
std::optional< MuEdcaParameterSet > GetMuEdcaParameterSet() const
Return the MU EDCA Parameter Set of the current AP, if one needs to be advertised.
void ProcessPowerManagementFlag(Ptr< const WifiMpdu > mpdu, uint8_t linkId)
Process the Power Management bit in the Frame Control field of an MPDU successfully received on the g...
void DeaggregateAmsduAndForward(Ptr< const WifiMpdu > mpdu) override
This method is called to de-aggregate an A-MSDU and forward the constituent packets up the stack.
bool SupportsSendFrom() const override
MgtAssocResponseHeader GetAssocResp(Mac48Address to, uint8_t linkId)
Get the Association Response frame to send on a given link.
Ptr< WifiMpdu > GetFilsDiscovery(uint8_t linkId) const
Get the FILS Discovery frame to send on the given link.
static Ptr< const AttributeChecker > GetUintAccessParamsChecker()
Get a checker for the CwMinsForSta, CwMaxsForSta and AifsnsForSta attributes, which can be used to de...
void DoInitialize() override
Initialize() implementation.
std::optional< uint8_t > IsAssociated(const Mac48Address &address) const
Get the ID of a link (if any) that has been setup with the station having the given MAC address.
void SendOneBeacon(uint8_t linkId)
Forward a beacon packet to the beacon special DCF for transmission on the given link.
Ptr< WifiMacQueue > GetTxopQueue(AcIndex ac) const override
Get the wifi MAC queue of the (Qos)Txop associated with the given AC, if such (Qos)Txop is installed,...
void SetApEmlsrManager(Ptr< ApEmlsrManager > apEmlsrManager)
Set the AP EMLSR Manager.
bool GetUseNonErpProtection(uint8_t linkId) const
Return whether protection for non-ERP stations is used in the BSS corresponding to the given link.
UintAccessParamsMap m_cwMinsForSta
Per-AC CW min values to advertise to stations.
UintAccessParamsMap m_aifsnsForSta
Per-AC AIFS values to advertise to stations.
void StaSwitchingToPsMode(const Mac48Address &staAddr, uint8_t linkId)
Perform the necessary actions when a given station switches from active mode to powersave mode.
AllSupportedRates GetSupportedRates(uint8_t linkId) const
Return an instance of SupportedRates that contains all rates that we support for the given link (incl...
A container for one type of attribute.
AttributeValue implementation for Boolean.
Definition boolean.h:26
bool Get() const
Definition boolean.cc:44
Base class for Callback class.
Definition callback.h:344
Callback template class.
Definition callback.h:422
void SetEss()
Set the Extended Service Set (ESS) bit in the capability information field.
bool IsShortSlotTime() const
Check if the short slot time in the capability information field is set to 1.
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.
bool IsShortPreamble() const
Check if the short preamble bit in the capability information field is set to 1.
The DSSS Parameter Set.
void SetCurrentChannel(uint8_t currentChannel)
Set the Current Channel field in the DsssParameterSet information element.
The EDCA Parameter Set.
void SetViTxopLimit(uint16_t txop)
Set the AC_VI TXOP Limit field in the EdcaParameterSet information element.
void SetViAifsn(uint8_t aifsn)
Set the AC_VI AIFSN field in the EdcaParameterSet information element.
void SetVoAci(uint8_t aci)
Set the AC_VO ACI field in the EdcaParameterSet information element.
void SetVoCWmax(uint32_t cwMax)
Set the AC_VO CWmax field in the EdcaParameterSet information element.
void SetViCWmin(uint32_t cwMin)
Set the AC_VI CWmin field in the EdcaParameterSet information element.
void SetVoTxopLimit(uint16_t txop)
Set the AC_VO TXOP Limit field in the EdcaParameterSet information element.
void SetVoAifsn(uint8_t aifsn)
Set the AC_VO AIFSN field in the EdcaParameterSet information element.
void SetQosInfo(uint8_t qosInfo)
Set the QoS Info field in the EdcaParameterSet information element.
void SetBkCWmin(uint32_t cwMin)
Set the AC_BK CWmin field in the EdcaParameterSet information element.
void SetViAci(uint8_t aci)
Set the AC_VI ACI field in the EdcaParameterSet information element.
void SetViCWmax(uint32_t cwMax)
Set the AC_VI CWmax field in the EdcaParameterSet information element.
void SetVoCWmin(uint32_t cwMin)
Set the AC_VO CWmin field in the EdcaParameterSet information element.
void SetBeTxopLimit(uint16_t txop)
Set the AC_BE TXOP Limit field in the EdcaParameterSet information element.
void SetBeCWmax(uint32_t cwMax)
Set the AC_BE CWmax field in the EdcaParameterSet information element.
void SetBeAci(uint8_t aci)
Set the AC_BE ACI field in the EdcaParameterSet information element.
void SetBkCWmax(uint32_t cwMax)
Set the AC_BK CWmax field in the EdcaParameterSet information element.
void SetBkTxopLimit(uint16_t txop)
Set the AC_BK TXOP Limit field in the EdcaParameterSet information element.
void SetBkAifsn(uint8_t aifsn)
Set the AC_BK AIFSN field in the EdcaParameterSet information element.
void SetBeCWmin(uint32_t cwMin)
Set the AC_BE CWmin field in the EdcaParameterSet information element.
void SetBkAci(uint8_t aci)
Set the AC_BK ACI field in the EdcaParameterSet information element.
void SetBeAifsn(uint8_t aifsn)
Set the AC_BE AIFSN field in the EdcaParameterSet information element.
The IEEE 802.11be EHT Capabilities.
EHT Operation Information Element.
void SetMaxTxNss(uint8_t maxNss, uint8_t mcsStart, uint8_t mcsEnd)
Set the max Tx NSS for input MCS index range.
void SetMaxRxNss(uint8_t maxNss, uint8_t mcsStart, uint8_t mcsEnd)
Set the max Rx NSS for input MCS index range.
Hold variables of type enum.
Definition enum.h:52
T Get() const
Definition enum.h:87
The ErpInformation Information Element.
void SetBarkerPreambleMode(uint8_t barkerPreambleMode)
Set the Barker_Preamble_Mode field in the ErpInformation information element.
void SetUseProtection(uint8_t useProtection)
Set the Use_Protection field in the ErpInformation information element.
void SetNonErpPresent(uint8_t nonErpPresent)
Set the Non_Erp_Present field in the ErpInformation information element.
void Cancel()
This method is syntactic sugar for the ns3::Simulator::Cancel method.
Definition event-id.cc:44
The Extended Capabilities Information Element.
The Extended Supported Rates Information Element.
Implement the FILS (Fast Initial Link Setup) action frame.
uint16_t m_beaconInt
Beacon Interval in TU (1024 us)
std::optional< ReducedNeighborReport > m_rnr
Reduced Neighbor Report.
void SetSsid(const std::string &ssid)
Set the SSID field.
OptFieldWithPresenceInd< FdCapability > m_fdCap
FD Capability.
void SetLengthSubfield()
sets value of Length subfield
The HE 6 GHz Band Capabilities (IEEE 802.11ax-2021 9.4.2.263)
The IEEE 802.11ax HE Capabilities.
The HE Operation Information Element.
OptFieldWithPresenceInd< OpInfo6GHz > m_6GHzOpInfo
6 GHz Operation Information field
void SetMaxHeMcsPerNss(uint8_t nss, uint8_t maxHeMcs)
Set the Basic HE-MCS and NSS field in the HE Operation information element by specifying the pair (ns...
BssColorInfo m_bssColorInfo
BSS Color Information field.
The HT Capabilities Information Element.
The HT Operation Information Element.
void SetObssNonHtStasPresent(uint8_t obssNonHtStasPresent)
Set the OBSS non HT STAs present.
void SetRifsMode(uint8_t rifsMode)
Set the RIFS mode.
void SetSecondaryChannelOffset(uint8_t secondaryChannelOffset)
Set the secondary channel offset.
void SetPcoActive(uint8_t pcoActive)
Set the PCO active.
void SetTxUnequalModulation(uint8_t txUnequalModulation)
Set the transmit unequal modulation.
void SetHtProtection(uint8_t htProtection)
Set the HT protection.
void SetTxMaxNSpatialStreams(uint8_t maxTxSpatialStreams)
Set the transmit maximum number spatial streams.
void SetTxRxMcsSetUnequal(uint8_t txRxMcsSetUnequal)
Set the transmit / receive MCS set unequal.
void SetDualBeacon(uint8_t dualBeacon)
Set the dual beacon.
void SetNonGfHtStasPresent(uint8_t nonGfHtStasPresent)
Set the non GF HT STAs present.
void SetTxMcsSetDefined(uint8_t txMcsSetDefined)
Set the transmit MCS set defined.
void SetLSigTxopProtectionFullSupport(uint8_t lSigTxopProtectionFullSupport)
Set the LSIG TXOP protection full support.
void SetStaChannelWidth(uint8_t staChannelWidth)
Set the STA channel width.
void SetRxHighestSupportedDataRate(uint16_t maxSupportedRate)
Set the receive highest supported data rate.
void SetRxMcsBitmask(uint8_t index)
Set the receive MCS bitmask.
void SetPrimaryChannel(uint8_t ctrl)
Set the Primary Channel field in the HT Operation information element.
void SetDualCtsProtection(uint8_t dualCtsProtection)
Set the dual CTS protection.
void SetPhase(uint8_t pcoPhase)
Set the PCO phase.
void SetStbcBeacon(uint8_t stbcBeacon)
Set the STBC beacon.
Hold a signed integer type.
Definition integer.h:34
int64_t Get() const
Definition integer.cc:26
an EUI-48 address
bool IsGroup() const
static Mac48Address GetBroadcast()
bool IsBroadcast() const
Implement the header for management frames of type association request.
Implement the header for management frames of type association and reassociation response.
CapabilityInformation & Capabilities()
StatusCode GetStatusCode()
Return the status code.
void SetStatusCode(StatusCode code)
Set the status code.
void SetAssociationId(uint16_t aid)
Set the association ID.
Implement the header for management frames of type beacon.
Implement the header for Action frames of type EML Operating Mode Notification.
EmlControl m_emlControl
EML Control field.
std::optional< EmlsrParamUpdate > m_emlsrParamUpdate
EMLSR Parameter Update field.
std::list< uint8_t > GetLinkBitmap() const
Implement the header for management frames of type probe request.
Implement the header for management frames of type probe response.
void SetBeaconIntervalUs(uint64_t us)
Set the beacon interval in microseconds unit.
CapabilityInformation & Capabilities()
Implement the header for management frames of type reassociation request.
The MU EDCA Parameter Set.
void SetMuCwMin(uint8_t aci, uint16_t cwMin)
Set the ECWmin subfield of the ECWmin/ECWmax field in the MU AC Parameter Record field corresponding ...
void SetMuEdcaTimer(uint8_t aci, Time timer)
Set the MU EDCA Timer field in the MU AC Parameter Record field corresponding to the given AC Index (...
void SetMuAifsn(uint8_t aci, uint8_t aifsn)
Set the AIFSN subfield of the ACI/AIFSN field in the MU AC Parameter Record field corresponding to th...
void SetQosInfo(uint8_t qosInfo)
Set the QoS Info field in the MuEdcaParameterSet information element.
Time GetMuEdcaTimer(uint8_t aci) const
Get the MU EDCA Timer value encoded in the MU AC Parameter Record field corresponding to the given AC...
void SetMuCwMax(uint8_t aci, uint16_t cwMax)
Set the ECWmax subfield of the ECWmin/ECWmax field in the MU AC Parameter Record field corresponding ...
bool TraceConnectWithoutContext(std::string name, const CallbackBase &cb)
Connect a TraceSource to a Callback without a context.
void Initialize()
Invoke DoInitialize on all Objects aggregated to this one.
Definition object.cc:203
void Dispose()
Dispose of this Object.
Definition object.cc:247
Smart pointer class similar to boost::intrusive_ptr.
The Reduced Neighbor Report element.
std::size_t GetNNbrApInfoFields() const
Get the number of Neighbor AP Information fields.
void SetMldParameters(std::size_t nbrApInfoId, std::size_t index, uint8_t mldId, uint8_t linkId, uint8_t changeSequence)
Set the MLD Parameters subfield of the i-th TBTT Information field of the given Neighbor AP Informati...
void SetShortSsid(std::size_t nbrApInfoId, std::size_t index, uint32_t shortSsid)
Set the Short SSID field of the i-th TBTT Information field of the given Neighbor AP Information fiel...
void SetBssid(std::size_t nbrApInfoId, std::size_t index, Mac48Address bssid)
Set the BSSID field of the i-th TBTT Information field of the given Neighbor AP Information field.
void SetPsd20MHz(std::size_t nbrApInfoId, std::size_t index, uint8_t psd20MHz)
Set the 20 MHz PSD field of the i-th TBTT Information field of the given Neighbor AP Information fiel...
void AddNbrApInfoField()
Add a Neighbor AP Information field.
void SetBssParameters(std::size_t nbrApInfoId, std::size_t index, uint8_t bssParameters)
Set the BSS Parameters field of the i-th TBTT Information field of the given Neighbor AP Information ...
void AddTbttInformationField(std::size_t nbrApInfoId)
Add a TBTT Information fields to the TBTT Information Set field of the given Neighbor AP Information ...
void SetOperatingChannel(std::size_t nbrApInfoId, const WifiPhyOperatingChannel &channel)
Set the Operating Class and the Channel Number fields of the given Neighbor AP Information field base...
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition simulator.h:560
static Time Now()
Return the current simulation virtual time.
Definition simulator.cc:197
static EventId ScheduleNow(FUNC f, Ts &&... args)
Schedule an event to expire Now.
Definition simulator.h:594
The IEEE 802.11 SSID Information Element.
Definition ssid.h:25
Status code for association response.
Definition status-code.h:21
bool IsSuccess() const
Return whether the status code is success.
void SetFailure()
Set success bit to 1 (failure).
void SetSuccess()
Set success bit to 0 (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
bool IsZero() const
Exactly equivalent to t == 0.
Definition nstime.h:304
int64_t GetMicroSeconds() const
Get an approximation of the time stored in this instance in the indicated unit.
Definition nstime.h:402
AttributeValue implementation for Time.
Definition nstime.h:1395
Time Get() const
Definition time.cc:519
int64_t AssignStreams(int64_t stream)
Assign a fixed random variable stream number to the random variables used by this model.
Definition txop.cc:674
void SetMaxCws(const std::vector< uint32_t > &maxCws)
Set the maximum contention window size for each link.
Definition txop.cc:319
void SetAifsns(const std::vector< uint8_t > &aifsns)
Set the number of slots that make up an AIFS for each link.
Definition txop.cc:430
Ptr< WifiMacQueue > GetWifiMacQueue() const
Return the packet queue associated with this Txop.
Definition txop.cc:264
virtual void SetWifiMac(const Ptr< WifiMac > mac)
Set the wifi MAC this Txop is associated to.
Definition txop.cc:242
virtual void Queue(Ptr< WifiMpdu > mpdu)
Definition txop.cc:612
void SetTxMiddle(const Ptr< MacTxMiddle > txMiddle)
Set MacTxMiddle this Txop is associated to.
Definition txop.cc:235
void SetMinCws(const std::vector< uint32_t > &minCws)
Set the minimum contention window size for each link.
Definition txop.cc:276
a unique identifier for an interface.
Definition type-id.h:48
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition type-id.cc:1001
Hold an unsigned integer type.
Definition uinteger.h:34
uint64_t Get() const
Definition uinteger.cc:26
The IEEE 802.11ac VHT Capabilities.
The VHT Operation Information Element.
void SetMaxVhtMcsPerNss(uint8_t nss, uint8_t maxVhtMcs)
Set the Basic VHT-MCS and NSS field in the VHT Operation information element by specifying the tuple ...
void SetChannelWidth(uint8_t channelWidth)
Set the Channel Width field in the VHT Operation information element.
void SetChannelCenterFrequencySegment1(uint8_t channelCenterFrequencySegment1)
Set the Channel Center Frequency Segment 1 field in the VHT Operation information element.
void SetChannelCenterFrequencySegment0(uint8_t channelCenterFrequencySegment0)
Set the Channel Center Frequency Segment 0 field in the VHT Operation information element.
See IEEE 802.11 chapter 7.3.1.11 Header format: | category: 1 | action value: 1 |.
static std::pair< CategoryValue, ActionValue > Peek(Ptr< const Packet > pkt)
Peek an Action header from the given packet.
static std::pair< CategoryValue, ActionValue > Remove(Ptr< Packet > pkt)
Remove an Action header from the given packet.
void SetAction(CategoryValue type, ActionValue action)
Set action for this Action header.
Implements the IEEE 802.11 MAC header.
uint8_t GetQosTid() const
Return the Traffic ID of a QoS header.
bool IsAssocReq() const
Return true if the header is an Association Request header.
bool IsProbeReq() const
Return true if the header is a Probe Request header.
bool IsQosAmsdu() const
Check if the A-MSDU present bit is set in the QoS control field.
Mac48Address GetAddr3() const
Return the address in the Address 3 field.
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 IsMgt() const
Return true if the Type is Management.
void SetDsNotFrom()
Un-set the From DS bit in the Frame Control field.
bool IsAction() const
Return true if the header is an Action 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 IsReassocReq() const
Return true if the header is a Reassociation Request header.
bool IsData() const
Return true if the Type is DATA.
bool IsReassocResp() const
Return true if the header is a Reassociation Response header.
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.
base class for all MAC-level wifi objects.
Definition wifi-mac.h:89
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:996
Ptr< QosTxop > GetBEQueue() const
Accessor for the AC_BE channel access function.
Definition wifi-mac.cc:637
std::optional< Mac48Address > GetMldAddress(const Mac48Address &remoteAddr) const
Definition wifi-mac.cc:1798
Ptr< HeConfiguration > GetHeConfiguration() const
Definition wifi-mac.cc:1915
const std::map< uint8_t, std::unique_ptr< LinkEntity > > & GetLinks() const
Definition wifi-mac.cc:1072
Ptr< Txop > GetTxop() const
Accessor for the Txop object.
Definition wifi-mac.cc:557
VhtCapabilities GetVhtCapabilities(uint8_t linkId) const
Return the VHT capabilities of the device for the given link.
Definition wifi-mac.cc:2184
bool GetQosSupported() const
Return whether the device supports QoS.
Definition wifi-mac.cc:1380
uint8_t GetNLinks() const
Get the number of links (can be greater than 1 for 11be devices only).
Definition wifi-mac.cc:1087
void Enqueue(Ptr< Packet > packet, Mac48Address to)
Definition wifi-mac.cc:1673
void DoInitialize() override
Initialize() implementation.
Definition wifi-mac.cc:403
void UnblockUnicastTxOnLinks(WifiQueueBlockedReason reason, const Mac48Address &address, const std::set< uint8_t > &linkIds)
Unblock the transmission on the given links of all unicast frames addressed to the station with the g...
Definition wifi-mac.cc:1591
Ssid GetSsid() const
Definition wifi-mac.cc:519
bool GetErpSupported(uint8_t linkId) const
Return whether the device supports ERP on the given link.
Definition wifi-mac.cc:1386
Ptr< QosTxop > GetVOQueue() const
Accessor for the AC_VO channel access function.
Definition wifi-mac.cc:625
void SetTypeOfStation(TypeOfStation type)
This method is invoked by a subclass to specify what type of station it is implementing.
Definition wifi-mac.cc:469
Ptr< WifiPhy > GetWifiPhy(uint8_t linkId=SINGLE_LINK_OP_ID) const
Definition wifi-mac.cc:1348
void BlockUnicastTxOnLinks(WifiQueueBlockedReason reason, const Mac48Address &address, const std::set< uint8_t > &linkIds)
Block the transmission on the given links of all unicast frames addressed to the station with the giv...
Definition wifi-mac.cc:1545
bool GetEhtSupported() const
Return whether the device supports EHT.
Definition wifi-mac.cc:1948
bool GetHeSupported() const
Return whether the device supports HE.
Definition wifi-mac.cc:1942
HtCapabilities GetHtCapabilities(uint8_t linkId) const
Return the HT capabilities of the device for the given link.
Definition wifi-mac.cc:2125
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:1115
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:1463
Ptr< EhtConfiguration > GetEhtConfiguration() const
Definition wifi-mac.cc:1921
bool GetVhtSupported(uint8_t linkId) const
Return whether the device supports VHT on the given link.
Definition wifi-mac.cc:1934
Ptr< MacTxMiddle > m_txMiddle
TX middle (aggregation etc.)
Definition wifi-mac.h:937
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:374
Ptr< HtConfiguration > GetHtConfiguration() const
Definition wifi-mac.cc:1903
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:1246
ExtendedCapabilities GetExtendedCapabilities() const
Return the extended capabilities of the device.
Definition wifi-mac.cc:2116
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:2324
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:649
bool GetShortSlotTimeSupported() const
Definition wifi-mac.cc:1430
void NotifyRxDrop(Ptr< const Packet > packet)
Definition wifi-mac.cc:723
virtual void SetLinkUpCallback(Callback< void > linkUp)
Definition wifi-mac.cc:1449
Ptr< WifiRemoteStationManager > GetWifiRemoteStationManager(uint8_t linkId=0) const
Definition wifi-mac.cc:1060
bool GetHtSupported(uint8_t linkId) const
Return whether the device supports HT on the given link.
Definition wifi-mac.cc:1927
void ForwardUp(Ptr< const Packet > packet, Mac48Address from, Mac48Address to)
Forward the packet up to the device.
Definition wifi-mac.cc:1749
bool Is6GhzBand(uint8_t linkId) const
Indicate if a given link is on the 6 GHz band.
Definition wifi-mac.cc:1238
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:1756
Mac48Address GetAddress() const
Definition wifi-mac.cc:506
EhtCapabilities GetEhtCapabilities(uint8_t linkId) const
Return the EHT capabilities of the device for the given link.
Definition wifi-mac.cc:2359
LinkEntity & GetLink(uint8_t linkId) const
Get a reference to the link associated with the given ID.
Definition wifi-mac.cc:1078
HeCapabilities GetHeCapabilities(uint8_t linkId) const
Return the HE capabilities of the device for the given link.
Definition wifi-mac.cc:2266
Ptr< QosTxop > GetQosTxop(AcIndex ac) const
Accessor for a specified EDCA object.
Definition wifi-mac.cc:603
void DoDispose() override
Destructor implementation.
Definition wifi-mac.cc:427
bool GetDsssSupported(uint8_t linkId) const
Return whether the device supports DSSS on the given link.
Definition wifi-mac.cc:1410
represent a single transmission mode
Definition wifi-mode.h:40
const std::string & GetUniqueName() const
Definition wifi-mode.cc:137
uint64_t GetDataRate(MHz_u channelWidth, Time guardInterval, uint8_t nss) const
Definition wifi-mode.cc:111
uint8_t GetMcsValue() const
Definition wifi-mode.cc:152
std::list< uint8_t > GetBssMembershipSelectorList() const
The WifiPhy::BssMembershipSelector() method is used (e.g., by a WifiRemoteStationManager) to determin...
Definition wifi-phy.cc:1402
uint8_t GetMaxSupportedRxSpatialStreams() const
Definition wifi-phy.cc:1396
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
Definition wifi-phy.cc:1587
void SetSlot(Time slot)
Set the slot duration for this PHY.
Definition wifi-phy.cc:835
MHz_u GetChannelWidth() const
Definition wifi-phy.cc:1099
std::list< WifiMode > GetModeList() const
The WifiPhy::GetModeList() method is used (e.g., by a WifiRemoteStationManager) to determine the set ...
Definition wifi-phy.cc:2064
const WifiPhyOperatingChannel & GetOperatingChannel() const
Get a const reference to the operating channel.
Definition wifi-phy.cc:1081
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< AttributeChecker > MakeAttributeContainerChecker()
Make uninitialized AttributeContainerChecker using explicit types.
Ptr< const AttributeAccessor > MakeAttributeContainerAccessor(T1 a1)
Make AttributeContainerAccessor using explicit types.
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< 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:1396
Ptr< const AttributeChecker > MakeTimeChecker()
Helper to make an unbounded Time checker.
Definition nstime.h:1416
Ptr< const AttributeChecker > MakeUintegerChecker()
Definition uinteger.h:85
#define NS_ABORT_MSG_UNLESS(cond, msg)
Abnormal program termination if a condition is false, with a message.
Definition abort.h:133
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
#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_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_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition log.h:264
Ptr< T > CreateObjectWithAttributes(Args... args)
Allocate an Object on the heap and initialize with a set of attributes.
#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:1332
Time NanoSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1344
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1320
Ptr< const TraceSourceAccessor > MakeTraceSourceAccessor(T a)
Create a TraceSourceAccessor which will control access to the underlying trace source.
WifiMacDropReason
The reason why an MPDU was dropped.
Definition wifi-mac.h:70
AcIndex
This enumeration defines the Access Categories as an enumeration with values corresponding to the AC ...
Definition qos-utils.h:62
@ AP
Definition wifi-mac.h:59
@ WIFI_STANDARD_80211ax
@ WIFI_PHY_BAND_6GHZ
The 6 GHz band.
@ WIFI_MOD_CLASS_HR_DSSS
HR/DSSS (Clause 16)
@ WIFI_MOD_CLASS_HT
HT (Clause 19)
@ WIFI_MOD_CLASS_EHT
EHT (Clause 36)
@ 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
@ AC_BEACON
Beacon queue.
Definition qos-utils.h:74
Every class exported by the ns3 library is enclosed in the ns3 namespace.
U * PeekPointer(const Ptr< U > &p)
Definition ptr.h:443
std::unordered_map< uint16_t, Ptr< const WifiPsdu > > WifiConstPsduMap
Map of const PSDUs indexed by STA-ID.
const Time WIFI_TU
Wi-Fi Time Unit (see IEEE 802.11-2020 sec. 3.1)
Definition wifi-utils.cc:22
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
@ NO_PROTECTION
@ MIXED_MODE_PROTECTION
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
std::map< uint8_t, std::set< uint8_t > > WifiTidLinkMapping
TID-indexed map of the link set to which the TID is mapped.
Definition wifi-utils.h:65
constexpr uint8_t WIFI_EHT_MAX_MCS_INDEX
IEEE 802.11be D2.0 Figure 9-1002ai.
WifiMacType
Combination of valid MAC header type/subtype.
@ WIFI_MAC_MGT_BEACON
@ WIFI_MAC_MGT_ACTION
@ WIFI_MAC_MGT_ASSOCIATION_RESPONSE
@ WIFI_MAC_MGT_DISASSOCIATION
@ 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.
Ptr< T1 > StaticCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:587
std::pair< Mac48Address, uint8_t > WifiAddressTidPair
(MAC address, TID) pair
Definition qos-utils.h:25
std::variant< std::reference_wrapper< MgtAssocRequestHeader >, std::reference_wrapper< MgtReassocRequestHeader > > AssocReqRefVariant
variant holding a reference to a (Re)Association Request
Definition ap-wifi-mac.h:48
static constexpr uint16_t SU_STA_ID
STA_ID to identify a single user (SU)
Definition wifi-mode.h:24
Struct containing all supported rates.
void SetBasicRate(uint64_t bs)
Set the given rate to basic 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.
void SetMediumSyncDelayTimer(Time delay)
Set the Medium Synchronization Duration subfield of the Medium Synchronization Delay Information in t...
FD Capability subfield of FILS Discovery Information field.
6 GHz Operation Information field
uint8_t m_chCntrFreqSeg0
Channel center frequency segment 0.
uint8_t m_chWid
Channel Width.
uint8_t m_chCntrFreqSeg1
Channel center frequency segment 1.
uint8_t m_primCh
Primary Channel.
uint8_t emlsrParamUpdateCtrl
EMLSR Parameter Update Control.
typedef for union of different ActionValues