From a36de7eafefbc65a934bc34aeb022f19cc424bdb Mon Sep 17 00:00:00 2001 From: Stefano Avallone Date: Wed, 17 May 2023 15:54:01 +0200 Subject: [PATCH] wifi: Non-AP MLDs include TID-to-Link Mapping element(s) in AssocReq frames --- src/wifi/model/sta-wifi-mac.cc | 85 ++++++++++++++++++- src/wifi/model/sta-wifi-mac.h | 12 +++ src/wifi/model/wifi-remote-station-manager.cc | 11 +++ src/wifi/model/wifi-remote-station-manager.h | 6 ++ src/wifi/model/wifi-utils.cc | 33 +++++++ src/wifi/model/wifi-utils.h | 11 +++ 6 files changed, 157 insertions(+), 1 deletion(-) diff --git a/src/wifi/model/sta-wifi-mac.cc b/src/wifi/model/sta-wifi-mac.cc index 4851dd017..efee4fb69 100644 --- a/src/wifi/model/sta-wifi-mac.cc +++ b/src/wifi/model/sta-wifi-mac.cc @@ -467,6 +467,76 @@ StaWifiMac::GetMultiLinkElement(bool isReassoc, uint8_t linkId) const return multiLinkElement; } +std::vector +StaWifiMac::GetTidToLinkMappingElements(uint8_t apNegSupport) +{ + NS_LOG_FUNCTION(this << apNegSupport); + + auto ehtConfig = GetEhtConfiguration(); + NS_ASSERT(ehtConfig); + + EnumValue negSupport; + ehtConfig->GetAttributeFailSafe("TidToLinkMappingNegSupport", negSupport); + + NS_ABORT_MSG_IF(negSupport.Get() == 0, + "Cannot request TID-to-Link Mapping if negotiation is not supported"); + + // store the mappings, so that we can enforce them when the AP MLD accepts them + m_dlTidLinkMappingInAssocReq = ehtConfig->GetTidLinkMapping(WifiDirection::DOWNLINK); + m_ulTidLinkMappingInAssocReq = ehtConfig->GetTidLinkMapping(WifiDirection::UPLINK); + + bool mappingValidForNegType1 = TidToLinkMappingValidForNegType1(m_dlTidLinkMappingInAssocReq, + m_ulTidLinkMappingInAssocReq); + NS_ABORT_MSG_IF( + negSupport.Get() == 1 && !mappingValidForNegType1, + "Mapping TIDs to distinct link sets is incompatible with negotiation support of 1"); + + if (apNegSupport == 1 && !mappingValidForNegType1) + { + // If the TID-to-link Mapping Negotiation Support subfield value received from a peer + // MLD is equal to 1, the MLD that initiates a TID-to-link mapping negotiation with the + // peer MLD shall send only the TID-to-link Mapping element where all TIDs are mapped to + // the same link set (Sec. 35.3.7.1.3 of 802.11be D3.1). We use default mapping to meet + // this requirement. + NS_LOG_DEBUG("Using default mapping because AP MLD advertised negotiation support of 1"); + m_dlTidLinkMappingInAssocReq.clear(); + m_ulTidLinkMappingInAssocReq.clear(); + } + + std::vector ret(1); + + ret.back().m_control.direction = WifiDirection::DOWNLINK; + + // lambda to fill the last TID-to-Link Mapping IE in the vector to return + auto fillIe = [&ret](const auto& mapping) { + ret.back().m_control.defaultMapping = mapping.empty(); + + for (const auto& [tid, linkSet] : mapping) + { + // At any point in time, a TID shall always be mapped to at least one setup link both + // in DL and UL, which means that a TID-to-link mapping change is only valid and + // successful if it will not result in having any TID for which the link set for DL + // or UL is made of zero setup links (Sec. 35.3.7.1.1 of 802.11be D3.1) + NS_ABORT_MSG_IF(linkSet.empty(), "Cannot map a TID to an empty link set"); + ret.back().SetLinkMappingOfTid(tid, linkSet); + } + }; + + fillIe(m_dlTidLinkMappingInAssocReq); + + if (m_ulTidLinkMappingInAssocReq == m_dlTidLinkMappingInAssocReq) + { + ret.back().m_control.direction = WifiDirection::BOTH_DIRECTIONS; + return ret; + } + + ret.emplace_back(); + ret.back().m_control.direction = WifiDirection::UPLINK; + fillIe(m_ulTidLinkMappingInAssocReq); + + return ret; +} + void StaWifiMac::SendAssociationRequest(bool isReassoc) { @@ -500,7 +570,9 @@ StaWifiMac::SendAssociationRequest(bool isReassoc) auto frame = GetAssociationRequest(isReassoc, linkId); // include a Multi-Link Element if this device has multiple links (independently - // of how many links will be setup) and the AP is a multi-link device + // of how many links will be setup) and the AP is a multi-link device; + // if the AP MLD has indicated a support of TID-to-link mapping negotiation, also + // include the TID-to-link Mapping element(s) if (GetNLinks() > 1 && GetWifiRemoteStationManager(linkId)->GetMldAddress(*link.bssid).has_value()) { @@ -508,6 +580,17 @@ StaWifiMac::SendAssociationRequest(bool isReassoc) frame.template Get() = GetMultiLinkElement(isReassoc, linkId); }; std::visit(addMle, frame); + + uint8_t negSupport; + if (const auto& mldCapabilities = + GetWifiRemoteStationManager(linkId)->GetStationMldCapabilities(*link.bssid); + mldCapabilities && (negSupport = mldCapabilities->get().tidToLinkMappingSupport) > 0) + { + auto addTlm = [&](auto&& frame) { + frame.template Get() = GetTidToLinkMappingElements(negSupport); + }; + std::visit(addTlm, frame); + } } if (!isReassoc) diff --git a/src/wifi/model/sta-wifi-mac.h b/src/wifi/model/sta-wifi-mac.h index 64c483572..fced80373 100644 --- a/src/wifi/model/sta-wifi-mac.h +++ b/src/wifi/model/sta-wifi-mac.h @@ -504,6 +504,13 @@ class StaWifiMac : public WifiMac * \return the Multi-Link Element */ MultiLinkElement GetMultiLinkElement(bool isReassoc, uint8_t linkId) const; + + /** + * \param apNegSupport the negotiation type supported by the AP MLD + * \return the TID-to-Link Mapping element(s) to include in Association Request frame. + */ + std::vector GetTidToLinkMappingElements(uint8_t apNegSupport); + /** * Set the current MAC state. * @@ -590,6 +597,11 @@ class StaWifiMac : public WifiMac ///< of the first Probe Response on each channel Time m_pmModeSwitchTimeout; ///< PM mode switch timeout + /// store the DL TID-to-Link Mapping included in the Association Request frame + WifiTidLinkMapping m_dlTidLinkMappingInAssocReq; + /// store the UL TID-to-Link Mapping included in the Association Request frame + WifiTidLinkMapping m_ulTidLinkMappingInAssocReq; + TracedCallback m_assocLogger; ///< association logger TracedCallback m_setupCompleted; ///< link setup completed logger TracedCallback m_deAssocLogger; ///< disassociation logger diff --git a/src/wifi/model/wifi-remote-station-manager.cc b/src/wifi/model/wifi-remote-station-manager.cc index a32048e01..554b6fe7b 100644 --- a/src/wifi/model/wifi-remote-station-manager.cc +++ b/src/wifi/model/wifi-remote-station-manager.cc @@ -1642,6 +1642,17 @@ WifiRemoteStationManager::GetStationEmlCapabilities(const Mac48Address& from) return std::nullopt; } +std::optional> +WifiRemoteStationManager::GetStationMldCapabilities(const Mac48Address& from) +{ + if (auto state = LookupState(from); + state->m_mleCommonInfo && state->m_mleCommonInfo->m_mldCapabilities) + { + return state->m_mleCommonInfo->m_mldCapabilities.value(); + } + return std::nullopt; +} + bool WifiRemoteStationManager::GetLdpcSupported(Mac48Address address) const { diff --git a/src/wifi/model/wifi-remote-station-manager.h b/src/wifi/model/wifi-remote-station-manager.h index 5e29b8a5f..c4e35e351 100644 --- a/src/wifi/model/wifi-remote-station-manager.h +++ b/src/wifi/model/wifi-remote-station-manager.h @@ -310,6 +310,12 @@ class WifiRemoteStationManager : public Object */ std::optional> GetStationEmlCapabilities(const Mac48Address& from); + /** + * \param from the (MLD or link) address of the remote non-AP MLD + * \return the MLD Capabilities advertised by the remote non-AP MLD + */ + std::optional> + GetStationMldCapabilities(const Mac48Address& from); /** * Return whether the device has HT capability support enabled. * diff --git a/src/wifi/model/wifi-utils.cc b/src/wifi/model/wifi-utils.cc index 2f69798c1..276f0e476 100644 --- a/src/wifi/model/wifi-utils.cc +++ b/src/wifi/model/wifi-utils.cc @@ -144,4 +144,37 @@ GetSize(Ptr packet, const WifiMacHeader* hdr, bool isAmpdu) return size; } +bool +TidToLinkMappingValidForNegType1(const WifiTidLinkMapping& dlLinkMapping, + const WifiTidLinkMapping& ulLinkMapping) +{ + if (dlLinkMapping.empty() && ulLinkMapping.empty()) + { + // default mapping is valid + return true; + } + + if (dlLinkMapping.size() != 8 || ulLinkMapping.size() != 8) + { + // not all TIDs have been mapped + return false; + } + + const auto& linkSet = dlLinkMapping.cbegin()->second; + + for (const auto& linkMapping : {std::cref(dlLinkMapping), std::cref(ulLinkMapping)}) + { + for (const auto& [tid, links] : linkMapping.get()) + { + if (links != linkSet) + { + // distinct link sets + return false; + } + } + } + + return true; +} + } // namespace ns3 diff --git a/src/wifi/model/wifi-utils.h b/src/wifi/model/wifi-utils.h index 9aad32cf7..5a66517b9 100644 --- a/src/wifi/model/wifi-utils.h +++ b/src/wifi/model/wifi-utils.h @@ -170,6 +170,17 @@ void AddWifiMacTrailer(Ptr packet); */ uint32_t GetSize(Ptr packet, const WifiMacHeader* hdr, bool isAmpdu); +/** + * Check if the given TID-to-Link Mappings are valid for a negotiation type of 1. Specifically, + * it is checked whether all TIDs are mapped to the same set of links. + * + * \param dlLinkMapping the given TID-to-Link Mapping for Downlink + * \param ulLinkMapping the given TID-to-Link Mapping for Uplink + * \return whether the given TID-to-Link Mappings are valid for a negotiation type of 1 + */ +bool TidToLinkMappingValidForNegType1(const WifiTidLinkMapping& dlLinkMapping, + const WifiTidLinkMapping& ulLinkMapping); + /// Size of the space of sequence numbers static constexpr uint16_t SEQNO_SPACE_SIZE = 4096;