A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
wifi-mgt-header.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2023 Universita' degli Studi di Napoli Federico II
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation;
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 *
17 * Author: Stefano Avallone <stavallo@unina.it>
18 */
19
20#ifndef WIFI_MGT_HEADER_H
21#define WIFI_MGT_HEADER_H
22
23#include "non-inheritance.h"
24
25#include "ns3/eht-capabilities.h"
26#include "ns3/header.h"
27#include "ns3/multi-link-element.h"
28#include "ns3/supported-rates.h"
29
30#include <algorithm>
31#include <iterator>
32#include <numeric>
33#include <optional>
34#include <utility>
35#include <vector>
36
37namespace ns3
38{
39
40namespace internal
41{
42
52template <class T>
54{
56 typedef std::optional<T> type;
57};
58
60template <class T>
61struct GetStoredIe<std::optional<T>>
62{
64 typedef std::optional<T> type;
65};
66
68template <class T>
69struct GetStoredIe<std::vector<T>>
70{
72 typedef std::vector<T> type;
73};
74
76template <class T>
78
79} // namespace internal
80
87template <typename Derived, typename Tuple>
89
105template <typename Derived, typename... Elems>
106class WifiMgtHeader<Derived, std::tuple<Elems...>> : public Header
107{
108 public:
115 template <typename T,
116 std::enable_if_t<(std::is_same_v<std::vector<T>, Elems> + ...) == 0, int> = 0>
117 std::optional<T>& Get();
118
125 template <typename T,
126 std::enable_if_t<(std::is_same_v<std::vector<T>, Elems> + ...) == 0, int> = 0>
127 const std::optional<T>& Get() const;
128
135 template <typename T,
136 std::enable_if_t<(std::is_same_v<std::vector<T>, Elems> + ...) == 1, int> = 0>
137 std::vector<T>& Get();
138
145 template <typename T,
146 std::enable_if_t<(std::is_same_v<std::vector<T>, Elems> + ...) == 1, int> = 0>
147 const std::vector<T>& Get() const;
148
149 void Print(std::ostream& os) const final;
150 uint32_t GetSerializedSize() const final;
151 void Serialize(Buffer::Iterator start) const final;
152 uint32_t Deserialize(Buffer::Iterator start) final;
153
154 protected:
161 template <typename IE>
162 void InitForDeserialization(std::optional<IE>& optElem);
163
168 void InitForDeserialization(std::optional<EhtCapabilities>& optElem);
169
171 void PrintImpl(std::ostream& os) const;
173 uint32_t GetSerializedSizeImpl() const;
175 void SerializeImpl(Buffer::Iterator start) const;
177 uint32_t DeserializeImpl(Buffer::Iterator start);
178
185 template <typename T>
186 Buffer::Iterator DoDeserialize(std::optional<T>& elem, Buffer::Iterator start);
187
194 template <typename T>
195 Buffer::Iterator DoDeserialize(std::vector<T>& elems, Buffer::Iterator start);
196
198 using Elements = std::tuple<internal::GetStoredIeT<Elems>...>;
199
201};
202
209template <class T>
210struct CanBeInPerStaProfile : std::true_type
211{
212};
213
215template <class T>
217
225template <typename Derived, typename Tuple>
227
237template <typename Derived, typename... Elems>
238class MgtHeaderInPerStaProfile<Derived, std::tuple<Elems...>>
239 : public WifiMgtHeader<Derived, std::tuple<Elems...>>
240{
241 public:
247 uint32_t GetSerializedSizeInPerStaProfile(const Derived& frame) const;
248
255 void SerializeInPerStaProfile(Buffer::Iterator start, const Derived& frame) const;
256
265 uint32_t DeserializeFromPerStaProfile(Buffer::Iterator start,
266 uint16_t length,
267 const Derived& frame);
268
277 void CopyIesFromContainingFrame(const Derived& frame);
278
279 protected:
280 using WifiMgtHeader<Derived, std::tuple<Elems...>>::InitForDeserialization;
281
287 uint32_t GetSerializedSizeInPerStaProfileImpl(const Derived& frame) const;
288
295 void SerializeInPerStaProfileImpl(Buffer::Iterator start, const Derived& frame) const;
296
305 uint32_t DeserializeFromPerStaProfileImpl(Buffer::Iterator start,
306 uint16_t length,
307 const Derived& frame);
308
312 void SetMleContainingFrame() const;
313
318 void InitForDeserialization(std::optional<MultiLinkElement>& optElem);
319
320 private:
321 using WifiMgtHeader<Derived, std::tuple<Elems...>>::DoDeserialize;
322 using WifiMgtHeader<Derived, std::tuple<Elems...>>::m_elements;
323
324 std::optional<NonInheritance> m_nonInheritance;
326};
327
332template <typename Derived, typename... Elems>
333template <typename T, std::enable_if_t<(std::is_same_v<std::vector<T>, Elems> + ...) == 0, int>>
334std::optional<T>&
335WifiMgtHeader<Derived, std::tuple<Elems...>>::Get()
336{
337 return std::get<std::optional<T>>(m_elements);
338}
339
340template <typename Derived, typename... Elems>
341template <typename T, std::enable_if_t<(std::is_same_v<std::vector<T>, Elems> + ...) == 0, int>>
342const std::optional<T>&
343WifiMgtHeader<Derived, std::tuple<Elems...>>::Get() const
344{
345 return std::get<std::optional<T>>(m_elements);
346}
347
348template <typename Derived, typename... Elems>
349template <typename T, std::enable_if_t<(std::is_same_v<std::vector<T>, Elems> + ...) == 1, int>>
350std::vector<T>&
351WifiMgtHeader<Derived, std::tuple<Elems...>>::Get()
352{
353 return std::get<std::vector<T>>(m_elements);
354}
355
356template <typename Derived, typename... Elems>
357template <typename T, std::enable_if_t<(std::is_same_v<std::vector<T>, Elems> + ...) == 1, int>>
358const std::vector<T>&
359WifiMgtHeader<Derived, std::tuple<Elems...>>::Get() const
360{
361 return std::get<std::vector<T>>(m_elements);
362}
363
364template <typename Derived, typename... Elems>
365template <typename IE>
366void
367WifiMgtHeader<Derived, std::tuple<Elems...>>::InitForDeserialization(std::optional<IE>& optElem)
368{
369 optElem.emplace();
370}
371
372template <typename Derived, typename... Elems>
373void
374WifiMgtHeader<Derived, std::tuple<Elems...>>::InitForDeserialization(
375 std::optional<EhtCapabilities>& optElem)
376{
377 NS_ASSERT(Get<SupportedRates>());
378 auto rates = AllSupportedRates{*Get<SupportedRates>(), std::nullopt};
379 const bool is2_4Ghz = rates.IsSupportedRate(
380 1000000 /* 1 Mbit/s */); // TODO: use presence of VHT capabilities IE and HE 6 GHz Band
381 // Capabilities IE once the later is implemented
382 auto& heCapabilities = Get<HeCapabilities>();
383 if (heCapabilities)
384 {
385 optElem.emplace(is2_4Ghz, heCapabilities.value());
386 }
387 else
388 {
389 optElem.emplace();
390 }
391}
392
393template <typename Derived, typename... Elems>
394void
395MgtHeaderInPerStaProfile<Derived, std::tuple<Elems...>>::InitForDeserialization(
396 std::optional<MultiLinkElement>& optElem)
397{
398 optElem.emplace(*static_cast<const Derived*>(this));
399}
400
401namespace internal
402{
403
409template <typename T>
410uint16_t
411DoGetSerializedSize(const std::optional<T>& elem)
412{
413 return elem.has_value() ? elem->GetSerializedSize() : 0;
414}
415
421template <typename T>
422uint16_t
423DoGetSerializedSize(const std::vector<T>& elems)
424{
425 return std::accumulate(elems.cbegin(), elems.cend(), 0, [](uint16_t a, const auto& b) {
426 return b.GetSerializedSize() + a;
427 });
428}
429
430} // namespace internal
431
432template <typename Derived, typename... Elems>
434WifiMgtHeader<Derived, std::tuple<Elems...>>::GetSerializedSize() const
435{
436 return static_cast<const Derived*>(this)->GetSerializedSizeImpl();
437}
438
439template <typename Derived, typename... Elems>
441WifiMgtHeader<Derived, std::tuple<Elems...>>::GetSerializedSizeImpl() const
442{
443 return std::apply([&](auto&... elems) { return (internal::DoGetSerializedSize(elems) + ...); },
444 m_elements);
445}
446
447namespace internal
448{
449
456template <typename T>
458DoSerialize(const std::optional<T>& elem, Buffer::Iterator start)
459{
460 return elem.has_value() ? elem->Serialize(start) : start;
461}
462
469template <typename T>
471DoSerialize(const std::vector<T>& elems, Buffer::Iterator start)
472{
473 return std::accumulate(elems.cbegin(),
474 elems.cend(),
475 start,
476 [](Buffer::Iterator i, const auto& a) { return a.Serialize(i); });
477}
478
479} // namespace internal
480
481template <typename Derived, typename... Elems>
482void
483WifiMgtHeader<Derived, std::tuple<Elems...>>::Serialize(Buffer::Iterator start) const
484{
485 static_cast<const Derived*>(this)->SerializeImpl(start);
486}
487
488template <typename Derived, typename... Elems>
489void
490WifiMgtHeader<Derived, std::tuple<Elems...>>::SerializeImpl(Buffer::Iterator start) const
491{
492 auto i = start;
493 std::apply([&](auto&... elems) { ((i = internal::DoSerialize(elems, i)), ...); }, m_elements);
494}
495
496template <typename Derived, typename... Elems>
497template <typename T>
499WifiMgtHeader<Derived, std::tuple<Elems...>>::DoDeserialize(std::optional<T>& elem,
500 Buffer::Iterator start)
501{
502 auto i = start;
503 static_cast<Derived*>(this)->InitForDeserialization(elem);
504 i = elem->DeserializeIfPresent(i);
505 if (i.GetDistanceFrom(start) == 0)
506 {
507 elem.reset(); // the element is not present
508 }
509 return i;
510}
511
512template <typename Derived, typename... Elems>
513template <typename T>
515WifiMgtHeader<Derived, std::tuple<Elems...>>::DoDeserialize(std::vector<T>& elems,
516 Buffer::Iterator start)
517{
518 auto i = start;
519 do
520 {
521 auto tmp = i;
522 std::optional<T> item;
523 static_cast<Derived*>(this)->InitForDeserialization(item);
524 i = item->DeserializeIfPresent(i);
525 if (i.GetDistanceFrom(tmp) == 0)
526 {
527 break;
528 }
529 elems.push_back(std::move(*item));
530 } while (true);
531 return i;
532}
533
534template <typename Derived, typename... Elems>
536WifiMgtHeader<Derived, std::tuple<Elems...>>::Deserialize(Buffer::Iterator start)
537{
538 return static_cast<Derived*>(this)->DeserializeImpl(start);
539}
540
541template <typename Derived, typename... Elems>
543WifiMgtHeader<Derived, std::tuple<Elems...>>::DeserializeImpl(Buffer::Iterator start)
544{
545 auto i = start;
546
547 std::apply(
548 // auto cannot be used until gcc 10.4 due to gcc bug 97938
549 // (see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97938)
550 [&](internal::GetStoredIeT<Elems>&... elems) {
551 (
552 [&] {
553 if constexpr (std::is_same_v<std::remove_reference_t<decltype(elems)>, Elems>)
554 {
555 // optional IE or IE that can be present 0 or more times
556 i = DoDeserialize(elems, i);
557 }
558 else
559 {
560 // mandatory IE
561 static_cast<Derived*>(this)->InitForDeserialization(elems);
562 i = elems->Deserialize(i);
563 }
564 }(),
565 ...);
566 },
567 m_elements);
568
569 return i.GetDistanceFrom(start);
570}
571
572namespace internal
573{
574
580template <typename T>
581void
582DoPrint(const std::optional<T>& elem, std::ostream& os)
583{
584 if (elem.has_value())
585 {
586 os << *elem << " , ";
587 }
588}
589
595template <typename T>
596void
597DoPrint(const std::vector<T>& elems, std::ostream& os)
598{
599 std::copy(elems.cbegin(), elems.cend(), std::ostream_iterator<T>(os, " , "));
600}
601
602} // namespace internal
603
604template <typename Derived, typename... Elems>
605void
606WifiMgtHeader<Derived, std::tuple<Elems...>>::Print(std::ostream& os) const
607{
608 static_cast<const Derived*>(this)->PrintImpl(os);
609}
610
611template <typename Derived, typename... Elems>
612void
613WifiMgtHeader<Derived, std::tuple<Elems...>>::PrintImpl(std::ostream& os) const
614{
615 std::apply([&](auto&... elems) { ((internal::DoPrint(elems, os)), ...); }, m_elements);
616}
617
618namespace internal
619{
620
629template <typename T, typename Derived>
630bool
631MustBeSerializedInPerStaProfile(const std::optional<T>& elem, const Derived& frame)
632{
633 if (!CanBeInPerStaProfileV<T>)
634 {
635 return false;
636 }
637
638 if (auto& outsideIe = frame.template Get<T>();
639 outsideIe.has_value() && elem.has_value() && !(outsideIe.value() == elem.value()))
640 {
641 // the IE is present both outside the Multi-Link Element and in the Per-STA Profile,
642 // but they are different, hence the IE must be serialized in the Per-STA Profile
643 return true;
644 }
645
646 if (!frame.template Get<T>().has_value() && elem.has_value())
647 {
648 // the IE is not present outside the Multi-Link Element and is present in the Per-STA
649 // Profile, hence the IE must be serialized in the Per-STA Profile
650 return true;
651 }
652
653 return false;
654}
655
664template <typename T, typename Derived>
665bool
666MustBeSerializedInPerStaProfile(const std::vector<T>& elems, const Derived& frame)
667{
668 if (!CanBeInPerStaProfileV<T>)
669 {
670 return false;
671 }
672
673 if (auto& outsideIe = frame.template Get<T>();
674 !outsideIe.empty() && !elems.empty() && !(outsideIe == elems))
675 {
676 // the IEs are present both outside the Multi-Link Element and in the Per-STA Profile,
677 // but they are different, hence the IEs must be serialized in the Per-STA Profile
678 return true;
679 }
680
681 if (frame.template Get<T>().empty() && !elems.empty())
682 {
683 // the IEs are not present outside the Multi-Link Element and is present in the Per-STA
684 // Profile, hence the IEs must be serialized in the Per-STA Profile
685 return true;
686 }
687
688 return false;
689}
690
700template <typename T, typename Derived>
701std::optional<std::pair<uint8_t, uint8_t>>
702MustBeListedInNonInheritance(const std::optional<T>& elem, const Derived& frame)
703{
704 if (auto& outsideIe = frame.template Get<T>();
705 CanBeInPerStaProfileV<T> && outsideIe.has_value() && !elem.has_value())
706 {
707 return {{outsideIe->ElementId(), outsideIe->ElementIdExt()}};
708 }
709 return std::nullopt;
710}
711
721template <typename T, typename Derived>
722std::optional<std::pair<uint8_t, uint8_t>>
723MustBeListedInNonInheritance(const std::vector<T>& elems, const Derived& frame)
724{
725 if (auto& outsideIe = frame.template Get<T>();
726 CanBeInPerStaProfileV<T> && !outsideIe.empty() && elems.empty())
727 {
728 return {{outsideIe.front().ElementId(), outsideIe.front().ElementIdExt()}};
729 }
730 return std::nullopt;
731}
732
733} // namespace internal
734
735template <typename Derived, typename... Elems>
737MgtHeaderInPerStaProfile<Derived, std::tuple<Elems...>>::GetSerializedSizeInPerStaProfile(
738 const Derived& frame) const
739{
740 return static_cast<const Derived*>(this)->GetSerializedSizeInPerStaProfileImpl(frame);
741}
742
743template <typename Derived, typename... Elems>
745MgtHeaderInPerStaProfile<Derived, std::tuple<Elems...>>::GetSerializedSizeInPerStaProfileImpl(
746 const Derived& frame) const
747{
748 uint32_t size = 0;
749 std::optional<NonInheritance> nonInheritance;
750
751 std::apply(
752 [&](auto&... elems) {
753 (
754 [&] {
756 {
757 size += internal::DoGetSerializedSize(elems);
758 }
759 else if (auto idPair = internal::MustBeListedInNonInheritance(elems, frame))
760 {
761 if (!nonInheritance)
762 {
763 nonInheritance.emplace();
764 }
765 nonInheritance->Add(idPair->first, idPair->second);
766 }
767 }(),
768 ...);
769 },
770 m_elements);
771
772 if (nonInheritance)
773 {
774 size += nonInheritance->GetSerializedSize();
775 }
776 return size;
777}
778
779template <typename Derived, typename... Elems>
780void
781MgtHeaderInPerStaProfile<Derived, std::tuple<Elems...>>::SerializeInPerStaProfile(
782 Buffer::Iterator start,
783 const Derived& frame) const
784{
785 static_cast<const Derived*>(this)->SerializeInPerStaProfileImpl(start, frame);
786}
787
788template <typename Derived, typename... Elems>
789void
790MgtHeaderInPerStaProfile<Derived, std::tuple<Elems...>>::SerializeInPerStaProfileImpl(
791 Buffer::Iterator start,
792 const Derived& frame) const
793{
794 auto i = start;
795 std::optional<NonInheritance> nonInheritance;
796
797 std::apply(
798 [&](auto&... elems) {
799 (
800 [&] {
802 {
803 i = internal::DoSerialize(elems, i);
804 }
805 else if (auto idPair = internal::MustBeListedInNonInheritance(elems, frame))
806 {
807 if (!nonInheritance)
808 {
809 nonInheritance.emplace();
810 }
811 nonInheritance->Add(idPair->first, idPair->second);
812 }
813 }(),
814 ...);
815 },
816 m_elements);
817
818 if (nonInheritance)
819 {
820 nonInheritance->Serialize(i);
821 }
822}
823
824namespace internal
825{
826
837template <typename T, typename Derived>
838void
839DoCopyIeFromContainingFrame(std::optional<T>& elem, const Derived& frame)
840{
841 if (auto& outsideIe = frame.template Get<T>();
842 CanBeInPerStaProfileV<T> && outsideIe.has_value() && !elem.has_value())
843 {
844 elem = outsideIe.value();
845 }
846}
847
858template <typename T, typename Derived>
859void
860DoCopyIeFromContainingFrame(std::vector<T>& elems, const Derived& frame)
861{
862 if (auto& outsideIe = frame.template Get<T>();
863 CanBeInPerStaProfileV<T> && !outsideIe.empty() && elems.empty())
864 {
865 elems = outsideIe;
866 }
867}
868
869} // namespace internal
870
871template <typename Derived, typename... Elems>
873MgtHeaderInPerStaProfile<Derived, std::tuple<Elems...>>::DeserializeFromPerStaProfile(
874 Buffer::Iterator start,
875 uint16_t length,
876 const Derived& frame)
877{
878 return static_cast<Derived*>(this)->DeserializeFromPerStaProfileImpl(start, length, frame);
879}
880
881template <typename Derived, typename... Elems>
883MgtHeaderInPerStaProfile<Derived, std::tuple<Elems...>>::DeserializeFromPerStaProfileImpl(
884 Buffer::Iterator start,
885 uint16_t length,
886 const Derived& frame)
887{
888 auto i = start;
889
890 // deserialize the IEs in the Per-STA Profile subelement
891 std::apply(
892 [&](auto&... elems) {
893 (
894 [&] {
895 if (i.GetDistanceFrom(start) < length)
896 {
897 i = static_cast<Derived*>(this)->DoDeserialize(elems, i);
898 internal::DoCopyIeFromContainingFrame(elems, frame);
899 }
900 }(),
901 ...);
902 },
903 m_elements);
904
905 // deserialize the Non-Inheritance IE, if present
906 m_nonInheritance.reset();
907 i = DoDeserialize(m_nonInheritance, i);
908
909 auto distance = i.GetDistanceFrom(start);
910 NS_ASSERT_MSG(distance == length,
911 "Bytes read (" << distance << ") not matching expected number (" << length
912 << ")");
913 return distance;
914}
915
916namespace internal
917{
918
927template <typename T>
928void
929RemoveIfNotInherited(std::optional<T>& elem, const NonInheritance& nonInheritance)
930{
931 if (elem.has_value() && nonInheritance.IsPresent(elem->ElementId(), elem->ElementIdExt()))
932 {
933 elem.reset();
934 }
935}
936
945template <typename T>
946void
947RemoveIfNotInherited(std::vector<T>& elem, const NonInheritance& nonInheritance)
948{
949 if (!elem.empty() &&
950 nonInheritance.IsPresent(elem.front().ElementId(), elem.front().ElementIdExt()))
951 {
952 elem.clear();
953 }
954}
955
956} // namespace internal
957
958template <typename Derived, typename... Elems>
959void
960MgtHeaderInPerStaProfile<Derived, std::tuple<Elems...>>::CopyIesFromContainingFrame(
961 const Derived& frame)
962{
963 // copy inherited Information Elements that appear in the containing frame after the
964 // MLE (those appearing before have been copied by DeserializeFromPerStaProfileImpl)
965 std::apply(
966 [&](auto&... elems) { ((internal::DoCopyIeFromContainingFrame(elems, frame)), ...); },
967 m_elements);
968
969 // we have possibly deserialized a Non-Inheritance element; remove IEs listed therein
970 if (m_nonInheritance)
971 {
972 std::apply(
973 [&](auto&... elems) {
974 ((internal::RemoveIfNotInherited(elems, *m_nonInheritance)), ...);
975 },
976 m_elements);
977 }
978}
979
980template <typename Derived, typename... Elems>
981void
982MgtHeaderInPerStaProfile<Derived, std::tuple<Elems...>>::SetMleContainingFrame() const
983{
984 if (auto& mle = WifiMgtHeader<Derived, std::tuple<Elems...>>::template Get<MultiLinkElement>())
985 {
986 mle->m_containingFrame = *static_cast<const Derived*>(this);
987 }
988}
989
990} // namespace ns3
991
992#endif /* WIFI_MGT_HEADER_H */
Simple class derived from ns3::Object, used to check attribute constructors.
iterator in a Buffer instance
Definition: buffer.h:100
Protocol header serialization and deserialization.
Definition: header.h:44
std::optional< NonInheritance > m_nonInheritance
the Non-Inheritance IE possibly appended to the Per-STA Profile subelement
Implement the header for management frames that can be included in a Per-STA Profile subelement of a ...
The IEEE 802.11 Non-Inheritance Information Element.
bool IsPresent(uint8_t elemId, uint8_t elemIdExt=0) const
Elements m_elements
Information Elements contained by this frame.
std::tuple< internal::GetStoredIeT< Elems >... > Elements
type of the Information Elements contained by this frame
Implement the header for management frames.
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition: assert.h:66
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition: assert.h:86
void Print(ComponentCarrier cc)
void DoPrint(const std::optional< T > &elem, std::ostream &os)
bool MustBeSerializedInPerStaProfile(const std::optional< T > &elem, const Derived &frame)
void DoCopyIeFromContainingFrame(std::optional< T > &elem, const Derived &frame)
typename GetStoredIe< T >::type GetStoredIeT
uint16_t DoGetSerializedSize(const std::optional< T > &elem)
std::optional< std::pair< uint8_t, uint8_t > > MustBeListedInNonInheritance(const std::optional< T > &elem, const Derived &frame)
void RemoveIfNotInherited(std::optional< T > &elem, const NonInheritance &nonInheritance)
Buffer::Iterator DoSerialize(const std::optional< T > &elem, Buffer::Iterator start)
Every class exported by the ns3 library is enclosed in the ns3 namespace.
constexpr bool CanBeInPerStaProfileV
Inspect a type to deduce whether it is an Information Element that can be included in a Per-STA Profi...
STL namespace.
Struct containing all supported rates.
bool IsSupportedRate(uint64_t bs) const
Check if the given rate is supported.
Inspect a type to deduce whether it is an Information Element that can be included in a Per-STA Profi...
std::optional< T > type
typedef for the resulting optional type
std::vector< T > type
typedef for the resulting optional type
std::optional< T > type
typedef for the resulting optional type