A Discrete-Event Network Simulator
API
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 #include "supported-rates.h"
25 
26 #include "ns3/eht-capabilities.h"
27 #include "ns3/header.h"
28 #include "ns3/multi-link-element.h"
29 
30 #include <algorithm>
31 #include <iterator>
32 #include <numeric>
33 #include <optional>
34 #include <utility>
35 #include <vector>
36 
37 namespace ns3
38 {
39 
40 namespace internal
41 {
42 
52 template <class T>
54 {
56  typedef std::optional<T> type;
57 };
58 
60 template <class T>
61 struct GetStoredIe<std::optional<T>>
62 {
64  typedef std::optional<T> type;
65 };
66 
68 template <class T>
69 struct GetStoredIe<std::vector<T>>
70 {
72  typedef std::vector<T> type;
73 };
74 
76 template <class T>
78 
79 } // namespace internal
80 
87 template <typename Derived, typename Tuple>
89 
105 template <typename Derived, typename... Elems>
106 class 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 
209 template <class T>
210 struct CanBeInPerStaProfile : std::true_type
211 {
212 };
213 
215 template <class T>
217 
225 template <typename Derived, typename Tuple>
227 
237 template <typename Derived, typename... Elems>
238 class 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 
332 template <typename Derived, typename... Elems>
333 template <typename T, std::enable_if_t<(std::is_same_v<std::vector<T>, Elems> + ...) == 0, int>>
334 std::optional<T>&
335 WifiMgtHeader<Derived, std::tuple<Elems...>>::Get()
336 {
337  return std::get<std::optional<T>>(m_elements);
338 }
339 
340 template <typename Derived, typename... Elems>
341 template <typename T, std::enable_if_t<(std::is_same_v<std::vector<T>, Elems> + ...) == 0, int>>
342 const std::optional<T>&
343 WifiMgtHeader<Derived, std::tuple<Elems...>>::Get() const
344 {
345  return std::get<std::optional<T>>(m_elements);
346 }
347 
348 template <typename Derived, typename... Elems>
349 template <typename T, std::enable_if_t<(std::is_same_v<std::vector<T>, Elems> + ...) == 1, int>>
350 std::vector<T>&
351 WifiMgtHeader<Derived, std::tuple<Elems...>>::Get()
352 {
353  return std::get<std::vector<T>>(m_elements);
354 }
355 
356 template <typename Derived, typename... Elems>
357 template <typename T, std::enable_if_t<(std::is_same_v<std::vector<T>, Elems> + ...) == 1, int>>
358 const std::vector<T>&
359 WifiMgtHeader<Derived, std::tuple<Elems...>>::Get() const
360 {
361  return std::get<std::vector<T>>(m_elements);
362 }
363 
364 template <typename Derived, typename... Elems>
365 template <typename IE>
366 void
367 WifiMgtHeader<Derived, std::tuple<Elems...>>::InitForDeserialization(std::optional<IE>& optElem)
368 {
369  optElem.emplace();
370 }
371 
372 template <typename Derived, typename... Elems>
373 void
374 WifiMgtHeader<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 
393 template <typename Derived, typename... Elems>
394 void
395 MgtHeaderInPerStaProfile<Derived, std::tuple<Elems...>>::InitForDeserialization(
396  std::optional<MultiLinkElement>& optElem)
397 {
398  optElem.emplace(*static_cast<const Derived*>(this));
399 }
400 
401 namespace internal
402 {
403 
409 template <typename T>
410 uint16_t
411 DoGetSerializedSize(const std::optional<T>& elem)
412 {
413  return elem.has_value() ? elem->GetSerializedSize() : 0;
414 }
415 
421 template <typename T>
422 uint16_t
423 DoGetSerializedSize(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 
432 template <typename Derived, typename... Elems>
433 uint32_t
434 WifiMgtHeader<Derived, std::tuple<Elems...>>::GetSerializedSize() const
435 {
436  return static_cast<const Derived*>(this)->GetSerializedSizeImpl();
437 }
438 
439 template <typename Derived, typename... Elems>
440 uint32_t
441 WifiMgtHeader<Derived, std::tuple<Elems...>>::GetSerializedSizeImpl() const
442 {
443  return std::apply([&](auto&... elems) { return (internal::DoGetSerializedSize(elems) + ...); },
444  m_elements);
445 }
446 
447 namespace internal
448 {
449 
456 template <typename T>
458 DoSerialize(const std::optional<T>& elem, Buffer::Iterator start)
459 {
460  return elem.has_value() ? elem->Serialize(start) : start;
461 }
462 
469 template <typename T>
471 DoSerialize(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 
481 template <typename Derived, typename... Elems>
482 void
483 WifiMgtHeader<Derived, std::tuple<Elems...>>::Serialize(Buffer::Iterator start) const
484 {
485  static_cast<const Derived*>(this)->SerializeImpl(start);
486 }
487 
488 template <typename Derived, typename... Elems>
489 void
490 WifiMgtHeader<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 
496 template <typename Derived, typename... Elems>
497 template <typename T>
499 WifiMgtHeader<Derived, std::tuple<Elems...>>::DoDeserialize(std::optional<T>& elem,
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 
512 template <typename Derived, typename... Elems>
513 template <typename T>
515 WifiMgtHeader<Derived, std::tuple<Elems...>>::DoDeserialize(std::vector<T>& elems,
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 
534 template <typename Derived, typename... Elems>
535 uint32_t
536 WifiMgtHeader<Derived, std::tuple<Elems...>>::Deserialize(Buffer::Iterator start)
537 {
538  return static_cast<Derived*>(this)->DeserializeImpl(start);
539 }
540 
541 template <typename Derived, typename... Elems>
542 uint32_t
543 WifiMgtHeader<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 
572 namespace internal
573 {
574 
580 template <typename T>
581 void
582 DoPrint(const std::optional<T>& elem, std::ostream& os)
583 {
584  if (elem.has_value())
585  {
586  os << *elem << " , ";
587  }
588 }
589 
595 template <typename T>
596 void
597 DoPrint(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 
604 template <typename Derived, typename... Elems>
605 void
606 WifiMgtHeader<Derived, std::tuple<Elems...>>::Print(std::ostream& os) const
607 {
608  static_cast<const Derived*>(this)->PrintImpl(os);
609 }
610 
611 template <typename Derived, typename... Elems>
612 void
613 WifiMgtHeader<Derived, std::tuple<Elems...>>::PrintImpl(std::ostream& os) const
614 {
615  std::apply([&](auto&... elems) { ((internal::DoPrint(elems, os)), ...); }, m_elements);
616 }
617 
618 namespace internal
619 {
620 
629 template <typename T, typename Derived>
630 bool
631 MustBeSerializedInPerStaProfile(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 
664 template <typename T, typename Derived>
665 bool
666 MustBeSerializedInPerStaProfile(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 
700 template <typename T, typename Derived>
701 std::optional<std::pair<uint8_t, uint8_t>>
702 MustBeListedInNonInheritance(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 
721 template <typename T, typename Derived>
722 std::optional<std::pair<uint8_t, uint8_t>>
723 MustBeListedInNonInheritance(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 
735 template <typename Derived, typename... Elems>
736 uint32_t
737 MgtHeaderInPerStaProfile<Derived, std::tuple<Elems...>>::GetSerializedSizeInPerStaProfile(
738  const Derived& frame) const
739 {
740  return static_cast<const Derived*>(this)->GetSerializedSizeInPerStaProfileImpl(frame);
741 }
742 
743 template <typename Derived, typename... Elems>
744 uint32_t
745 MgtHeaderInPerStaProfile<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 
779 template <typename Derived, typename... Elems>
780 void
781 MgtHeaderInPerStaProfile<Derived, std::tuple<Elems...>>::SerializeInPerStaProfile(
783  const Derived& frame) const
784 {
785  static_cast<const Derived*>(this)->SerializeInPerStaProfileImpl(start, frame);
786 }
787 
788 template <typename Derived, typename... Elems>
789 void
790 MgtHeaderInPerStaProfile<Derived, std::tuple<Elems...>>::SerializeInPerStaProfileImpl(
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 
824 namespace internal
825 {
826 
837 template <typename T, typename Derived>
838 void
839 DoCopyIeFromContainingFrame(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 
858 template <typename T, typename Derived>
859 void
860 DoCopyIeFromContainingFrame(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 
871 template <typename Derived, typename... Elems>
872 uint32_t
873 MgtHeaderInPerStaProfile<Derived, std::tuple<Elems...>>::DeserializeFromPerStaProfile(
875  uint16_t length,
876  const Derived& frame)
877 {
878  return static_cast<Derived*>(this)->DeserializeFromPerStaProfileImpl(start, length, frame);
879 }
880 
881 template <typename Derived, typename... Elems>
882 uint32_t
883 MgtHeaderInPerStaProfile<Derived, std::tuple<Elems...>>::DeserializeFromPerStaProfileImpl(
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 
916 namespace internal
917 {
918 
927 template <typename T>
928 void
929 RemoveIfNotInherited(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 
945 template <typename T>
946 void
947 RemoveIfNotInherited(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 
958 template <typename Derived, typename... Elems>
959 void
960 MgtHeaderInPerStaProfile<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 
980 template <typename Derived, typename... Elems>
981 void
982 MgtHeaderInPerStaProfile<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)
typename std::enable_if< B, T >::type enable_if_t
Definition: json.h:2746
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...
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