[mac] redesign Information Element (IE) processing and generation (#13263)

This commit redesigns IEEE 802.15.4 Information Element (IE) handling by
introducing dedicated subclass definitions (`CslIe`, `TimeIe`,
`ConnectionIe`, `RendezvousTimeIe`, `LinkMetricsProbingIe`, etc.) inheriting
from `HeaderIe` or `VendorIe`.

In addition, `Mac::Frame` is updated with generic template methods:
- `Has<IeType>()`: Checks if the frame contains a well-formed instance of
  the specified IE subclass.
- `Find<IeType>()`: Finds, validates and returns a type-safe pointer to the
  IE.

Key benefits of this redesign:

- Streamlined IE Generation: Constructing and populating IEs is significantly
  simplified. Each subclass exposes a unified `Init()` method that cleanly
  computes the exact IE content length, sets the Element ID and known content
  (e.g. vendor OUI) in a single step.

- True Encapsulation: Each IE subclass serves as the single source of truth
  for its Element ID (`kId`), content layout, and structural validation logic
  (`IsValid()`). Callers no longer pass magic ID constants or perform
  error-prone pointer arithmetic.

- By implementing `HeaderIe::ValidateAs<IeType>()` as a static polymorphic
  matcher passed to `FindHeaderIe()`, the compiler devirtualizes
  and inlines ID match and content verifications directly into MAC frame
  parsing loops.

- Scalable Integration: Adding new Information Elements is trivial. Defining
  a new subclass automatically equips MAC frame handling with type-safe
  search without bloating `Mac::Frame` with ad-hoc getter methods.
This commit is contained in:
Abtin Keshavarzian
2026-06-23 11:05:15 -07:00
committed by GitHub
parent 9b8d5b7471
commit ac689dd9a4
14 changed files with 332 additions and 430 deletions
+13 -13
View File
@@ -761,7 +761,7 @@ void TestMacFrameAckGeneration(void)
SuccessOrQuit(ackFrame.GenerateEnhAck(receivedFrame, false, ie_data, sizeof(ie_data)));
csl = reinterpret_cast<Mac::CslIe *>(ackFrame.GetHeaderIe(Mac::CslIe::kHeaderIeId) + sizeof(Mac::HeaderIe));
csl = ackFrame.Find<Mac::CslIe>();
VerifyOrQuit(ackFrame.mLength == 25);
VerifyOrQuit(ackFrame.GetType() == Mac::Frame::kTypeAck);
VerifyOrQuit(ackFrame.GetSecurityEnabled());
@@ -775,8 +775,8 @@ void TestMacFrameAckGeneration(void)
VerifyOrQuit(csl->GetPeriod() == 3125 && csl->GetPhase() == 3105);
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
ackFrame.SetCslIe(123, 456);
csl = reinterpret_cast<Mac::CslIe *>(ackFrame.GetHeaderIe(Mac::CslIe::kHeaderIeId) + sizeof(Mac::HeaderIe));
ackFrame.UpdateCslIe(123, 456);
csl = ackFrame.Find<Mac::CslIe>();
VerifyOrQuit(csl->GetPeriod() == 123 && csl->GetPhase() == 456);
#endif
#endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
@@ -860,8 +860,8 @@ void TestMacWakeupFrameGeneration(void)
// Validate that the frame satisfies the wake-up frame definition
VerifyOrQuit(txFrame.GetType() == Mac::Frame::kTypeMultipurpose);
VerifyOrQuit(!txFrame.GetAckRequest());
VerifyOrQuit(txFrame.GetRendezvousTimeIe() != nullptr);
VerifyOrQuit(txFrame.GetConnectionIe() != nullptr);
VerifyOrQuit(txFrame.Has<Mac::RendezvousTimeIe>());
VerifyOrQuit(txFrame.Has<Mac::ConnectionIe>());
VerifyOrQuit(txFrame.GetPayloadLength() == 0);
SuccessOrQuit(txFrame.GetSrcAddr(addr));
VerifyOrQuit(CompareAddresses(src, addr));
@@ -872,13 +872,13 @@ void TestMacWakeupFrameGeneration(void)
txFrame.SetFrameCounter(0xfcfcfcfc);
txFrame.SetKeySource(kKeySource);
txFrame.SetKeyId(0x1d);
txFrame.GetRendezvousTimeIe()->SetRendezvousTime(0xabcd);
connectionIe = txFrame.GetConnectionIe();
txFrame.Find<Mac::RendezvousTimeIe>()->SetRendezvousTime(0xabcd);
connectionIe = txFrame.Find<Mac::ConnectionIe>();
connectionIe->SetRetryInterval(1);
connectionIe->SetRetryCount(12);
VerifyOrQuit(connectionIe->SetWakeupId(kWakeupId) == kErrorParse);
VerifyOrQuit(txFrame.GetRendezvousTimeIe()->GetRendezvousTime() == 0xabcd);
VerifyOrQuit(txFrame.Find<Mac::RendezvousTimeIe>()->GetRendezvousTime() == 0xabcd);
VerifyOrQuit(connectionIe->GetRetryInterval() == 1);
VerifyOrQuit(connectionIe->GetRetryCount() == 12);
VerifyOrQuit(connectionIe->GetWakeupId(wakeupId) == kErrorParse);
@@ -905,8 +905,8 @@ void TestMacWakeupFrameGeneration(void)
// Validate that the frame satisfies the wake-up frame definition
VerifyOrQuit(txFrame.GetType() == Mac::Frame::kTypeMultipurpose);
VerifyOrQuit(!txFrame.GetAckRequest());
VerifyOrQuit(txFrame.GetRendezvousTimeIe() != nullptr);
VerifyOrQuit(txFrame.GetConnectionIe() != nullptr);
VerifyOrQuit(txFrame.Has<Mac::RendezvousTimeIe>());
VerifyOrQuit(txFrame.Has<Mac::ConnectionIe>());
VerifyOrQuit(txFrame.GetPayloadLength() == 0);
SuccessOrQuit(txFrame.GetSrcAddr(addr));
VerifyOrQuit(CompareAddresses(src, addr));
@@ -917,13 +917,13 @@ void TestMacWakeupFrameGeneration(void)
txFrame.SetFrameCounter(0xfcfcfcfc);
txFrame.SetKeySource(kKeySource);
txFrame.SetKeyId(0x1d);
txFrame.GetRendezvousTimeIe()->SetRendezvousTime(0xabcd);
connectionIe = txFrame.GetConnectionIe();
txFrame.Find<Mac::RendezvousTimeIe>()->SetRendezvousTime(0xabcd);
connectionIe = txFrame.Find<Mac::ConnectionIe>();
connectionIe->SetRetryInterval(1);
connectionIe->SetRetryCount(12);
SuccessOrQuit(connectionIe->SetWakeupId(kWakeupId));
VerifyOrQuit(txFrame.GetRendezvousTimeIe()->GetRendezvousTime() == 0xabcd);
VerifyOrQuit(txFrame.Find<Mac::RendezvousTimeIe>()->GetRendezvousTime() == 0xabcd);
VerifyOrQuit(connectionIe->GetRetryInterval() == 1);
VerifyOrQuit(connectionIe->GetRetryCount() == 12);
SuccessOrQuit(connectionIe->GetWakeupId(wakeupId));