diff --git a/src/wifi/helper/wifi-mac-helper.cc b/src/wifi/helper/wifi-mac-helper.cc index d7d95d743..9eccfa138 100644 --- a/src/wifi/helper/wifi-mac-helper.cc +++ b/src/wifi/helper/wifi-mac-helper.cc @@ -23,6 +23,7 @@ #include "ns3/frame-exchange-manager.h" #include "ns3/wifi-protection-manager.h" #include "ns3/wifi-ack-manager.h" +#include "ns3/wifi-assoc-manager.h" #include "ns3/multi-user-scheduler.h" #include "ns3/boolean.h" @@ -33,6 +34,7 @@ WifiMacHelper::WifiMacHelper () //By default, we create an AdHoc MAC layer (without QoS). SetType ("ns3::AdhocWifiMac"); + m_assocManager.SetTypeId ("ns3::WifiDefaultAssocManager"); m_protectionManager.SetTypeId ("ns3::WifiDefaultProtectionManager"); m_ackManager.SetTypeId ("ns3::WifiDefaultAckManager"); } @@ -98,6 +100,13 @@ WifiMacHelper::Create (Ptr device, WifiStandard standard) const apMac->AggregateObject (muScheduler); } + // create and install the Association Manager if this is a STA + if (auto staMac = DynamicCast (mac); staMac != nullptr) + { + Ptr assocManager = m_assocManager.Create (); + staMac->SetAssocManager (assocManager); + } + return mac; } diff --git a/src/wifi/helper/wifi-mac-helper.h b/src/wifi/helper/wifi-mac-helper.h index e58e8eec7..dbfd396f1 100644 --- a/src/wifi/helper/wifi-mac-helper.h +++ b/src/wifi/helper/wifi-mac-helper.h @@ -68,6 +68,16 @@ public: template void SetType (std::string type, Args&&... args); + /** + * Helper function used to set the Association Manager. + * + * \tparam Args \deduced Template type parameter pack for the sequence of name-value pairs. + * \param type the type of Association Manager + * \param args A sequence of name-value pairs of the attributes to set. + */ + template + void SetAssocManager (std::string type, Args&&... args); + /** * Helper function used to set the Protection Manager. * @@ -111,6 +121,7 @@ public: protected: ObjectFactory m_mac; ///< MAC object factory + ObjectFactory m_assocManager; ///< Association Manager ObjectFactory m_protectionManager; ///< Factory to create a protection manager ObjectFactory m_ackManager; ///< Factory to create an acknowledgment manager ObjectFactory m_muScheduler; ///< Multi-user Scheduler object factory @@ -133,6 +144,14 @@ WifiMacHelper::SetType (std::string type, Args&&... args) m_mac.Set (args...); } +template +void +WifiMacHelper::SetAssocManager (std::string type, Args&&... args) +{ + m_assocManager.SetTypeId (type); + m_assocManager.Set (args...); +} + template void WifiMacHelper::SetProtectionManager (std::string type, Args&&... args) diff --git a/src/wifi/model/sta-wifi-mac.cc b/src/wifi/model/sta-wifi-mac.cc index d0dd06429..faeccfe5d 100644 --- a/src/wifi/model/sta-wifi-mac.cc +++ b/src/wifi/model/sta-wifi-mac.cc @@ -32,6 +32,7 @@ #include "wifi-phy.h" #include "mgt-headers.h" #include "snr-tag.h" +#include "wifi-assoc-manager.h" #include "wifi-net-device.h" #include "ns3/ht-configuration.h" #include "ns3/he-configuration.h" @@ -99,8 +100,6 @@ StaWifiMac::GetTypeId (void) StaWifiMac::StaWifiMac () : m_state (UNASSOCIATED), m_aid (0), - m_waitBeaconEvent (), - m_probeRequestEvent (), m_assocRequestEvent (), m_beaconWatchdogEnd (Seconds (0)) { @@ -118,6 +117,18 @@ StaWifiMac::DoInitialize (void) StartScanning (); } +void +StaWifiMac::DoDispose (void) +{ + NS_LOG_FUNCTION (this); + if (m_assocManager) + { + m_assocManager->Dispose (); + } + m_assocManager = nullptr; + WifiMac::DoDispose (); +} + StaWifiMac::~StaWifiMac () { NS_LOG_FUNCTION (this); @@ -131,6 +142,14 @@ StaWifiMac::AssignStreams (int64_t stream) return 1; } +void +StaWifiMac::SetAssocManager (Ptr assocManager) +{ + NS_LOG_FUNCTION (this << assocManager); + m_assocManager = assocManager; + m_assocManager->SetStaWifiMac (this); +} + uint16_t StaWifiMac::GetAssociationId (void) const { @@ -167,6 +186,15 @@ StaWifiMac::SetWifiPhys (const std::vector>& phys) } } +WifiScanParams::Channel +StaWifiMac::GetCurrentChannel (uint8_t linkId) const +{ + auto phy = GetWifiPhy (linkId); + uint16_t width = phy->GetOperatingChannel ().IsOfdm () ? 20 : phy->GetChannelWidth (); + uint8_t ch = phy->GetOperatingChannel ().GetPrimaryChannelNumber (width, phy->GetStandard ()); + return {ch, phy->GetPhyBand ()}; +} + void StaWifiMac::SendProbeRequest (void) { @@ -354,68 +382,68 @@ void StaWifiMac::StartScanning (void) { NS_LOG_FUNCTION (this); - m_candidateAps.clear (); - if (m_probeRequestEvent.IsRunning ()) + SetState (SCANNING); + NS_ASSERT (m_assocManager); + + WifiScanParams scanParams; + scanParams.ssid = GetSsid (); + for (uint8_t linkId = 0; linkId < GetNLinks (); linkId++) { - m_probeRequestEvent.Cancel (); + WifiScanParams::ChannelList channel {(GetWifiPhy (linkId)->HasFixedPhyBand ()) + ? WifiScanParams::Channel {0, GetWifiPhy (linkId)->GetPhyBand ()} + : WifiScanParams::Channel {0, WIFI_PHY_BAND_UNSPECIFIED}}; + + scanParams.channelList.push_back (channel); } - if (m_waitBeaconEvent.IsRunning ()) + if (m_activeProbing) { - m_waitBeaconEvent.Cancel (); - } - if (GetActiveProbing ()) - { - SendProbeRequest (); - m_probeRequestEvent = Simulator::Schedule (m_probeRequestTimeout, - &StaWifiMac::ScanningTimeout, - this); + scanParams.type = WifiScanParams::ACTIVE; + scanParams.probeDelay = MicroSeconds (m_probeDelay->GetValue ()); + scanParams.minChannelTime = scanParams.maxChannelTime = m_probeRequestTimeout; } else { - m_waitBeaconEvent = Simulator::Schedule (m_waitBeaconTimeout, - &StaWifiMac::ScanningTimeout, - this); + scanParams.type = WifiScanParams::PASSIVE; + scanParams.maxChannelTime = m_waitBeaconTimeout; } - SetState (SCANNING); + + m_assocManager->StartScanning (std::move (scanParams)); } void -StaWifiMac::ScanningTimeout (void) +StaWifiMac::ScanningTimeout (const std::optional& bestAp) { NS_LOG_FUNCTION (this); - if (!m_candidateAps.empty ()) - { - ApInfo bestAp = m_candidateAps.front(); - m_candidateAps.erase(m_candidateAps.begin ()); - NS_LOG_DEBUG ("Attempting to associate with BSSID " << bestAp.m_bssid); - UpdateApInfo (bestAp.m_frame, bestAp.m_apAddr, bestAp.m_bssid, bestAp.m_linkId); - // lambda to get beacon interval from Beacon or Probe Response - auto getBeaconInterval = - [](auto&& frame) - { - using T = std::decay_t; - if constexpr (std::is_same_v - || std::is_same_v) - { - return MicroSeconds (frame.GetBeaconIntervalUs ()); - } - else - { - NS_ABORT_MSG ("Unexpected frame type"); - return Seconds (0); - } - }; - Time beaconInterval = std::visit (getBeaconInterval, bestAp.m_frame); - Time delay = beaconInterval * m_maxMissedBeacons; - RestartBeaconWatchdog (delay); - SetState (WAIT_ASSOC_RESP); - SendAssociationRequest (false); - } - else + + if (!bestAp.has_value ()) { NS_LOG_DEBUG ("Exhausted list of candidate AP; restart scanning"); StartScanning (); + return; } + + NS_LOG_DEBUG ("Attempting to associate with BSSID " << bestAp->m_bssid); + UpdateApInfo (bestAp->m_frame, bestAp->m_apAddr, bestAp->m_bssid, bestAp->m_linkId); + // lambda to get beacon interval from Beacon or Probe Response + auto getBeaconInterval = + [](auto&& frame) + { + using T = std::decay_t; + if constexpr (std::is_same_v || std::is_same_v) + { + return MicroSeconds (frame.GetBeaconIntervalUs ()); + } + else + { + NS_ABORT_MSG ("Unexpected frame type"); + return Seconds (0); + } + }; + Time beaconInterval = std::visit (getBeaconInterval, bestAp->m_frame); + Time delay = beaconInterval * m_maxMissedBeacons; + RestartBeaconWatchdog (delay); + SetState (WAIT_ASSOC_RESP); + SendAssociationRequest (false); } void @@ -645,10 +673,9 @@ StaWifiMac::Receive (Ptr mpdu, uint8_t linkId) } else { - // we retain this Beacon as candidate AP if the SSID matches ours and the - // supported rates fit the configured BSS membership selector - goodBeacon = ((GetSsid ().IsBroadcast () || beacon.GetSsid ().IsEqual (GetSsid ())) - && CheckSupportedRates (beacon, linkId)); + // we retain this Beacon as candidate AP if the supported rates fit the + // configured BSS membership selector + goodBeacon = CheckSupportedRates (beacon, linkId); } if (!goodBeacon) @@ -663,9 +690,9 @@ StaWifiMac::Receive (Ptr mpdu, uint8_t linkId) RestartBeaconWatchdog (delay); UpdateApInfo (beacon, hdr->GetAddr2 (), hdr->GetAddr3 (), linkId); } - else if (m_state == SCANNING) + else { - NS_LOG_DEBUG ("Beacon received while scanning from " << hdr->GetAddr2 ()); + NS_LOG_DEBUG ("Beacon received from " << hdr->GetAddr2 ()); SnrTag snrTag; bool removed = copy->RemovePacketTag (snrTag); NS_ASSERT (removed); @@ -673,36 +700,34 @@ StaWifiMac::Receive (Ptr mpdu, uint8_t linkId) apInfo.m_apAddr = hdr->GetAddr2 (); apInfo.m_bssid = hdr->GetAddr3 (); apInfo.m_snr = snrTag.Get (); - apInfo.m_frame = beacon; + apInfo.m_frame = std::move (beacon); apInfo.m_linkId = linkId; - UpdateCandidateApList (apInfo); + apInfo.m_channel = {GetCurrentChannel (linkId)}; + m_assocManager->NotifyApInfo (std::move (apInfo)); } return; } else if (hdr->IsProbeResp ()) { - if (m_state == SCANNING) + NS_LOG_DEBUG ("Probe response received from " << hdr->GetAddr2 ()); + MgtProbeResponseHeader probeResp; + Ptr copy = packet->Copy (); + copy->RemoveHeader (probeResp); + if (!CheckSupportedRates (probeResp, linkId)) { - NS_LOG_DEBUG ("Probe response received while scanning from " << hdr->GetAddr2 ()); - MgtProbeResponseHeader probeResp; - Ptr copy = packet->Copy (); - copy->RemoveHeader (probeResp); - if ((!GetSsid ().IsBroadcast () && !probeResp.GetSsid ().IsEqual (GetSsid ())) - || !CheckSupportedRates (probeResp, linkId)) - { - return; - } - SnrTag snrTag; - bool removed = copy->RemovePacketTag (snrTag); - NS_ASSERT (removed); - ApInfo apInfo; - apInfo.m_apAddr = hdr->GetAddr2 (); - apInfo.m_bssid = hdr->GetAddr3 (); - apInfo.m_snr = snrTag.Get (); - apInfo.m_frame = probeResp; - apInfo.m_linkId = linkId; - UpdateCandidateApList (apInfo); + return; } + SnrTag snrTag; + bool removed = copy->RemovePacketTag (snrTag); + NS_ASSERT (removed); + ApInfo apInfo; + apInfo.m_apAddr = hdr->GetAddr2 (); + apInfo.m_bssid = hdr->GetAddr3 (); + apInfo.m_snr = snrTag.Get (); + apInfo.m_frame = std::move (probeResp); + apInfo.m_linkId = linkId; + apInfo.m_channel = {GetCurrentChannel (linkId)}; + m_assocManager->NotifyApInfo (std::move (apInfo)); return; } else if (hdr->IsAssocResp () || hdr->IsReassocResp ()) @@ -736,14 +761,8 @@ StaWifiMac::Receive (Ptr mpdu, uint8_t linkId) else { NS_LOG_DEBUG ("association refused"); - if (m_candidateAps.empty ()) - { - SetState (REFUSED); - } - else - { - ScanningTimeout (); - } + SetState (REFUSED); + StartScanning (); } } return; @@ -782,32 +801,6 @@ StaWifiMac::CheckSupportedRates (std::variant::iterator i = m_candidateAps.begin(); i != m_candidateAps.end(); ++i) - { - if (newApInfo.m_bssid == (*i).m_bssid) - { - m_candidateAps.erase(i); - break; - } - } - // Insert before the entry with lower SNR - for (std::vector::iterator i = m_candidateAps.begin(); i != m_candidateAps.end(); ++i) - { - if (newApInfo.m_snr > (*i).m_snr) - { - m_candidateAps.insert (i, newApInfo); - return; - } - } - // If new ApInfo is the lowest, insert at back - m_candidateAps.push_back(newApInfo); -} - void StaWifiMac::UpdateApInfo (const MgtFrameType& frame, const Mac48Address& apAddr, const Mac48Address& bssid, uint8_t linkId) diff --git a/src/wifi/model/sta-wifi-mac.h b/src/wifi/model/sta-wifi-mac.h index 38853b534..b4186d6b1 100644 --- a/src/wifi/model/sta-wifi-mac.h +++ b/src/wifi/model/sta-wifi-mac.h @@ -36,6 +36,7 @@ namespace ns3 { class SupportedRates; class CapabilityInformation; class RandomVariableStream; +class WifiAssocManager; /** @@ -168,6 +169,13 @@ public: */ void SetWifiPhys (const std::vector>& phys) override; + /** + * Set the Association Manager. + * + * \param assocManager the Association Manager + */ + void SetAssocManager (Ptr assocManager); + /** * Forward a probe request packet to the DCF. The standard is not clear on the correct * queue for management frames if QoS is supported. We always use the DCF. @@ -178,8 +186,10 @@ public: * This method is called after wait beacon timeout or wait probe request timeout has * occurred. This will trigger association process from beacons or probe responses * gathered while scanning. + * + * \param bestAp the info about the best AP to associate with, if one was found */ - void ScanningTimeout (void); + void ScanningTimeout (const std::optional& bestAp); /** * Return whether we are associated with an AP. @@ -260,14 +270,6 @@ private: void UpdateApInfo (const MgtFrameType& frame, const Mac48Address& apAddr, const Mac48Address& bssid, uint8_t linkId); - /** - * Update list of candidate AP to associate. The list should contain ApInfo sorted from - * best to worst SNR, with no duplicate. - * - * \param newApInfo the new ApInfo to be inserted - */ - void UpdateCandidateApList (ApInfo newApInfo); - /** * Forward an association or reassociation request packet to the DCF. * The standard is not clear on the correct queue for management frames if QoS is supported. @@ -357,15 +359,24 @@ private: */ void PhyCapabilitiesChanged (void); + /** + * Get the current primary20 channel used on the given link as a + * (channel number, PHY band) pair. + * + * \param linkId the ID of the given link + * \return a (channel number, PHY band) pair + */ + WifiScanParams::Channel GetCurrentChannel (uint8_t linkId) const; + void DoInitialize (void) override; + void DoDispose (void) override; MacState m_state; ///< MAC state uint16_t m_aid; ///< Association AID + Ptr m_assocManager; ///< Association Manager Time m_waitBeaconTimeout; ///< wait beacon timeout Time m_probeRequestTimeout; ///< probe request timeout Time m_assocRequestTimeout; ///< association request timeout - EventId m_waitBeaconEvent; ///< wait beacon event - EventId m_probeRequestEvent; ///< probe request event EventId m_assocRequestEvent; ///< association request event EventId m_beaconWatchdog; ///< beacon watchdog Time m_beaconWatchdogEnd; ///< beacon watchdog end @@ -373,11 +384,6 @@ private: bool m_activeProbing; ///< active probing Ptr m_probeDelay; ///< RandomVariable used to randomize the time ///< of the first Probe Response on each channel - std::vector m_candidateAps; ///< list of candidate APs to associate to - // Note: std::multiset might be a candidate container to implement - // this sorted list, but we are using a std::vector because we want to sort - // based on SNR but find duplicates based on BSSID, and in practice this - // candidate vector should not be too large. TracedCallback m_assocLogger; ///< association logger TracedCallback m_deAssocLogger; ///< disassociation logger diff --git a/src/wifi/model/wifi-assoc-manager.cc b/src/wifi/model/wifi-assoc-manager.cc index ea404728e..608509756 100644 --- a/src/wifi/model/wifi-assoc-manager.cc +++ b/src/wifi/model/wifi-assoc-manager.cc @@ -226,7 +226,7 @@ WifiAssocManager::ScanningTimeout (void) { if (m_apList.empty ()) { - m_mac->ScanningTimeout (/*std::nullopt*/); + m_mac->ScanningTimeout (std::nullopt); return; } @@ -234,7 +234,7 @@ WifiAssocManager::ScanningTimeout (void) m_apListIt.erase (bestAp.m_bssid); } while (!CanBeReturned (bestAp)); - m_mac->ScanningTimeout (/*std::move (bestAp)*/); + m_mac->ScanningTimeout (std::move (bestAp)); } } //namespace ns3 diff --git a/src/wifi/test/wifi-primary-channels-test.cc b/src/wifi/test/wifi-primary-channels-test.cc index d8925d7da..0887c2af6 100644 --- a/src/wifi/test/wifi-primary-channels-test.cc +++ b/src/wifi/test/wifi-primary-channels-test.cc @@ -436,13 +436,18 @@ WifiPrimaryChannelsTest::DoSetup (void) void WifiPrimaryChannelsTest::DoRun (void) { - // schedule association requests at different times + // schedule association requests at different times. One station's SSID is + // set to the correct value before initialization, so that such a station + // starts the scanning procedure by looking for the correct SSID Ptr dev; - for (uint16_t i = 0; i < m_nStationsPerBss; i++) + // association can be done in parallel over the multiple BSSes + for (uint8_t bss = 0; bss < m_nBss; bss++) { - // association can be done in parallel over the multiple BSSes - for (uint8_t bss = 0; bss < m_nBss; bss++) + dev = DynamicCast (m_staDevices[bss].Get (0)); + dev->GetMac ()->SetSsid (Ssid ("wifi-ssid-" + std::to_string (bss))); + + for (uint16_t i = 1; i < m_nStationsPerBss; i++) { dev = DynamicCast (m_staDevices[bss].Get (i)); Simulator::Schedule (i * MicroSeconds (102400), &WifiMac::SetSsid, diff --git a/src/wifi/test/wifi-test.cc b/src/wifi/test/wifi-test.cc index c9b32b0c8..e9d986e01 100644 --- a/src/wifi/test/wifi-test.cc +++ b/src/wifi/test/wifi-test.cc @@ -53,6 +53,7 @@ #include "ns3/frame-exchange-manager.h" #include "ns3/wifi-default-protection-manager.h" #include "ns3/wifi-default-ack-manager.h" +#include "ns3/wifi-default-assoc-manager.h" using namespace ns3; @@ -160,6 +161,10 @@ WifiTest::CreateOne (Vector pos, Ptr channel) mac->SetAddress (Mac48Address::Allocate ()); dev->SetMac (mac); mac->ConfigureStandard (WIFI_STANDARD_80211a); + if (mac->GetTypeOfStation () == STA) + { + StaticCast (mac)->SetAssocManager (CreateObject ()); + } Ptr fem = mac->GetFrameExchangeManager (); Ptr protectionManager = CreateObject (); protectionManager->SetWifiMac (mac); @@ -1801,6 +1806,7 @@ Bug2831TestCase::DoRun (void) staMac->SetDevice (staDev); staMac->SetAddress (Mac48Address::Allocate ()); staMac->ConfigureStandard (WIFI_STANDARD_80211ax); + StaticCast (staMac)->SetAssocManager (CreateObject ()); fem = staMac->GetFrameExchangeManager (); protectionManager = CreateObject (); protectionManager->SetWifiMac (staMac); diff --git a/src/wifi/test/wifi-txop-test.cc b/src/wifi/test/wifi-txop-test.cc index 77c0a7ee2..0bf92bd8d 100644 --- a/src/wifi/test/wifi-txop-test.cc +++ b/src/wifi/test/wifi-txop-test.cc @@ -192,14 +192,16 @@ WifiTxopTest::DoRun (void) m_apDevices = wifi.Install (phy, mac, wifiApNode); - // schedule association requests at different times - Time init = MilliSeconds (100); - Ptr dev; + // schedule association requests at different times. One station's SSID is + // set to the correct value before initialization, so that such a station + // starts the scanning procedure by looking for the correct SSID + Ptr dev = DynamicCast (m_staDevices.Get (0)); + dev->GetMac ()->SetSsid (Ssid ("wifi-txop-ssid")); - for (uint16_t i = 0; i < m_nStations; i++) + for (uint16_t i = 1; i < m_nStations; i++) { dev = DynamicCast (m_staDevices.Get (i)); - Simulator::Schedule (init + i * MicroSeconds (102400), &WifiMac::SetSsid, + Simulator::Schedule (i * MicroSeconds (102400), &WifiMac::SetSsid, dev->GetMac (), Ssid ("wifi-txop-ssid")); }