mirror of
https://github.com/espressif/openthread.git
synced 2026-08-01 08:37:47 +00:00
[mac] add dynamic HeaderIe building with StartIe() and EndIe() (#13291)
This commit introduces dynamic Header IE generation in `HeaderIe` using `StartIe()`, `EndIe()`, and `Bookmark`. It allows appending variable-length Header IEs directly into a `FrameBuilder` without pre-computing the IE length. It also adds unit test case `TestMacHeaderIeStartEnd()` in `test_mac_frame.cpp` covering normal usage, empty IEs, max length (127 bytes), and error cases for the new methods.
This commit is contained in:
@@ -297,6 +297,25 @@ public:
|
||||
*/
|
||||
void RemoveBytes(uint16_t aOffset, uint16_t aLength);
|
||||
|
||||
/**
|
||||
* Reads a pointer to a previously appended object in the `FrameBuilder` at a given byte offset.
|
||||
*
|
||||
* This method does not perform any bounds checking. The caller MUST ensure the object of type `ObjectType`
|
||||
* fits within the previously appended content.
|
||||
*
|
||||
* @tparam ObjectType The object type to read.
|
||||
*
|
||||
* @param[in] aOffset The byte offset where the object starts.
|
||||
*
|
||||
* @returns A pointer to the `ObjectType` at @p aOffset.
|
||||
*/
|
||||
template <typename ObjectType> ObjectType *Read(uint16_t aOffset)
|
||||
{
|
||||
static_assert(!TypeTraits::IsPointer<ObjectType>::kValue, "ObjectType must not be a pointer");
|
||||
|
||||
return reinterpret_cast<ObjectType *>(mBuffer + aOffset);
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t *mBuffer;
|
||||
uint16_t mLength;
|
||||
|
||||
@@ -43,6 +43,42 @@ void HeaderIe::Init(uint8_t aId, uint8_t aLen)
|
||||
SetId(aId);
|
||||
}
|
||||
|
||||
Error HeaderIe::StartIe(FrameBuilder &aBuilder, uint8_t aId, Bookmark &aBookmark)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
HeaderIe *ie;
|
||||
|
||||
aBookmark = aBuilder.GetLength();
|
||||
|
||||
ie = aBuilder.Append<HeaderIe>();
|
||||
VerifyOrExit(ie != nullptr, error = kErrorNoBufs);
|
||||
|
||||
ie->Init(aId, 0);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
Error HeaderIe::EndIe(FrameBuilder &aBuilder, const Bookmark &aBookmark)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
uint16_t offset = aBookmark;
|
||||
uint16_t length;
|
||||
HeaderIe *ie;
|
||||
|
||||
VerifyOrExit(offset + sizeof(HeaderIe) <= aBuilder.GetLength(), error = kErrorInvalidArgs);
|
||||
|
||||
ie = aBuilder.Read<HeaderIe>(offset);
|
||||
|
||||
length = aBuilder.GetLength() - offset - sizeof(HeaderIe);
|
||||
VerifyOrExit(length <= kMaxLength, error = kErrorInvalidArgs);
|
||||
|
||||
ie->SetLength(static_cast<uint8_t>(length));
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
|
||||
|
||||
Error ConnectionIe::SetWakeupId(WakeupId aWakeupId)
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "common/bit_utils.hpp"
|
||||
#include "common/const_cast.hpp"
|
||||
#include "common/encoding.hpp"
|
||||
#include "common/frame_builder.hpp"
|
||||
#include "common/num_utils.hpp"
|
||||
#include "common/numeric_limits.hpp"
|
||||
#include "mac/mac_types.hpp"
|
||||
@@ -60,6 +61,8 @@ OT_TOOL_PACKED_BEGIN
|
||||
class HeaderIe
|
||||
{
|
||||
public:
|
||||
static constexpr uint8_t kMaxLength = 127; ///< Maximum Header IE length in bytes.
|
||||
|
||||
/**
|
||||
* Returns the IE Element ID.
|
||||
*
|
||||
@@ -115,6 +118,41 @@ public:
|
||||
return (aIe.GetId() == IeType::kId) && static_cast<const IeType *>(&aIe)->IsValid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the opaque type for a bookmark used by `StartIe()/EndIe()`.
|
||||
*/
|
||||
typedef uint16_t Bookmark;
|
||||
|
||||
/**
|
||||
* Starts appending a (variable-length) `HeaderIe` in a `FrameBuilder`.
|
||||
*
|
||||
* On success, this method appends a `HeaderIe` descriptor (with length initialized to zero) to @p aBuilder and
|
||||
* saves the current byte offset as @p aBookmark. The caller can then append the IE content bytes to @p aBuilder,
|
||||
* and finally call `EndIe()` to update the IE length field automatically.
|
||||
*
|
||||
* @param[in,out] aBuilder The `FrameBuilder` instance to append to.
|
||||
* @param[in] aId The IE Element ID.
|
||||
* @param[out] aBookmark A reference to a `Bookmark` to save the start offset.
|
||||
*
|
||||
* @retval kErrorNone Successfully started the `HeaderIe`.
|
||||
* @retval kErrorNoBufs Insufficient space in @p aBuilder to append the `HeaderIe` header.
|
||||
*/
|
||||
static Error StartIe(FrameBuilder &aBuilder, uint8_t aId, Bookmark &aBookmark);
|
||||
|
||||
/**
|
||||
* Finishes appending a `HeaderIe` in a `FrameBuilder`.
|
||||
*
|
||||
* This method updates the length field of the `HeaderIe` previously started using `StartIe()`. It determines the
|
||||
* IE length based on the number of bytes appended to @p aBuilder since `StartIe()` was called.
|
||||
*
|
||||
* @param[in,out] aBuilder The `FrameBuilder` instance.
|
||||
* @param[in] aBookmark The `Bookmark` used when calling `StartIe()`.
|
||||
*
|
||||
* @retval kErrorNone Successfully finalized the `HeaderIe` length.
|
||||
* @retval kErrorInvalidArgs The @p aBookmark is invalid or the appended IE length exceeds `kMaxLength`.
|
||||
*/
|
||||
static Error EndIe(FrameBuilder &aBuilder, const Bookmark &aBookmark);
|
||||
|
||||
protected:
|
||||
void Init(uint8_t aId, uint8_t aLen);
|
||||
uint8_t *GetBytes(void) { return reinterpret_cast<uint8_t *>(this); }
|
||||
|
||||
@@ -470,6 +470,88 @@ void VerifyChannelMaskContent(const Mac::ChannelMask &aMask, uint8_t *aChannels,
|
||||
VerifyOrQuit(aLength == aMask.GetNumberOfChannels());
|
||||
}
|
||||
|
||||
void TestMacHeaderIeStartEnd(void)
|
||||
{
|
||||
using HeaderIe = Mac::HeaderIe;
|
||||
|
||||
static const uint8_t kIeId1 = 0x0a;
|
||||
static const uint8_t kIeId2 = 0x0b;
|
||||
static const uint8_t kIeId3 = 0x0c;
|
||||
static const uint8_t kContent[] = {0x11, 0x22, 0x33, 0x44, 0x55};
|
||||
|
||||
uint8_t buffer[256];
|
||||
FrameBuilder builder;
|
||||
uint16_t offset;
|
||||
HeaderIe::Bookmark bookmark;
|
||||
HeaderIe *ie;
|
||||
uint8_t largeContent[128];
|
||||
|
||||
printf("TestMacHeaderIeStartEnd\n");
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Normal use: Append an IE (5 bytes content)
|
||||
|
||||
builder.Init(buffer, sizeof(buffer));
|
||||
SuccessOrQuit(HeaderIe::StartIe(builder, kIeId1, bookmark));
|
||||
VerifyOrQuit(builder.GetLength() == sizeof(HeaderIe));
|
||||
|
||||
SuccessOrQuit(builder.AppendBytes(kContent, sizeof(kContent)));
|
||||
SuccessOrQuit(HeaderIe::EndIe(builder, bookmark));
|
||||
|
||||
VerifyOrQuit(builder.GetLength() == sizeof(HeaderIe) + sizeof(kContent));
|
||||
|
||||
ie = reinterpret_cast<HeaderIe *>(buffer);
|
||||
VerifyOrQuit(ie->GetId() == kIeId1);
|
||||
VerifyOrQuit(ie->GetLength() == sizeof(kContent));
|
||||
VerifyOrQuit(ie->GetSize() == sizeof(HeaderIe) + sizeof(kContent));
|
||||
VerifyOrQuit(memcmp(ie->GetContent(), kContent, sizeof(kContent)) == 0);
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Normal use: Append a new IE with empty payload (0-length IE)
|
||||
|
||||
offset = builder.GetLength();
|
||||
SuccessOrQuit(HeaderIe::StartIe(builder, kIeId2, bookmark));
|
||||
SuccessOrQuit(HeaderIe::EndIe(builder, bookmark));
|
||||
|
||||
// First IE should remain untouched
|
||||
ie = reinterpret_cast<HeaderIe *>(buffer);
|
||||
VerifyOrQuit(ie->GetId() == kIeId1);
|
||||
VerifyOrQuit(ie->GetLength() == sizeof(kContent));
|
||||
VerifyOrQuit(ie->GetSize() == sizeof(HeaderIe) + sizeof(kContent));
|
||||
VerifyOrQuit(memcmp(ie->GetContent(), kContent, sizeof(kContent)) == 0);
|
||||
|
||||
// Second IE with empty content
|
||||
ie = builder.Read<HeaderIe>(offset);
|
||||
VerifyOrQuit(ie->GetId() == kIeId2);
|
||||
VerifyOrQuit(ie->GetLength() == 0);
|
||||
VerifyOrQuit(ie->GetSize() == sizeof(HeaderIe));
|
||||
|
||||
VerifyOrQuit(builder.GetLength() == 2 * sizeof(HeaderIe) + sizeof(kContent));
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Maximum allowed IE length (127 bytes)
|
||||
|
||||
memset(largeContent, 0xaa, sizeof(largeContent));
|
||||
|
||||
builder.Init(buffer, sizeof(buffer));
|
||||
SuccessOrQuit(HeaderIe::StartIe(builder, kIeId3, bookmark));
|
||||
SuccessOrQuit(builder.AppendBytes(largeContent, HeaderIe::kMaxLength));
|
||||
SuccessOrQuit(HeaderIe::EndIe(builder, bookmark));
|
||||
|
||||
ie = builder.Read<HeaderIe>(0);
|
||||
VerifyOrQuit(ie->GetId() == kIeId3);
|
||||
VerifyOrQuit(ie->GetLength() == HeaderIe::kMaxLength);
|
||||
VerifyOrQuit(builder.GetLength() == ie->GetSize());
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Exceeding maximum length (128 bytes > 127)
|
||||
|
||||
builder.Init(buffer, sizeof(buffer));
|
||||
SuccessOrQuit(HeaderIe::StartIe(builder, kIeId3, bookmark));
|
||||
SuccessOrQuit(builder.AppendBytes(largeContent, HeaderIe::kMaxLength + 1));
|
||||
VerifyOrQuit(HeaderIe::EndIe(builder, bookmark) == kErrorInvalidArgs);
|
||||
}
|
||||
|
||||
void TestMacChannelMask(void)
|
||||
{
|
||||
uint8_t allChannels[] = {11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26};
|
||||
@@ -788,6 +870,7 @@ int main(void)
|
||||
{
|
||||
ot::TestMacAddress();
|
||||
ot::TestMacHeader();
|
||||
ot::TestMacHeaderIeStartEnd();
|
||||
ot::TestMacChannelMask();
|
||||
ot::TestMacFrameApi();
|
||||
ot::TestMacFrameAckGeneration();
|
||||
|
||||
Reference in New Issue
Block a user