From bdddd9511d5f8a7afcdfd6fa7984ed9ebef23c8a Mon Sep 17 00:00:00 2001 From: Stefano Avallone Date: Sat, 31 Dec 2022 15:58:28 +0100 Subject: [PATCH] wifi: Add MU-RTS support to default protection manager --- .../model/wifi-default-protection-manager.cc | 195 ++++++++++++++++-- .../model/wifi-default-protection-manager.h | 36 ++++ src/wifi/model/wifi-protection-manager.cc | 44 +++- src/wifi/model/wifi-protection-manager.h | 15 ++ 4 files changed, 271 insertions(+), 19 deletions(-) diff --git a/src/wifi/model/wifi-default-protection-manager.cc b/src/wifi/model/wifi-default-protection-manager.cc index cfe927611..0e2f17878 100644 --- a/src/wifi/model/wifi-default-protection-manager.cc +++ b/src/wifi/model/wifi-default-protection-manager.cc @@ -19,12 +19,16 @@ #include "wifi-default-protection-manager.h" -#include "wifi-mac.h" +#include "ap-wifi-mac.h" #include "wifi-mpdu.h" #include "wifi-tx-parameters.h" +#include "ns3/boolean.h" +#include "ns3/erp-ofdm-phy.h" #include "ns3/log.h" +#include + namespace ns3 { @@ -35,10 +39,16 @@ NS_OBJECT_ENSURE_REGISTERED(WifiDefaultProtectionManager); TypeId WifiDefaultProtectionManager::GetTypeId() { - static TypeId tid = TypeId("ns3::WifiDefaultProtectionManager") - .SetParent() - .SetGroupName("Wifi") - .AddConstructor(); + static TypeId tid = + TypeId("ns3::WifiDefaultProtectionManager") + .SetParent() + .SetGroupName("Wifi") + .AddConstructor() + .AddAttribute("EnableMuRts", + "If enabled, always protect a DL/UL MU frame exchange with MU-RTS/CTS.", + BooleanValue(false), + MakeBooleanAccessor(&WifiDefaultProtectionManager::m_sendMuRts), + MakeBooleanChecker()); return tid; } @@ -57,12 +67,20 @@ WifiDefaultProtectionManager::TryAddMpdu(Ptr mpdu, const WifiTxP { NS_LOG_FUNCTION(this << *mpdu << &txParams); - // TB PPDUs need no protection (the soliciting Trigger Frame can be protected - // by an MU-RTS). Until MU-RTS is implemented, we disable protection also for: - // - Trigger Frames - // - DL MU PPDUs containing more than one PSDU - if (txParams.m_txVector.IsUlMu() || mpdu->GetHeader().IsTrigger() || - (txParams.m_txVector.IsDlMu() && txParams.GetPsduInfoMap().size() > 1)) + // For a DL MU PPDU containing more than one PSDU, call a separate method. + // A DL MU PPDU contains more than one PSDU if either the TX params' PSDU info map + // contains more than one entry or it contains one entry but the MPDU being added is + // addressed to a different receiver (hence generating a new entry if the MPDU is added) + if (const auto& psduInfoMap = txParams.GetPsduInfoMap(); + txParams.m_txVector.IsDlMu() && + (psduInfoMap.size() > 1 || + (psduInfoMap.size() == 1 && psduInfoMap.begin()->first != mpdu->GetHeader().GetAddr1()))) + { + return TryAddMpduToMuPpdu(mpdu, txParams); + } + + // No protection for TB PPDUs (the soliciting Trigger Frame can be protected by an MU-RTS) + if (txParams.m_txVector.IsUlMu()) { if (txParams.m_protection) { @@ -72,12 +90,10 @@ WifiDefaultProtectionManager::TryAddMpdu(Ptr mpdu, const WifiTxP return std::unique_ptr(new WifiNoProtection); } - // If we are adding a second PSDU to a DL MU PPDU, switch to no protection - // (until MU-RTS is implemented) - if (txParams.m_txVector.IsDlMu() && txParams.GetPsduInfoMap().size() == 1 && - !txParams.GetPsduInfo(mpdu->GetHeader().GetAddr1())) + // if this is a Trigger Frame, call a separate method + if (mpdu->GetHeader().IsTrigger()) { - return std::unique_ptr(new WifiNoProtection); + return TryUlMuTransmission(mpdu, txParams); } // if the current protection method (if any) is already RTS/CTS or CTS-to-Self, @@ -110,11 +126,12 @@ WifiDefaultProtectionManager::TryAggregateMsdu(Ptr msdu, { NS_LOG_FUNCTION(this << *msdu << &txParams); - // if the current protection method is already RTS/CTS or CTS-to-Self, + // if the current protection method is already RTS/CTS, CTS-to-Self or MU-RTS/CTS, // it will not change by aggregating an MSDU NS_ASSERT(txParams.m_protection); if (txParams.m_protection->method == WifiProtection::RTS_CTS || - txParams.m_protection->method == WifiProtection::CTS_TO_SELF) + txParams.m_protection->method == WifiProtection::CTS_TO_SELF || + txParams.m_protection->method == WifiProtection::MU_RTS_CTS) { return nullptr; } @@ -179,4 +196,146 @@ WifiDefaultProtectionManager::GetPsduProtection(const WifiMacHeader& hdr, return std::unique_ptr(new WifiNoProtection()); } +std::unique_ptr +WifiDefaultProtectionManager::TryAddMpduToMuPpdu(Ptr mpdu, + const WifiTxParameters& txParams) +{ + NS_LOG_FUNCTION(this << *mpdu << &txParams); + NS_ASSERT(txParams.m_txVector.IsDlMu()); + + if (!m_sendMuRts) + { + // No protection because sending MU-RTS is disabled + if (txParams.m_protection && txParams.m_protection->method == WifiProtection::NONE) + { + return nullptr; + } + return std::unique_ptr(new WifiNoProtection()); + } + + WifiMuRtsCtsProtection* protection = nullptr; + if (txParams.m_protection && txParams.m_protection->method == WifiProtection::MU_RTS_CTS) + { + protection = static_cast(txParams.m_protection.get()); + } + + auto receiver = mpdu->GetHeader().GetAddr1(); + + if (txParams.GetPsduInfo(receiver) == nullptr) + { + // we get here if this is the first MPDU for this receiver. + NS_ABORT_MSG_IF(m_mac->GetTypeOfStation() != AP, "HE APs only can send DL MU PPDUs"); + auto apMac = StaticCast(m_mac); + auto txWidth = txParams.m_txVector.GetChannelWidth(); + + if (protection != nullptr) + { + // txParams.m_protection points to an existing WifiMuRtsCtsProtection object. + // We have to return a copy of this object including the needed changes + protection = new WifiMuRtsCtsProtection(*protection); + } + else + { + // we have to create a new WifiMuRtsCtsProtection object + protection = new WifiMuRtsCtsProtection; + + // initialize the MU-RTS Trigger Frame + // The UL Length, GI And HE-LTF Type, MU-MIMO HE-LTF Mode, Number Of HE-LTF Symbols, + // UL STBC, LDPC Extra Symbol Segment, AP TX Power, Pre-FEC Padding Factor, + // PE Disambiguity, UL Spatial Reuse, Doppler and UL HE-SIG-A2 Reserved subfields in + // the Common Info field are reserved. (Sec. 9.3.1.22.5 of 802.11ax) + protection->muRts.SetType(TriggerFrameType::MU_RTS_TRIGGER); + protection->muRts.SetUlBandwidth(txWidth); + + NS_ASSERT_MSG(txParams.GetPsduInfoMap().size() == 1, + "There should be one PSDU in the DL MU PPDU when creating a new " + "WifiMuRtsCtsProtection object"); + + // this is the first MPDU for the second receiver added to the DL MU PPDU. + // Add a User Info field for the first receiver + AddUserInfoToMuRts(protection->muRts, + txWidth, + txParams.GetPsduInfoMap().cbegin()->first); + + // compute the TXVECTOR to use to send the MU-RTS Trigger Frame + protection->muRtsTxVector = GetWifiRemoteStationManager()->GetRtsTxVector(receiver); + // The transmitter of an MU-RTS Trigger frame shall not request a non-AP STA to send + // a CTS frame response in a 20 MHz channel that is not occupied by the PPDU that + // contains the MU-RTS Trigger frame. (Sec. 26.2.6.2 of 802.11ax) + protection->muRtsTxVector.SetChannelWidth(txWidth); + } + + // Add a User Info field for the new receiver + // The UL HE-MCS, UL FEC Coding Type, UL DCM, SS Allocation and UL Target RSSI fields + // in the User Info field are reserved (Sec. 9.3.1.22.5 of 802.11ax) + AddUserInfoToMuRts(protection->muRts, txWidth, receiver); + + return std::unique_ptr(protection); + } + + // an MPDU addressed to the same receiver has been already added + NS_ASSERT(protection != nullptr); + + // no change is needed + return nullptr; +} + +std::unique_ptr +WifiDefaultProtectionManager::TryUlMuTransmission(Ptr mpdu, + const WifiTxParameters& txParams) +{ + NS_LOG_FUNCTION(this << *mpdu << &txParams); + NS_ASSERT(mpdu->GetHeader().IsTrigger()); + + if (!m_sendMuRts) + { + // No protection because sending MU-RTS is disabled + return std::unique_ptr(new WifiNoProtection()); + } + + CtrlTriggerHeader trigger; + mpdu->GetPacket()->PeekHeader(trigger); + NS_ASSERT(trigger.GetNUserInfoFields() > 0); + auto txWidth = trigger.GetUlBandwidth(); + + WifiMuRtsCtsProtection* protection = new WifiMuRtsCtsProtection; + // initialize the MU-RTS Trigger Frame + // The UL Length, GI And HE-LTF Type, MU-MIMO HE-LTF Mode, Number Of HE-LTF Symbols, + // UL STBC, LDPC Extra Symbol Segment, AP TX Power, Pre-FEC Padding Factor, + // PE Disambiguity, UL Spatial Reuse, Doppler and UL HE-SIG-A2 Reserved subfields in + // the Common Info field are reserved. (Sec. 9.3.1.22.5 of 802.11ax) + protection->muRts.SetType(TriggerFrameType::MU_RTS_TRIGGER); + protection->muRts.SetUlBandwidth(txWidth); + + NS_ABORT_MSG_IF(m_mac->GetTypeOfStation() != AP, "HE APs only can send DL MU PPDUs"); + const auto& staList = StaticCast(m_mac)->GetStaList(m_linkId); + std::remove_reference_t::const_iterator staIt; + + for (const auto& userInfo : trigger) + { + // Add a User Info field to the MU-RTS for this solicited station + // The UL HE-MCS, UL FEC Coding Type, UL DCM, SS Allocation and UL Target RSSI fields + // in the User Info field are reserved (Sec. 9.3.1.22.5 of 802.11ax) + staIt = staList.find(userInfo.GetAid12()); + NS_ASSERT(staIt != staList.cend()); + AddUserInfoToMuRts(protection->muRts, txWidth, staIt->second); + } + + // compute the TXVECTOR to use to send the MU-RTS Trigger Frame + protection->muRtsTxVector = + GetWifiRemoteStationManager()->GetRtsTxVector(mpdu->GetHeader().GetAddr1()); + // The transmitter of an MU-RTS Trigger frame shall not request a non-AP STA to send + // a CTS frame response in a 20 MHz channel that is not occupied by the PPDU that + // contains the MU-RTS Trigger frame. (Sec. 26.2.6.2 of 802.11ax) + protection->muRtsTxVector.SetChannelWidth(txWidth); + // OFDM is needed to transmit the PPDU over a bandwidth that is a multiple of 20 MHz + const auto modulation = protection->muRtsTxVector.GetModulationClass(); + if (modulation == WIFI_MOD_CLASS_DSSS || modulation == WIFI_MOD_CLASS_HR_DSSS) + { + protection->muRtsTxVector.SetMode(ErpOfdmPhy::GetErpOfdmRate6Mbps()); + } + + return std::unique_ptr(protection); +} + } // namespace ns3 diff --git a/src/wifi/model/wifi-default-protection-manager.h b/src/wifi/model/wifi-default-protection-manager.h index 6a649f660..f0dcb3b3f 100644 --- a/src/wifi/model/wifi-default-protection-manager.h +++ b/src/wifi/model/wifi-default-protection-manager.h @@ -64,6 +64,42 @@ class WifiDefaultProtectionManager : public WifiProtectionManager virtual std::unique_ptr GetPsduProtection(const WifiMacHeader& hdr, uint32_t size, const WifiTxVector& txVector) const; + + private: + /** + * Calculate the protection method to use if the given MPDU is added to the + * current DL MU PPDU (represented by the given TX parameters). If the computed + * protection method is the same as the current one, a null pointer is + * returned. Otherwise, the computed protection method is returned. + * The TX width of the PPDU containing the MU-RTS is the same as the DL MU PPDU + * being protected. Each non-AP station is solicited to transmit a CTS occupying a + * bandwidth equal to the minimum between the TX width of the DL MU PPDU and the + * maximum channel width supported by the non-AP station. + * + * \param mpdu the given MPDU + * \param txParams the TX parameters describing the current DL MU PPDU + * \return the new protection method or a null pointer if the protection method + * is unchanged + */ + virtual std::unique_ptr TryAddMpduToMuPpdu(Ptr mpdu, + const WifiTxParameters& txParams); + + /** + * Calculate the protection method for the UL MU transmission solicited by the given + * Trigger Frame. + * The TX width of the PPDU containing the MU-RTS is the same as the TB PPDUs being + * solicited by the given Trigger Frame. Each non-AP station is solicited to transmit a CTS + * occupying a bandwidth equal to the minimum between the TX width of the PPDU containing + * the MU-RTS and the maximum channel width supported by the non-AP station. + * + * \param mpdu the given Trigger Frame + * \param txParams the current TX parameters (just the TXVECTOR needs to be set) + * \return the protection method for the UL MU transmission solicited by the given Trigger Frame + */ + virtual std::unique_ptr TryUlMuTransmission(Ptr mpdu, + const WifiTxParameters& txParams); + + bool m_sendMuRts; //!< true for sending an MU-RTS to protect DL MU PPDUs }; } // namespace ns3 diff --git a/src/wifi/model/wifi-protection-manager.cc b/src/wifi/model/wifi-protection-manager.cc index 09fd54dae..3febadb91 100644 --- a/src/wifi/model/wifi-protection-manager.cc +++ b/src/wifi/model/wifi-protection-manager.cc @@ -19,7 +19,8 @@ #include "wifi-protection-manager.h" -#include "wifi-mac.h" +#include "ap-wifi-mac.h" +#include "wifi-phy.h" #include "ns3/log.h" @@ -77,4 +78,45 @@ WifiProtectionManager::SetLinkId(uint8_t linkId) m_linkId = linkId; } +void +WifiProtectionManager::AddUserInfoToMuRts(CtrlTriggerHeader& muRts, + uint16_t txWidth, + const Mac48Address& receiver) const +{ + NS_LOG_FUNCTION(this << muRts << txWidth << receiver); + + CtrlTriggerUserInfoField& ui = muRts.AddUserInfoField(); + + NS_ABORT_MSG_IF(m_mac->GetTypeOfStation() != AP, "HE APs only can send MU-RTS"); + auto apMac = StaticCast(m_mac); + ui.SetAid12(apMac->GetAssociationId(receiver, m_linkId)); + + const uint16_t ctsTxWidth = + std::min(txWidth, GetWifiRemoteStationManager()->GetChannelWidthSupported(receiver)); + auto phy = m_mac->GetWifiPhy(m_linkId); + std::size_t primaryIdx = phy->GetOperatingChannel().GetPrimaryChannelIndex(ctsTxWidth); + if (phy->GetChannelWidth() == 160 && ctsTxWidth <= 40 && primaryIdx >= 80 / ctsTxWidth) + { + // the primary80 is in the higher part of the 160 MHz channel + primaryIdx -= 80 / ctsTxWidth; + } + switch (ctsTxWidth) + { + case 20: + ui.SetMuRtsRuAllocation(61 + primaryIdx); + break; + case 40: + ui.SetMuRtsRuAllocation(65 + primaryIdx); + break; + case 80: + ui.SetMuRtsRuAllocation(67); + break; + case 160: + ui.SetMuRtsRuAllocation(68); + break; + default: + NS_ABORT_MSG("Unhandled TX width: " << ctsTxWidth << " MHz"); + } +} + } // namespace ns3 diff --git a/src/wifi/model/wifi-protection-manager.h b/src/wifi/model/wifi-protection-manager.h index 0c9eb344c..62b8409df 100644 --- a/src/wifi/model/wifi-protection-manager.h +++ b/src/wifi/model/wifi-protection-manager.h @@ -98,6 +98,21 @@ class WifiProtectionManager : public Object */ Ptr GetWifiRemoteStationManager() const; + /** + * Add a User Info field to the given MU-RTS Trigger Frame to solicit a CTS from + * the station with the given MAC address. The MU-RTS is intended to protect a data + * frame having the given TX width. The TX width of the solicited CTS is the minimum + * between the TX width of the protected data frame and the maximum width supported + * by the solicited station. + * + * \param muRts the MU-RTS Trigger Frame + * \param txWidth the TX width of the protected data frame + * \param receiver the MAC address of the solicited station + */ + void AddUserInfoToMuRts(CtrlTriggerHeader& muRts, + uint16_t txWidth, + const Mac48Address& receiver) const; + Ptr m_mac; //!< MAC which is using this Protection Manager uint8_t m_linkId; //!< ID of the link this Protection Manager is operating on };