wifi: EMLSR Manager handles successful/failed TX of EML Notification frame

This commit is contained in:
Stefano Avallone
2023-02-03 18:34:37 +01:00
committed by Stefano Avallone
parent e41dfba58f
commit 917022c28c
5 changed files with 171 additions and 1 deletions

View File

@@ -68,4 +68,18 @@ DefaultEmlsrManager::GetLinkToSendEmlNotification()
return *m_assocLinkId;
}
std::optional<uint8_t>
DefaultEmlsrManager::ResendNotification(Ptr<const WifiMpdu> mpdu)
{
NS_LOG_FUNCTION(this);
NS_ASSERT_MSG(m_assocLinkId, "No recorded link on which Assoc Response was received");
return *m_assocLinkId;
}
void
DefaultEmlsrManager::NotifyEmlsrModeChanged()
{
NS_LOG_FUNCTION(this);
}
} // namespace ns3

View File

@@ -46,9 +46,11 @@ class DefaultEmlsrManager : public EmlsrManager
protected:
uint8_t GetLinkToSendEmlNotification() override;
std::optional<uint8_t> ResendNotification(Ptr<const WifiMpdu> mpdu) override;
private:
void DoNotifyMgtFrameReceived(Ptr<const WifiMpdu> mpdu, uint8_t linkId) override;
void NotifyEmlsrModeChanged() override;
std::optional<uint8_t> m_assocLinkId; /**< ID of the link on which Association Response
was received */

View File

@@ -79,7 +79,10 @@ EmlsrManager::DoDispose()
{
NS_LOG_FUNCTION(this);
m_staMac->TraceDisconnectWithoutContext("AckedMpdu", MakeCallback(&EmlsrManager::TxOk, this));
m_staMac->TraceDisconnectWithoutContext("DroppedMpdu",
MakeCallback(&EmlsrManager::TxDropped, this));
m_staMac = nullptr;
m_transitionTimeoutEvent.Cancel();
Object::DoDispose();
}
@@ -96,6 +99,14 @@ EmlsrManager::SetWifiMac(Ptr<StaWifiMac> mac)
"EmlsrManager can only be installed on non-AP MLDs");
m_staMac->TraceConnectWithoutContext("AckedMpdu", MakeCallback(&EmlsrManager::TxOk, this));
m_staMac->TraceConnectWithoutContext("DroppedMpdu",
MakeCallback(&EmlsrManager::TxDropped, this));
}
const std::set<uint8_t>&
EmlsrManager::GetEmlsrLinks() const
{
return m_emlsrLinks;
}
Ptr<StaWifiMac>
@@ -129,7 +140,10 @@ EmlsrManager::SetEmlsrLinks(const std::set<uint8_t>& linkIds)
NS_LOG_FUNCTION(this);
NS_ABORT_MSG_IF(linkIds.size() == 1, "Cannot enable EMLSR mode on a single link");
m_nextEmlsrLinks = linkIds;
if (linkIds != m_emlsrLinks)
{
m_nextEmlsrLinks = linkIds;
}
if (GetStaMac() && GetStaMac()->IsAssociated() && GetTransitionTimeout() && m_nextEmlsrLinks)
{
@@ -154,6 +168,23 @@ EmlsrManager::NotifyMgtFrameReceived(Ptr<const WifiMpdu> mpdu, uint8_t linkId)
// set of EMLSR links have been configured, hence enable EMLSR mode on those links
SendEmlOperatingModeNotification();
}
if (hdr.IsAction() && hdr.GetAddr2() == m_staMac->GetBssid(linkId))
{
// this is an action frame sent by an AP of the AP MLD we are associated with
auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket());
if (category == WifiActionHeader::PROTECTED_EHT &&
action.protectedEhtAction ==
WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION)
{
if (m_transitionTimeoutEvent.IsRunning())
{
// no need to wait until the expiration of the transition timeout
m_transitionTimeoutEvent.PeekEventImpl()->Invoke();
m_transitionTimeoutEvent.Cancel();
}
}
}
}
void
@@ -234,6 +265,77 @@ EmlsrManager::TxOk(Ptr<const WifiMpdu> mpdu)
m_lastAdvPaddingDelay = mle->GetEmlsrPaddingDelay();
m_lastAdvTransitionDelay = mle->GetEmlsrTransitionDelay();
}
if (hdr.IsMgt() && hdr.IsAction())
{
if (auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket());
category == WifiActionHeader::PROTECTED_EHT &&
action.protectedEhtAction ==
WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION)
{
// the EML Operating Mode Notification frame that we sent has been acknowledged.
// Start the transition timeout to wait until the request can be made effective
NS_ASSERT_MSG(m_emlsrTransitionTimeout, "No transition timeout received from AP");
m_transitionTimeoutEvent = Simulator::Schedule(*m_emlsrTransitionTimeout,
&EmlsrManager::ChangeEmlsrMode,
this);
}
}
}
void
EmlsrManager::TxDropped(WifiMacDropReason reason, Ptr<const WifiMpdu> mpdu)
{
NS_LOG_FUNCTION(this << reason << *mpdu);
const auto& hdr = mpdu->GetHeader();
if (hdr.IsMgt() && hdr.IsAction())
{
auto pkt = mpdu->GetPacket()->Copy();
if (auto [category, action] = WifiActionHeader::Remove(pkt);
category == WifiActionHeader::PROTECTED_EHT &&
action.protectedEhtAction ==
WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION)
{
// the EML Operating Mode Notification frame has been dropped. Ask the subclass
// whether the frame needs to be resent
auto linkId = ResendNotification(mpdu);
if (linkId)
{
MgtEmlOperatingModeNotification frame;
pkt->RemoveHeader(frame);
GetEhtFem(*linkId)->SendEmlOperatingModeNotification(m_staMac->GetBssid(*linkId),
frame);
}
else
{
m_nextEmlsrLinks.reset();
}
}
}
}
void
EmlsrManager::ChangeEmlsrMode()
{
NS_LOG_FUNCTION(this);
// After the successful transmission of the EML Operating Mode Notification frame by the
// non-AP STA affiliated with the non-AP MLD, the non-AP MLD shall operate in the EMLSR mode
// and the other non-AP STAs operating on the corresponding EMLSR links shall transition to
// active mode after the transition delay indicated in the Transition Timeout subfield in the
// EML Capabilities subfield of the Basic Multi-Link element or immediately after receiving an
// EML Operating Mode Notification frame from one of the APs operating on the EMLSR links and
// affiliated with the AP MLD. (Sec. 35.3.17 of 802.11be D3.0)
NS_ASSERT_MSG(m_nextEmlsrLinks, "No set of EMLSR links stored");
m_emlsrLinks.swap(*m_nextEmlsrLinks);
m_nextEmlsrLinks.reset();
// TODO Make other non-AP STAs operating on the corresponding EMLSR links transition to
// active mode or passive mode (depending on whether EMLSR mode has been enabled or disabled)
NotifyEmlsrModeChanged();
}
} // namespace ns3

