wifi: Add support for FILS Discovery action frame

This commit is contained in:
Rami Abdallah
2023-08-24 10:35:06 +02:00
committed by Stefano Avallone
parent 4fcdc10998
commit e87b1e0d34
3 changed files with 589 additions and 0 deletions

View File

@@ -25,6 +25,9 @@
#include "ns3/multi-link-element.h"
#include "ns3/packet.h"
#include "ns3/simulator.h"
#include <vector>
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<Header>()
.SetGroupName("Wifi")
.AddConstructor<FilsDiscHeader>();
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<const uint8_t*>(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<uint8_t> 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<char*>(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<uint8_t>(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

View File

@@ -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<EmlsrParamUpdate> 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<uint8_t> m_len; ///< Length
OptFieldWithPresenceInd<FdCapability> m_fdCap; ///< FD Capability
std::optional<uint8_t> m_opClass; ///< Operating Class
OptFieldWithPresenceInd<uint8_t> m_primaryCh; ///< Primary Channel
OptFieldWithPresenceInd<uint8_t>
m_apConfigSeqNum; ///< AP Configuration Sequence Number (AP-CSN)
OptFieldWithPresenceInd<uint8_t> m_accessNetOpt; ///< Access Network Options
OptFieldWithPresenceInd<uint8_t> m_chCntrFreqSeg1; ///< Channel Center Frequency Segment 1
// (Optional) Information Elements
std::optional<ReducedNeighborReport> m_rnr; ///< Reduced Neighbor Report
std::optional<Tim> 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 */

View File

@@ -26,6 +26,7 @@ interm
#Wi-Fi
aci
fils
#Wimax
aas