diff --git a/src/wifi/model/eht/multi-link-element.cc b/src/wifi/model/eht/multi-link-element.cc index d64aab47e..595b2cdd0 100644 --- a/src/wifi/model/eht/multi-link-element.cc +++ b/src/wifi/model/eht/multi-link-element.cc @@ -728,11 +728,18 @@ MultiLinkElement::PerStaProfileSubelement::GetInformationFieldSize() const using T = std::decay_t; if constexpr (std::is_same_v) { + NS_ASSERT_MSG(std::holds_alternative(m_containingFrame), + "Missing management frame for Per-STA Profile subelement"); return static_cast(0); } else { - return frame->GetSerializedSize(); + using U = std::decay_t; + NS_ASSERT_MSG( + std::holds_alternative>(m_containingFrame), + "Containing frame type and frame type in Per-STA Profile do not match"); + const auto& containing = std::get>(m_containingFrame); + return frame->GetSerializedSizeInPerStaProfile(containing); } }; ret += std::visit(staProfileSize, m_staProfile); @@ -755,11 +762,18 @@ MultiLinkElement::PerStaProfileSubelement::SerializeInformationField(Buffer::Ite using T = std::decay_t; if constexpr (std::is_same_v) { + NS_ASSERT_MSG(std::holds_alternative(m_containingFrame), + "Missing management frame for Per-STA Profile subelement"); return; } else { - frame->Serialize(start); + using U = std::decay_t; + NS_ASSERT_MSG( + std::holds_alternative>(m_containingFrame), + "Containing frame type and frame type in Per-STA Profile do not match"); + const auto& containing = std::get>(m_containingFrame); + frame->SerializeInPerStaProfile(start, containing); } }; std::visit(staProfileSerialize, m_staProfile); @@ -770,46 +784,37 @@ MultiLinkElement::PerStaProfileSubelement::DeserializeInformationField(Buffer::I uint16_t length) { Buffer::Iterator i = start; - uint16_t count = 0; m_staControl = i.ReadLsbtohU16(); - count += 2; - i.ReadU8(); // STA Info Length - count++; if (HasStaMacAddress()) { ReadFrom(i, m_staMacAddress); - count += 6; } // TODO add other subfields of the STA Info field + uint16_t count = i.GetDistanceFrom(start); - if (count >= length) + NS_ASSERT_MSG(count <= length, + "Bytes read (" << count << ") exceed expected number (" << length << ")"); + + if (count == length) { return count; } - if (m_frameType == WIFI_MAC_MGT_ASSOCIATION_REQUEST) - { - MgtAssocRequestHeader assoc; - count += assoc.Deserialize(i); - SetAssocRequest(std::move(assoc)); - } - else if (m_frameType == WIFI_MAC_MGT_REASSOCIATION_REQUEST) - { - MgtReassocRequestHeader reassoc; - count += reassoc.Deserialize(i); - SetAssocRequest(std::move(reassoc)); - } - else if (m_frameType == WIFI_MAC_MGT_ASSOCIATION_RESPONSE || - m_frameType == WIFI_MAC_MGT_REASSOCIATION_RESPONSE) - { - MgtAssocResponseHeader assoc; - count += assoc.Deserialize(i); - SetAssocResponse(assoc); - } + auto staProfileDeserialize = [&](auto&& frame) { + using T = std::decay_t; + if constexpr (!std::is_same_v) + { + using U = std::decay_t; + U assoc; + count += assoc.DeserializeFromPerStaProfile(i, length - count, frame.get()); + m_staProfile = std::make_unique(std::move(assoc)); + } + }; + std::visit(staProfileDeserialize, m_containingFrame); return count; } @@ -864,6 +869,7 @@ MultiLinkElement::GetInformationFieldSize() const for (const auto& subelement : m_perStaProfileSubelements) { + subelement.m_containingFrame = m_containingFrame; ret += subelement.GetSerializedSize(); } @@ -930,11 +936,14 @@ MultiLinkElement::DeserializeInformationField(Buffer::Iterator start, uint16_t l { switch (static_cast(i.PeekU8())) { - case PER_STA_PROFILE_SUBELEMENT_ID: + case PER_STA_PROFILE_SUBELEMENT_ID: { AddPerStaProfileSubelement(); - i = GetPerStaProfile(GetNPerStaProfileSubelements() - 1).Deserialize(i); + auto& perStaProfile = GetPerStaProfile(GetNPerStaProfileSubelements() - 1); + perStaProfile.m_containingFrame = m_containingFrame; + i = perStaProfile.Deserialize(i); count = i.GetDistanceFrom(start); - break; + } + break; default: NS_ABORT_MSG("Unsupported Subelement ID: " << +i.PeekU8()); } diff --git a/src/wifi/model/eht/multi-link-element.h b/src/wifi/model/eht/multi-link-element.h index b917e33ea..1465e064b 100644 --- a/src/wifi/model/eht/multi-link-element.h +++ b/src/wifi/model/eht/multi-link-element.h @@ -561,6 +561,9 @@ class MultiLinkElement : public WifiInformationElement */ uint8_t GetStaInfoLength() const; + mutable ContainingFrame + m_containingFrame; //!< the mgt frame containing this Per-STA Profile + private: uint16_t GetInformationFieldSize() const override; void SerializeInformationField(Buffer::Iterator start) const override; diff --git a/src/wifi/model/mgt-headers.cc b/src/wifi/model/mgt-headers.cc index 2ee5a5556..fafe17b83 100644 --- a/src/wifi/model/mgt-headers.cc +++ b/src/wifi/model/mgt-headers.cc @@ -210,10 +210,7 @@ MgtAssocRequestHeader::Capabilities() uint32_t MgtAssocRequestHeader::GetSerializedSizeImpl() const { - if (auto& mle = Get()) - { - mle->m_containingFrame = *this; - } + SetMleContainingFrame(); uint32_t size = 0; size += m_capability.GetSerializedSize(); @@ -222,13 +219,22 @@ MgtAssocRequestHeader::GetSerializedSizeImpl() const return size; } +uint32_t +MgtAssocRequestHeader::GetSerializedSizeInPerStaProfileImpl( + const MgtAssocRequestHeader& frame) const +{ + uint32_t size = 0; + size += m_capability.GetSerializedSize(); + size += + MgtHeaderInPerStaProfile::GetSerializedSizeInPerStaProfileImpl(frame); + return size; +} + void MgtAssocRequestHeader::SerializeImpl(Buffer::Iterator start) const { - if (auto& mle = Get()) - { - mle->m_containingFrame = *this; - } + SetMleContainingFrame(); Buffer::Iterator i = start; i = m_capability.Serialize(i); @@ -236,20 +242,62 @@ MgtAssocRequestHeader::SerializeImpl(Buffer::Iterator start) const WifiMgtHeader::SerializeImpl(i); } +void +MgtAssocRequestHeader::SerializeInPerStaProfileImpl(Buffer::Iterator start, + const MgtAssocRequestHeader& frame) const +{ + Buffer::Iterator i = start; + i = m_capability.Serialize(i); + MgtHeaderInPerStaProfile::SerializeInPerStaProfileImpl(i, frame); +} + uint32_t MgtAssocRequestHeader::DeserializeImpl(Buffer::Iterator start) { Buffer::Iterator i = start; i = m_capability.Deserialize(i); m_listenInterval = i.ReadLsbtohU16(); + auto distance = i.GetDistanceFrom(start) + + WifiMgtHeader::DeserializeImpl(i); + if (auto& mle = Get()) + { + for (std::size_t id = 0; id < mle->GetNPerStaProfileSubelements(); id++) + { + auto& perStaProfile = mle->GetPerStaProfile(id); + if (perStaProfile.HasAssocRequest()) + { + auto& frameInPerStaProfile = + std::get>( + perStaProfile.GetAssocRequest()) + .get(); + frameInPerStaProfile.CopyIesFromContainingFrame(*this); + } + } + } + return distance; +} + +uint32_t +MgtAssocRequestHeader::DeserializeFromPerStaProfileImpl(Buffer::Iterator start, + uint16_t length, + const MgtAssocRequestHeader& frame) +{ + Buffer::Iterator i = start; + i = m_capability.Deserialize(i); + m_listenInterval = frame.m_listenInterval; auto distance = i.GetDistanceFrom(start); - return distance + WifiMgtHeader::DeserializeImpl(i); + NS_ASSERT_MSG(distance <= length, + "Bytes read (" << distance << ") exceed expected number (" << length << ")"); + return distance + MgtHeaderInPerStaProfile:: + DeserializeFromPerStaProfileImpl(i, length - distance, frame); } void MgtAssocRequestHeader::InitForDeserialization(std::optional& optElem) { optElem.emplace(WIFI_MAC_MGT_ASSOCIATION_REQUEST); + optElem->m_containingFrame = *this; } /*********************************************************** @@ -307,10 +355,7 @@ MgtReassocRequestHeader::SetCurrentApAddress(Mac48Address currentApAddr) uint32_t MgtReassocRequestHeader::GetSerializedSizeImpl() const { - if (auto& mle = Get()) - { - mle->m_containingFrame = *this; - } + SetMleContainingFrame(); uint32_t size = 0; size += m_capability.GetSerializedSize(); @@ -320,6 +365,18 @@ MgtReassocRequestHeader::GetSerializedSizeImpl() const return size; } +uint32_t +MgtReassocRequestHeader::GetSerializedSizeInPerStaProfileImpl( + const MgtReassocRequestHeader& frame) const +{ + uint32_t size = 0; + size += m_capability.GetSerializedSize(); + size += + MgtHeaderInPerStaProfile::GetSerializedSizeInPerStaProfileImpl(frame); + return size; +} + void MgtReassocRequestHeader::PrintImpl(std::ostream& os) const { @@ -330,10 +387,7 @@ MgtReassocRequestHeader::PrintImpl(std::ostream& os) const void MgtReassocRequestHeader::SerializeImpl(Buffer::Iterator start) const { - if (auto& mle = Get()) - { - mle->m_containingFrame = *this; - } + SetMleContainingFrame(); Buffer::Iterator i = start; i = m_capability.Serialize(i); @@ -342,6 +396,16 @@ MgtReassocRequestHeader::SerializeImpl(Buffer::Iterator start) const WifiMgtHeader::SerializeImpl(i); } +void +MgtReassocRequestHeader::SerializeInPerStaProfileImpl(Buffer::Iterator start, + const MgtReassocRequestHeader& frame) const +{ + Buffer::Iterator i = start; + i = m_capability.Serialize(i); + MgtHeaderInPerStaProfile::SerializeInPerStaProfileImpl(i, frame); +} + uint32_t MgtReassocRequestHeader::DeserializeImpl(Buffer::Iterator start) { @@ -349,14 +413,47 @@ MgtReassocRequestHeader::DeserializeImpl(Buffer::Iterator start) i = m_capability.Deserialize(i); m_listenInterval = i.ReadLsbtohU16(); ReadFrom(i, m_currentApAddr); + auto distance = i.GetDistanceFrom(start) + + WifiMgtHeader::DeserializeImpl(i); + if (auto& mle = Get()) + { + for (std::size_t id = 0; id < mle->GetNPerStaProfileSubelements(); id++) + { + auto& perStaProfile = mle->GetPerStaProfile(id); + if (perStaProfile.HasReassocRequest()) + { + auto& frameInPerStaProfile = + std::get>( + perStaProfile.GetAssocRequest()) + .get(); + frameInPerStaProfile.CopyIesFromContainingFrame(*this); + } + } + } + return distance; +} + +uint32_t +MgtReassocRequestHeader::DeserializeFromPerStaProfileImpl(Buffer::Iterator start, + uint16_t length, + const MgtReassocRequestHeader& frame) +{ + Buffer::Iterator i = start; + i = m_capability.Deserialize(i); + m_listenInterval = frame.m_listenInterval; + m_currentApAddr = frame.m_currentApAddr; auto distance = i.GetDistanceFrom(start); - return distance + WifiMgtHeader::DeserializeImpl(i); + NS_ASSERT_MSG(distance <= length, + "Bytes read (" << distance << ") exceed expected number (" << length << ")"); + return distance + MgtHeaderInPerStaProfile:: + DeserializeFromPerStaProfileImpl(i, length - distance, frame); } void MgtReassocRequestHeader::InitForDeserialization(std::optional& optElem) { optElem.emplace(WIFI_MAC_MGT_REASSOCIATION_REQUEST); + optElem->m_containingFrame = *this; } /*********************************************************** @@ -420,10 +517,7 @@ MgtAssocResponseHeader::GetAssociationId() const uint32_t MgtAssocResponseHeader::GetSerializedSizeImpl() const { - if (auto& mle = Get()) - { - mle->m_containingFrame = *this; - } + SetMleContainingFrame(); uint32_t size = 0; size += m_capability.GetSerializedSize(); @@ -433,6 +527,19 @@ MgtAssocResponseHeader::GetSerializedSizeImpl() const return size; } +uint32_t +MgtAssocResponseHeader::GetSerializedSizeInPerStaProfileImpl( + const MgtAssocResponseHeader& frame) const +{ + uint32_t size = 0; + size += m_capability.GetSerializedSize(); + size += m_code.GetSerializedSize(); + size += + MgtHeaderInPerStaProfile::GetSerializedSizeInPerStaProfileImpl(frame); + return size; +} + void MgtAssocResponseHeader::PrintImpl(std::ostream& os) const { @@ -444,10 +551,7 @@ MgtAssocResponseHeader::PrintImpl(std::ostream& os) const void MgtAssocResponseHeader::SerializeImpl(Buffer::Iterator start) const { - if (auto& mle = Get()) - { - mle->m_containingFrame = *this; - } + SetMleContainingFrame(); Buffer::Iterator i = start; i = m_capability.Serialize(i); @@ -456,6 +560,17 @@ MgtAssocResponseHeader::SerializeImpl(Buffer::Iterator start) const WifiMgtHeader::SerializeImpl(i); } +void +MgtAssocResponseHeader::SerializeInPerStaProfileImpl(Buffer::Iterator start, + const MgtAssocResponseHeader& frame) const +{ + Buffer::Iterator i = start; + i = m_capability.Serialize(i); + i = m_code.Serialize(i); + MgtHeaderInPerStaProfile::SerializeInPerStaProfileImpl(i, frame); +} + uint32_t MgtAssocResponseHeader::DeserializeImpl(Buffer::Iterator start) { @@ -463,14 +578,44 @@ MgtAssocResponseHeader::DeserializeImpl(Buffer::Iterator start) i = m_capability.Deserialize(i); i = m_code.Deserialize(i); m_aid = i.ReadLsbtohU16(); + auto distance = i.GetDistanceFrom(start) + + WifiMgtHeader::DeserializeImpl(i); + if (auto& mle = Get()) + { + for (std::size_t id = 0; id < mle->GetNPerStaProfileSubelements(); id++) + { + auto& perStaProfile = mle->GetPerStaProfile(id); + if (perStaProfile.HasAssocResponse()) + { + auto& frameInPerStaProfile = perStaProfile.GetAssocResponse(); + frameInPerStaProfile.CopyIesFromContainingFrame(*this); + } + } + } + return distance; +} + +uint32_t +MgtAssocResponseHeader::DeserializeFromPerStaProfileImpl(Buffer::Iterator start, + uint16_t length, + const MgtAssocResponseHeader& frame) +{ + Buffer::Iterator i = start; + i = m_capability.Deserialize(i); + i = m_code.Deserialize(i); + m_aid = frame.m_aid; auto distance = i.GetDistanceFrom(start); - return distance + WifiMgtHeader::DeserializeImpl(i); + NS_ASSERT_MSG(distance <= length, + "Bytes read (" << distance << ") exceed expected number (" << length << ")"); + return distance + MgtHeaderInPerStaProfile:: + DeserializeFromPerStaProfileImpl(i, length - distance, frame); } void MgtAssocResponseHeader::InitForDeserialization(std::optional& optElem) { optElem.emplace(WIFI_MAC_MGT_ASSOCIATION_RESPONSE); + optElem->m_containingFrame = *this; } /********************************************************** diff --git a/src/wifi/model/mgt-headers.h b/src/wifi/model/mgt-headers.h index fe41e5c60..1d9840522 100644 --- a/src/wifi/model/mgt-headers.h +++ b/src/wifi/model/mgt-headers.h @@ -42,12 +42,52 @@ #include "ns3/mac48-address.h" #include "ns3/mu-edca-parameter-set.h" #include "ns3/multi-link-element.h" +#include "ns3/tid-to-link-mapping-element.h" #include "ns3/vht-capabilities.h" #include "ns3/vht-operation.h" namespace ns3 { +/** + * Indicate which Information Elements cannot be included in a Per-STA Profile subelement of + * a Basic Multi-Link Element (see Sec. 35.3.3.4 of 802.11be D3.1): + * + * An AP affiliated with an AP MLD shall not include a Timestamp field, a Beacon Interval field, + * an AID field, a BSS Max Idle Period element, a Neighbor Report element, a Reduced Neighbor + * Report element, a Multiple BSSID element, TIM element, Multiple BSSID-Index element, Multiple + * BSSID Configuration element, TID-to-Link Mapping element, Multi-Link Traffic Indication element + * or another Multi- Link element in the STA Profile field of the Basic Multi-Link element. + * + * A non-AP STA affiliated with a non-AP MLD shall not include a Listen Interval field, a Current + * AP Address field, an SSID element, BSS Max Idle Period element or another Multi-Link element in + * the STA Profile field of the Basic Multi-Link element. + */ + +/** \copydoc CanBeInPerStaProfile */ +template <> +struct CanBeInPerStaProfile : std::false_type +{ +}; + +/** \copydoc CanBeInPerStaProfile */ +template <> +struct CanBeInPerStaProfile : std::false_type +{ +}; + +/** \copydoc CanBeInPerStaProfile */ +template <> +struct CanBeInPerStaProfile : std::false_type +{ +}; + +/** \copydoc CanBeInPerStaProfile */ +template <> +struct CanBeInPerStaProfile : std::false_type +{ +}; + /// List of Information Elements included in Probe Request frames using ProbeRequestElems = std::tuple +class MgtAssocRequestHeader + : public MgtHeaderInPerStaProfile { friend class WifiMgtHeader; + friend class MgtHeaderInPerStaProfile; public: ~MgtAssocRequestHeader() override = default; @@ -154,6 +196,34 @@ class MgtAssocRequestHeader : public WifiMgtHeader::InitForDeserialization; @@ -171,9 +241,11 @@ class MgtAssocRequestHeader : public WifiMgtHeader +class MgtReassocRequestHeader + : public MgtHeaderInPerStaProfile { friend class WifiMgtHeader; + friend class MgtHeaderInPerStaProfile; public: ~MgtReassocRequestHeader() override = default; @@ -224,6 +296,34 @@ class MgtReassocRequestHeader : public WifiMgtHeader::InitForDeserialization; @@ -242,9 +342,11 @@ class MgtReassocRequestHeader : public WifiMgtHeader +class MgtAssocResponseHeader + : public MgtHeaderInPerStaProfile { friend class WifiMgtHeader; + friend class MgtHeaderInPerStaProfile; public: ~MgtAssocResponseHeader() override = default; @@ -301,6 +403,34 @@ class MgtAssocResponseHeader : public WifiMgtHeader::InitForDeserialization; diff --git a/src/wifi/test/wifi-eht-info-elems-test.cc b/src/wifi/test/wifi-eht-info-elems-test.cc index 393164d2d..88ee49825 100644 --- a/src/wifi/test/wifi-eht-info-elems-test.cc +++ b/src/wifi/test/wifi-eht-info-elems-test.cc @@ -62,7 +62,8 @@ class BasicMultiLinkElementTest : public HeaderSerializationTestCase private: void DoRun() override; - WifiMacType m_frameType; //!< the type of frame possibly included in the MLE + WifiMacType m_frameType; //!< the type of frame possibly included in the MLE + MgtAssocRequestHeader m_outerAssoc; //!< the frame containing the MLE }; BasicMultiLinkElementTest::BasicMultiLinkElementTest() @@ -114,6 +115,12 @@ BasicMultiLinkElementTest::GetMultiLinkElement( mle.AddPerStaProfileSubelement(); mle.GetPerStaProfile(i) = std::move(subelements[i]); } + + if (!subelements.empty()) + { + mle.m_containingFrame = m_outerAssoc; + } + return mle; } @@ -177,11 +184,14 @@ BasicMultiLinkElementTest::DoRun() perStaProfile.SetAssocRequest(assoc); // Adding Per-STA Profile Subelement - TestHeaderSerialization(GetMultiLinkElement(commonInfo, {perStaProfile}), m_frameType); + TestHeaderSerialization(GetMultiLinkElement(commonInfo, {perStaProfile}), + m_frameType, + m_outerAssoc); // Adding two Per-STA Profile Subelements TestHeaderSerialization(GetMultiLinkElement(commonInfo, {perStaProfile, perStaProfile}), - m_frameType); + m_frameType, + m_outerAssoc); } /** diff --git a/src/wifi/test/wifi-mlo-test.cc b/src/wifi/test/wifi-mlo-test.cc index 4dfe981b6..68d8f2f9d 100644 --- a/src/wifi/test/wifi-mlo-test.cc +++ b/src/wifi/test/wifi-mlo-test.cc @@ -452,7 +452,7 @@ MultiLinkOperationsTestBase::DoSetup() { RngSeedManager::SetSeed(1); RngSeedManager::SetRun(2); - int64_t streamNumber = 100; + int64_t streamNumber = 30; NodeContainer wifiApNode; wifiApNode.Create(1); @@ -1493,7 +1493,7 @@ MultiLinkTxTest::StartTraffic() client2->SetRemote(socket); m_sourceMac->GetDevice()->GetNode()->AddApplication(client2); // start during transmission of first A-MPDU, if multiple links are setup - client2->SetStartTime(MilliSeconds(3)); + client2->SetStartTime(MilliSeconds(4)); client2->SetStopTime(duration); }