View File

@@ -25,6 +25,7 @@
#include "ns3/object.h"
#include "ns3/sta-wifi-mac.h"
#include <optional>
#include <set>
namespace ns3
@@ -78,6 +79,11 @@ class EmlsrManager : public Object
*/
void SetEmlsrLinks(const std::set<uint8_t>& linkIds);
/**
* \return the set of links on which EMLSR mode is enabled
*/
const std::set<uint8_t>& GetEmlsrLinks() const;
/**
* Notify the reception of a management frame addressed to us.
*
@@ -105,6 +111,15 @@ class EmlsrManager : public Object
*/
virtual uint8_t GetLinkToSendEmlNotification() = 0;
/**
* A previous EML Operating Mode Notification frame was dropped. Ask the subclass whether
* the frame needs to be re-sent on the given link (if any).
*
* \param mpdu the dropped MPDU that includes the EML Operating Mode Notification frame
* \return the ID of the link over which to re-send the frame, if needed
*/
virtual std::optional<uint8_t> ResendNotification(Ptr<const WifiMpdu> mpdu) = 0;
Time m_emlsrPaddingDelay; //!< EMLSR Padding delay
Time m_emlsrTransitionDelay; //!< EMLSR Transition delay
@@ -129,14 +144,36 @@ class EmlsrManager : public Object
*/
void TxOk(Ptr<const WifiMpdu> mpdu);
/**
* Notify that the given MPDU has been discarded for the given reason.
*
* \param reason the reason why the MPDU was dropped
* \param mpdu the dropped MPDU
*/
void TxDropped(WifiMacDropReason reason, Ptr<const WifiMpdu> mpdu);
/**
* This method is called to make an EMLSR mode change effective after the transition
* delay has elapsed or a notification response has been received from the AP.
*/
void ChangeEmlsrMode();
/**
* Notify subclass that EMLSR mode changed.
*/
virtual void NotifyEmlsrModeChanged() = 0;
Ptr<StaWifiMac> m_staMac; //!< the MAC of the managed non-AP MLD
std::optional<Time> m_emlsrTransitionTimeout; /**< Transition timeout advertised by APs with
EMLSR activated */
std::set<uint8_t> m_emlsrLinks; //!< ID of the EMLSR links (empty if EMLSR mode is disabled)
std::optional<std::set<uint8_t>> m_nextEmlsrLinks; /**< ID of the links that will become the
EMLSR links when the pending
notification frame is acknowledged */
Time m_lastAdvPaddingDelay; //!< last advertised padding delay
Time m_lastAdvTransitionDelay; //!< last advertised transition delay
EventId m_transitionTimeoutEvent; /**< Timer started after the successful transmission of an
EML Operating Mode Notification frame */
};
} // namespace ns3

View File

@@ -966,12 +966,27 @@ StaWifiMac::Receive(Ptr<const WifiMpdu> mpdu, uint8_t linkId)
ReceiveAssocResp(mpdu, linkId);
break;
case WIFI_MAC_MGT_ACTION:
if (auto [category, action] = WifiActionHeader::Peek(packet);
category == WifiActionHeader::PROTECTED_EHT &&
action.protectedEhtAction ==
WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION)
{
// this is handled by the EMLSR Manager
break;
}
default:
// Invoke the receive handler of our parent class to deal with any
// other frames. Specifically, this will handle Block Ack-related
// Management Action frames.
WifiMac::Receive(mpdu, linkId);
}
if (m_emlsrManager)
{
m_emlsrManager->NotifyMgtFrameReceived(mpdu, linkId);
}
}
void