From 60d0450efa35a97988117ba4d69d75466dd828a8 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Sat, 1 Aug 2026 09:49:16 -0700 Subject: [PATCH] [mac-frame] consolidate frame parsing and payload building (#13466) This commit refactors IEEE 802.15.4 MAC frame parsing and payload construction in `Mac::Frame`, `TxFrame`, and `RxFrame`: - Introduces `Frame::ParseInfo::ParseFrom()` to consolidate frame header validation, parsing, and field extraction into a single, unified pass. Supports flexible parsing modes (`kParseAddrFields`, `kParseSecurityHeader`, and `kParseFully`). - Eliminates legacy index-search helpers (`FindPayloadIndex()`, `FindHeaderIeIndex()`, `SkipSecurityHeaderIndex()`, etc.) and replaces direct pointer indexing with `ParseInfo` and `FrameData`. - Introduces `Frame::Lengths` struct and `DetermineLengths()` to provide a clear breakdown of header, payload, footer, and maximum payload lengths. - Introduces `TxFrame::PayloadBuilder` (derived from `FrameBuilder`) to safely build and append frame payloads with automatic bounds checking, eliminating direct raw buffer manipulation of `GetPayload()`. - Updates `PrepareHeaders()`, `PrepareHeadersWithEmptyPayload()`, and `FinishPayload()` in `TxFrame` to manage header preparation and payload length calculation cleanly. - Updates `MessageFramer`, `Mac`, `DataPollSender`, and test suites to use the new header preparation and `PayloadBuilder` workflow. --- src/core/BUILD.gn | 2 + src/core/CMakeLists.txt | 1 + src/core/common/frame_data.cpp | 11 + src/core/common/frame_data.hpp | 10 + src/core/mac/data_poll_sender.cpp | 2 +- src/core/mac/mac.cpp | 31 +- src/core/mac/mac_frame.cpp | 775 +++++++++++--------------- src/core/mac/mac_frame.hpp | 322 ++++++----- src/core/mac/scan_result.cpp | 2 +- src/core/mac/sub_mac.cpp | 2 +- src/core/radio/radio_frame.hpp | 18 + src/core/thread/mesh_forwarder.cpp | 2 +- src/core/thread/message_framer.cpp | 55 +- src/core/thread/message_framer.hpp | 6 +- tests/gtest/radio_spinel_rcp_test.cpp | 12 +- tests/unit/test_mac_frame.cpp | 15 +- 16 files changed, 646 insertions(+), 620 deletions(-) diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index dd42ac562..15825100b 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -841,6 +841,8 @@ openthread_radio_sources = [ "common/error.hpp", "common/frame_builder.cpp", "common/frame_builder.hpp", + "common/frame_data.cpp", + "common/frame_data.hpp", "common/log.cpp", "common/random.cpp", "common/string.cpp", diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 77ee043b8..4c7b083dd 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -319,6 +319,7 @@ set(RADIO_COMMON_SOURCES common/binary_search.cpp common/error.cpp common/frame_builder.cpp + common/frame_data.cpp common/log.cpp common/random.cpp common/string.cpp diff --git a/src/core/common/frame_data.cpp b/src/core/common/frame_data.cpp index 2d55fe5dc..eac2c42be 100644 --- a/src/core/common/frame_data.cpp +++ b/src/core/common/frame_data.cpp @@ -86,4 +86,15 @@ exit: return data; } +Error FrameData::RemoveFooter(uint16_t aLength) +{ + Error error = kErrorNone; + + VerifyOrExit(GetLength() >= aLength, error = kErrorParse); + SetLength(GetLength() - aLength); + +exit: + return error; +} + } // namespace ot diff --git a/src/core/common/frame_data.hpp b/src/core/common/frame_data.hpp index e63f249d1..df51cea95 100644 --- a/src/core/common/frame_data.hpp +++ b/src/core/common/frame_data.hpp @@ -147,6 +147,16 @@ public: */ void SkipOver(uint16_t aLength); + /** + * Removes a footer of a given length from the end of the `FrameData`. + * + * @param[in] aLength The length of the footer (number of bytes) to remove. + * + * @retval kErrorNone Successfully removed the footer. + * @retval kErrorParse The current data length is smaller than @p aLength. + */ + Error RemoveFooter(uint16_t aLength); + private: const void *ReadLength(uint16_t aLength) OT_LIFETIME_BOUND; }; diff --git a/src/core/mac/data_poll_sender.cpp b/src/core/mac/data_poll_sender.cpp index 11241b9d4..4c3382ac5 100644 --- a/src/core/mac/data_poll_sender.cpp +++ b/src/core/mac/data_poll_sender.cpp @@ -568,7 +568,7 @@ Mac::TxFrame *DataPollSender::PrepareDataRequest(Mac::TxFrames &aTxFrames) buildInfo.mSecurityLevel = Mac::Frame::kSecurityEncMic32; buildInfo.mKeyIdMode = Mac::Frame::kKeyIdMode1; - Get().PrepareMacHeaders(*frame, buildInfo, nullptr); + Get().PrepareMacHeaders(*frame, buildInfo); #if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT && OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE if (frame->Has()) diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp index 931a6cd78..9737ab83e 100644 --- a/src/core/mac/mac.cpp +++ b/src/core/mac/mac.cpp @@ -725,7 +725,7 @@ TxFrame *Mac::PrepareBeaconRequest(TxFrames &aTxFrames) buildInfo.mCommandId = Frame::kMacCmdBeaconRequest; buildInfo.mVersion = Frame::kVersion2003; - buildInfo.PrepareHeadersIn(frame); + frame.PrepareHeadersWithEmptyPayload(buildInfo); LogInfo("Sending Beacon Request"); @@ -734,9 +734,9 @@ TxFrame *Mac::PrepareBeaconRequest(TxFrames &aTxFrames) TxFrame *Mac::PrepareBeacon(TxFrames &aTxFrames) { - TxFrame *frame; - TxFrame::BuildInfo buildInfo; - FrameBuilder builder; + TxFrame *frame; + TxFrame::BuildInfo buildInfo; + TxFrame::PayloadBuilder builder; #if OPENTHREAD_CONFIG_MULTI_RADIO OT_ASSERT(!mTxBeaconRadioLinks.IsEmpty()); @@ -753,16 +753,15 @@ TxFrame *Mac::PrepareBeacon(TxFrames &aTxFrames) buildInfo.mType = Frame::kTypeBeacon; buildInfo.mVersion = Frame::kVersion2003; - buildInfo.PrepareHeadersIn(*frame); + frame->PrepareHeaders(buildInfo, builder); - builder.Init(frame->GetPayload(), frame->GetMaxPayloadLength()); builder.Append()->Init(); #if OPENTHREAD_CONFIG_MAC_OUTGOING_BEACON_PAYLOAD_ENABLE builder.Append()->Init(Get(), IsJoinable()); #endif - frame->SetPayloadLength(builder.GetLength()); + frame->FinishPayload(builder); LogBeacon("Sending"); @@ -1184,7 +1183,7 @@ void Mac::RecordFrameTransmitStatus(const TxFrame &aFrame, Error aError, uint8_t if (aError != kErrorNone) { LogFrameTxFailure(aFrame, aError, aRetryCount, aWillRetx); - DumpDebg("TX ERR", aFrame.GetHeader(), 16); + DumpDebg("TX ERR", aFrame.GetPsdu(), 16); if (aWillRetx) { @@ -1475,7 +1474,7 @@ void Mac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, Error aError) } #endif - DumpDebg("TX", aFrame.GetHeader(), aFrame.GetLength()); + DumpDebg("TX", aFrame.GetPsdu(), aFrame.GetLength()); FinishOperation(); Get().HandleSentFrame(aFrame, aError); #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 @@ -1488,7 +1487,7 @@ void Mac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, Error aError) case kOperationTransmitDataCsl: mCounters.mTxData++; - DumpDebg("TX", aFrame.GetHeader(), aFrame.GetLength()); + DumpDebg("TX", aFrame.GetPsdu(), aFrame.GetLength()); FinishOperation(); Get().HandleSentFrame(aFrame, aError); PerformNextOperation(); @@ -1511,7 +1510,7 @@ void Mac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, Error aError) } #endif - DumpDebg("TX", aFrame.GetHeader(), aFrame.GetLength()); + DumpDebg("TX", aFrame.GetPsdu(), aFrame.GetLength()); FinishOperation(); Get().HandleSentFrame(aFrame, aError); PerformNextOperation(); @@ -1714,13 +1713,15 @@ Error Mac::ProcessReceiveSecurity(RxFrame &aFrame, const Address &aSrcAddr, Neig #if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE if (aFrame.IsWakeupFrame()) { - uint32_t sequence; - uint8_t keyIndex; + uint32_t sequence; + uint8_t keyIndex; + FrameData keySource; // TODO: Avoid generating a new key if a wake-up frame was recently received already IgnoreError(aFrame.GetKeyIndex(keyIndex)); - sequence = BigEndian::ReadUint32(aFrame.GetKeySource()); + aFrame.GetKeySource(keySource); + sequence = BigEndian::ReadUint32(keySource.GetBytes()); VerifyOrExit(DetermineKeyIndexFor(sequence) == keyIndex, error = kErrorSecurity); macKey = &keyManager.GetTemporaryMacKey(sequence); @@ -2139,7 +2140,7 @@ void Mac::HandleReceivedFrame(RxFrame *aFrame, Error aError) ExitNow(); } - DumpDebg("RX", aFrame->GetHeader(), aFrame->GetLength()); + DumpDebg("RX", aFrame->GetPsdu(), aFrame->GetLength()); Get().HandleReceivedFrame(*aFrame); UpdateIdleMode(); diff --git a/src/core/mac/mac_frame.cpp b/src/core/mac/mac_frame.cpp index 6235dc4e2..cc3697ab5 100644 --- a/src/core/mac/mac_frame.cpp +++ b/src/core/mac/mac_frame.cpp @@ -37,7 +37,6 @@ #include "common/code_utils.hpp" #include "common/debug.hpp" -#include "common/frame_builder.hpp" #include "common/log.hpp" #include "common/num_utils.hpp" #include "crypto/aes_ccm.hpp" @@ -45,6 +44,8 @@ namespace ot { namespace Mac { +//---------------------------------------------------------------------------------------------------------------------- + void TxFrame::BuildInfo::PrepareHeadersIn(TxFrame &aTxFrame) const { uint16_t fcf; @@ -248,41 +249,224 @@ void TxFrame::BuildInfo::PrepareHeadersIn(TxFrame &aTxFrame) const aTxFrame.mLength = builder.GetLength(); } -Error Frame::ValidatePsdu(void) const +//---------------------------------------------------------------------------------------------------------------------- + +Error Frame::ParseInfo::ParseFrom(const Frame &aFrame, ParseMode aMode) { - Error error = kErrorNone; - uint8_t index = FindPayloadIndex(); + // Parses and validates the MAC frame header and extracts header + // fields according to `aMode`: + // + // - `kParseAddrFields`: Parses up through address fields + // (FCF, sequence number, PAN IDs, source and destination + // addresses) and FCS. + // + // - `kParseSecurityHeader`: Parses up through Auxiliary Security + // Header. Under `kParseSecurityHeader` mode, the frame is + // explicitly required to have a security header (security enabled + // in FCF). Otherwise `kErrorNotFound` is returned. + // + // - `kParseFully`: Parses all header fields including Auxiliary + // Security Header, Header IEs, and MAC Command ID (if + // applicable), determining the exact header and payload + // boundaries (`mHeader` and `mPayload`). - VerifyOrExit(index != kInvalidIndex, error = kErrorParse); + Error error = kErrorParse; + FrameData frameData; + uint16_t value; + uint8_t size; - if (IsMacCommand() && IsVersion2015()) + VerifyOrExit(aFrame.GetPsdu() != nullptr); + + frameData.Init(aFrame.GetPsdu(), aFrame.GetLength()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Address Fields + + SuccessOrExit(frameData.ReadUint(mFcf)); + + // Only accept standard frame types (Beacon, Data, Ack, MAC Command). + // Other types (e.g., Multipurpose) use a different FCF/header layout. + // Also restrict frame version to 2003, 2006, 2015. Future frame + // versions can alter the MAC header layout. + + VerifyOrExit(GetType(mFcf) <= kTypeMacCmd); + VerifyOrExit(GetVersion(mFcf) <= kVersion2015); + + if (IsSeqPresent(mFcf)) { + SuccessOrExit(frameData.ReadUint8(mSequenceNum)); + } + + if (IsDstPanIdPresent(mFcf)) + { + SuccessOrExit(frameData.ReadUint(value)); + mPanIds.SetDestination(value); + } + + SuccessOrExit(ParseAddress(frameData, ReadDstAddrMode(mFcf), mAddrs.mDestination)); + + if (IsSrcPanIdPresent(mFcf)) + { + SuccessOrExit(frameData.ReadUint(value)); + mPanIds.SetSource(value); + } + + SuccessOrExit(ParseAddress(frameData, ReadSrcAddrMode(mFcf), mAddrs.mSource)); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // FCS + + SuccessOrExit(frameData.RemoveFooter(aFrame.GetFcsSize())); + + if (aMode == kParseAddrFields) + { + ExitNow(error = kErrorNone); + } + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Aux Security Header + + if (IsSecurityEnabled(mFcf)) + { + SuccessOrExit(frameData.ReadUint8(mSecCtl)); + + mSecurityLevel = ReadSecurityLevel(mSecCtl); + mKeyIdMode = ReadKeyIdMode(mSecCtl); + + VerifyOrExit(mSecurityLevel != kSecurityNone); + + mFrameCounterBytes = AsNonConst(frameData.GetBytes()); + SuccessOrExit(frameData.ReadUint(mFrameCounter)); + + size = CalculateKeySourceSize(mSecCtl); + + VerifyOrExit(frameData.CanRead(size)); + mKeySource.Init(frameData.GetBytes(), size); + frameData.SkipOver(size); + + if (mKeyIdMode != kKeyIdMode0) + { + mKeyIndexByte = AsNonConst(frameData.GetBytes()); + SuccessOrExit(frameData.ReadUint8(mKeyIndex)); + } + + mMicSize = CalculateMicSize(mSecCtl); + SuccessOrExit(frameData.RemoveFooter(mMicSize)); + } + + if (aMode == kParseSecurityHeader) + { + VerifyOrExit(IsSecurityEnabled(mFcf), error = kErrorNotFound); + ExitNow(error = kErrorNone); + } + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Header IE + + if (IsIePresent(mFcf)) + { +#if !OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT + ExitNow(); +#else + + mIeData = frameData; + + do + { + const HeaderIe *ie = frameData.Read(); + + VerifyOrExit(ie != nullptr); + + VerifyOrExit(frameData.CanRead(ie->GetLength())); + frameData.SkipOver(ie->GetLength()); + + if (ie->GetId() == Termination2Ie::kId) + { + break; + } + + // If the `frameData.IsEmpty()`, we exit the `while()` + // loop. This covers the case where frame contains one or more + // Header IEs but no data payload. In this case, spec does not + // require Header IE termination to be included (it is optional) + // since the end of frame can be determined from frame length and + // footer length. + + } while (!frameData.IsEmpty()); + + mIeData.InitFromRange(mIeData.GetBytes(), frameData.GetBytes()); + +#endif // OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT + } + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // MAC Command + + if (GetType(mFcf) == kTypeMacCmd) + { + VerifyOrExit(frameData.CanRead(sizeof(mCommandId))); + + mCommandId = *frameData.GetBytes(); + // The treatment of the Command ID field in a MAC command frame // is version-dependent. In the 2015 spec, it is part of the // encrypted payload, while in earlier versions, it is part of // the MAC header. - // - // `FindPayloadIndex()` accounts for this difference and returns - // the starting index of the payload. To correctly validate a - // 2015 frame, we must ensure it is long enough to contain the - // Command ID, so we include its size in the length check. - index += kCommandIdSize; + if (!IsVersion2015(mFcf)) + { + frameData.SkipOver(sizeof(mCommandId)); + } } - VerifyOrExit((index + GetFooterLength()) <= mLength, error = kErrorParse); + mHeader.InitFromRange(aFrame.GetPsdu(), frameData.GetBytes()); + mPayload = frameData; + + error = kErrorNone; exit: return error; } -#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE -bool Frame::IsWakeupFrame(void) const +Error Frame::ParseInfo::ParseAddress(FrameData &aFrameData, AddrMode aAddrMode, Address &aAddress) { - // Placeholder implementation following removal of legacy Multipurpose frame format. - return false; + Error error = kErrorNone; + uint16_t shortAddr; + + switch (aAddrMode) + { + case kAddrModeNone: + aAddress.SetNone(); + break; + + case kAddrModeShort: + SuccessOrExit(error = aFrameData.ReadUint(shortAddr)); + aAddress.SetShort(shortAddr); + break; + + case kAddrModeExt: + VerifyOrExit(aFrameData.CanRead(sizeof(ExtAddress)), error = kErrorParse); + aAddress.SetExtended(aFrameData.GetBytes(), ExtAddress::kReverseByteOrder); + aFrameData.SkipOver(sizeof(ExtAddress)); + break; + + default: + error = kErrorParse; + break; + } + +exit: + return error; +} + +//--------------------------------------------------------------------------------------------------------------------- + +Error Frame::ValidatePsdu(void) const +{ + ParseInfo info; + + return info.ParseFrom(*this, kParseFully); } -#endif void Frame::UpdateFcfFlag(bool aSet, uint16_t aBitFlag) { @@ -297,32 +481,7 @@ void Frame::UpdateFcfFlag(bool aSet, uint16_t aBitFlag) fcf &= ~aBitFlag; } - SetFrameControlField(fcf); -} - -uint8_t Frame::SkipSequenceIndex(void) const -{ - uint16_t fcf = GetFrameControlField(); - uint8_t index = kFcfSize; - - if (IsSeqPresent(fcf)) - { - index += kDsnSize; - } - - return index; -} - -uint8_t Frame::FindDstPanIdIndex(void) const -{ - uint8_t index; - - VerifyOrExit(IsDstPanIdPresent(), index = kInvalidIndex); - - index = SkipSequenceIndex(); - -exit: - return index; + LittleEndian::WriteUint16(fcf, mPsdu); } bool Frame::IsDstPanIdPresent(uint16_t aFcf) @@ -381,11 +540,12 @@ bool Frame::IsDstPanIdPresent(uint16_t aFcf) Error Frame::GetDstPanId(PanId &aPanId) const { - Error error = kErrorNone; - uint8_t index = FindDstPanIdIndex(); + Error error; + ParseInfo info; - VerifyOrExit(index != kInvalidIndex, error = kErrorParse); - aPanId = LittleEndian::ReadUint16(&mPsdu[index]); + SuccessOrExit(error = info.ParseFrom(*this, kParseAddrFields)); + VerifyOrExit(info.mPanIds.IsDestinationPresent(), error = kErrorNotFound); + aPanId = info.mPanIds.GetDestination(); exit: return error; @@ -405,62 +565,18 @@ void Frame::SetSequence(uint8_t aSequence) GetPsdu()[kFcfSize] = aSequence; } -uint8_t Frame::FindDstAddrIndex(void) const { return SkipSequenceIndex() + (IsDstPanIdPresent() ? sizeof(PanId) : 0); } - -Error Frame::ReadAddressAt(uint8_t aIndex, AddrMode aAddrMode, Address &aAddress) const +Error Frame::GetDstAddr(Address &aAddress) const { - Error error = kErrorNone; + Error error; + ParseInfo info; - VerifyOrExit(aIndex != kInvalidIndex, error = kErrorParse); - - switch (aAddrMode) - { - case kAddrModeNone: - aAddress.SetNone(); - break; - - case kAddrModeReserved: - error = kErrorParse; - break; - - case kAddrModeShort: - aAddress.SetShort(LittleEndian::ReadUint16(&mPsdu[aIndex])); - break; - - case kAddrModeExt: - aAddress.SetExtended(&mPsdu[aIndex], ExtAddress::kReverseByteOrder); - break; - } + SuccessOrExit(error = info.ParseFrom(*this, kParseAddrFields)); + aAddress = info.mAddrs.mDestination; exit: return error; } -Error Frame::GetDstAddr(Address &aAddress) const -{ - return ReadAddressAt(FindDstAddrIndex(), ReadDstAddrMode(GetFrameControlField()), aAddress); -} - -uint8_t Frame::FindSrcPanIdIndex(void) const -{ - uint16_t fcf = GetFrameControlField(); - uint8_t index; - - VerifyOrExit(IsSrcPanIdPresent(fcf), index = kInvalidIndex); - - index = SkipSequenceIndex(); - - if (IsDstPanIdPresent(fcf)) - { - index += sizeof(PanId); - } - - SuccessOrExit(AddAddrSizeTo(index, ReadDstAddrMode(fcf))); - -exit: - return index; -} - bool Frame::IsSrcPanIdPresent(uint16_t aFcf) { bool present; @@ -507,75 +623,48 @@ bool Frame::IsSrcPanIdPresent(uint16_t aFcf) Error Frame::GetSrcPanId(PanId &aPanId) const { - Error error = kErrorNone; - uint8_t index = FindSrcPanIdIndex(); + Error error; + ParseInfo info; - VerifyOrExit(index != kInvalidIndex, error = kErrorParse); - aPanId = LittleEndian::ReadUint16(&mPsdu[index]); + SuccessOrExit(error = info.ParseFrom(*this, kParseAddrFields)); + VerifyOrExit(info.mPanIds.IsSourcePresent(), error = kErrorNotFound); + aPanId = info.mPanIds.GetSource(); exit: return error; } -uint8_t Frame::FindSrcAddrIndex(void) const -{ - uint16_t fcf = GetFrameControlField(); - uint8_t index = SkipSequenceIndex(); - - if (IsDstPanIdPresent(fcf)) - { - index += sizeof(PanId); - } - - SuccessOrExit(AddAddrSizeTo(index, ReadDstAddrMode(fcf))); - - if (IsSrcPanIdPresent(fcf)) - { - index += sizeof(PanId); - } - -exit: - return index; -} - Error Frame::GetSrcAddr(Address &aAddress) const { - return ReadAddressAt(FindSrcAddrIndex(), ReadSrcAddrMode(GetFrameControlField()), aAddress); -} + Error error; + ParseInfo info; -Error Frame::GetSecurityControlField(uint8_t &aSecurityControlField) const -{ - Error error = kErrorNone; - uint8_t index = FindSecurityHeaderIndex(); - - VerifyOrExit(index != kInvalidIndex, error = kErrorParse); - - aSecurityControlField = mPsdu[index]; + SuccessOrExit(error = info.ParseFrom(*this, kParseAddrFields)); + aAddress = info.mAddrs.mSource; exit: return error; } -uint8_t Frame::FindSecurityHeaderIndex(void) const +Error Frame::GetSecurityControlField(uint8_t &aSecurityControlField) const { - uint8_t index; + Error error; + ParseInfo info; - VerifyOrExit(kFcfSize < mLength, index = kInvalidIndex); - VerifyOrExit(GetSecurityEnabled(), index = kInvalidIndex); - index = SkipAddrFieldIndex(); + SuccessOrExit(error = info.ParseFrom(*this, kParseSecurityHeader)); + aSecurityControlField = info.mSecCtl; exit: - return index; + return error; } Error Frame::GetSecurityLevel(SecurityLevel &aSecurityLevel) const { - Error error = kErrorNone; - uint8_t index = FindSecurityHeaderIndex(); + Error error; + ParseInfo info; - VerifyOrExit(index != kInvalidIndex, error = kErrorParse); - - aSecurityLevel = ReadSecurityLevel(mPsdu[index]); + SuccessOrExit(error = info.ParseFrom(*this, kParseSecurityHeader)); + aSecurityLevel = info.mSecurityLevel; exit: return error; @@ -595,12 +684,11 @@ exit: Error Frame::GetKeyIdMode(KeyIdMode &aKeyIdMode) const { - Error error = kErrorNone; - uint8_t index = FindSecurityHeaderIndex(); + Error error; + ParseInfo info; - VerifyOrExit(index != kInvalidIndex, error = kErrorParse); - - aKeyIdMode = ReadKeyIdMode(mPsdu[index]); + SuccessOrExit(error = info.ParseFrom(*this, kParseSecurityHeader)); + aKeyIdMode = info.mKeyIdMode; exit: return error; @@ -620,15 +708,11 @@ exit: Error Frame::GetFrameCounter(uint32_t &aFrameCounter) const { - Error error = kErrorNone; - uint8_t index = FindSecurityHeaderIndex(); + Error error; + ParseInfo info; - VerifyOrExit(index != kInvalidIndex, error = kErrorParse); - - // Security Control - index += kSecurityControlSize; - - aFrameCounter = LittleEndian::ReadUint32(&mPsdu[index]); + SuccessOrExit(error = info.ParseFrom(*this, kParseSecurityHeader)); + aFrameCounter = info.mFrameCounter; exit: return error; @@ -636,25 +720,20 @@ exit: void Frame::SetFrameCounter(uint32_t aFrameCounter) { - uint8_t index = FindSecurityHeaderIndex(); + ParseInfo info; - OT_ASSERT(index != kInvalidIndex); - - // Security Control - index += kSecurityControlSize; - - LittleEndian::WriteUint32(aFrameCounter, &mPsdu[index]); + SuccessOrAssert(info.ParseFrom(*this, kParseSecurityHeader)); + LittleEndian::WriteUint32(aFrameCounter, info.mFrameCounterBytes); static_cast(this)->SetIsHeaderUpdated(true); } -const uint8_t *Frame::GetKeySource(void) const +void Frame::GetKeySource(FrameData &aKeySource) const { - uint8_t index = FindSecurityHeaderIndex(); + ParseInfo info; - OT_ASSERT(index != kInvalidIndex); - - return &mPsdu[index + kSecurityControlSize + kFrameCounterSize]; + SuccessOrAssert(info.ParseFrom(*this, kParseSecurityHeader)); + aKeySource = info.mKeySource; } uint8_t Frame::CalculateKeySourceSize(uint8_t aSecurityControl) @@ -674,65 +753,49 @@ uint8_t Frame::CalculateKeySourceSize(uint8_t aSecurityControl) return kKeySourceSize[ReadKeyIdMode(aSecurityControl)]; } +// NOLINTNEXTLINE(readability-make-member-function-const) void Frame::SetKeySource(const uint8_t *aKeySource) { - uint8_t keySourceSize; - uint8_t index = FindSecurityHeaderIndex(); + ParseInfo info; - OT_ASSERT(index != kInvalidIndex); - - keySourceSize = CalculateKeySourceSize(mPsdu[index]); - - memcpy(&mPsdu[index + kSecurityControlSize + kFrameCounterSize], aKeySource, keySourceSize); + SuccessOrAssert(info.ParseFrom(*this, kParseSecurityHeader)); + memcpy(AsNonConst(info.mKeySource.GetBytes()), aKeySource, info.mKeySource.GetLength()); } Error Frame::GetKeyIndex(uint8_t &aKeyIndex) const { - Error error = kErrorNone; - uint8_t keySourceSize; - uint8_t index = FindSecurityHeaderIndex(); + Error error; + ParseInfo info; - VerifyOrExit(index != kInvalidIndex, error = kErrorParse); - - keySourceSize = CalculateKeySourceSize(mPsdu[index]); - - aKeyIndex = mPsdu[index + kSecurityControlSize + kFrameCounterSize + keySourceSize]; + SuccessOrExit(error = info.ParseFrom(*this, kParseSecurityHeader)); + VerifyOrExit(info.mKeyIdMode != kKeyIdMode0, error = kErrorNotFound); + aKeyIndex = info.mKeyIndex; exit: return error; } +// NOLINTNEXTLINE(readability-make-member-function-const) void Frame::SetKeyIndex(uint8_t aKeyIndex) { - uint8_t keySourceSize; - uint8_t index = FindSecurityHeaderIndex(); + ParseInfo info; - OT_ASSERT(index != kInvalidIndex); + SuccessOrAssert(info.ParseFrom(*this, kParseSecurityHeader)); + VerifyOrExit(info.mKeyIdMode != kKeyIdMode0); + *info.mKeyIndexByte = aKeyIndex; - keySourceSize = CalculateKeySourceSize(mPsdu[index]); - - mPsdu[index + kSecurityControlSize + kFrameCounterSize + keySourceSize] = aKeyIndex; +exit: + return; } Error Frame::GetCommandId(uint8_t &aCommandId) const { - Error error = kErrorNone; - uint8_t index = FindPayloadIndex(); + Error error; + ParseInfo info; - VerifyOrExit(index != kInvalidIndex, error = kErrorParse); - - // The treatment of the Command ID field in a MAC command frame - // is version-dependent. In the 2015 spec, it is part of the - // encrypted payload, while in earlier versions, it is part of - // the MAC header. `FindPayloadIndex() accounts for both cases. - - if (!IsVersion2015()) - { - index -= kCommandIdSize; - } - - VerifyOrExit(index + kCommandIdSize + GetFooterLength() <= mLength, error = kErrorParse); - aCommandId = mPsdu[index]; + SuccessOrExit(error = info.ParseFrom(*this, kParseFully)); + VerifyOrExit(GetType(info.mFcf) == kTypeMacCmd, error = kErrorNotFound); + aCommandId = info.mCommandId; exit: return error; @@ -743,7 +806,6 @@ bool Frame::IsDataRequestCommand(void) const bool isDataRequest = false; uint8_t commandId; - VerifyOrExit(IsMacCommand()); SuccessOrExit(GetCommandId(commandId)); isDataRequest = (commandId == kMacCmdDataRequest); @@ -751,18 +813,34 @@ exit: return isDataRequest; } -uint8_t Frame::GetHeaderLength(void) const { return static_cast(GetPayload() - mPsdu); } - -uint8_t Frame::GetFooterLength(void) const +Error Frame::GetPayload(FrameData &aPayloadData) const { - uint8_t footerLength = static_cast(GetFcsSize()); - uint8_t index = FindSecurityHeaderIndex(); + Error error; + ParseInfo info; - VerifyOrExit(index != kInvalidIndex); - footerLength += CalculateMicSize(mPsdu[index]); + SuccessOrExit(error = info.ParseFrom(*this, kParseFully)); + aPayloadData = info.mPayload; exit: - return footerLength; + return error; +} + +Error Frame::DetermineLengths(Lengths &aLengths) const +{ + Error error; + ParseInfo info; + + ClearAllBytes(aLengths); + + SuccessOrExit(error = info.ParseFrom(*this, kParseFully)); + + aLengths.mHeader = info.mHeader.GetLength(); + aLengths.mPayload = info.mPayload.GetLength(); + aLengths.mFooter = GetLength() - aLengths.mHeader - aLengths.mPayload; + aLengths.mMaxPayload = GetMtu() - (aLengths.mHeader + aLengths.mFooter); + +exit: + return error; } uint8_t Frame::CalculateMicSize(uint8_t aSecurityControl) @@ -790,38 +868,6 @@ uint8_t Frame::CalculateMicSize(uint8_t aSecurityControl) return kMicSize[ReadSecurityLevel(aSecurityControl)]; } -uint16_t Frame::GetMaxPayloadLength(void) const { return GetMtu() - (GetHeaderLength() + GetFooterLength()); } - -uint16_t Frame::GetPayloadLength(void) const { return mLength - (GetHeaderLength() + GetFooterLength()); } - -void Frame::SetPayloadLength(uint16_t aLength) { mLength = GetHeaderLength() + GetFooterLength() + aLength; } - -uint8_t Frame::SkipSecurityHeaderIndex(void) const -{ - uint8_t index = SkipAddrFieldIndex(); - - VerifyOrExit(index != kInvalidIndex); - - if (GetSecurityEnabled()) - { - uint8_t securityControl; - uint8_t headerSize; - - VerifyOrExit(index < mLength, index = kInvalidIndex); - securityControl = mPsdu[index]; - - headerSize = CalculateSecurityHeaderSize(securityControl); - VerifyOrExit(headerSize != kInvalidSize, index = kInvalidIndex); - - index += headerSize; - - VerifyOrExit(index <= mLength, index = kInvalidIndex); - } - -exit: - return index; -} - Frame::AddrMode Frame::DetermineAddrMode(const Address &aAddress) { AddrMode addrMode = kAddrModeNone; @@ -868,182 +914,26 @@ exit: return size; } -Error Frame::AddAddrSizeTo(uint8_t &aIndex, AddrMode aAddrMode) -{ - static constexpr uint8_t kSizeForAddrMode[] = { - /* [0] kAddrModeNone */ 0, - /* [1] kAddrModeReserved */ kInvalidSize, - /* [2] kAddrModeShort */ sizeof(ShortAddress), - /* [3] kAddrModeExt */ sizeof(ExtAddress), - }; - - static_assert(kSizeForAddrMode[kAddrModeNone] == 0, "kSizeForAddrMode[] array is incorrect"); - static_assert(kSizeForAddrMode[kAddrModeReserved] == kInvalidSize, "kSizeForAddrMode[] array is incorrect"); - static_assert(kSizeForAddrMode[kAddrModeShort] == sizeof(ShortAddress), "kSizeForAddrMode[] array is incorrect"); - static_assert(kSizeForAddrMode[kAddrModeExt] == sizeof(ExtAddress), "kSizeForAddrMode[] array is incorrect"); - - Error error = kErrorNone; - - if (aAddrMode == kAddrModeReserved) - { - aIndex = kInvalidIndex; - error = kErrorParse; - ExitNow(); - } - - aIndex += kSizeForAddrMode[aAddrMode]; - -exit: - return error; -} - -uint8_t Frame::SkipAddrFieldIndex(void) const -{ - // Returns the index after the MAC address header fields (Frame Control, - // Sequence Number, Destination/Source PAN ID, and Destination/Source - // Addresses). If the header is invalid, returns `kInvalidIndex`. - - uint8_t index = kInvalidIndex; - uint8_t size; - uint16_t fcf; - - VerifyOrExit(kFcfSize + GetFcsSize() <= GetLength()); - - // Only accept standard frame types (Beacon, Data, Ack, MAC Command). - // Other types (e.g., Multipurpose) use a different FCF/header layout. - VerifyOrExit(GetType() <= kTypeMacCmd); - - fcf = GetFrameControlField(); - - // Only accept supported frame versions (2003, 2006, 2015). - // Future frame versions can alter the MAC header layout. - VerifyOrExit(GetVersion(fcf) <= kVersion2015); - - size = kFcfSize + (IsSeqPresent(fcf) ? kDsnSize : 0); - - if (IsDstPanIdPresent(fcf)) - { - size += sizeof(PanId); - } - - SuccessOrExit(AddAddrSizeTo(size, ReadDstAddrMode(fcf))); - - if (IsSrcPanIdPresent(fcf)) - { - size += sizeof(PanId); - } - - SuccessOrExit(AddAddrSizeTo(size, ReadSrcAddrMode(fcf))); - - index = size; - -exit: - return index; -} - -uint8_t Frame::FindPayloadIndex(void) const -{ - // We use `uint16_t` for `index` to handle its potential roll-over - // while parsing and verifying Header IE(s). - - uint16_t index = SkipSecurityHeaderIndex(); - - VerifyOrExit(index != kInvalidIndex); - #if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT - if (IsIePresent()) - { - uint8_t footerLength = GetFooterLength(); - - do - { - const HeaderIe *ie; - - VerifyOrExit(index + footerLength + sizeof(HeaderIe) <= mLength, index = kInvalidIndex); - - ie = reinterpret_cast(&mPsdu[index]); - index += ie->GetSize(); - - VerifyOrExit(index + footerLength <= mLength, index = kInvalidIndex); - - if (ie->GetId() == Termination2Ie::kId) - { - break; - } - - // If the `index + footerLength == mLength`, we exit the `while()` - // loop. This covers the case where frame contains one or more - // Header IEs but no data payload. In this case, spec does not - // require Header IE termination to be included (it is optional) - // since the end of frame can be determined from frame length and - // footer length. - - } while (index + footerLength < mLength); - - // Assume no Payload IE in current implementation - } -#endif // OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT - - if (IsMacCommand() && !IsVersion2015()) - { - // The treatment of the Command ID field in a MAC command frame - // is version-dependent. In IEEE 802.15.4-2015, it is part of - // the payload and therefore encrypted. In earlier versions, it - // is part of the MAC header and not encrypted. - // - // This adjusts the index to point to the start of the payload - // for pre-2015 frames. The `GetCommandId()` method also - // accounts for this version-specific difference. - - index += kCommandIdSize; - } - -exit: - return (index <= kMaxPsduSize) ? static_cast(index) : kInvalidIndex; -} - -const uint8_t *Frame::GetPayload(void) const -{ - uint8_t index = FindPayloadIndex(); - const uint8_t *payload; - - VerifyOrExit(index != kInvalidIndex, payload = nullptr); - payload = &mPsdu[index]; - -exit: - return payload; -} - -const uint8_t *Frame::GetFooter(void) const { return mPsdu + mLength - GetFooterLength(); } - -#if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT - -uint8_t Frame::FindHeaderIeIndex(void) const -{ - uint8_t index; - - VerifyOrExit(IsIePresent(), index = kInvalidIndex); - - index = SkipSecurityHeaderIndex(); - -exit: - return index; -} const HeaderIe *Frame::FindHeaderIe(HeaderIeMatcher aMatcher) const { - uint16_t index = FindHeaderIeIndex(); - uint16_t payloadIndex = FindPayloadIndex(); - const HeaderIe *matchedIe = nullptr; + const HeaderIe *matchedIe = nullptr; + ParseInfo info; - // `FindPayloadIndex()` verifies that Header IE(s) in frame (if present) - // are well-formed. + SuccessOrExit(info.ParseFrom(*this, kParseFully)); - VerifyOrExit((index != kInvalidIndex) && (payloadIndex != kInvalidIndex)); + VerifyOrExit(IsIePresent(info.mFcf)); - while (index < payloadIndex) + // `ParseFrom()` already validates that Header IE(s) are + // well-formed and contained within the frame. Here we + // just iterate through them and try to match them. + + while (true) { - const HeaderIe *ie = reinterpret_cast(&mPsdu[index]); + const HeaderIe *ie = info.mIeData.Read(); + + VerifyOrExit(ie != nullptr); if (aMatcher(*ie)) { @@ -1051,7 +941,7 @@ const HeaderIe *Frame::FindHeaderIe(HeaderIeMatcher aMatcher) const ExitNow(); } - index += ie->GetSize(); + info.mIeData.SkipOver(ie->GetLength()); } exit: @@ -1090,6 +980,18 @@ exit: #endif // OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT +void TxFrame::PrepareHeaders(const BuildInfo &aBuildInfo, PayloadBuilder &aPayloadBuilder) +{ + aBuildInfo.PrepareHeadersIn(*this); + aPayloadBuilder.InitFrom(*this); +} + +void TxFrame::PayloadBuilder::InitFrom(TxFrame &aFrame) +{ + IgnoreError(aFrame.DetermineLengths(mLengths)); + Init(aFrame.GetPsduStartingAt(mLengths.mHeader), mLengths.mMaxPayload); +} + void TxFrame::CopyFrom(const TxFrame &aFromFrame) { uint8_t *psduBuffer = mPsdu; @@ -1164,22 +1066,21 @@ Error TxFrame::PerformAesCcm(AesCcmOperation aOperation, const ExtAddress &aExtA static_assert(static_cast(kDecrypt) == Crypto::AesCcm::kDecrypt, "kDecrypt enum value is incorrect"); Error error; - uint32_t frameCounter; - SecurityLevel securityLevel; + ParseInfo info; Crypto::AesCcm aesCcm; Crypto::AesCcm::Nonce nonce; - SuccessOrExit(error = GetSecurityLevel(securityLevel)); - SuccessOrExit(error = GetFrameCounter(frameCounter)); + SuccessOrExit(error = info.ParseFrom(*this, kParseFully)); - nonce.InitFrom(aExtAddress, frameCounter, securityLevel); + nonce.InitFrom(aExtAddress, info.mFrameCounter, info.mSecurityLevel); aesCcm.SetKey(GetAesKey()); aesCcm.SetNonce(nonce); - aesCcm.SetAuthData(GetHeader(), GetHeaderLength()); - aesCcm.SetTagLength(GetFooterLength() - GetFcsSize()); + aesCcm.SetAuthData(info.mHeader.GetBytes(), info.mHeader.GetLength()); + aesCcm.SetTagLength(info.mMicSize); - error = aesCcm.Process(static_cast(aOperation), GetPayload(), GetPayloadLength()); + error = aesCcm.Process(static_cast(aOperation), AsNonConst(info.mPayload.GetBytes()), + info.mPayload.GetLength()); exit: return error; @@ -1262,10 +1163,9 @@ Error TxFrame::GenerateEnhAck(const RxFrame &aRxFrame, bool aIsFramePending, con buildInfo.mSecurityLevel = securityLevel; buildInfo.mKeyIdMode = keyIdMode; - buildInfo.PrepareHeadersIn(*this); + PrepareHeadersWithEmptyPayload(buildInfo); SetFramePending(aIsFramePending); - SetIePresent(aIeLength != 0); SetSequence(aRxFrame.GetSequence()); if (aRxFrame.GetSecurityEnabled()) @@ -1278,9 +1178,14 @@ Error TxFrame::GenerateEnhAck(const RxFrame &aRxFrame, bool aIsFramePending, con if (aIeLength > 0) { + ParseInfo info; + + SuccessOrAssert(info.ParseFrom(*this, kParseFully)); OT_ASSERT(aIeData != nullptr); - memcpy(&mPsdu[FindHeaderIeIndex()], aIeData, aIeLength); - mLength += aIeLength; + + SetIePresent(true); + memcpy(GetPsduStartingAt(info.mHeader.GetLength()), aIeData, aIeLength); + SetLength(GetLength() + aIeLength); } exit: @@ -1330,30 +1235,28 @@ exit: Error RxFrame::ProcessReceiveAesCcm(const ExtAddress &aExtAddress, const KeyMaterial &aMacKey) { - Error error = kErrorSecurity; - uint32_t frameCounter = 0; - SecurityLevel securityLevel; + Error error = kErrorSecurity; + ParseInfo info; Crypto::AesCcm aesCcm; Crypto::AesCcm::Nonce nonce; VerifyOrExit(GetSecurityEnabled(), error = kErrorNone); - SuccessOrExit(GetSecurityLevel(securityLevel)); - SuccessOrExit(GetFrameCounter(frameCounter)); + SuccessOrExit(info.ParseFrom(*this, kParseFully)); - nonce.InitFrom(aExtAddress, frameCounter, securityLevel); + nonce.InitFrom(aExtAddress, info.mFrameCounter, info.mSecurityLevel); aesCcm.SetKey(aMacKey); aesCcm.SetNonce(nonce); - aesCcm.SetAuthData(GetHeader(), GetHeaderLength()); - aesCcm.SetTagLength(GetFooterLength() - GetFcsSize()); + aesCcm.SetAuthData(info.mHeader.GetBytes(), info.mHeader.GetLength()); + aesCcm.SetTagLength(info.mMicSize); #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION // Do not decrypt when fuzzing ExitNow(error = kErrorNone); #endif - error = aesCcm.Process(Crypto::AesCcm::kDecrypt, GetPayload(), GetPayloadLength()); + error = aesCcm.Process(Crypto::AesCcm::kDecrypt, AsNonConst(info.mPayload.GetBytes()), info.mPayload.GetLength()); exit: return error; diff --git a/src/core/mac/mac_frame.hpp b/src/core/mac/mac_frame.hpp index 9b936d926..25b036553 100644 --- a/src/core/mac/mac_frame.hpp +++ b/src/core/mac/mac_frame.hpp @@ -40,6 +40,8 @@ #include "common/bit_utils.hpp" #include "common/const_cast.hpp" #include "common/encoding.hpp" +#include "common/frame_builder.hpp" +#include "common/frame_data.hpp" #include "common/numeric_limits.hpp" #include "mac/mac_header_ie.hpp" #include "mac/mac_types.hpp" @@ -148,6 +150,17 @@ public: */ typedef String InfoString; + /** + * Represents the length breakdown of a MAC frame. + */ + struct Lengths + { + uint16_t mHeader; ///< Header length (in bytes). + uint16_t mPayload; ///< Payload length (in bytes). + uint16_t mFooter; ///< Footer length (in bytes). + uint16_t mMaxPayload; ///< Maximum allowed payload length (in bytes). + }; + /** * Validates the frame. * @@ -183,10 +196,12 @@ public: /** * This method returns whether the frame is an IEEE 802.15.4 Wake-up frame. * + * This is a placeholder implementation following removal of legacy Multipurpose frame format. + * * @retval TRUE If this is a Wake-up frame. * @retval FALSE If this is not a Wake-up frame. */ - bool IsWakeupFrame(void) const; + bool IsWakeupFrame(void) const { return false; } #endif /** @@ -297,8 +312,9 @@ public: * * @param[out] aPanId The Destination PAN Identifier. * - * @retval kErrorNone Successfully retrieved the Destination PAN Identifier. - * @retval kErrorParse Failed to parse the PAN Identifier. + * @retval kErrorNone Successfully retrieved the Destination PAN Identifier. + * @retval kErrorNotFound Destination PAN Identifier is not present in the frame. + * @retval kErrorParse Failed to parse the frame. */ Error GetDstPanId(PanId &aPanId) const; @@ -315,7 +331,8 @@ public: * * @param[out] aAddress The Destination Address. * - * @retval kErrorNone Successfully retrieved the Destination Address. + * @retval kErrorNone Successfully retrieved the Destination Address. + * @retval kErrorParse Failed to parse the frame. */ Error GetDstAddr(Address &aAddress) const; @@ -332,7 +349,9 @@ public: * * @param[out] aPanId The Source PAN Identifier. * - * @retval kErrorNone Successfully retrieved the Source PAN Identifier. + * @retval kErrorNone Successfully retrieved the Source PAN Identifier. + * @retval kErrorNotFound Source PAN Identifier is not present in the frame. + * @retval kErrorParse Failed to parse the frame. */ Error GetSrcPanId(PanId &aPanId) const; @@ -349,7 +368,8 @@ public: * * @param[out] aAddress The Source Address. * - * @retval kErrorNone Successfully retrieved the Source Address. + * @retval kErrorNone Successfully retrieved the Source Address. + * @retval kErrorParse Failed to parse the frame. */ Error GetSrcAddr(Address &aAddress) const; @@ -358,8 +378,9 @@ public: * * @param[out] aSecurityControlField The Security Control Field. * - * @retval kErrorNone Successfully retrieved the Security Level Identifier. - * @retval kErrorParse Failed to find the security control field in the frame. + * @retval kErrorNone Successfully retrieved the Security Control Field. + * @retval kErrorNotFound Frame does not have a security header (security is not enabled) + * @retval kErrorParse Failed to parse the frame. */ Error GetSecurityControlField(uint8_t &aSecurityControlField) const; @@ -368,8 +389,9 @@ public: * * @param[out] aSecurityLevel The Security Level Identifier. * - * @retval kErrorNone Successfully retrieved the Security Level Identifier. - * @retval kErrorParse Failed to parse MAC or security header. + * @retval kErrorNone Successfully retrieved the Security Level Identifier. + * @retval kErrorNotFound Frame does not have a security header (security is not enabled) + * @retval kErrorParse Failed to parse MAC or security header. */ Error GetSecurityLevel(SecurityLevel &aSecurityLevel) const; @@ -423,9 +445,9 @@ public: /** * Returns a pointer to the Key Source. * - * @returns A pointer to the Key Source. + * @param[out] aKeySource A `FrameData` to point to key source data bytes. */ - const uint8_t *GetKeySource(void) const; + void GetKeySource(FrameData &aKeySource) const; /** * Sets the Key Source. @@ -439,8 +461,9 @@ public: * * @param[out] aKeyIndex The Key Index * - * @retval kErrorNone Successfully retrieved the Key Index. - * @retval kErrorParse Failed to parse MAC or security header. + * @retval kErrorNone Successfully retrieved the Key Index. + * @retval kErrorNotFound Frame is using `kKeyIdMode0` which does not have any Key Index. + * @retval kErrorParse Failed to parse MAC or security header. */ Error GetKeyIndex(uint8_t &aKeyIndex) const; @@ -456,7 +479,9 @@ public: * * @param[out] aCommandId The Command ID. * - * @retval kErrorNone Successfully retrieved the Command ID. + * @retval kErrorNone Successfully retrieved the Command ID. + * @retval kErrorNotFound The frame is not a MAC command. + * @retval kErrorParse Failed to parse frame. */ Error GetCommandId(uint8_t &aCommandId) const; @@ -470,89 +495,31 @@ public: bool IsDataRequestCommand(void) const; /** - * Returns the MAC header size. + * Gets the frame payload as `FrameData`. * - * @returns The MAC header size. + * For MAC Command frames (`kTypeMacCmd`), the treatment of the Command ID field depends on the frame version: + * - For 2015 version , the Command ID is part of the payload, so @p aPayloadData includes it. + * - For earlier versions (2003/2006), the Command ID is part of the MAC header, so @p aPayloadData starts after + * the Command ID. + * + * @param[out] aPayloadData A reference to a `FrameData` to return the frame payload. + * + * @retval kErrorNone Successfully retrieved the frame payload. + * @retval kErrorParse Failed to parse the frame. */ - uint8_t GetHeaderLength(void) const; + Error GetPayload(FrameData &aPayloadData) const; /** - * Returns the MAC footer size. + * Determines the length breakdown of the frame. * - * @returns The MAC footer size. - */ - uint8_t GetFooterLength(void) const; - - /** - * Returns the current MAC Payload length. + * @param[out] aLengths A reference to a `Lengths` structure to return the frame lengths. * - * @returns The current MAC Payload length. + * @retval kErrorNone Successfully calculated frame lengths. + * @retval kErrorParse Failed to parse the frame. */ - uint16_t GetPayloadLength(void) const; - - /** - * Returns the maximum MAC Payload length for the given MAC header and footer. - * - * @returns The maximum MAC Payload length for the given MAC header and footer. - */ - uint16_t GetMaxPayloadLength(void) const; - - /** - * Sets the MAC Payload length. - */ - void SetPayloadLength(uint16_t aLength); - - /** - * Returns a pointer to the MAC Header. - * - * @returns A pointer to the MAC Header. - */ - uint8_t *GetHeader(void) { return GetPsdu(); } - - /** - * Returns a pointer to the MAC Header. - * - * @returns A pointer to the MAC Header. - */ - const uint8_t *GetHeader(void) const { return GetPsdu(); } - - /** - * Returns a pointer to the MAC Payload. - * - * @returns A pointer to the MAC Payload. - */ - uint8_t *GetPayload(void) { return AsNonConst(AsConst(this)->GetPayload()); } - - /** - * Returns a pointer to the MAC Payload. - * - * @returns A pointer to the MAC Payload. - */ - const uint8_t *GetPayload(void) const; - - /** - * Returns a pointer to the MAC Footer. - * - * @returns A pointer to the MAC Footer. - */ - uint8_t *GetFooter(void) { return AsNonConst(AsConst(this)->GetFooter()); } - - /** - * Returns a pointer to the MAC Footer. - * - * @returns A pointer to the MAC Footer. - */ - const uint8_t *GetFooter(void) const; + Error DetermineLengths(Lengths &aLengths) const; #if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT - /** - * Indicates whether the frame contains header IEs. - * - * @retval TRUE The frame contains header IEs. - * @retval FALSE The frame contains no header IEs. - */ - bool HasAnyHeaderIe(void) const { return FindHeaderIeIndex() != kInvalidIndex; } - /** * Finds a specific Information Element (IE) in the frame. * @@ -706,21 +673,57 @@ protected: static constexpr uint8_t kInvalidSize = kInvalidIndex; static constexpr uint8_t kMaxPsduSize = kInvalidSize - 1; - void SetFrameControlField(uint16_t aFcf) { LittleEndian::WriteUint16(aFcf, mPsdu); } - void UpdateFcfFlag(bool aSet, uint16_t aBitFlag); - uint8_t SkipSequenceIndex(void) const; - uint8_t FindDstPanIdIndex(void) const; - uint8_t FindDstAddrIndex(void) const; - uint8_t FindSrcPanIdIndex(void) const; - uint8_t FindSrcAddrIndex(void) const; - uint8_t SkipAddrFieldIndex(void) const; - uint8_t FindSecurityHeaderIndex(void) const; - uint8_t SkipSecurityHeaderIndex(void) const; - uint8_t FindPayloadIndex(void) const; -#if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT - uint8_t FindHeaderIeIndex(void) const; -#endif + enum ParseMode : uint8_t + { + kParseAddrFields, + kParseSecurityHeader, + kParseFully, + }; + class ParseInfo + { + public: + // - - - - - - - - - - - - - - - - - - - - - - - - - + // Mac Header Address Info + uint16_t mFcf; + PanIds mPanIds; + Addresses mAddrs; + uint8_t mSequenceNum; + + // - - - - - - - - - - - - - - - - - - - - - - - - - + // Aux Security Header + uint8_t mSecCtl; + SecurityLevel mSecurityLevel; + KeyIdMode mKeyIdMode; + uint8_t mKeyIndex; + uint8_t mMicSize; + uint32_t mFrameCounter; + FrameData mKeySource; + uint8_t *mFrameCounterBytes; + uint8_t *mKeyIndexByte; + + // - - - - - - - - - - - - - - - - - - - - - - - - - + // Header IEs + FrameData mIeData; + + // - - - - - - - - - - - - - - - - - - - - - - - - - + // MAC Command ID + uint8_t mCommandId; + + // - - - - - - - - - - - - - - - - - - - - - - - - - + // Header and Payload breakdown + FrameData mHeader; + FrameData mPayload; + + Error ParseFrom(const Frame &aFrame, ParseMode aMode); + + private: + static Error ParseAddress(FrameData &aFrameData, AddrMode aAddrMode, Address &aAddress); + }; + + void UpdateFcfFlag(bool aSet, uint16_t aBitFlag); + + static uint16_t GetType(uint16_t aFcf) { return (aFcf & kFcfFrameTypeMask); } static AddrMode ReadDstAddrMode(uint16_t aFcf) { return As(ReadBits(aFcf)); } static AddrMode ReadSrcAddrMode(uint16_t aFcf) { return As(ReadBits(aFcf)); } static bool IsSeqSuppressed(uint16_t aFcf) { return IsVersion2015(aFcf) && ((aFcf & kFcfSeqSuppression) != 0); } @@ -754,7 +757,6 @@ private: const HeaderIe *FindHeaderIe(HeaderIeMatcher aMatcher) const; HeaderIe *FindHeaderIe(HeaderIeMatcher aMatcher) { return AsNonConst(AsConst(this)->FindHeaderIe(aMatcher)); } #endif - Error ReadAddressAt(uint8_t aIndex, AddrMode aAddrMode, Address &aAddress) const; }; /** @@ -813,36 +815,16 @@ public: /** * Represents the information to use to build the frame. */ - struct BuildInfo : public Clearable + class BuildInfo : public Clearable { + friend class TxFrame; + + public: /** * Initializes the `BuildInfo` by clearing all its fields (setting all bytes to zero). */ BuildInfo(void) { Clear(); } - /** - * Prepares MAC headers based on `BuildInfo` fields in a given `TxFrame`. - * - * This method uses the `BuildInfo` structure to construct the MAC address and security headers in @p aTxFrame. - * It determines the Frame Control Field (FCF), including setting the appropriate frame type, security level, - * and addressing mode flags. It populates the source and destination addresses and PAN IDs within the MAC - * header based on the information provided in the `BuildInfo` structure. - * - * It sets the Ack Request bit in the FCF if the following criteria are met: - * - A destination address is present - * - The destination address is not the broadcast address - * - The frame type is not an ACK frame - * - * The header IE entries are prepared based on `mAppendTimeIe` and `mAppendCslIe` flags and the IE Present - * flag in FCF is determined accordingly. - * - * The Frame Pending flag in FCF is not set. It may need to be set separately depending on the specific - * requirements of the frame being transmitted. - * - * @param[in,out] aTxFrame The `TxFrame` instance in which to prepare and append the MAC headers. - */ - void PrepareHeadersIn(TxFrame &aTxFrame) const; - Type mType; ///< Frame type. Version mVersion; ///< Frame version. Addresses mAddrs; ///< Frame source and destination addresses. @@ -861,8 +843,92 @@ public: #endif bool mEmptyPayload : 1; ///< Whether payload is empty (to decide about appending Termination2 IE). #endif + + private: + void PrepareHeadersIn(TxFrame &aTxFrame) const; }; + /** + * Helper class for building the payload of a `TxFrame`. + */ + class PayloadBuilder : public FrameBuilder + { + friend class TxFrame; + + public: + /** + * Gets the header length of the frame being built. + * + * @returns The header length (in bytes). + */ + uint16_t GetHeaderLength(void) const { return mLengths.mHeader; } + + /** + * Gets the footer length of the frame being built. + * + * @returns The footer length (in bytes). + */ + uint16_t GetFooterLength(void) const { return mLengths.mFooter; } + + private: + void InitFrom(TxFrame &aFrame); + uint16_t GetTotalLength(void) const { return GetLength() + mLengths.mHeader + mLengths.mFooter; } + + Lengths mLengths; + }; + + /** + * Prepares MAC headers in the frame based on `BuildInfo` settings and initializes a `PayloadBuilder`. + * + * This method uses the `BuildInfo` structure to construct the MAC address and security headers in the frame. + * It determines the Frame Control Field (FCF), including setting the appropriate frame type, security level, + * and addressing mode flags. It populates the source and destination addresses and PAN IDs within the MAC + * header based on the information provided in the `BuildInfo` structure. + * + * It sets the Ack Request bit in the FCF if the following criteria are met: + * - A destination address is present + * - The destination address is not the broadcast address + * - The frame type is not an ACK frame + * + * The header IE entries are prepared based on `mAppendTimeIe` and `mAppendCslIe` flags and the IE Present + * flag in FCF is determined accordingly. + * + * The Frame Pending flag in FCF is not set. It may need to be set separately depending on the specific + * requirements of the frame being transmitted. + * + * The provided @p aPayloadBuilder is initialized to allow building and appending payload bytes directly + * into the frame buffer following the prepared headers. It is set up with the maximum available payload + * capacity based on the frame header and footer lengths and its MTU. Callers can use @p aPayloadBuilder to + * construct the frame payload. Once payload construction is complete, `FinishPayload()` can be called to update + * and finalize the total frame length. + * + * For MAC Command frames (`kTypeMacCmd`), whether the Command ID field is treated as part of the header or + * payload depends on the frame version (see `GetPayload()`). The same rules apply to @p aPayloadBuilder here: + * - For 2015 version, the Command ID is part of the payload, so @p aPayloadBuilder starts before the Command ID. + * - For earlier versions (2003/2006), the Command ID is part of the MAC header, so @p aPayloadBuilder starts + * after the Command ID. + * + * @param[in] aBuildInfo The `BuildInfo` containing settings for the MAC headers. + * @param[out] aPayloadBuilder A reference to a `PayloadBuilder` to initialize for payload construction. + */ + void PrepareHeaders(const BuildInfo &aBuildInfo, PayloadBuilder &aPayloadBuilder); + + /** + * Finishes building the frame payload and updates the total frame length. + * + * @param[in] aPayloadBuilder The `PayloadBuilder` used to construct the payload. + */ + void FinishPayload(const PayloadBuilder &aPayloadBuilder) { SetLength(aPayloadBuilder.GetTotalLength()); } + + /** + * Prepares MAC headers in the frame assuming an empty payload. + * + * See `PrepareHeaders()` for more details on how the MAC headers are constructed. + * + * @param[in] aBuildInfo The `BuildInfo` structure containing settings for the MAC headers. + */ + void PrepareHeadersWithEmptyPayload(const BuildInfo &aBuildInfo) { aBuildInfo.PrepareHeadersIn(*this); } + /** * Copies the PSDU and all attributes (except for frame link type) from another frame. * diff --git a/src/core/mac/scan_result.cpp b/src/core/mac/scan_result.cpp index fe2cf1a1f..5534175d7 100644 --- a/src/core/mac/scan_result.cpp +++ b/src/core/mac/scan_result.cpp @@ -68,7 +68,7 @@ Error ScanResult::PopulateFromBeacon(const Mac::RxFrame *aBeaconFrame) const Mac::BeaconHeader *beaconHeader; const Mac::BeaconPayload *beaconPayload; - frameData.Init(aBeaconFrame->GetPayload(), aBeaconFrame->GetPayloadLength()); + SuccessOrExit(aBeaconFrame->GetPayload(frameData)); beaconHeader = frameData.Read(); VerifyOrExit((beaconHeader != nullptr) && beaconHeader->IsValid()); diff --git a/src/core/mac/sub_mac.cpp b/src/core/mac/sub_mac.cpp index 2668924ae..c7cb9f1f7 100644 --- a/src/core/mac/sub_mac.cpp +++ b/src/core/mac/sub_mac.cpp @@ -617,7 +617,7 @@ void SubMac::ReprocessSecurityForRetx(TxFrame &aFrame) // it contains Header IEs. The frame is first restored back to // plaintext and then re-encrypted with a new frame counter value. - VerifyOrExit(aFrame.GetSecurityEnabled() && aFrame.HasAnyHeaderIe()); + VerifyOrExit(aFrame.GetSecurityEnabled() && aFrame.IsIePresent()); // When transmit security is handled by `SubMac`, the AES key is already set // on `aFrame`. However, when transmit security is delegated to the radio diff --git a/src/core/radio/radio_frame.hpp b/src/core/radio/radio_frame.hpp index 6d38116c0..12d342cda 100644 --- a/src/core/radio/radio_frame.hpp +++ b/src/core/radio/radio_frame.hpp @@ -100,6 +100,24 @@ public: */ const uint8_t *GetPsdu(void) const { return mPsdu; } + /** + * Returns a pointer to the PSDU starting at a given index. + * + * @param[in] aIndex The index to start from. + * + * @returns A pointer to the PSDU starting at @p aIndex. + */ + uint8_t *GetPsduStartingAt(uint16_t aIndex) { return mPsdu + aIndex; } + + /** + * Returns a pointer to the PSDU starting at a given index. + * + * @param[in] aIndex The index to start from. + * + * @returns A pointer to the PSDU starting at @p aIndex. + */ + const uint8_t *GetPsduStartingAt(uint16_t aIndex) const { return mPsdu + aIndex; } + #if OPENTHREAD_CONFIG_MULTI_RADIO /** * Gets the radio link type of the frame. diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp index ed9fc4042..6c7236742 100644 --- a/src/core/thread/mesh_forwarder.cpp +++ b/src/core/thread/mesh_forwarder.cpp @@ -1012,7 +1012,7 @@ void MeshForwarder::HandleReceivedFrame(Mac::RxFrame &aFrame) VerifyOrExit(mEnabled, error = kErrorInvalidState); - rxInfo.mFrameData.Init(aFrame.GetPayload(), aFrame.GetPayloadLength()); + SuccessOrExit(error = aFrame.GetPayload(rxInfo.mFrameData)); SuccessOrExit(error = aFrame.GetSrcAddr(rxInfo.mMacAddrs.mSource)); SuccessOrExit(error = aFrame.GetDstAddr(rxInfo.mMacAddrs.mDestination)); diff --git a/src/core/thread/message_framer.cpp b/src/core/thread/message_framer.cpp index fc2220df9..53a8e031f 100644 --- a/src/core/thread/message_framer.cpp +++ b/src/core/thread/message_framer.cpp @@ -53,9 +53,17 @@ void MessageFramer::DetermineMacSourceAddress(const Ip6::Address &aIp6Addr, Mac: } } -void MessageFramer::PrepareMacHeaders(Mac::TxFrame &aTxFrame, - Mac::TxFrame::BuildInfo &aBuildInfo, - const Message *aMessage) +void MessageFramer::PrepareMacHeaders(Mac::TxFrame &aTxFrame, Mac::TxFrame::BuildInfo &aBuildInfo) +{ + Mac::TxFrame::PayloadBuilder builder; + + PrepareMacHeaders(aTxFrame, aBuildInfo, builder, nullptr); +} + +void MessageFramer::PrepareMacHeaders(Mac::TxFrame &aTxFrame, + Mac::TxFrame::BuildInfo &aBuildInfo, + Mac::TxFrame::PayloadBuilder &aPayloadBuilder, + const Message *aMessage) { const Neighbor *neighbor; @@ -110,9 +118,9 @@ void MessageFramer::PrepareMacHeaders(Mac::TxFrame &aTxFrame, #endif // OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Prepare MAC headers + // Prepare MAC headers and init `aPayloadBuilder`. - aBuildInfo.PrepareHeadersIn(aTxFrame); + aTxFrame.PrepareHeaders(aBuildInfo, aPayloadBuilder); OT_UNUSED_VARIABLE(aMessage); OT_UNUSED_VARIABLE(neighbor); @@ -136,10 +144,9 @@ void MessageFramer::PrepareEmptyFrame(Mac::TxFrame &aFrame, const Mac::Address & buildInfo.mSecurityLevel = Mac::Frame::kSecurityEncMic32; buildInfo.mKeyIdMode = Mac::Frame::kKeyIdMode1; - PrepareMacHeaders(aFrame, buildInfo, nullptr); + PrepareMacHeaders(aFrame, buildInfo); aFrame.SetAckRequest(aAckRequest); - aFrame.SetPayloadLength(0); } uint16_t MessageFramer::PrepareFrame(Mac::TxFrame &aFrame, @@ -150,11 +157,11 @@ uint16_t MessageFramer::PrepareFrame(Mac::TxFrame &aFrame, uint16_t aMeshDest, bool aAddFragHeader) { - Mac::TxFrame::BuildInfo buildInfo; - uint16_t payloadLength; - uint16_t origMsgOffset; - uint16_t nextOffset; - FrameBuilder frameBuilder; + Mac::TxFrame::BuildInfo buildInfo; + Mac::TxFrame::PayloadBuilder frameBuilder; + uint16_t payloadLength; + uint16_t origMsgOffset; + uint16_t nextOffset; start: buildInfo.Clear(); @@ -202,9 +209,7 @@ start: buildInfo.mType = Mac::Frame::kTypeData; buildInfo.mAddrs = aMacAddrs; - PrepareMacHeaders(aFrame, buildInfo, &aMessage); - - frameBuilder.Init(aFrame.GetPayload(), aFrame.GetMaxPayloadLength()); + PrepareMacHeaders(aFrame, buildInfo, frameBuilder, &aMessage); #if OPENTHREAD_FTD @@ -229,10 +234,10 @@ start: // then adding the fixed `kMeshHeaderFrameFcsSize` instead // (updating the FCS size in the calculation of footer length). - maxPayloadLength = kMeshHeaderFrameMtu - aFrame.GetHeaderLength() - - (aFrame.GetFooterLength() - aFrame.GetFcsSize() + kMeshHeaderFrameFcsSize); + maxPayloadLength = kMeshHeaderFrameMtu - frameBuilder.GetHeaderLength() - + (frameBuilder.GetFooterLength() - aFrame.GetFcsSize() + kMeshHeaderFrameFcsSize); - frameBuilder.Init(aFrame.GetPayload(), maxPayloadLength); + frameBuilder.SetMaxLength(Min(maxPayloadLength, frameBuilder.GetMaxLength())); meshHeader.Init(aMeshSource, aMeshDest, kMeshHeaderHopsLeft); @@ -331,7 +336,8 @@ start: // Copy IPv6 Payload SuccessOrAssert(frameBuilder.AppendBytesFromMessage(aMessage, aMessage.GetOffset(), payloadLength)); - aFrame.SetPayloadLength(frameBuilder.GetLength()); + + aFrame.FinishPayload(frameBuilder); nextOffset = aMessage.GetOffset() + payloadLength; @@ -352,7 +358,8 @@ start: uint16_t MessageFramer::PrepareMeshFrame(Mac::TxFrame &aFrame, Message &aMessage, const Mac::Addresses &aMacAddrs) { - Mac::TxFrame::BuildInfo buildInfo; + Mac::TxFrame::BuildInfo buildInfo; + Mac::TxFrame::PayloadBuilder frameBuilder; buildInfo.mType = Mac::Frame::kTypeData; buildInfo.mAddrs = aMacAddrs; @@ -360,12 +367,10 @@ uint16_t MessageFramer::PrepareMeshFrame(Mac::TxFrame &aFrame, Message &aMessage buildInfo.mKeyIdMode = Mac::Frame::kKeyIdMode1; buildInfo.mPanIds.SetBothSourceDestination(Get().GetPanId()); - PrepareMacHeaders(aFrame, buildInfo, &aMessage); + PrepareMacHeaders(aFrame, buildInfo, frameBuilder, &aMessage); - // write payload - OT_ASSERT(aMessage.GetLength() <= aFrame.GetMaxPayloadLength()); - aMessage.ReadBytes(0, aFrame.GetPayload(), aMessage.GetLength()); - aFrame.SetPayloadLength(aMessage.GetLength()); + SuccessOrAssert(frameBuilder.AppendBytesFromMessage(aMessage, 0, aMessage.GetLength())); + aFrame.FinishPayload(frameBuilder); return aMessage.GetLength(); } diff --git a/src/core/thread/message_framer.hpp b/src/core/thread/message_framer.hpp index 55026776f..be94ea6c1 100644 --- a/src/core/thread/message_framer.hpp +++ b/src/core/thread/message_framer.hpp @@ -131,7 +131,11 @@ private: // (requiring one hop) and one as additional guard increment. static constexpr uint8_t kMeshHeaderHopsLeft = Mle::kMaxRouteCost + 3; - void PrepareMacHeaders(Mac::TxFrame &aTxFrame, Mac::TxFrame::BuildInfo &aBuildInfo, const Message *aMessage); + void PrepareMacHeaders(Mac::TxFrame &aTxFrame, Mac::TxFrame::BuildInfo &aBuildInfo); + void PrepareMacHeaders(Mac::TxFrame &aTxFrame, + Mac::TxFrame::BuildInfo &aBuildInfo, + Mac::TxFrame::PayloadBuilder &aPayloadBuilder, + const Message *aMessage); uint16_t mFragTag; }; diff --git a/tests/gtest/radio_spinel_rcp_test.cpp b/tests/gtest/radio_spinel_rcp_test.cpp index 9d3c53ce3..91644598a 100644 --- a/tests/gtest/radio_spinel_rcp_test.cpp +++ b/tests/gtest/radio_spinel_rcp_test.cpp @@ -76,7 +76,7 @@ TEST(RadioSpinelTransmit, shouldPassDesiredTxPowerToRadioPlatform) buildInfo.mPanIds.SetDestination(kDstPanId); buildInfo.mSecurityLevel = Mac::Frame::kSecurityEncMic32; - buildInfo.PrepareHeadersIn(txFrame); + txFrame.PrepareHeadersWithEmptyPayload(buildInfo); } txFrame.mInfo.mTxInfo.mTxPower = kTxPower; @@ -120,7 +120,7 @@ TEST(RadioSpinelTransmit, shouldCauseSwitchingToRxChannelAfterTxDone) buildInfo.mPanIds.SetDestination(kDstPanId); buildInfo.mSecurityLevel = Mac::Frame::kSecurityEncMic32; - buildInfo.PrepareHeadersIn(txFrame); + txFrame.PrepareHeadersWithEmptyPayload(buildInfo); } txFrame.mInfo.mTxInfo.mTxPower = kTxPower; @@ -166,7 +166,7 @@ TEST(RadioSpinelTransmit, shouldSkipCsmaCaWhenDisabled) buildInfo.mPanIds.SetDestination(kDstPanId); buildInfo.mSecurityLevel = Mac::Frame::kSecurityEncMic32; - buildInfo.PrepareHeadersIn(txFrame); + txFrame.PrepareHeadersWithEmptyPayload(buildInfo); } txFrame.mInfo.mTxInfo.mCsmaCaEnabled = false; @@ -222,7 +222,7 @@ TEST(RadioSpinelTransmit, shouldPerformCsmaCaWhenEnabled) buildInfo.mPanIds.SetDestination(kDstPanId); buildInfo.mSecurityLevel = Mac::Frame::kSecurityEncMic32; - buildInfo.PrepareHeadersIn(txFrame); + txFrame.PrepareHeadersWithEmptyPayload(buildInfo); } txFrame.mInfo.mTxInfo.mCsmaCaEnabled = true; @@ -272,7 +272,7 @@ TEST(RadioSpinelTransmit, shouldNotCauseSwitchingToRxAfterTxDoneIfNotRxOnWhenIdl buildInfo.mPanIds.SetDestination(kDstPanId); buildInfo.mSecurityLevel = Mac::Frame::kSecurityEncMic32; - buildInfo.PrepareHeadersIn(txFrame); + txFrame.PrepareHeadersWithEmptyPayload(buildInfo); } txFrame.mInfo.mTxInfo.mTxPower = kTxPower; @@ -348,7 +348,7 @@ TEST(RadioSpinelTransmit, shouldSkipCsmaBackoffWhenCsmaCaIsEnabledAndMaxBackoffs buildInfo.mPanIds.SetDestination(kDstPanId); buildInfo.mSecurityLevel = Mac::Frame::kSecurityEncMic32; - buildInfo.PrepareHeadersIn(txFrame); + txFrame.PrepareHeadersWithEmptyPayload(buildInfo); } txFrame.mInfo.mTxInfo.mCsmaCaEnabled = true; diff --git a/tests/unit/test_mac_frame.cpp b/tests/unit/test_mac_frame.cpp index c30313d3f..cc25e35b0 100644 --- a/tests/unit/test_mac_frame.cpp +++ b/tests/unit/test_mac_frame.cpp @@ -294,6 +294,7 @@ void TestMacHeader(void) Mac::TxFrame::BuildInfo buildInfo; Mac::Address address; Mac::PanId panId; + Mac::Frame::Lengths lengths; frame.mPsdu = psdu; frame.mLength = 0; @@ -360,10 +361,12 @@ void TestMacHeader(void) buildInfo.mKeyIdMode = testCase.mKeyIdMode; buildInfo.mSuppressSequence = testCase.mSuppressSequence; - buildInfo.PrepareHeadersIn(frame); + frame.PrepareHeadersWithEmptyPayload(buildInfo); - VerifyOrQuit(frame.GetHeaderLength() == testCase.mHeaderLength); - VerifyOrQuit(frame.GetFooterLength() == testCase.mFooterLength); + SuccessOrQuit(frame.DetermineLengths(lengths)); + + VerifyOrQuit(lengths.mHeader == testCase.mHeaderLength); + VerifyOrQuit(lengths.mFooter == testCase.mFooterLength); VerifyOrQuit(frame.GetLength() == testCase.mHeaderLength + testCase.mFooterLength); VerifyOrQuit(frame.GetType() == Mac::Frame::kTypeData); @@ -666,8 +669,10 @@ void TestMacFrameApi(void) Mac::Frame frame; #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) - uint8_t data_psdu1[] = {0x29, 0xee, 0x53, 0xce, 0xfa, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x6e, 0x16, 0x05, - 0x00, 0x00, 0x00, 0x00, 0x0a, 0x6e, 0x16, 0x0d, 0x01, 0x00, 0x00, 0x00, 0x01}; + uint8_t data_psdu1[] = {0x29, 0xee, 0x53, 0xce, 0xfa, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0a, + 0x6e, 0x16, 0x05, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x6e, 0x16, 0x0d, + 0x01, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00}; + uint8_t mac_cmd_psdu2[] = {0x6b, 0xaa, 0x8d, 0xce, 0xfa, 0x00, 0x68, 0x01, 0x68, 0x0d, 0x08, 0x00, 0x00, 0x00, 0x01, 0x04, 0x0d, 0xed, 0x0b, 0x35, 0x0c, 0x80, 0x3f, 0x04, 0x4b, 0x88, 0x89, 0xd6, 0x59, 0xe1};