From e87b1e0d342108843af8d696ce7f4aad7e64b9ac Mon Sep 17 00:00:00 2001 From: Rami Abdallah Date: Thu, 24 Aug 2023 10:35:06 +0200 Subject: [PATCH] wifi: Add support for FILS Discovery action frame --- src/wifi/model/mgt-action-headers.cc | 414 +++++++++++++++++++++++++++ src/wifi/model/mgt-action-headers.h | 174 +++++++++++ utils/codespell-ignored-words | 1 + 3 files changed, 589 insertions(+) diff --git a/src/wifi/model/mgt-action-headers.cc b/src/wifi/model/mgt-action-headers.cc index 6a368ad9c..9759853ce 100644 --- a/src/wifi/model/mgt-action-headers.cc +++ b/src/wifi/model/mgt-action-headers.cc @@ -25,6 +25,9 @@ #include "ns3/multi-link-element.h" #include "ns3/packet.h" +#include "ns3/simulator.h" + +#include namespace ns3 { @@ -191,6 +194,9 @@ WifiActionHeader::GetAction() const case QAB_RESPONSE: retval.publicAction = QAB_RESPONSE; break; + case FILS_DISCOVERY: + retval.publicAction = FILS_DISCOVERY; + break; default: NS_FATAL_ERROR("Unknown public action code"); retval.publicAction = QAB_REQUEST; /* quiet compiler */ @@ -553,6 +559,7 @@ WifiActionHeader::Print(std::ostream& os) const { CASE_ACTION_VALUE(QAB_REQUEST); CASE_ACTION_VALUE(QAB_RESPONSE); + CASE_ACTION_VALUE(FILS_DISCOVERY); default: NS_FATAL_ERROR("Unknown public action code"); } @@ -1349,4 +1356,411 @@ MgtEmlOmn::GetLinkBitmap() const return list; } +/*************************************************** + * FILS Discovery + ****************************************************/ + +NS_OBJECT_ENSURE_REGISTERED(FilsDiscHeader); + +TypeId +FilsDiscHeader::GetTypeId() +{ + static TypeId tid = TypeId("ns3::FilsDiscHeader") + .SetParent
() + .SetGroupName("Wifi") + .AddConstructor(); + return tid; +} + +TypeId +FilsDiscHeader::GetInstanceTypeId() const +{ + return GetTypeId(); +} + +FilsDiscHeader::FilsDiscHeader() + : m_len(m_frameCtl.m_lenPresenceInd), + m_fdCap(m_frameCtl.m_capPresenceInd), + m_primaryCh(m_frameCtl.m_primChPresenceInd), + m_apConfigSeqNum(m_frameCtl.m_apCsnPresenceInd), + m_accessNetOpt(m_frameCtl.m_anoPresenceInd), + m_chCntrFreqSeg1(m_frameCtl.m_chCntrFreqSeg1PresenceInd) +{ +} + +void +FilsDiscHeader::SetSsid(const std::string& ssid) +{ + m_ssid = ssid; + m_frameCtl.m_ssidLen = ssid.length() - 1; +} + +const std::string& +FilsDiscHeader::GetSsid() const +{ + return m_ssid; +} + +uint32_t +FilsDiscHeader::GetInformationFieldSize() const +{ + auto size = GetSizeNonOptSubfields(); + size += m_len.has_value() ? 1 : 0; + size += m_fdCap.has_value() ? 2 : 0; + size += m_opClass.has_value() ? 1 : 0; + size += m_primaryCh.has_value() ? 1 : 0; + size += m_apConfigSeqNum.has_value() ? 1 : 0; + size += m_accessNetOpt.has_value() ? 1 : 0; + size += m_chCntrFreqSeg1.has_value() ? 1 : 0; + return size; +} + +uint32_t +FilsDiscHeader::GetSerializedSize() const +{ + auto size = GetInformationFieldSize(); + // Optional elements + size += m_rnr.has_value() ? m_rnr->GetSerializedSize() : 0; + size += m_tim.has_value() ? m_tim->GetSerializedSize() : 0; + return size; +} + +uint32_t +FilsDiscHeader::GetSizeNonOptSubfields() const +{ + return 2 /* FILS Discovery Frame Control */ + + 8 /* Timestamp */ + + 2 /* Beacon Interval */ + + m_ssid.length(); /* SSID */ +} + +void +FilsDiscHeader::SetLengthSubfield() +{ + m_len.reset(); // so that Length size is not included by GetInformationFieldSize() + auto infoFieldSize = GetInformationFieldSize(); + auto nonOptSubfieldsSize = GetSizeNonOptSubfields(); + NS_ABORT_MSG_IF(infoFieldSize < nonOptSubfieldsSize, "Length subfield is less than 0"); + m_len = infoFieldSize - nonOptSubfieldsSize; +} + +void +FilsDiscHeader::Print(std::ostream& os) const +{ + os << "Control=" << m_frameCtl << ", " + << "Time Stamp=" << m_timeStamp << ", " + << "Beacon Interval=" << m_beaconInt << ", " + << "SSID=" << m_ssid << ", "; + if (m_len.has_value()) + { + os << "Length=" << *m_len << ", "; + } + if (m_fdCap.has_value()) + { + os << "FD Capability=" << *m_fdCap << ", "; + } + if (m_opClass.has_value()) + { + os << "Operating Class=" << *m_opClass << ", "; + } + if (m_primaryCh.has_value()) + { + os << "Primary Channel=" << *m_primaryCh << ", "; + } + if (m_apConfigSeqNum.has_value()) + { + os << "AP-CSN=" << *m_apConfigSeqNum << ", "; + } + if (m_accessNetOpt.has_value()) + { + os << "ANO=" << *m_accessNetOpt << ", "; + } + if (m_chCntrFreqSeg1.has_value()) + { + os << "Channel Center Frequency Seg 1=" << *m_chCntrFreqSeg1 << ", "; + } + if (m_tim.has_value()) + { + os << "Traffic Indicator Map=" << *m_tim; + } +} + +void +FilsDiscHeader::Serialize(Buffer::Iterator start) const +{ + Buffer::Iterator i = start; + m_frameCtl.Serialize(i); + i.WriteHtolsbU64(Simulator::Now().GetMicroSeconds()); // Time stamp + i.WriteHtolsbU16(m_beaconInt); + i.Write(reinterpret_cast(m_ssid.data()), m_ssid.length()); + if (m_len.has_value()) + { + i.WriteU8(*m_len); + } + if (m_fdCap.has_value()) + { + m_fdCap->Serialize(i); + } + NS_ASSERT(m_opClass.has_value() == m_primaryCh.has_value()); + if (m_opClass.has_value()) + { + i.WriteU8(*m_opClass); + } + if (m_primaryCh.has_value()) + { + i.WriteU8(*m_primaryCh); + } + if (m_apConfigSeqNum.has_value()) + { + i.WriteU8(*m_apConfigSeqNum); + } + if (m_accessNetOpt.has_value()) + { + i.WriteU8(*m_accessNetOpt); + } + if (m_chCntrFreqSeg1.has_value()) + { + i.WriteU8(*m_chCntrFreqSeg1); + } + i = m_rnr.has_value() ? m_rnr->Serialize(i) : i; + i = m_tim.has_value() ? m_tim->Serialize(i) : i; +} + +uint32_t +FilsDiscHeader::Deserialize(Buffer::Iterator start) +{ + Buffer::Iterator i = start; + auto nOctets = m_frameCtl.Deserialize(i); + i.Next(nOctets); + m_timeStamp = i.ReadLsbtohU64(); + m_beaconInt = i.ReadLsbtohU16(); + std::vector ssid(m_frameCtl.m_ssidLen + 2); + i.Read(ssid.data(), m_frameCtl.m_ssidLen + 1); + ssid[m_frameCtl.m_ssidLen + 1] = 0; + m_ssid = std::string(reinterpret_cast(ssid.data())); + // Optional subfields + if (m_frameCtl.m_lenPresenceInd) + { + m_len = i.ReadU8(); + } + if (m_frameCtl.m_capPresenceInd) + { + nOctets = m_fdCap->Deserialize(i); + i.Next(nOctets); + } + if (m_frameCtl.m_primChPresenceInd) + { + m_opClass = i.ReadU8(); + m_primaryCh = i.ReadU8(); + } + if (m_frameCtl.m_apCsnPresenceInd) + { + m_apConfigSeqNum = i.ReadU8(); + } + if (m_frameCtl.m_anoPresenceInd) + { + m_accessNetOpt = i.ReadU8(); + } + if (m_frameCtl.m_chCntrFreqSeg1PresenceInd) + { + m_chCntrFreqSeg1 = i.ReadU8(); + } + // Optional elements + m_rnr.emplace(); + auto tmp = i; + i = m_rnr->DeserializeIfPresent(i); + if (i.GetDistanceFrom(tmp) == 0) + { + m_rnr.reset(); + } + + m_tim.emplace(); + tmp = i; + i = m_tim->DeserializeIfPresent(i); + if (i.GetDistanceFrom(tmp) == 0) + { + m_tim.reset(); + } + + return i.GetDistanceFrom(start); +} + +std::ostream& +operator<<(std::ostream& os, const FilsDiscHeader::FilsDiscFrameControl& control) +{ + os << "ssidLen:" << control.m_ssidLen << " capPresenceInd:" << control.m_capPresenceInd + << " shortSsidInd:" << control.m_shortSsidInd + << " apCsnPresenceInd:" << control.m_apCsnPresenceInd + << " anoPresenceInd:" << control.m_anoPresenceInd + << " chCntrFreqSeg1PresenceInd:" << control.m_chCntrFreqSeg1PresenceInd + << " primChPresenceInd:" << control.m_primChPresenceInd + << " rsnInfoPresenceInd:" << control.m_rsnInfoPresenceInd + << " lenPresenceInd:" << control.m_lenPresenceInd + << " mdPresenceInd:" << control.m_mdPresenceInd; + return os; +} + +void +FilsDiscHeader::FilsDiscFrameControl::Serialize(Buffer::Iterator& start) const +{ + uint16_t val = m_ssidLen | ((m_capPresenceInd ? 1 : 0) << 5) | (m_shortSsidInd << 6) | + ((m_apCsnPresenceInd ? 1 : 0) << 7) | ((m_anoPresenceInd ? 1 : 0) << 8) | + ((m_chCntrFreqSeg1PresenceInd ? 1 : 0) << 9) | + ((m_primChPresenceInd ? 1 : 0) << 10) | (m_rsnInfoPresenceInd << 11) | + ((m_lenPresenceInd ? 1 : 0) << 12) | (m_mdPresenceInd << 13); + start.WriteHtolsbU16(val); +} + +uint32_t +FilsDiscHeader::FilsDiscFrameControl::Deserialize(Buffer::Iterator start) +{ + auto val = start.ReadLsbtohU16(); + + m_ssidLen = val & 0x001f; + m_capPresenceInd = ((val >> 5) & 0x0001) == 1; + m_shortSsidInd = (val >> 6) & 0x0001; + m_apCsnPresenceInd = ((val >> 7) & 0x0001) == 1; + m_anoPresenceInd = ((val >> 8) & 0x0001) == 1; + m_chCntrFreqSeg1PresenceInd = ((val >> 9) & 0x0001) == 1; + m_primChPresenceInd = ((val >> 10) & 0x0001) == 1; + m_rsnInfoPresenceInd = (val >> 11) & 0x0001; + m_lenPresenceInd = ((val >> 12) & 0x0001) == 1; + m_mdPresenceInd = (val >> 13) & 0x0001; + + return 2; +} + +std::ostream& +operator<<(std::ostream& os, const FilsDiscHeader::FdCapability& capability) +{ + os << "ess:" << capability.m_ess << " privacy:" << capability.m_privacy + << " channelWidth:" << capability.m_chWidth << " maxNss:" << capability.m_maxNss + << " multiBssidInd:" << capability.m_multiBssidPresenceInd + << " phyIdx:" << capability.m_phyIdx << " minRate:" << capability.m_minRate; + return os; +} + +void +FilsDiscHeader::FdCapability::Serialize(Buffer::Iterator& start) const +{ + uint16_t val = m_ess | (m_privacy << 1) | (m_chWidth << 2) | (m_maxNss << 5) | + (m_multiBssidPresenceInd << 9) | (m_phyIdx << 10) | (m_minRate << 13); + start.WriteHtolsbU16(val); +} + +uint32_t +FilsDiscHeader::FdCapability::Deserialize(Buffer::Iterator start) +{ + auto val = start.ReadLsbtohU16(); + + m_ess = val & 0x0001; + m_privacy = (val >> 1) & 0x0001; + m_chWidth = (val >> 2) & 0x0007; + m_maxNss = (val >> 5) & 0x0007; + m_multiBssidPresenceInd = (val >> 9) & 0x0001; + m_phyIdx = (val >> 10) & 0x0007; + m_minRate = (val >> 13) & 0x0007; + + return 2; +} + +void +FilsDiscHeader::FdCapability::SetOpChannelWidth(uint16_t width) +{ + m_chWidth = (width == 20 || width == 22) ? 0 + : (width == 40) ? 1 + : (width == 80) ? 2 + : (width == 160) ? 3 + : 4; +} + +uint16_t +FilsDiscHeader::FdCapability::GetOpChannelWidth() const +{ + switch (m_chWidth) + { + case 0: + return m_phyIdx == 0 ? 22 : 20; // PHY Index 0 indicates 802.11b + case 1: + return 40; + case 2: + return 80; + case 3: + return 160; + default: + NS_ABORT_MSG("Reserved value: " << +m_chWidth); + } + return 0; +} + +void +FilsDiscHeader::FdCapability::SetMaxNss(uint8_t maxNss) +{ + NS_ABORT_MSG_IF(maxNss < 1, "NSS is equal to 0"); + maxNss--; + // 4 is the maximum value for the Maximum Number of Spatial Streams subfield + m_maxNss = std::min(maxNss, 4); +} + +uint8_t +FilsDiscHeader::FdCapability::GetMaxNss() const +{ + return m_maxNss + 1; +} + +void +FilsDiscHeader::FdCapability::SetStandard(WifiStandard standard) +{ + switch (standard) + { + case WIFI_STANDARD_80211b: + m_phyIdx = 0; + break; + case WIFI_STANDARD_80211a: + case WIFI_STANDARD_80211g: + m_phyIdx = 1; + break; + case WIFI_STANDARD_80211n: + m_phyIdx = 2; + break; + case WIFI_STANDARD_80211ac: + m_phyIdx = 3; + break; + case WIFI_STANDARD_80211ax: + m_phyIdx = 4; + break; + case WIFI_STANDARD_80211be: + m_phyIdx = 5; + break; + default: + NS_ABORT_MSG("Unsupported standard: " << standard); + } +} + +WifiStandard +FilsDiscHeader::FdCapability::GetStandard(WifiPhyBand band) const +{ + switch (m_phyIdx) + { + case 0: + return WIFI_STANDARD_80211b; + case 1: + NS_ABORT_MSG_IF(band != WIFI_PHY_BAND_2_4GHZ && band != WIFI_PHY_BAND_5GHZ, + "Invalid PHY band (" << band << ") with PHY index of 1"); + return band == WIFI_PHY_BAND_5GHZ ? WIFI_STANDARD_80211a : WIFI_STANDARD_80211g; + case 2: + return WIFI_STANDARD_80211n; + case 3: + return WIFI_STANDARD_80211ac; + case 4: + return WIFI_STANDARD_80211ax; + case 5: + return WIFI_STANDARD_80211be; + default: + NS_ABORT_MSG("Invalid PHY index: " << m_phyIdx); + } + + return WIFI_STANDARD_UNSPECIFIED; +} + } // namespace ns3 diff --git a/src/wifi/model/mgt-action-headers.h b/src/wifi/model/mgt-action-headers.h index 73a1f1bbe..53b1c5f83 100644 --- a/src/wifi/model/mgt-action-headers.h +++ b/src/wifi/model/mgt-action-headers.h @@ -22,7 +22,11 @@ #ifndef MGT_ACTION_HEADERS_H #define MGT_ACTION_HEADERS_H +#include "reduced-neighbor-report.h" #include "status-code.h" +#include "tim.h" +#include "wifi-opt-field.h" +#include "wifi-standards.h" #include "ns3/header.h" @@ -99,6 +103,7 @@ class WifiActionHeader : public Header { QAB_REQUEST = 16, QAB_RESPONSE = 17, + FILS_DISCOVERY = 34 }; /// RadioMeasurementActionValue enumeration @@ -666,6 +671,175 @@ class MgtEmlOmn : public Header std::optional m_emlsrParamUpdate{}; //!< EMLSR Parameter Update field }; +/** + * \ingroup wifi + * Implement the FILS (Fast Initial Link Setup) action frame. + * See sec. 9.6.7.36 of IEEE 802.11-2020 and IEEE 802.11ax-2021. + */ +class FilsDiscHeader : public Header +{ + public: + FilsDiscHeader(); + + /// \return the object TypeId + static TypeId GetTypeId(); + TypeId GetInstanceTypeId() const override; + void Print(std::ostream& os) const override; + uint32_t GetSerializedSize() const override; + void Serialize(Buffer::Iterator start) const override; + uint32_t Deserialize(Buffer::Iterator start) override; + + /** + * Set the SSID field. + * + * \param ssid the SSID + */ + void SetSsid(const std::string& ssid); + + /// \return the SSID + const std::string& GetSsid() const; + + /// \return size of FILS Discovery Information field in octets + uint32_t GetInformationFieldSize() const; + + /// \return size of non-optional subfields in octets + uint32_t GetSizeNonOptSubfields() const; + + /// \brief sets value of Length subfield + void SetLengthSubfield(); + + /// FILS Discovery Frame Control subfield of FILS Discovery Information field + struct FilsDiscFrameControl // 2 octets + { + uint8_t m_ssidLen : 5 {0}; ///< SSID Length + bool m_capPresenceInd{false}; ///< Capability Presence Indicator + uint8_t m_shortSsidInd : 1 {0}; ///< Short SSID Indicator (not supported) + bool m_apCsnPresenceInd{false}; ///< AP-CSN Presence Indicator + bool m_anoPresenceInd{false}; ///< ANO Presence Indicator + bool m_chCntrFreqSeg1PresenceInd{false}; ///< Channel Center Frequency Segment 1 + ///< Presence Indicator + bool m_primChPresenceInd{false}; ///< Primary Channel Presence Indicator + uint8_t m_rsnInfoPresenceInd : 1 {0}; ///< RSN info Presence Indicator (not supported) + bool m_lenPresenceInd{false}; ///< Length Presence Indicator + uint8_t m_mdPresenceInd : 1 {0}; ///< MD Presence Indicator (not supported) + uint8_t m_reserved : 2 {0}; ///< Reserved Bits + + /** + * \brief serialize content to a given buffer + * \param start given input buffer iterator + */ + void Serialize(Buffer::Iterator& start) const; + + /** + * \brief read content from a given buffer + * \param start input buffer iterator + * \return number of read octets + */ + uint32_t Deserialize(Buffer::Iterator start); + }; + + /// FD Capability subfield of FILS Discovery Information field + struct FdCapability // 2 octets + { + uint8_t m_ess : 1 {0}; ///< ESS + uint8_t m_privacy : 1 {0}; ///< Privacy + uint8_t m_chWidth : 3 {0}; ///< BSS Operating Channel Width + uint8_t m_maxNss : 3 {0}; ///< Maximum Number of Spatial Streams + uint8_t m_reserved : 1 {0}; ///< Reserved Bit + uint8_t m_multiBssidPresenceInd : 1 {0}; ///< Multiple BSSIDs Presence Indicator + uint8_t m_phyIdx : 3 {0}; ///< PHY Index + uint8_t m_minRate : 3 {0}; ///< FILS Minimum Rate + + /** + * \brief Set the BSS Operating Channel Width field based on the operating channel width + * \param width the operating channel width in MHz + */ + void SetOpChannelWidth(uint16_t width); + + /// \return the operating channel width encoded in the BSS Operating Channel Width field + uint16_t GetOpChannelWidth() const; + + /** + * \brief Set the Maximum Number of Spatial Streams field + * \param maxNss the maximum number of supported spatial streams + */ + void SetMaxNss(uint8_t maxNss); + + /** + * Note that this function returns 5 if the maximum number of supported spatial streams + * is greater than 4. + * + * \return the maximum number of supported spatial streams + */ + uint8_t GetMaxNss() const; + + /** + * \brief Set the PHY Index field based on the given wifi standard + * \param standard the wifi standard + */ + void SetStandard(WifiStandard standard); + + /** + * \param band the PHY band in which the device is operating (needed to distinguish + * between 802.11a and 802.11g) + * \return the wifi standard encoded in the PHY Index field + */ + WifiStandard GetStandard(WifiPhyBand band) const; + + /** + * \brief serialize content to a given buffer + * \param start given input buffer iterator + */ + void Serialize(Buffer::Iterator& start) const; + + /** + * \brief read content from a given buffer + * \param start input buffer iterator + * \return number of read octets + */ + uint32_t Deserialize(Buffer::Iterator start); + }; + + // FILS Discovery Frame Information field + // TODO: add optional FD-RSN and Mobility domain subfields + FilsDiscFrameControl m_frameCtl; ///< FILS Discovery Frame Control + uint64_t m_timeStamp{0}; ///< Timestamp + uint16_t m_beaconInt{0}; ///< Beacon Interval in TU (1024 us) + OptFieldWithPresenceInd m_len; ///< Length + OptFieldWithPresenceInd m_fdCap; ///< FD Capability + std::optional m_opClass; ///< Operating Class + OptFieldWithPresenceInd m_primaryCh; ///< Primary Channel + OptFieldWithPresenceInd + m_apConfigSeqNum; ///< AP Configuration Sequence Number (AP-CSN) + OptFieldWithPresenceInd m_accessNetOpt; ///< Access Network Options + OptFieldWithPresenceInd m_chCntrFreqSeg1; ///< Channel Center Frequency Segment 1 + + // (Optional) Information Elements + std::optional m_rnr; ///< Reduced Neighbor Report + std::optional m_tim; ///< Traffic Indication Map element + + private: + std::string m_ssid; ///< SSID +}; + +/** + * \brief Stream insertion operator. + * + * \param os the output stream + * \param control the Fils Discovery Frame Control field + * \returns a reference to the stream + */ +std::ostream& operator<<(std::ostream& os, const FilsDiscHeader::FilsDiscFrameControl& control); + +/** + * \brief Stream insertion operator. + * + * \param os the output stream + * \param capability the Fils Discovery Frame Capability field + * \returns a reference to the stream + */ +std::ostream& operator<<(std::ostream& os, const FilsDiscHeader::FdCapability& capability); + } // namespace ns3 #endif /* MGT_ACTION_HEADERS_H */ diff --git a/utils/codespell-ignored-words b/utils/codespell-ignored-words index eac37a4b4..6ad8e7c23 100644 --- a/utils/codespell-ignored-words +++ b/utils/codespell-ignored-words @@ -26,6 +26,7 @@ interm #Wi-Fi aci +fils #Wimax aas