A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
multi-link-element.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2021 Universita' degli Studi di Napoli Federico II
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 * Author: Stefano Avallone <stavallo@unina.it>
7 */
8
10
11#include "ns3/address-utils.h"
12#include "ns3/log.h"
13#include "ns3/mgt-headers.h"
14
15#include <utility>
16
17NS_LOG_COMPONENT_DEFINE("MultiLinkElement");
18
19namespace ns3
20{
21
23 : m_containingFrame(frame),
24 m_commonInfo(std::in_place_type<std::monostate>) // initialize as UNSET
25{
26}
27
29 : MultiLinkElement(frame)
30{
31 NS_ASSERT(variant != UNSET);
32 SetVariant(variant);
33}
34
37{
38 return IE_EXTENSION;
39}
40
46
49{
50 return static_cast<Variant>(m_commonInfo.index());
51}
52
53void
55{
56 NS_ABORT_MSG_IF(GetVariant() != UNSET, "Multi-Link Element variant already set");
57 NS_ABORT_MSG_IF(variant == UNSET, "Invalid variant");
58
59 switch (variant)
60 {
61 case BASIC_VARIANT:
63 break;
66 break;
67 default:
68 NS_ABORT_MSG("Unsupported variant: " << +variant);
69 }
70}
71
74{
75 return std::get<BASIC_VARIANT>(m_commonInfo);
76}
77
80{
81 return std::get<BASIC_VARIANT>(m_commonInfo);
82}
83
84void
86{
87 std::get<BASIC_VARIANT>(m_commonInfo).m_mldMacAddress = address;
88}
89
92{
93 return std::get<BASIC_VARIANT>(m_commonInfo).m_mldMacAddress;
94}
95
96void
98{
99 std::get<BASIC_VARIANT>(m_commonInfo).m_linkIdInfo = (linkIdInfo & 0x0f);
100}
101
102bool
104{
105 return std::get<BASIC_VARIANT>(m_commonInfo).m_linkIdInfo.has_value();
106}
107
108uint8_t
110{
111 return std::get<BASIC_VARIANT>(m_commonInfo).m_linkIdInfo.value();
112}
113
114void
116{
117 std::get<BASIC_VARIANT>(m_commonInfo).m_bssParamsChangeCount = count;
118}
119
120bool
122{
123 return std::get<BASIC_VARIANT>(m_commonInfo).m_bssParamsChangeCount.has_value();
124}
125
126uint8_t
128{
129 return std::get<BASIC_VARIANT>(m_commonInfo).m_bssParamsChangeCount.value();
130}
131
132void
134{
135 auto& emlCapabilities = std::get<BASIC_VARIANT>(m_commonInfo).m_emlCapabilities;
136 if (!emlCapabilities.has_value())
137 {
138 emlCapabilities = CommonInfoBasicMle::EmlCapabilities{};
139 }
140 emlCapabilities->emlsrSupport = supported ? 1 : 0;
141}
142
143void
145{
146 auto& emlCapabilities = std::get<BASIC_VARIANT>(m_commonInfo).m_emlCapabilities;
147 if (!emlCapabilities.has_value())
148 {
149 emlCapabilities = CommonInfoBasicMle::EmlCapabilities{};
150 }
152}
153
154void
156{
157 auto& emlCapabilities = std::get<BASIC_VARIANT>(m_commonInfo).m_emlCapabilities;
158 if (!emlCapabilities.has_value())
159 {
160 emlCapabilities = CommonInfoBasicMle::EmlCapabilities{};
161 }
163}
164
165void
167{
168 auto& emlCapabilities = std::get<BASIC_VARIANT>(m_commonInfo).m_emlCapabilities;
169 if (!emlCapabilities.has_value())
170 {
171 emlCapabilities = CommonInfoBasicMle::EmlCapabilities{};
172 }
173 auto timeoutUs = timeout.GetMicroSeconds();
174
175 if (timeoutUs == 0)
176 {
177 emlCapabilities->transitionTimeout = 0;
178 }
179 else
180 {
181 uint8_t i;
182 for (i = 1; i <= 10; i++)
183 {
184 if (1 << (i + 6) == timeoutUs)
185 {
186 emlCapabilities->transitionTimeout = i;
187 break;
188 }
189 }
190 NS_ABORT_MSG_IF(i > 10, "Value not allowed (" << timeout.As(Time::US) << ")");
191 }
192}
193
194bool
196{
197 return std::get<BASIC_VARIANT>(m_commonInfo).m_emlCapabilities.has_value();
198}
199
200bool
202{
203 return std::get<BASIC_VARIANT>(m_commonInfo).m_emlCapabilities->emlsrSupport;
204}
205
206Time
208{
209 auto& emlCapabilities = std::get<BASIC_VARIANT>(m_commonInfo).m_emlCapabilities;
210 NS_ASSERT(emlCapabilities);
211 return CommonInfoBasicMle::DecodeEmlsrPaddingDelay(emlCapabilities->emlsrPaddingDelay);
212}
213
214Time
216{
217 auto& emlCapabilities = std::get<BASIC_VARIANT>(m_commonInfo).m_emlCapabilities;
218 NS_ASSERT(emlCapabilities);
219 return CommonInfoBasicMle::DecodeEmlsrTransitionDelay(emlCapabilities->emlsrTransitionDelay);
220}
221
222Time
224{
225 auto& emlCapabilities = std::get<BASIC_VARIANT>(m_commonInfo).m_emlCapabilities;
226 NS_ASSERT(emlCapabilities);
227 if (emlCapabilities->transitionTimeout == 0)
228 {
229 return MicroSeconds(0);
230 }
231 return MicroSeconds(1 << (6 + emlCapabilities->transitionTimeout));
232}
233
234void
236{
237 const auto variant = GetVariant();
238 switch (variant)
239 {
240 case BASIC_VARIANT:
241 std::get<CommonInfoBasicMle>(m_commonInfo).m_apMldId = id;
242 return;
244 std::get<CommonInfoProbeReqMle>(m_commonInfo).m_apMldId = id;
245 return;
246 default:
247 NS_ABORT_MSG("AP MLD ID field not present in input variant " << variant);
248 }
249}
250
251std::optional<uint8_t>
253{
254 const auto variant = GetVariant();
255 switch (variant)
256 {
257 case BASIC_VARIANT:
258 return std::get<CommonInfoBasicMle>(m_commonInfo).m_apMldId;
260 return std::get<CommonInfoProbeReqMle>(m_commonInfo).m_apMldId;
261 default:
262 NS_LOG_DEBUG("AP MLD ID field not present in input variant");
263 }
264 return std::nullopt;
265}
266
268 : m_variant(variant),
269 m_staControl(0)
270{
271}
272
274 const PerStaProfileSubelement& perStaProfile)
275 : m_variant(perStaProfile.m_variant),
276 m_staControl(perStaProfile.m_staControl),
277 m_staMacAddress(perStaProfile.m_staMacAddress),
278 m_bssParamsChgCnt(perStaProfile.m_bssParamsChgCnt)
279{
280 // deep copy of the STA Profile field
281 auto staProfileCopy = [&](auto&& frame) {
282 using Ptr = std::decay_t<decltype(frame)>;
283 if constexpr (std::is_same_v<Ptr, std::monostate>)
284 {
285 return;
286 }
287 else
288 {
289 using T = std::decay_t<decltype(*frame.get())>;
290 m_staProfile = std::make_unique<T>(*frame.get());
291 }
292 };
293 std::visit(staProfileCopy, perStaProfile.m_staProfile);
294}
295
298{
299 // check for self-assignment
300 if (&perStaProfile == this)
301 {
302 return *this;
303 }
304
305 m_variant = perStaProfile.m_variant;
306 m_staControl = perStaProfile.m_staControl;
307 m_staMacAddress = perStaProfile.m_staMacAddress;
308 m_bssParamsChgCnt = perStaProfile.m_bssParamsChgCnt;
309
310 // deep copy of the STA Profile field
311 auto staProfileCopy = [&](auto&& frame) {
312 using Ptr = std::decay_t<decltype(frame)>;
313 if constexpr (std::is_same_v<Ptr, std::monostate>)
314 {
315 return;
316 }
317 else
318 {
319 using T = std::decay_t<decltype(*frame.get())>;
320 m_staProfile = std::make_unique<T>(*frame.get());
321 }
322 };
323 std::visit(staProfileCopy, perStaProfile.m_staProfile);
324
325 return *this;
326}
327
328void
330{
331 m_staControl &= 0xfff0; // reset Link ID subfield in the STA Control field
332 m_staControl |= (linkId & 0x0f);
333}
334
335uint8_t
337{
338 return static_cast<uint8_t>(m_staControl & 0x000f);
339}
340
341void
346
347bool
349{
350 return (m_staControl & 0x0010) != 0;
351}
352
353void
355{
356 NS_ABORT_IF(m_variant != BASIC_VARIANT);
357 m_staMacAddress = address;
358 m_staControl |= 0x0020;
359}
360
361bool
363{
364 return (m_staControl & 0x0020) != 0;
365}
366
369{
370 NS_ABORT_IF(!HasStaMacAddress());
371 return m_staMacAddress;
372}
373
374void
376{
377 NS_ABORT_MSG_IF(m_variant != BASIC_VARIANT,
378 "Expected Basic Variant, variant:" << +static_cast<uint8_t>(m_variant));
379 m_bssParamsChgCnt = count;
380 m_staControl |= 0x0800;
381}
382
383bool
385{
386 return (m_staControl & 0x0800) != 0;
387}
388
389uint8_t
391{
392 NS_ASSERT_MSG(m_bssParamsChgCnt.has_value(), "No value set for m_bssParamsChgCnt");
393 NS_ASSERT_MSG(HasBssParamsChgCnt(), "BSS Parameters Change count bit not set");
394 return m_bssParamsChgCnt.value();
395}
396
397void
399 const std::variant<MgtAssocRequestHeader, MgtReassocRequestHeader>& assoc)
400{
401 std::visit(
402 [&](auto&& frame) {
403 m_staProfile = std::make_unique<std::decay_t<decltype(frame)>>(frame);
404 },
405 assoc);
406}
407
408void
410 std::variant<MgtAssocRequestHeader, MgtReassocRequestHeader>&& assoc)
411{
412 std::visit(
413 [&](auto&& frame) {
414 using T = std::decay_t<decltype(frame)>;
415 m_staProfile = std::make_unique<T>(std::forward<T>(frame));
416 },
417 assoc);
418}
419
420bool
422{
423 return std::holds_alternative<std::unique_ptr<MgtAssocRequestHeader>>(m_staProfile);
424}
425
426bool
428{
429 return std::holds_alternative<std::unique_ptr<MgtReassocRequestHeader>>(m_staProfile);
430}
431
434{
435 if (HasAssocRequest())
436 {
437 return *std::get<std::unique_ptr<MgtAssocRequestHeader>>(m_staProfile);
438 }
439 NS_ABORT_UNLESS(HasReassocRequest());
440 return *std::get<std::unique_ptr<MgtReassocRequestHeader>>(m_staProfile);
441}
442
443void
445{
446 m_staProfile = std::make_unique<MgtAssocResponseHeader>(assoc);
447}
448
449void
451{
452 m_staProfile = std::make_unique<MgtAssocResponseHeader>(std::move(assoc));
453}
454
455bool
457{
458 return std::holds_alternative<std::unique_ptr<MgtAssocResponseHeader>>(m_staProfile);
459}
460
463{
464 NS_ABORT_IF(!HasAssocResponse());
465 return *std::get<std::unique_ptr<MgtAssocResponseHeader>>(m_staProfile);
466}
467
468void
470{
471 m_staProfile = std::make_unique<MgtProbeResponseHeader>(probeResp);
472}
473
474void
476{
477 m_staProfile = std::make_unique<MgtProbeResponseHeader>(std::move(probeResp));
478}
479
480bool
482{
483 return std::holds_alternative<std::unique_ptr<MgtProbeResponseHeader>>(m_staProfile);
484}
485
488{
489 NS_ABORT_IF(!HasProbeResponse());
490 return *std::get<std::unique_ptr<MgtProbeResponseHeader>>(m_staProfile);
491}
492
493uint8_t
495{
496 if (m_variant == PROBE_REQUEST_VARIANT)
497 {
498 return 0; // IEEE 802.11be 6.0 Figure 9-1072s
499 }
500
501 uint8_t ret = 1; // STA Info Length
502
503 if (HasStaMacAddress())
504 {
505 ret += 6;
506 }
507 if (HasBssParamsChgCnt())
508 {
509 ret += 1;
510 }
511 // TODO add other subfields of the STA Info field
512 return ret;
513}
514
520
521uint16_t
523{
524 uint16_t ret = 2; // STA Control field
525
526 ret += GetStaInfoLength();
527
528 auto staProfileSize = [&](auto&& frame) {
529 using T = std::decay_t<decltype(frame)>;
530 if constexpr (std::is_same_v<T, std::monostate>)
531 {
532 NS_ASSERT_MSG(std::holds_alternative<std::monostate>(m_containingFrame),
533 "Missing management frame for Per-STA Profile subelement");
534 return static_cast<uint32_t>(0);
535 }
536 else
537 {
538 using U = std::decay_t<decltype(*frame)>;
540 std::holds_alternative<std::reference_wrapper<const U>>(m_containingFrame),
541 "Containing frame type and frame type in Per-STA Profile do not match");
542 const auto& containing = std::get<std::reference_wrapper<const U>>(m_containingFrame);
543 return frame->GetSerializedSizeInPerStaProfile(containing);
544 }
545 };
546 ret += std::visit(staProfileSize, m_staProfile);
547
548 return ret;
549}
550
551void
553{
554 if (m_variant == PROBE_REQUEST_VARIANT)
555 {
556 NS_ASSERT_MSG(IsCompleteProfileSet(), "Encoding of STA Profile not supported");
557 start.WriteHtolsbU16(m_staControl);
558 return;
559 }
560
561 start.WriteHtolsbU16(m_staControl);
562 start.WriteU8(GetStaInfoLength());
563
564 if (HasStaMacAddress())
565 {
566 WriteTo(start, m_staMacAddress);
567 }
568 if (HasBssParamsChgCnt())
569 {
570 start.WriteU8(GetBssParamsChgCnt());
571 }
572 // TODO add other subfields of the STA Info field
573 auto staProfileSerialize = [&](auto&& frame) {
574 using T = std::decay_t<decltype(frame)>;
575 if constexpr (std::is_same_v<T, std::monostate>)
576 {
577 NS_ASSERT_MSG(std::holds_alternative<std::monostate>(m_containingFrame),
578 "Missing management frame for Per-STA Profile subelement");
579 return;
580 }
581 else
582 {
583 using U = std::decay_t<decltype(*frame)>;
585 std::holds_alternative<std::reference_wrapper<const U>>(m_containingFrame),
586 "Containing frame type and frame type in Per-STA Profile do not match");
587 const auto& containing = std::get<std::reference_wrapper<const U>>(m_containingFrame);
588 frame->SerializeInPerStaProfile(start, containing);
589 }
590 };
591 std::visit(staProfileSerialize, m_staProfile);
592}
593
594uint16_t
596 uint16_t length)
597{
598 if (m_variant == PROBE_REQUEST_VARIANT)
599 {
600 return DeserProbeReqMlePerSta(start, length);
601 }
602
603 Buffer::Iterator i = start;
604
605 m_staControl = i.ReadLsbtohU16();
606 i.ReadU8(); // STA Info Length
607
608 if (HasStaMacAddress())
609 {
610 ReadFrom(i, m_staMacAddress);
611 }
612 if (HasBssParamsChgCnt())
613 {
614 m_bssParamsChgCnt = i.ReadU8();
615 }
616
617 // TODO add other subfields of the STA Info field
618 uint16_t count = i.GetDistanceFrom(start);
619
620 NS_ASSERT_MSG(count <= length,
621 "Bytes read (" << count << ") exceed expected number (" << length << ")");
622
623 if (count == length)
624 {
625 return count;
626 }
627
628 auto staProfileDeserialize = [&](auto&& frame) {
629 using T = std::decay_t<decltype(frame)>;
630 if constexpr (!std::is_same_v<T, std::monostate>)
631 {
632 using U = std::decay_t<decltype(frame.get())>;
633 U assoc;
634 count += assoc.DeserializeFromPerStaProfile(i, length - count, frame.get());
635 m_staProfile = std::make_unique<U>(std::move(assoc));
636 }
637 };
638 std::visit(staProfileDeserialize, m_containingFrame);
639
640 return count;
641}
642
643uint16_t
645 uint16_t length)
646{
648 "Invalid Multi-link Element variant = " << static_cast<uint8_t>(m_variant));
649 Buffer::Iterator i = start;
650 uint16_t count = 0;
651
652 m_staControl = i.ReadLsbtohU16();
653 count += 2;
654
655 NS_ASSERT_MSG(count <= length,
656 "Incorrect decoded size count =" << count << ", length=" << length);
657 if (count == length)
658 {
659 return count;
660 }
661
662 // TODO: Support decoding of Partial Per-STA Profile
663 // IEEE 802.11be D5.0 9.4.2.312.3 Probe Request Multi-Link element
664 // If the Complete Profile Requested subfield is set to 0 and the STA Profile field
665 // is present in a Per-STA Profile subelement,
666 // the STA Profile field includes exactly one of the following:
667 // - one Request element (see 9.4.2.9 (Request element)), or
668 // — one Extended Request element (see 9.4.2.10 (Extended Request element)), or
669 // — one Request element and one Extended Request element
670 NS_LOG_DEBUG("Decoding of STA Profile in Per-STA Profile subelement not supported");
671 while (count < length)
672 {
673 i.ReadU8();
674 count++;
675 }
676 return count;
677}
678
679void
681{
682 auto variant = GetVariant();
683 NS_ABORT_IF(variant == UNSET);
684 m_perStaProfileSubelements.emplace_back(variant);
685}
686
687std::size_t
692
695{
696 return m_perStaProfileSubelements.at(i);
697}
698
701{
702 return m_perStaProfileSubelements.at(i);
703}
704
705uint16_t
707{
708 uint16_t ret = 3; // ElementIdExt (1) + Multi-Link Control (2)
709
710 // add the Common Info field size (dependent on the Multi-Link Element variant)
711 ret += std::visit(
712 [](auto&& arg) -> uint8_t {
713 using T = std::decay_t<decltype(arg)>;
714 if constexpr (std::is_same_v<T, std::monostate>)
715 {
716 NS_ABORT_MSG("Multi-Link Element variant not set");
717 return 0;
718 }
719 else
720 {
721 return arg.GetSize();
722 }
723 },
725
726 for (const auto& subelement : m_perStaProfileSubelements)
727 {
728 subelement.m_containingFrame = m_containingFrame;
729 ret += subelement.GetSerializedSize();
730 }
731
732 return ret;
733}
734
735void
737{
738 // serialize the Multi-Link Control and Common Info fields
739 std::visit(
740 [this, &start](auto&& arg) {
741 using T = std::decay_t<decltype(arg)>;
742 if constexpr (std::is_same_v<T, std::monostate>)
743 {
744 NS_ABORT_MSG("Multi-Link Element variant not set");
745 }
746 else
747 {
748 uint16_t mlControl =
749 static_cast<uint8_t>(GetVariant()) + (arg.GetPresenceBitmap() << 4);
750 start.WriteHtolsbU16(mlControl);
751 arg.Serialize(start);
752 }
753 },
755
756 for (const auto& subelement : m_perStaProfileSubelements)
757 {
758 start = subelement.Serialize(start);
759 }
760}
761
762uint16_t
764{
765 Buffer::Iterator i = start;
766 uint16_t count = 0;
767
768 uint16_t mlControl = i.ReadLsbtohU16();
769 count += 2;
770
771 SetVariant(static_cast<Variant>(mlControl & 0x0007));
772 uint16_t presence = mlControl >> 4;
773
774 uint8_t nBytes = std::visit(
775 [&i, &presence](auto&& arg) -> uint8_t {
776 using T = std::decay_t<decltype(arg)>;
777 if constexpr (std::is_same_v<T, std::monostate>)
778 {
779 NS_ABORT_MSG("Multi-Link Element variant not set");
780 return 0;
781 }
782 else
783 {
784 return arg.Deserialize(i, presence);
785 }
786 },
788 i.Next(nBytes);
789 count += nBytes;
790
791 while (count < length)
792 {
793 switch (static_cast<SubElementId>(i.PeekU8()))
794 {
797 auto& perStaProfile = GetPerStaProfile(GetNPerStaProfileSubelements() - 1);
798 perStaProfile.m_containingFrame = m_containingFrame;
799 i = perStaProfile.Deserialize(i);
800 count = i.GetDistanceFrom(start);
801 }
802 break;
803 default:
804 NS_ABORT_MSG("Unsupported Subelement ID: " << +i.PeekU8());
805 }
806 }
807
808 return count;
809}
810
811} // namespace ns3
iterator in a Buffer instance
Definition buffer.h:89
uint16_t ReadLsbtohU16()
Definition buffer.cc:1053
uint32_t GetDistanceFrom(const Iterator &o) const
Definition buffer.cc:769
void Next()
go forward by one byte
Definition buffer.h:842
an EUI-48 address
Implement the header for management frames of type association and reassociation response.
Implement the header for management frames of type probe response.
Smart pointer class similar to boost::intrusive_ptr.
Simulation virtual time values and global simulation resolution.
Definition nstime.h:94
TimeWithUnit As(const Unit unit=Time::AUTO) const
Attach a unit to a Time, to facilitate output in a specific unit.
Definition time.cc:404
@ US
microsecond
Definition nstime.h:107
int64_t GetMicroSeconds() const
Get an approximation of the time stored in this instance in the indicated unit.
Definition nstime.h:402
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition assert.h:55
#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:75
#define NS_ABORT_MSG(msg)
Unconditional abnormal program termination with a message.
Definition abort.h:38
#define NS_ABORT_UNLESS(cond)
Abnormal program termination if a condition is false.
Definition abort.h:118
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition abort.h:97
#define NS_ABORT_IF(cond)
Abnormal program termination if a condition is true.
Definition abort.h:65
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:191
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition log.h:257
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1368
Variant
Multi-Link element variants.
Every class exported by the ns3 library is enclosed in the ns3 namespace.
void WriteTo(Buffer::Iterator &i, Ipv4Address ad)
Write an Ipv4Address to a Buffer.
std::variant< std::reference_wrapper< MgtAssocRequestHeader >, std::reference_wrapper< MgtReassocRequestHeader > > AssocReqRefVariant
variant holding a reference to a (Re)Association Request
Definition ap-wifi-mac.h:45
uint8_t WifiInformationElementId
This type is used to represent an Information Element ID.
void ReadFrom(Buffer::Iterator &i, Ipv4Address &ad)
Read an Ipv4Address from a Buffer.
STL namespace.
ns3::Time timeout
uint8_t emlsrTransitionDelay
EMLSR Transition Delay.
Common Info field of the Basic Multi-Link element.
static uint8_t EncodeEmlsrTransitionDelay(Time delay)
static Time DecodeEmlsrTransitionDelay(uint8_t value)
static Time DecodeEmlsrPaddingDelay(uint8_t value)
static uint8_t EncodeEmlsrPaddingDelay(Time delay)
Common Info field of Multi-link Element Probe Request variant.
#define IE_EXTENSION
#define IE_EXT_MULTI_LINK_ELEMENT