From b9ed68da5f5507da83b00860e0cdcc821abc80e5 Mon Sep 17 00:00:00 2001 From: Muhammad Iqbal CR Date: Thu, 7 Jun 2018 12:48:30 -0700 Subject: [PATCH] wifi: (fixes #2399) Improve scanning procedure of StaWifiMac --- src/wifi/doc/source/wifi-design.rst | 30 +++-- src/wifi/model/sta-wifi-mac.cc | 199 +++++++++++++++++++++------- src/wifi/model/sta-wifi-mac.h | 102 ++++++++++---- src/wifi/test/wifi-test.cc | 178 +++++++++++++++++++++++++ 4 files changed, 423 insertions(+), 86 deletions(-) diff --git a/src/wifi/doc/source/wifi-design.rst b/src/wifi/doc/source/wifi-design.rst index 7eb3ece0a..cbca3e26a 100644 --- a/src/wifi/doc/source/wifi-design.rst +++ b/src/wifi/doc/source/wifi-design.rst @@ -485,25 +485,27 @@ Infrastructure association Association in infrastructure (IBSS) mode is a high-level MAC function. Either active probing or passive scanning is used (default is passive scan). At the start of the simulation, Wi-Fi network devices configured as -STA will listen for beacons on the same SSID and will attempt to associate -with the AP that generates the first received beacon that is considered -to be good. If active probing is enabled, STA will regularly send probe -request until it receive probe response and will similarly attempt to -associate with the AP that generates the first received probe response. +STA will attempt to scan the channel. Depends on whether passive or active +scanning is selected, STA will attempt to gather beacons, or send a probe +request and gather probe responses until the respective timeout occurs. The +end result will be a list of candidate AP to associate to. STA will then try +to associate to the best AP (i.e., best SNR). -If association is rejected by the AP for some reason, the STA will -move to a 'REFUSED' state and the simulation user will need to force a -reassociation retry in some way, perhaps by changing configuration -(i.e. the STA will not persistently try to associate upon a refusal). +If association is rejected by the AP for some reason, the STA will try to +associate to the next best AP until the candidate list is exhausted which +then sends STA to 'REFUSED' state. If this occurs, the simulation user will +need to force reassociation retry in some way, perhaps by changing +configuration (i.e. the STA will not persistently try to associate upon a +refusal). When associated, if the configuration is changed by the simulation user, the STA will try to reassociate with the existing AP. -If a number of missed beacons exceeds a threshold, the STA will notify -the rest of the device that the link is down (association is lost). If -configured for passive scanning, the STA will try to associate again with -the first good beacon heard. If in active probing, the STA will initiate -a new probe request. +If the number of missed beacons exceeds the threshold, the STA will notify +the rest of the device that the link is down (association is lost) and +restart the scanning process. Note that this can also happen when an +association request fails without explicit refusal (i.e., the AP fails to +respond to association request). Roaming ####### diff --git a/src/wifi/model/sta-wifi-mac.cc b/src/wifi/model/sta-wifi-mac.cc index b5ae50f6e..16675e3f3 100644 --- a/src/wifi/model/sta-wifi-mac.cc +++ b/src/wifi/model/sta-wifi-mac.cc @@ -27,6 +27,7 @@ #include "wifi-phy.h" #include "mac-low.h" #include "mgt-headers.h" +#include "snr-tag.h" namespace ns3 { @@ -41,10 +42,14 @@ StaWifiMac::GetTypeId (void) .SetParent () .SetGroupName ("Wifi") .AddConstructor () - .AddAttribute ("ProbeRequestTimeout", "The interval between two consecutive probe request attempts.", + .AddAttribute ("ProbeRequestTimeout", "The duration to actively probe the channel.", TimeValue (Seconds (0.05)), MakeTimeAccessor (&StaWifiMac::m_probeRequestTimeout), MakeTimeChecker ()) + .AddAttribute ("WaitBeaconTimeout", "The duration to dwell on a channel while passively scanning for beacon", + TimeValue (MilliSeconds (120)), + MakeTimeAccessor (&StaWifiMac::m_waitBeaconTimeout), + MakeTimeChecker ()) .AddAttribute ("AssocRequestTimeout", "The interval between two consecutive association request attempts.", TimeValue (Seconds (0.5)), MakeTimeAccessor (&StaWifiMac::m_assocRequestTimeout), @@ -75,7 +80,8 @@ StaWifiMac::GetTypeId (void) } StaWifiMac::StaWifiMac () - : m_state (BEACON_MISSED), + : m_state (UNASSOCIATED), + m_waitBeaconEvent (), m_probeRequestEvent (), m_assocRequestEvent (), m_beaconWatchdogEnd (Seconds (0)) @@ -87,6 +93,13 @@ StaWifiMac::StaWifiMac () SetTypeOfStation (STA); } +void +StaWifiMac::DoInitialize (void) +{ + NS_LOG_FUNCTION (this); + StartScanning (); +} + StaWifiMac::~StaWifiMac () { NS_LOG_FUNCTION (this); @@ -96,15 +109,12 @@ void StaWifiMac::SetActiveProbing (bool enable) { NS_LOG_FUNCTION (this << enable); - if (enable) - { - Simulator::ScheduleNow (&StaWifiMac::TryToEnsureAssociated, this); - } - else - { - m_probeRequestEvent.Cancel (); - } m_activeProbing = enable; + if (m_state == WAIT_PROBE_RESP || m_state == WAIT_BEACON) + { + NS_LOG_DEBUG ("STA is still scanning, reset scanning process"); + StartScanning (); + } } bool @@ -165,13 +175,6 @@ StaWifiMac::SendProbeRequest (void) //use the non-QoS for these regardless of whether we have a QoS //association or not. m_txop->Queue (packet, hdr); - - if (m_probeRequestEvent.IsRunning ()) - { - m_probeRequestEvent.Cancel (); - } - m_probeRequestEvent = Simulator::Schedule (m_probeRequestTimeout, - &StaWifiMac::ProbeRequestTimeout, this); } void @@ -271,17 +274,18 @@ StaWifiMac::TryToEnsureAssociated (void) or until we get a probe response */ break; - case BEACON_MISSED: + case WAIT_BEACON: + /* we have initiated passive scanning, continue to wait + and gather beacons + */ + break; + case UNASSOCIATED: /* we were associated but we missed a bunch of beacons * so we should assume we are not associated anymore. - * We try to initiate a probe request now. + * We try to initiate a scan now. */ m_linkDown (); - if (GetActiveProbing ()) - { - SetState (WAIT_PROBE_RESP); - SendProbeRequest (); - } + StartScanning (); break; case WAIT_ASSOC_RESP: /* we have sent an association request so we do not need to @@ -299,6 +303,69 @@ StaWifiMac::TryToEnsureAssociated (void) } } +void +StaWifiMac::StartScanning (void) +{ + NS_LOG_FUNCTION (this); + m_candidateAps.clear (); + if (m_probeRequestEvent.IsRunning ()) + { + m_probeRequestEvent.Cancel (); + } + if (m_waitBeaconEvent.IsRunning ()) + { + m_waitBeaconEvent.Cancel (); + } + if (GetActiveProbing ()) + { + SetState (WAIT_PROBE_RESP); + SendProbeRequest (); + m_probeRequestEvent = Simulator::Schedule (m_probeRequestTimeout, + &StaWifiMac::ScanningTimeout, + this); + } + else + { + SetState (WAIT_BEACON); + m_waitBeaconEvent = Simulator::Schedule (m_waitBeaconTimeout, + &StaWifiMac::ScanningTimeout, + this); + } +} + +void +StaWifiMac::ScanningTimeout (void) +{ + 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); + Time beaconInterval; + if (bestAp.m_activeProbing) + { + UpdateApInfoFromProbeResp (bestAp.m_probeResp, bestAp.m_apAddr, bestAp.m_bssid); + beaconInterval = MicroSeconds (bestAp.m_probeResp.GetBeaconIntervalUs ()); + } + else + { + UpdateApInfoFromBeacon (bestAp.m_beacon, bestAp.m_apAddr, bestAp.m_bssid); + beaconInterval = MicroSeconds (bestAp.m_beacon.GetBeaconIntervalUs ()); + } + + Time delay = beaconInterval * m_maxMissedBeacons; + RestartBeaconWatchdog (delay); + SetState (WAIT_ASSOC_RESP); + SendAssociationRequest (false); + } + else + { + NS_LOG_DEBUG ("Exhausted list of candidate AP; restart scanning"); + StartScanning (); + } +} + void StaWifiMac::AssocRequestTimeout (void) { @@ -307,14 +374,6 @@ StaWifiMac::AssocRequestTimeout (void) SendAssociationRequest (false); } -void -StaWifiMac::ProbeRequestTimeout (void) -{ - NS_LOG_FUNCTION (this); - SetState (WAIT_PROBE_RESP); - SendProbeRequest (); -} - void StaWifiMac::MissedBeacons (void) { @@ -330,7 +389,7 @@ StaWifiMac::MissedBeacons (void) return; } NS_LOG_DEBUG ("beacon missed"); - SetState (BEACON_MISSED); + SetState (UNASSOCIATED); TryToEnsureAssociated (); } @@ -545,17 +604,25 @@ StaWifiMac::Receive (Ptr packet, const WifiMacHeader *hdr) NS_LOG_LOGIC ("Beacon is not for us"); goodBeacon = false; } - if (goodBeacon) + if (goodBeacon && m_state == ASSOCIATED) { Time delay = MicroSeconds (beacon.GetBeaconIntervalUs () * m_maxMissedBeacons); RestartBeaconWatchdog (delay); UpdateApInfoFromBeacon (beacon, hdr->GetAddr2 (), hdr->GetAddr3 ()); } - if (goodBeacon && m_state == BEACON_MISSED) + if (goodBeacon && m_state == WAIT_BEACON) { - SetState (WAIT_ASSOC_RESP); - NS_LOG_DEBUG ("Good beacon received: send association request"); - SendAssociationRequest (false); + NS_LOG_DEBUG ("Beacon received while scanning from " << hdr->GetAddr2 ()); + SnrTag snrTag; + bool removed = packet->RemovePacketTag (snrTag); + NS_ASSERT (removed); + ApInfo apInfo; + apInfo.m_apAddr = hdr->GetAddr2 (); + apInfo.m_bssid = hdr->GetAddr3 (); + apInfo.m_activeProbing = false; + apInfo.m_snr = snrTag.Get (); + apInfo.m_beacon = beacon; + UpdateCandidateApList (apInfo); } return; } @@ -563,7 +630,7 @@ StaWifiMac::Receive (Ptr packet, const WifiMacHeader *hdr) { if (m_state == WAIT_PROBE_RESP) { - NS_LOG_DEBUG ("Probe response received"); + NS_LOG_DEBUG ("Probe response received while scanning from " << hdr->GetAddr2 ()); MgtProbeResponseHeader probeResp; packet->RemoveHeader (probeResp); if (!probeResp.GetSsid ().IsEqual (GetSsid ())) @@ -571,15 +638,16 @@ StaWifiMac::Receive (Ptr packet, const WifiMacHeader *hdr) NS_LOG_DEBUG ("Probe response is not for our SSID"); return; } - UpdateApInfoFromProbeResp (probeResp, hdr->GetAddr2 (), hdr->GetAddr3 ()); - Time delay = MicroSeconds (probeResp.GetBeaconIntervalUs () * m_maxMissedBeacons); - RestartBeaconWatchdog (delay); - if (m_probeRequestEvent.IsRunning ()) - { - m_probeRequestEvent.Cancel (); - } - SetState (WAIT_ASSOC_RESP); - SendAssociationRequest (false); + SnrTag snrTag; + bool removed = packet->RemovePacketTag (snrTag); + NS_ASSERT (removed); + ApInfo apInfo; + apInfo.m_apAddr = hdr->GetAddr2 (); + apInfo.m_bssid = hdr->GetAddr3 (); + apInfo.m_activeProbing = true; + apInfo.m_snr = snrTag.Get (); + apInfo.m_probeResp = probeResp; + UpdateCandidateApList (apInfo); } return; } @@ -613,7 +681,14 @@ StaWifiMac::Receive (Ptr packet, const WifiMacHeader *hdr) else { NS_LOG_DEBUG ("association refused"); - SetState (REFUSED); + if (m_candidateAps.empty ()) + { + SetState (REFUSED); + } + else + { + ScanningTimeout (); + } } } return; @@ -625,6 +700,32 @@ StaWifiMac::Receive (Ptr packet, const WifiMacHeader *hdr) RegularWifiMac::Receive (packet, hdr); } +void +StaWifiMac::UpdateCandidateApList (ApInfo newApInfo) +{ + NS_LOG_FUNCTION (this << newApInfo.m_bssid << newApInfo.m_apAddr << newApInfo.m_snr << newApInfo.m_activeProbing << newApInfo.m_beacon << newApInfo.m_probeResp); + // Remove duplicate ApInfo entry + for (std::vector::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::UpdateApInfoFromBeacon (MgtBeaconHeader beacon, Mac48Address apAddr, Mac48Address bssid) { diff --git a/src/wifi/model/sta-wifi-mac.h b/src/wifi/model/sta-wifi-mac.h index fcd8d6b15..fd2bfe8a1 100644 --- a/src/wifi/model/sta-wifi-mac.h +++ b/src/wifi/model/sta-wifi-mac.h @@ -24,16 +24,29 @@ #define STA_WIFI_MAC_H #include "infrastructure-wifi-mac.h" +#include "mgt-headers.h" namespace ns3 { -class MgtAddBaRequestHeader; -class MgtBeaconHeader; -class MgtProbeResponseHeader; -class MgtAssocResponseHeader; class SupportedRates; class CapabilityInformation; +/** + * \ingroup wifi + * + * Struct to hold information regarding observed AP through + * active/passive scanning + */ +struct ApInfo +{ + Mac48Address m_bssid; + Mac48Address m_apAddr; + double m_snr; + bool m_activeProbing; + MgtBeaconHeader m_beacon; + MgtProbeResponseHeader m_probeResp; +}; + /** * \ingroup wifi * @@ -41,30 +54,50 @@ class CapabilityInformation; * machine is as follows: * \verbatim - --------- -------------- ----------- - | Start | | Associated | <-------- ------> | Refused | - --------- -------------- | / ----------- - | | | / - \ v v / - \ ----------------- ----------------------------- - \-> | Beacon Missed | <--> | Wait Association Response | - ----------------- ----------------------------- - \ ^ ^ | - \ | | | - \ ----------------------- - - \-> | Wait Probe Response | - ----------------------- + --------- -------------- ----------- + | Start | | Associated | <------------------------- ----> | Refused | + --------- -------------- | / ----------- + | | /------------------------------\ | / + \ v v | v / + \ ---------------- --------------- ----------------------------- + \-> | Unassociated | --> | Wait Beacon | --> | Wait Association Response | + ---------------- --------------- ----------------------------- + \ ^ ^ | ^ ^ | + \ | | | | | | + \ v - / - + \ ----------------------- / + \-> | Wait Probe Response | --------/ + ----------------------- + ^ | + | | + - \endverbatim * * Notes: * 1. The state 'Start' is not included in #MacState and only used * for illustration purpose. - * 2. The transition from Wait Association Response to Beacon Missed + * 2. The Unassociated state is a transient state before STA starts the + * scanning procedure which moves it into either Wait Beacon or Wait + * Probe Response, based on whether passive or active scanning is + * selected. + * 3. In Wait Beacon and Wait Probe Response, STA is gathering beacon or + * probe response packets from APs, resulted in a list of candidate AP. + * After the respective timeout, it then tries to associate to the best + * AP (i.e., best SNR). STA will switch between the two states and + * restart the scanning procedure if SetActiveProbing() called. + * 4. In the case when AP responded to STA's association request with a + * refusal, STA will try to associate to the next best AP until the list + * of candidate AP is exhausted which sends STA to Refused state. + * - Note that this behavior is not currently tested since ns-3 does not + * implement association refusal at present. + * 5. The transition from Wait Association Response to Unassociated * occurs if an association request fails without explicit * refusal (i.e., the AP fails to respond). - * 3. The transition from Associated to Wait Association Response + * 6. The transition from Associated to Wait Association Response * occurs when STA's PHY capabilities changed. In this state, STA * tries to reassociate with the previously associated AP. + * 7. The transition from Associated to Unassociated occurs if the number + * of missed beacons exceeds the threshold. */ class StaWifiMac : public InfrastructureWifiMac { @@ -108,9 +141,10 @@ private: enum MacState { ASSOCIATED, + WAIT_BEACON, WAIT_PROBE_RESP, WAIT_ASSOC_RESP, - BEACON_MISSED, + UNASSOCIATED, REFUSED }; @@ -159,6 +193,13 @@ private: * \param apAddr mac address of the AP */ void UpdateApInfoFromAssocResp (MgtAssocResponseHeader assocResp, Mac48Address apAddr); + /** + * 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 a probe request packet to the DCF. The standard is not clear on the correct @@ -189,10 +230,16 @@ private: */ void AssocRequestTimeout (void); /** - * This method is called after the probe request timeout occurred. We switch the state to - * WAIT_PROBE_RESP and re-send a probe request. + * Start the scanning process which trigger active or passive scanning based on the + * active probing flag. */ - void ProbeRequestTimeout (void); + void StartScanning (void); + /** + * This method is called after wait beacon timeout or wait probe request timeout has + * occured. This will trigger association process from beacons or probe responses + * gathered while scanning. + */ + void ScanningTimeout (void); /** * Return whether we are associated with an AP. * @@ -250,15 +297,24 @@ private: */ void PhyCapabilitiesChanged (void); + void DoInitialize (void); + MacState m_state; ///< MAC state + Time m_waitBeaconTimeout; ///< wait beacon timeout Time m_probeRequestTimeout; ///< probe request timeout Time m_assocRequestTimeout; ///< assoc request timeout + EventId m_waitBeaconEvent; ///< wait beacon event EventId m_probeRequestEvent; ///< probe request event EventId m_assocRequestEvent; ///< assoc request event EventId m_beaconWatchdog; ///< beacon watchdog Time m_beaconWatchdogEnd; ///< beacon watchdog end uint32_t m_maxMissedBeacons; ///< maximum missed beacons bool m_activeProbing; ///< active probing + std::vector m_candidateAps; ///< list of candidate APs to associate + // 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; ///< assoc logger TracedCallback m_deAssocLogger; ///< deassoc logger diff --git a/src/wifi/test/wifi-test.cc b/src/wifi/test/wifi-test.cc index aa05c8fad..e53711ca8 100644 --- a/src/wifi/test/wifi-test.cc +++ b/src/wifi/test/wifi-test.cc @@ -26,6 +26,7 @@ #include "ns3/mobility-helper.h" #include "ns3/wifi-net-device.h" #include "ns3/adhoc-wifi-mac.h" +#include "ns3/ap-wifi-mac.h" #include "ns3/propagation-loss-model.h" #include "ns3/yans-error-rate-model.h" #include "ns3/constant-position-mobility-model.h" @@ -1512,6 +1513,182 @@ Bug2831TestCase::DoRun (void) NS_TEST_ASSERT_MSG_EQ (m_countOperationalChannelWidth40, 20, "Incorrect operational channel width after channel change"); } +//----------------------------------------------------------------------------- +/** + * Make sure that Wifi STA is correctly associating to the best AP (i.e., + * nearest from STA). We consider 3 AP and 1 STA. This test case consisted of + * three sub tests: + * - The best AP sends its beacon later than the other APs. STA is expected + * to associate to the best AP. + * - The STA is using active scanning instead of passive, the rest of the + * APs works normally. STA is expected to associate to the best AP + * - The nearest AP is turned off after sending beacon and while STA is + * still scanning. STA is expected to associate to the second best AP. + * + * See \bugid{2399} + * \todo Add explicit association refusal test if ns-3 implemented it. + */ + +class StaWifiMacScanningTestCase : public TestCase +{ +public: + StaWifiMacScanningTestCase (); + virtual ~StaWifiMacScanningTestCase (); + virtual void DoRun (void); + +private: + /** + * Callback function on STA assoc event + * \param context context string + * \param bssid the associated AP's bssid + */ + void AssocCallback (std::string context, Mac48Address bssid); + /** + * Turn beacon generation on the AP node + * \param apNode the AP node + */ + void TurnBeaconGenerationOn (Ptr apNode); + /** + * Turn the AP node off + * \param apNode the AP node + */ + void TurnApOff (Ptr apNode); + /** + * Setup test + * \param nearestApBeaconGeneration set BeaconGeneration attribute of the nearest AP + * \param staActiveProbe set ActiveProbing attribute of the STA + * \return node container containing all nodes + */ + NodeContainer Setup (bool nearestApBeaconGeneration, bool staActiveProbe); + + Mac48Address m_associatedApBssid; ///< Associated AP's bssid +}; + +StaWifiMacScanningTestCase::StaWifiMacScanningTestCase () + : TestCase ("Test case for StaWifiMac scanning capability") +{ +} + +StaWifiMacScanningTestCase::~StaWifiMacScanningTestCase () +{ +} + +void +StaWifiMacScanningTestCase::AssocCallback (std::string context, Mac48Address bssid) +{ + m_associatedApBssid = bssid; +} + +void +StaWifiMacScanningTestCase::TurnBeaconGenerationOn (Ptr apNode) +{ + Ptr netDevice = DynamicCast (apNode->GetDevice (0)); + Ptr mac = DynamicCast (netDevice->GetMac ()); + mac->SetAttribute ("BeaconGeneration", BooleanValue (true)); +} + +void +StaWifiMacScanningTestCase::TurnApOff (Ptr apNode) +{ + Ptr netDevice = DynamicCast (apNode->GetDevice (0)); + Ptr phy = netDevice->GetPhy (); + phy->SetOffMode(); +} + +NodeContainer +StaWifiMacScanningTestCase::Setup (bool nearestApBeaconGeneration, bool staActiveProbe) +{ + NodeContainer apNodes; + apNodes.Create (2); + + Ptr apNodeNearest = CreateObject (); + Ptr staNode = CreateObject (); + + YansWifiPhyHelper phy = YansWifiPhyHelper::Default (); + YansWifiChannelHelper channel = YansWifiChannelHelper::Default (); + phy.SetChannel (channel.Create ()); + + WifiHelper wifi; + wifi.SetStandard (WIFI_PHY_STANDARD_80211n_2_4GHZ); + wifi.SetRemoteStationManager ("ns3::ConstantRateWifiManager"); + + WifiMacHelper mac; + NetDeviceContainer apDevice, apDeviceNearest; + mac.SetType ("ns3::ApWifiMac", + "BeaconGeneration", BooleanValue (true)); + apDevice = wifi.Install (phy, mac, apNodes); + mac.SetType ("ns3::ApWifiMac", + "BeaconGeneration", BooleanValue (nearestApBeaconGeneration)); + apDeviceNearest = wifi.Install (phy, mac, apNodeNearest); + + NetDeviceContainer staDevice; + mac.SetType ("ns3::StaWifiMac", + "ActiveProbing", BooleanValue (staActiveProbe)); + staDevice = wifi.Install (phy, mac, staNode); + + MobilityHelper mobility; + Ptr positionAlloc = CreateObject (); + positionAlloc->Add (Vector (0.0, 0.0, 0.0)); // Furthest AP + positionAlloc->Add (Vector (10.0, 0.0, 0.0)); // Second nearest AP + positionAlloc->Add (Vector (5.0, 5.0, 0.0)); // Nearest AP + positionAlloc->Add (Vector (6.0, 5.0, 0.0)); // STA + mobility.SetPositionAllocator (positionAlloc); + + mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel"); + mobility.Install (apNodes); + mobility.Install (apNodeNearest); + mobility.Install (staNode); + + Config::Connect ("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Mac/$ns3::StaWifiMac/Assoc", MakeCallback (&StaWifiMacScanningTestCase::AssocCallback, this)); + + NodeContainer allNodes = NodeContainer (apNodes, apNodeNearest, staNode); + return allNodes; +} + +void +StaWifiMacScanningTestCase::DoRun (void) +{ + { + NodeContainer nodes = Setup (false, false); + Ptr nearestAp = nodes.Get (2); + Mac48Address nearestApAddr = DynamicCast (nearestAp->GetDevice (0))->GetMac ()->GetAddress (); + + Simulator::Schedule (Seconds (0.05), &StaWifiMacScanningTestCase::TurnBeaconGenerationOn, this, nearestAp); + + Simulator::Stop (Seconds (0.2)); + Simulator::Run (); + Simulator::Destroy (); + + NS_TEST_ASSERT_MSG_EQ (m_associatedApBssid, nearestApAddr, "STA is associated to the wrong AP"); + } + m_associatedApBssid = Mac48Address (); + { + NodeContainer nodes = Setup (true, true); + Ptr nearestAp = nodes.Get (2); + Mac48Address nearestApAddr = DynamicCast (nearestAp->GetDevice (0))->GetMac ()->GetAddress (); + + Simulator::Stop (Seconds (0.2)); + Simulator::Run (); + Simulator::Destroy (); + + NS_TEST_ASSERT_MSG_EQ (m_associatedApBssid, nearestApAddr, "STA is associated to the wrong AP"); + } + m_associatedApBssid = Mac48Address (); + { + NodeContainer nodes = Setup (true, false); + Ptr nearestAp = nodes.Get (2); + Mac48Address secondNearestApAddr = DynamicCast (nodes.Get (1)->GetDevice (0))->GetMac ()->GetAddress (); + + Simulator::Schedule (Seconds (0.1), &StaWifiMacScanningTestCase::TurnApOff, this, nearestAp); + + Simulator::Stop (Seconds (1.5)); + Simulator::Run (); + Simulator::Destroy (); + + NS_TEST_ASSERT_MSG_EQ (m_associatedApBssid, secondNearestApAddr, "STA is associated to the wrong AP"); + } +} + /** * \ingroup wifi-test * \ingroup tests @@ -1536,6 +1713,7 @@ WifiTestSuite::WifiTestSuite () AddTestCase (new Bug2222TestCase, TestCase::QUICK); //Bug 2222 AddTestCase (new Bug2483TestCase, TestCase::QUICK); //Bug 2483 AddTestCase (new Bug2831TestCase, TestCase::QUICK); //Bug 2831 + AddTestCase (new StaWifiMacScanningTestCase, TestCase::QUICK); //Bug 2399 } static WifiTestSuite g_wifiTestSuite; ///< the test suite