diff --git a/src/wifi/CMakeLists.txt b/src/wifi/CMakeLists.txt index d7f65ffd7..0aa984c73 100644 --- a/src/wifi/CMakeLists.txt +++ b/src/wifi/CMakeLists.txt @@ -277,6 +277,7 @@ set(header_files model/wifi-mac-queue.h model/wifi-mac-trailer.h model/wifi-mac.h + model/wifi-mgt-header.h model/wifi-mode.h model/wifi-mpdu-type.h model/wifi-mpdu.h diff --git a/src/wifi/model/wifi-information-element.h b/src/wifi/model/wifi-information-element.h index b27f7e834..28ead92f9 100644 --- a/src/wifi/model/wifi-information-element.h +++ b/src/wifi/model/wifi-information-element.h @@ -305,6 +305,18 @@ class WifiInformationElement : public SimpleRefCount * \return an iterator */ Buffer::Iterator Deserialize(Buffer::Iterator i); + /** + * Deserialize entire IE (which may possibly be fragmented into multiple + * elements) if it is present. The iterator passed in + * must be pointing at the Element ID of an information element. If + * the Element ID is not the one that the given class is interested + * in then it will return the same iterator. + * + * \param i an iterator which points to where the IE should be read. + * + * \return an iterator + */ + Buffer::Iterator DeserializeIfPresent(Buffer::Iterator i); /** * Deserialize an entire IE (which may possibly be fragmented into multiple * elements) that is optionally present. The iterator passed in @@ -365,18 +377,6 @@ class WifiInformationElement : public SimpleRefCount virtual bool operator==(const WifiInformationElement& a) const; private: - /** - * Deserialize entire IE (which may possibly be fragmented into multiple - * elements) if it is present. The iterator passed in - * must be pointing at the Element ID of an information element. If - * the Element ID is not the one that the given class is interested - * in then it will return the same iterator. - * - * \param i an iterator which points to where the IE should be read. - * - * \return an iterator - */ - Buffer::Iterator DeserializeIfPresent(Buffer::Iterator i); /** * Serialize an IE that needs to be fragmented. * diff --git a/src/wifi/model/wifi-mgt-header.h b/src/wifi/model/wifi-mgt-header.h new file mode 100644 index 000000000..281c4842b --- /dev/null +++ b/src/wifi/model/wifi-mgt-header.h @@ -0,0 +1,482 @@ +/* + * Copyright (c) 2023 Universita' degli Studi di Napoli Federico II + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Stefano Avallone + */ + +#ifndef WIFI_MGT_HEADER_H +#define WIFI_MGT_HEADER_H + +#include "ns3/eht-capabilities.h" +#include "ns3/header.h" +#include "ns3/supported-rates.h" + +#include +#include +#include +#include +#include + +namespace ns3 +{ + +namespace internal +{ + +/** + * \ingroup object + * \tparam T \explicit An Information Element type + * + * Provides the type used to store Information Elements in the tuple held by WifiMgtHeader: + * - a mandatory Information Element of type T is stored as std::optional\ + * - an optional Information Element of type T is stored as std::optional\ + * - an Information Element of type T that can appear 0 or more times is stored as std::vector\ + */ +template +struct GetStoredIe +{ + /// typedef for the resulting optional type + typedef std::optional type; +}; + +/** \copydoc GetStoredIe */ +template +struct GetStoredIe> +{ + /// typedef for the resulting optional type + typedef std::optional type; +}; + +/** \copydoc GetStoredIe */ +template +struct GetStoredIe> +{ + /// typedef for the resulting optional type + typedef std::vector type; +}; + +/** \copydoc GetStoredIe */ +template +using GetStoredIeT = typename GetStoredIe::type; + +} // namespace internal + +/** + * \ingroup wifi + * Implement the header for management frames. + * \tparam Derived \explicit the type of derived management frame + * \tparam Tuple \explicit A tuple of the types of Information Elements included in the mgt frame + */ +template +class WifiMgtHeader; + +/** + * \ingroup wifi + * Base class for implementing management frame headers. This class adopts the CRTP idiom, + * mainly to allow subclasses to specialize the method used to initialize Information + * Elements before deserialization (InitForDeserialization). + * + * The sorted list of Information Elements that can be included in the management frame implemented + * as a subclass of this class is provided as the template parameter pack. Specifically: + * - the type of a mandatory Information Element IE is IE + * - the type of an optional Information Element IE is std::optional\ + * - the type of an Information Element IE that can appear zero or more times is std::vector\ + * + * \tparam Derived \explicit the type of derived management frame + * \tparam Elems \explicit sorted list of Information Elements that can be included in mgt frame + */ +template +class WifiMgtHeader> : public Header +{ + public: + /** + * Access a (mandatory or optional) Information Element. + * + * \tparam T \explicit the type of the Information Element to return + * \return a reference to the Information Element of the given type + */ + template , Elems> + ...) == 0, int> = 0> + std::optional& Get(); + + /** + * Access a (mandatory or optional) Information Element. + * + * \tparam T \explicit the type of the Information Element to return + * \return a const reference to the Information Element of the given type + */ + template , Elems> + ...) == 0, int> = 0> + const std::optional& Get() const; + + /** + * Access an Information Element that can be present zero or more times. + * + * \tparam T \explicit the type of the Information Element to return + * \return a reference to the Information Element of the given type + */ + template , Elems> + ...) == 1, int> = 0> + std::vector& Get(); + + /** + * Access an Information Element that can be present zero or more times. + * + * \tparam T \explicit the type of the Information Element to return + * \return a reference to the Information Element of the given type + */ + template , Elems> + ...) == 1, int> = 0> + const std::vector& Get() const; + + void Print(std::ostream& os) const final; + uint32_t GetSerializedSize() const final; + void Serialize(Buffer::Iterator start) const final; + uint32_t Deserialize(Buffer::Iterator start) final; + + protected: + /** + * \tparam IE \deduced the type of the Information Element to initialize for deserialization + * \param optElem the object to initialize for deserializing the information element into + * + * The Information Element object is constructed by calling the object's default constructor. + */ + template + void InitForDeserialization(std::optional& optElem); + + /** + * \param optElem the EhtCapabilities object to initialize for deserializing the + * information element into + */ + void InitForDeserialization(std::optional& optElem); + + /** \copydoc ns3::Header::Print */ + void PrintImpl(std::ostream& os) const; + /** \copydoc ns3::Header::GetSerializedSize */ + uint32_t GetSerializedSizeImpl() const; + /** \copydoc ns3::Header::Serialize */ + void SerializeImpl(Buffer::Iterator start) const; + /** \copydoc ns3::Header::Deserialize */ + uint32_t DeserializeImpl(Buffer::Iterator start); + + private: + /** + * \tparam T \deduced the type of the Information Element + * \param elem the optional Information Element + * \param start the buffer iterator pointing to where deserialization starts + * \return an iterator pointing to where deserialization terminated + */ + template + Buffer::Iterator DoDeserialize(std::optional& elem, Buffer::Iterator start); + + /** + * \tparam T \deduced the type of the Information Elements + * \param elems a vector of Information Elements + * \param start the buffer iterator pointing to where deserialization starts + * \return an iterator pointing to where deserialization terminated + */ + template + Buffer::Iterator DoDeserialize(std::vector& elems, Buffer::Iterator start); + + /// type of the Information Elements contained by this frame + using Elements = std::tuple...>; + + Elements m_elements; //!< Information Elements contained by this frame +}; + +/** + * Implementation of the templates declared above. + */ + +template +template , Elems> + ...) == 0, int>> +std::optional& +WifiMgtHeader>::Get() +{ + return std::get>(m_elements); +} + +template +template , Elems> + ...) == 0, int>> +const std::optional& +WifiMgtHeader>::Get() const +{ + return std::get>(m_elements); +} + +template +template , Elems> + ...) == 1, int>> +std::vector& +WifiMgtHeader>::Get() +{ + return std::get>(m_elements); +} + +template +template , Elems> + ...) == 1, int>> +const std::vector& +WifiMgtHeader>::Get() const +{ + return std::get>(m_elements); +} + +template +template +void +WifiMgtHeader>::InitForDeserialization(std::optional& optElem) +{ + optElem.emplace(); +} + +template +void +WifiMgtHeader>::InitForDeserialization( + std::optional& optElem) +{ + NS_ASSERT(Get()); + auto rates = AllSupportedRates{*Get(), std::nullopt}; + const bool is2_4Ghz = rates.IsSupportedRate( + 1000000 /* 1 Mbit/s */); // TODO: use presence of VHT capabilities IE and HE 6 GHz Band + // Capabilities IE once the later is implemented + auto& heCapabilities = Get(); + if (heCapabilities) + { + optElem.emplace(is2_4Ghz, heCapabilities.value()); + } + else + { + optElem.emplace(); + } +} + +namespace internal +{ + +/** + * \tparam T \deduced the type of the Information Element + * \param elem the optional Information Element + * \return the serialized size of the Information Element, if present, or 0, otherwise + */ +template +uint16_t +DoGetSerializedSize(const std::optional& elem) +{ + return elem.has_value() ? elem->GetSerializedSize() : 0; +} + +/** + * \tparam T \deduced the type of the Information Elements + * \param elems a vector of Information Elements + * \return the serialized size of the Information Elements + */ +template +uint16_t +DoGetSerializedSize(const std::vector& elems) +{ + return std::accumulate(elems.cbegin(), elems.cend(), 0, [](uint16_t a, const auto& b) { + return b.GetSerializedSize() + a; + }); +} + +} // namespace internal + +template +uint32_t +WifiMgtHeader>::GetSerializedSize() const +{ + return static_cast(this)->GetSerializedSizeImpl(); +} + +template +uint32_t +WifiMgtHeader>::GetSerializedSizeImpl() const +{ + return std::apply([&](auto&... elems) { return (internal::DoGetSerializedSize(elems) + ...); }, + m_elements); +} + +namespace internal +{ + +/** + * \tparam T \deduced the type of the Information Element + * \param elem the optional Information Element + * \param start the buffer iterator pointing to where serialization starts + * \return an iterator pointing to where serialization terminated + */ +template +Buffer::Iterator +DoSerialize(const std::optional& elem, Buffer::Iterator start) +{ + return elem.has_value() ? elem->Serialize(start) : start; +} + +/** + * \tparam T \deduced the type of the Information Elements + * \param elems a vector of Information Elements + * \param start the buffer iterator pointing to where serialization starts + * \return an iterator pointing to where serialization terminated + */ +template +Buffer::Iterator +DoSerialize(const std::vector& elems, Buffer::Iterator start) +{ + return std::accumulate(elems.cbegin(), + elems.cend(), + start, + [](Buffer::Iterator i, const auto& a) { return a.Serialize(i); }); +} + +} // namespace internal + +template +void +WifiMgtHeader>::Serialize(Buffer::Iterator start) const +{ + static_cast(this)->SerializeImpl(start); +} + +template +void +WifiMgtHeader>::SerializeImpl(Buffer::Iterator start) const +{ + auto i = start; + std::apply([&](auto&... elems) { ((i = internal::DoSerialize(elems, i)), ...); }, m_elements); +} + +template +template +Buffer::Iterator +WifiMgtHeader>::DoDeserialize(std::optional& elem, + Buffer::Iterator start) +{ + auto i = start; + static_cast(this)->InitForDeserialization(elem); + i = elem->DeserializeIfPresent(i); + if (i.GetDistanceFrom(start) == 0) + { + elem.reset(); // the element is not present + } + return i; +} + +template +template +Buffer::Iterator +WifiMgtHeader>::DoDeserialize(std::vector& elems, + Buffer::Iterator start) +{ + auto i = start; + do + { + auto tmp = i; + std::optional item; + static_cast(this)->InitForDeserialization(item); + i = item->DeserializeIfPresent(i); + if (i.GetDistanceFrom(tmp) == 0) + { + break; + } + elems.push_back(std::move(*item)); + } while (true); + return i; +} + +template +uint32_t +WifiMgtHeader>::Deserialize(Buffer::Iterator start) +{ + return static_cast(this)->DeserializeImpl(start); +} + +template +uint32_t +WifiMgtHeader>::DeserializeImpl(Buffer::Iterator start) +{ + auto i = start; + + std::apply( + [&](auto&... elems) { + ( + [&] { + if constexpr (std::is_same_v, Elems>) + { + // optional IE or IE that can be present 0 or more times + i = DoDeserialize(elems, i); + } + else + { + // mandatory IE + static_cast(this)->InitForDeserialization(elems); + i = elems->Deserialize(i); + } + }(), + ...); + }, + m_elements); + + return i.GetDistanceFrom(start); +} + +namespace internal +{ + +/** + * \tparam T \deduced the type of the Information Element + * \param elem the optional Information Element + * \param os the output stream + */ +template +void +DoPrint(const std::optional& elem, std::ostream& os) +{ + if (elem.has_value()) + { + os << *elem << " , "; + } +} + +/** + * \tparam T \deduced the type of the Information Elements + * \param elems a vector of Information Elements + * \param os the output stream + */ +template +void +DoPrint(const std::vector& elems, std::ostream& os) +{ + std::copy(elems.cbegin(), elems.cend(), std::ostream_iterator(os, " , ")); +} + +} // namespace internal + +template +void +WifiMgtHeader>::Print(std::ostream& os) const +{ + static_cast(this)->PrintImpl(os); +} + +template +void +WifiMgtHeader>::PrintImpl(std::ostream& os) const +{ + std::apply([&](auto&... elems) { ((internal::DoPrint(elems, os)), ...); }, m_elements); +} + +} // namespace ns3 + +#endif /* WIFI_MGT_HEADER_H */