From a282aa9f9bb297950007442927a89fd1cd9b7f39 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Mon, 25 Jul 2022 19:59:55 -0700 Subject: [PATCH] [core] adding `FrameBuilder` for constructing frames (#7935) This commit adds a new class `FrameBuilder` which helps with construction of frames in a given buffer. It provides helper methods to append different items to the frame, e.g., bytes from a buffer or `Message`, a `uint8`, `uint16` or `uint32` value assuming big or little endian encoding, or a given object. It also provides methods to overwrite previously appended content in the frame at a given offset. This class is used in `Lowpan` (replacing `BufferWriter`) which helps simplify the code. This commit also updates `Appender` to use the `FrameBuilder` and adds a new unit test for `FrameBuidler`. --- Android.mk | 1 + src/core/BUILD.gn | 2 + src/core/CMakeLists.txt | 1 + src/core/Makefile.am | 2 + src/core/common/appender.cpp | 14 +- src/core/common/appender.hpp | 11 +- src/core/common/frame_builder.cpp | 99 ++++++++++++ src/core/common/frame_builder.hpp | 238 +++++++++++++++++++++++++++++ src/core/thread/lowpan.cpp | 177 ++++++++++----------- src/core/thread/lowpan.hpp | 164 ++------------------ src/core/thread/mesh_forwarder.cpp | 13 +- tests/unit/CMakeLists.txt | 21 +++ tests/unit/Makefile.am | 5 + tests/unit/test_frame_builder.cpp | 150 ++++++++++++++++++ tests/unit/test_lowpan.cpp | 12 +- 15 files changed, 635 insertions(+), 275 deletions(-) create mode 100644 src/core/common/frame_builder.cpp create mode 100644 src/core/common/frame_builder.hpp create mode 100644 tests/unit/test_frame_builder.cpp diff --git a/Android.mk b/Android.mk index c5681a7e0..ca6599afa 100644 --- a/Android.mk +++ b/Android.mk @@ -231,6 +231,7 @@ LOCAL_SRC_FILES := \ src/core/common/crc16.cpp \ src/core/common/data.cpp \ src/core/common/error.cpp \ + src/core/common/frame_builder.cpp \ src/core/common/frame_data.cpp \ src/core/common/heap.cpp \ src/core/common/heap_data.cpp \ diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index d885a6844..299059f39 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -397,6 +397,8 @@ openthread_core_files = [ "common/error.cpp", "common/error.hpp", "common/extension.hpp", + "common/frame_builder.cpp", + "common/frame_builder.hpp", "common/frame_data.cpp", "common/frame_data.hpp", "common/heap.cpp", diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index a2d342997..ce3668964 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -97,6 +97,7 @@ set(COMMON_SOURCES common/crc16.cpp common/data.cpp common/error.cpp + common/frame_builder.cpp common/frame_data.cpp common/heap.cpp common/heap_data.cpp diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 22797c039..7b180eac5 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -187,6 +187,7 @@ SOURCES_COMMON = \ common/crc16.cpp \ common/data.cpp \ common/error.cpp \ + common/frame_builder.cpp \ common/frame_data.cpp \ common/heap.cpp \ common/heap_data.cpp \ @@ -438,6 +439,7 @@ HEADERS_COMMON = \ common/equatable.hpp \ common/error.hpp \ common/extension.hpp \ + common/frame_builder.hpp \ common/frame_data.hpp \ common/heap.hpp \ common/heap_allocatable.hpp \ diff --git a/src/core/common/appender.cpp b/src/core/common/appender.cpp index f312f4202..caa46e091 100644 --- a/src/core/common/appender.cpp +++ b/src/core/common/appender.cpp @@ -45,9 +45,7 @@ Appender::Appender(Message &aMessage) Appender::Appender(uint8_t *aBuffer, uint16_t aSize) : mType(kBuffer) { - mShared.mBuffer.mStart = aBuffer; - mShared.mBuffer.mCur = aBuffer; - mShared.mBuffer.mEnd = aBuffer + aSize; + mShared.mFrameBuilder.Init(aBuffer, aSize); } Error Appender::AppendBytes(const void *aBuffer, uint16_t aLength) @@ -61,14 +59,10 @@ Error Appender::AppendBytes(const void *aBuffer, uint16_t aLength) break; case kBuffer: - VerifyOrExit(aLength <= static_cast(mShared.mBuffer.mEnd - mShared.mBuffer.mCur), - error = kErrorNoBufs); - memcpy(mShared.mBuffer.mCur, aBuffer, aLength); - mShared.mBuffer.mCur += aLength; + error = mShared.mFrameBuilder.AppendBytes(aBuffer, aLength); break; } -exit: return error; } @@ -83,7 +77,7 @@ uint16_t Appender::GetAppendedLength(void) const break; case kBuffer: - length = static_cast(mShared.mBuffer.mCur - mShared.mBuffer.mStart); + length = mShared.mFrameBuilder.GetLength(); break; } @@ -92,7 +86,7 @@ uint16_t Appender::GetAppendedLength(void) const void Appender::GetAsData(Data &aData) { - aData.Init(mShared.mBuffer.mStart, GetAppendedLength()); + aData.Init(mShared.mFrameBuilder.GetBytes(), mShared.mFrameBuilder.GetLength()); } } // namespace ot diff --git a/src/core/common/appender.hpp b/src/core/common/appender.hpp index efefb9416..9154c5339 100644 --- a/src/core/common/appender.hpp +++ b/src/core/common/appender.hpp @@ -36,7 +36,9 @@ #include "openthread-core-config.h" +#include "common/const_cast.hpp" #include "common/data.hpp" +#include "common/frame_builder.hpp" #include "common/message.hpp" #include "common/type_traits.hpp" @@ -150,7 +152,7 @@ public: * @returns A pointer to the start of the data buffer associated with `Appender`. * */ - uint8_t *GetBufferStart(void) { return mShared.mBuffer.mStart; } + uint8_t *GetBufferStart(void) { return AsNonConst(mShared.mFrameBuilder.GetBytes()); } /** * This method gets the data buffer associated with `Appender` as a `Data`. @@ -172,12 +174,7 @@ private: uint16_t mStartOffset; } mMessage; - struct - { - uint8_t *mStart; - uint8_t *mCur; - uint8_t *mEnd; - } mBuffer; + FrameBuilder mFrameBuilder; } mShared; }; diff --git a/src/core/common/frame_builder.cpp b/src/core/common/frame_builder.cpp new file mode 100644 index 000000000..6d95d1afb --- /dev/null +++ b/src/core/common/frame_builder.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2022, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file implements the `FrameBuilder` class. + */ + +#include "frame_builder.hpp" + +namespace ot { + +void FrameBuilder::Init(void *aBuffer, uint16_t aLength) +{ + mBuffer = static_cast(aBuffer); + mLength = 0; + mMaxLength = aLength; +} + +Error FrameBuilder::AppendUint8(uint8_t aUint8) +{ + return Append(aUint8); +} + +Error FrameBuilder::AppendBigEndianUint16(uint16_t aUint16) +{ + return Append(Encoding::BigEndian::HostSwap16(aUint16)); +} + +Error FrameBuilder::AppendBigEndianUint32(uint32_t aUint32) +{ + return Append(Encoding::BigEndian::HostSwap32(aUint32)); +} + +Error FrameBuilder::AppendLittleEndianUint16(uint16_t aUint16) +{ + return Append(Encoding::LittleEndian::HostSwap16(aUint16)); +} + +Error FrameBuilder::AppendLittleEndianUint32(uint32_t aUint32) +{ + return Append(Encoding::LittleEndian::HostSwap32(aUint32)); +} + +Error FrameBuilder::AppendBytes(const void *aBuffer, uint16_t aLength) +{ + Error error = kErrorNone; + + VerifyOrExit(CanAppend(aLength), error = kErrorNoBufs); + memcpy(mBuffer + mLength, aBuffer, aLength); + mLength += aLength; + +exit: + return error; +} + +Error FrameBuilder::AppendBytesFromMessage(const Message &aMessage, uint16_t aOffset, uint16_t aLength) +{ + Error error = kErrorNone; + + VerifyOrExit(CanAppend(aLength), error = kErrorNoBufs); + SuccessOrExit(error = aMessage.Read(aOffset, mBuffer + mLength, aLength)); + mLength += aLength; + +exit: + return error; +} + +void FrameBuilder::WriteBytes(uint16_t aOffset, const void *aBuffer, uint16_t aLength) +{ + memcpy(mBuffer + aOffset, aBuffer, aLength); +} + +} // namespace ot diff --git a/src/core/common/frame_builder.hpp b/src/core/common/frame_builder.hpp new file mode 100644 index 000000000..81150452f --- /dev/null +++ b/src/core/common/frame_builder.hpp @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2022, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file defines OpenThread `FrameBuilder` class. + */ + +#ifndef FRAME_BUILDER_HPP_ +#define FRAME_BUILDER_HPP_ + +#include "openthread-core-config.h" + +#include "common/error.hpp" +#include "common/message.hpp" +#include "common/type_traits.hpp" + +namespace ot { + +/** + * The `FrameBuilder` can be used to construct frame content in a given data buffer. + * + */ +class FrameBuilder +{ +public: + /** + * This method initializes the `FrameBuilder` to use a given buffer. + * + * `FrameBuilder` MUST be initialized before its other methods are used. + * + * @param[in] aBuffer A pointer to a buffer. + * @param[in] aLength The data length (number of bytes in @p aBuffer). + * + */ + void Init(void *aBuffer, uint16_t aLength); + + /** + * This method returns a pointer to the start of `FrameBuilder` buffer. + * + * @returns A pointer to the frame buffer. + * + */ + const uint8_t *GetBytes(void) const { return mBuffer; } + + /** + * This method returns the current length of frame (number of bytes appended so far). + * + * @returns The current frame length. + * + */ + uint16_t GetLength(void) const { return mLength; } + + /** + * This method returns the maximum length of frame. + * + * @returns The maximum frame length (max number of bytes in the frame buffer). + * + */ + uint16_t GetMaxLength(void) const { return mMaxLength; } + + /** + * This method indicates whether or not there are enough bytes remaining in the `FrameBuilder` buffer to append a + * given number of bytes. + * + * @param[in] aLength The append length. + * + * @retval TRUE There are enough remaining bytes to append @p aLength bytes. + * @retval FALSE There are not enough remaining bytes to append @p aLength bytes. + * + */ + bool CanAppend(uint16_t aLength) const { return (static_cast(mLength) + aLength) <= mMaxLength; } + + /** + * This method appends an `uint8_t` value to the `FrameBuilder`. + * + * @param[in] aUint8 The `uint8_t` value to append. + * + * @retval kErrorNone Successfully appended the value. + * @retval kErrorNoBufs Insufficient available buffers. + * + */ + Error AppendUint8(uint8_t aUint8); + + /** + * This method appends an `uint16_t` value assuming big endian encoding to the `FrameBuilder`. + * + * @param[in] aUint16 The `uint16_t` value to append. + * + * @retval kErrorNone Successfully appended the value. + * @retval kErrorNoBufs Insufficient available buffers. + * + */ + Error AppendBigEndianUint16(uint16_t aUint16); + + /** + * This method appends an `uint32_t` value assuming big endian encoding to the `FrameBuilder`. + * + * @param[in] aUint32 The `uint32_t` value to append. + * + * @retval kErrorNone Successfully appended the value. + * @retval kErrorNoBufs Insufficient available buffers. + * + */ + Error AppendBigEndianUint32(uint32_t aUint32); + + /** + * This method appends an `uint16_t` value assuming little endian encoding to the `FrameBuilder`. + * + * @param[in] aUint16 The `uint16_t` value to append. + * + * @retval kErrorNone Successfully appended the value. + * @retval kErrorNoBufs Insufficient available buffers. + * + */ + Error AppendLittleEndianUint16(uint16_t aUint16); + + /** + * This method appends an `uint32_t` value assuming little endian encoding to the `FrameBuilder`. + * + * @param[in] aUint32 The `uint32_t` value to append. + * + * @retval kErrorNone Successfully appended the value. + * @retval kErrorNoBufs Insufficient available buffers. + * + */ + Error AppendLittleEndianUint32(uint32_t aUint32); + + /** + * This method appends bytes from a given buffer to the `FrameBuilder`. + * + * @param[in] aBuffer A pointer to a data bytes to append. + * @param[in] aLength Number of bytes in @p aBuffer. + * + * @retval kErrorNone Successfully appended the bytes. + * @retval kErrorNoBufs Insufficient available buffers. + * + */ + Error AppendBytes(const void *aBuffer, uint16_t aLength); + + /** + * This method appends bytes read from a given message to the `FrameBuilder`. + * + * @param[in] aMessage The message to read the bytes from. + * @param[in] aOffset The offset in @p aMessage to start reading the bytes from. + * @param[in] aLength Number of bytes to read from @p aMessage and append. + * + * @retval kErrorNone Successfully appended the bytes. + * @retval kErrorNoBufs Insufficient available buffers to append the requested @p aLength bytes. + * @retval kErrorParse Not enough bytes in @p aMessage to read @p aLength bytes from @p aOffset. + * + */ + Error AppendBytesFromMessage(const Message &aMessage, uint16_t aOffset, uint16_t aLength); + + /** + * This method appends an object to the `FrameBuilder`. + * + * @tparam ObjectType The object type to append. + * + * @param[in] aObject A reference to the object to append. + * + * @retval kErrorNone Successfully appended the object. + * @retval kErrorNoBufs Insufficient available buffers to append @p aObject. + * + */ + template Error Append(const ObjectType &aObject) + { + static_assert(!TypeTraits::IsPointer::kValue, "ObjectType must not be a pointer"); + + return AppendBytes(&aObject, sizeof(ObjectType)); + } + + /** + * This method writes bytes in `FrameBuilder` at a given offset overwriting the previously appended content. + * + * This method does not perform any bound checks. The caller MUST ensure that the given data length fits within the + * previously appended content. Otherwise the behavior of this method is undefined. + * + * @param[in] aOffset The offset to begin writing. + * @param[in] aBuffer A pointer to a data buffer to write. + * @param[in] aLength Number of bytes in @p aBuffer. + * + */ + void WriteBytes(uint16_t aOffset, const void *aBuffer, uint16_t aLength); + + /** + * This methods writes an object to the `FrameBuilder` at a given offset overwriting previously appended content. + * + * This method does not perform any bound checks. The caller MUST ensure the given data length fits within the + * previously appended content. Otherwise the behavior of this method is undefined. + * + * @tparam ObjectType The object type to write. + * + * @param[in] aOffset The offset to begin writing. + * @param[in] aObject A reference to the object to write. + * + */ + template void Write(uint16_t aOffset, const ObjectType &aObject) + { + static_assert(!TypeTraits::IsPointer::kValue, "ObjectType must not be a pointer"); + + WriteBytes(aOffset, &aObject, sizeof(ObjectType)); + } + +private: + uint8_t *mBuffer; + uint16_t mLength; + uint16_t mMaxLength; +}; + +} // namespace ot + +#endif // FRAME_BUILDER_HPP_ diff --git a/src/core/thread/lowpan.cpp b/src/core/thread/lowpan.cpp index cfda2ee8e..d373fbe1c 100644 --- a/src/core/thread/lowpan.cpp +++ b/src/core/thread/lowpan.cpp @@ -95,10 +95,9 @@ Error Lowpan::CompressSourceIid(const Mac::Address &aMacAddr, const Ip6::Address &aIpAddr, const Context & aContext, uint16_t & aHcCtl, - BufferWriter & aBuf) + FrameBuilder & aFrameBuilder) { Error error = kErrorNone; - BufferWriter buf = aBuf; Ip6::Address ipaddr; Mac::Address tmp; @@ -116,21 +115,16 @@ Error Lowpan::CompressSourceIid(const Mac::Address &aMacAddr, if (ipaddr.GetIid() == aIpAddr.GetIid()) { aHcCtl |= kHcSrcAddrMode2; - SuccessOrExit(error = buf.Write(aIpAddr.mFields.m8 + 14, 2)); + SuccessOrExit(error = aFrameBuilder.AppendBytes(aIpAddr.mFields.m8 + 14, 2)); } else { aHcCtl |= kHcSrcAddrMode1; - SuccessOrExit(error = buf.Write(aIpAddr.GetIid().GetBytes(), Ip6::InterfaceIdentifier::kSize)); + SuccessOrExit(error = aFrameBuilder.Append(aIpAddr.GetIid())); } } exit: - if (error == kErrorNone) - { - aBuf = buf; - } - return error; } @@ -138,10 +132,9 @@ Error Lowpan::CompressDestinationIid(const Mac::Address &aMacAddr, const Ip6::Address &aIpAddr, const Context & aContext, uint16_t & aHcCtl, - BufferWriter & aBuf) + FrameBuilder & aFrameBuilder) { Error error = kErrorNone; - BufferWriter buf = aBuf; Ip6::Address ipaddr; Mac::Address tmp; @@ -159,29 +152,23 @@ Error Lowpan::CompressDestinationIid(const Mac::Address &aMacAddr, if (ipaddr.GetIid() == aIpAddr.GetIid()) { aHcCtl |= kHcDstAddrMode2; - SuccessOrExit(error = buf.Write(aIpAddr.mFields.m8 + 14, 2)); + SuccessOrExit(error = aFrameBuilder.AppendBytes(aIpAddr.mFields.m8 + 14, 2)); } else { aHcCtl |= kHcDstAddrMode1; - SuccessOrExit(error = buf.Write(aIpAddr.GetIid().GetBytes(), Ip6::InterfaceIdentifier::kSize)); + SuccessOrExit(error = aFrameBuilder.Append(aIpAddr.GetIid())); } } exit: - if (error == kErrorNone) - { - aBuf = buf; - } - return error; } -Error Lowpan::CompressMulticast(const Ip6::Address &aIpAddr, uint16_t &aHcCtl, BufferWriter &aBuf) +Error Lowpan::CompressMulticast(const Ip6::Address &aIpAddr, uint16_t &aHcCtl, FrameBuilder &aFrameBuilder) { - Error error = kErrorNone; - BufferWriter buf = aBuf; - Context multicastContext; + Error error = kErrorNone; + Context multicastContext; aHcCtl |= kHcMulticast; @@ -193,21 +180,21 @@ Error Lowpan::CompressMulticast(const Ip6::Address &aIpAddr, uint16_t &aHcCtl, B if (aIpAddr.mFields.m8[1] == 0x02 && i >= 15) { aHcCtl |= kHcDstAddrMode3; - SuccessOrExit(error = buf.Write(aIpAddr.mFields.m8[15])); + SuccessOrExit(error = aFrameBuilder.AppendUint8(aIpAddr.mFields.m8[15])); } // Check if multicast address can be compressed to 32-bits (ffxx::00xx:xxxx) else if (i >= 13) { aHcCtl |= kHcDstAddrMode2; - SuccessOrExit(error = buf.Write(aIpAddr.mFields.m8[1])); - SuccessOrExit(error = buf.Write(aIpAddr.mFields.m8 + 13, 3)); + SuccessOrExit(error = aFrameBuilder.AppendUint8(aIpAddr.mFields.m8[1])); + SuccessOrExit(error = aFrameBuilder.AppendBytes(aIpAddr.mFields.m8 + 13, 3)); } // Check if multicast address can be compressed to 48-bits (ffxx::00xx:xxxx:xxxx) else if (i >= 11) { aHcCtl |= kHcDstAddrMode1; - SuccessOrExit(error = buf.Write(aIpAddr.mFields.m8[1])); - SuccessOrExit(error = buf.Write(aIpAddr.mFields.m8 + 11, 5)); + SuccessOrExit(error = aFrameBuilder.AppendUint8(aIpAddr.mFields.m8[1])); + SuccessOrExit(error = aFrameBuilder.AppendBytes(aIpAddr.mFields.m8 + 11, 5)); } else { @@ -217,12 +204,12 @@ Error Lowpan::CompressMulticast(const Ip6::Address &aIpAddr, uint16_t &aHcCtl, B memcmp(multicastContext.mPrefix.GetBytes(), aIpAddr.mFields.m8 + 4, 8) == 0) { aHcCtl |= kHcDstAddrContext | kHcDstAddrMode0; - SuccessOrExit(error = buf.Write(aIpAddr.mFields.m8 + 1, 2)); - SuccessOrExit(error = buf.Write(aIpAddr.mFields.m8 + 12, 4)); + SuccessOrExit(error = aFrameBuilder.AppendBytes(aIpAddr.mFields.m8 + 1, 2)); + SuccessOrExit(error = aFrameBuilder.AppendBytes(aIpAddr.mFields.m8 + 12, 4)); } else { - SuccessOrExit(error = buf.Write(aIpAddr.mFields.m8, sizeof(Ip6::Address))); + SuccessOrExit(error = aFrameBuilder.Append(aIpAddr)); } } @@ -231,41 +218,47 @@ Error Lowpan::CompressMulticast(const Ip6::Address &aIpAddr, uint16_t &aHcCtl, B } exit: - if (error == kErrorNone) - { - aBuf = buf; - } - return error; } Error Lowpan::Compress(Message & aMessage, const Mac::Address &aMacSource, const Mac::Address &aMacDest, - BufferWriter & aBuf) + FrameBuilder & aFrameBuilder) { - Error error; + Error error = kErrorNone; uint8_t headerDepth = 0xff; - do + while (headerDepth > 0) { - error = Compress(aMessage, aMacSource, aMacDest, aBuf, headerDepth); - } while ((error != kErrorNone) && (headerDepth > 0)); + FrameBuilder frameBuilder = aFrameBuilder; + error = Compress(aMessage, aMacSource, aMacDest, aFrameBuilder, headerDepth); + + // We exit if `Compress()` is successful. Otherwise we reset + // the `aFrameBuidler` to its earlier state (remove all + // appended content from the failed `Compress()` call) and + // try again with a different `headerDepth`. + + VerifyOrExit(error != kErrorNone); + aFrameBuilder = frameBuilder; + } + +exit: return error; } Error Lowpan::Compress(Message & aMessage, const Mac::Address &aMacSource, const Mac::Address &aMacDest, - BufferWriter & aBuf, + FrameBuilder & aFrameBuilder, uint8_t & aHeaderDepth) { Error error = kErrorNone; NetworkData::Leader &networkData = Get(); uint16_t startOffset = aMessage.GetOffset(); - BufferWriter buf = aBuf; uint16_t hcCtl = kHcDispatch; + uint16_t hcCtlOffset = 0; Ip6::Header ip6Header; uint8_t * ip6HeaderBytes = reinterpret_cast(&ip6Header); Context srcContext, dstContext; @@ -295,13 +288,14 @@ Error Lowpan::Compress(Message & aMessage, } // Lowpan HC Control Bits - SuccessOrExit(error = buf.Advance(sizeof(hcCtl))); + hcCtlOffset = aFrameBuilder.GetLength(); + SuccessOrExit(error = aFrameBuilder.AppendBigEndianUint16(hcCtl)); // Context Identifier if (srcContext.mContextId != 0 || dstContext.mContextId != 0) { hcCtl |= kHcContextId; - SuccessOrExit(error = buf.Write(((srcContext.mContextId << 4) | dstContext.mContextId) & 0xff)); + SuccessOrExit(error = aFrameBuilder.AppendUint8(((srcContext.mContextId << 4) | dstContext.mContextId) & 0xff)); } dscp = ((ip6HeaderBytes[0] << 2) & 0x3c) | (ip6HeaderBytes[1] >> 6); @@ -320,7 +314,7 @@ Error Lowpan::Compress(Message & aMessage, // Elide Flow Label and carry Traffic Class in-line. hcCtl |= kHcFlowLabel; - SuccessOrExit(error = buf.Write(ecn | dscp)); + SuccessOrExit(error = aFrameBuilder.AppendUint8(ecn | dscp)); } } else if (dscp == 0) @@ -328,15 +322,15 @@ Error Lowpan::Compress(Message & aMessage, // Carry Flow Label and ECN only with 2-bit padding. hcCtl |= kHcTrafficClass; - SuccessOrExit(error = buf.Write(ecn | (ip6HeaderBytes[1] & 0x0f))); - SuccessOrExit(error = buf.Write(ip6HeaderBytes + 2, 2)); + SuccessOrExit(error = aFrameBuilder.AppendUint8(ecn | (ip6HeaderBytes[1] & 0x0f))); + SuccessOrExit(error = aFrameBuilder.AppendBytes(ip6HeaderBytes + 2, 2)); } else { // Carry Flow Label and Traffic Class in-line. - SuccessOrExit(error = buf.Write(ecn | dscp)); - SuccessOrExit(error = buf.Write(ip6HeaderBytes[1] & 0x0f)); - SuccessOrExit(error = buf.Write(ip6HeaderBytes + 2, 2)); + SuccessOrExit(error = aFrameBuilder.AppendUint8(ecn | dscp)); + SuccessOrExit(error = aFrameBuilder.AppendUint8(ip6HeaderBytes[1] & 0x0f)); + SuccessOrExit(error = aFrameBuilder.AppendBytes(ip6HeaderBytes + 2, 2)); } // Next Header @@ -353,7 +347,7 @@ Error Lowpan::Compress(Message & aMessage, OT_FALL_THROUGH; default: - SuccessOrExit(error = buf.Write(static_cast(ip6Header.GetNextHeader()))); + SuccessOrExit(error = aFrameBuilder.AppendUint8(static_cast(ip6Header.GetNextHeader()))); break; } @@ -373,7 +367,7 @@ Error Lowpan::Compress(Message & aMessage, break; default: - SuccessOrExit(error = buf.Write(ip6Header.GetHopLimit())); + SuccessOrExit(error = aFrameBuilder.AppendUint8(ip6Header.GetHopLimit())); break; } @@ -384,35 +378,37 @@ Error Lowpan::Compress(Message & aMessage, } else if (ip6Header.GetSource().IsLinkLocal()) { - SuccessOrExit(error = CompressSourceIid(aMacSource, ip6Header.GetSource(), srcContext, hcCtl, buf)); + SuccessOrExit(error = CompressSourceIid(aMacSource, ip6Header.GetSource(), srcContext, hcCtl, aFrameBuilder)); } else if (srcContextValid) { hcCtl |= kHcSrcAddrContext; - SuccessOrExit(error = CompressSourceIid(aMacSource, ip6Header.GetSource(), srcContext, hcCtl, buf)); + SuccessOrExit(error = CompressSourceIid(aMacSource, ip6Header.GetSource(), srcContext, hcCtl, aFrameBuilder)); } else { - SuccessOrExit(error = buf.Write(ip6Header.GetSource().mFields.m8, sizeof(ip6Header.GetSource()))); + SuccessOrExit(error = aFrameBuilder.Append(ip6Header.GetSource())); } // Destination Address if (ip6Header.GetDestination().IsMulticast()) { - SuccessOrExit(error = CompressMulticast(ip6Header.GetDestination(), hcCtl, buf)); + SuccessOrExit(error = CompressMulticast(ip6Header.GetDestination(), hcCtl, aFrameBuilder)); } else if (ip6Header.GetDestination().IsLinkLocal()) { - SuccessOrExit(error = CompressDestinationIid(aMacDest, ip6Header.GetDestination(), dstContext, hcCtl, buf)); + SuccessOrExit( + error = CompressDestinationIid(aMacDest, ip6Header.GetDestination(), dstContext, hcCtl, aFrameBuilder)); } else if (dstContextValid) { hcCtl |= kHcDstAddrContext; - SuccessOrExit(error = CompressDestinationIid(aMacDest, ip6Header.GetDestination(), dstContext, hcCtl, buf)); + SuccessOrExit( + error = CompressDestinationIid(aMacDest, ip6Header.GetDestination(), dstContext, hcCtl, aFrameBuilder)); } else { - SuccessOrExit(error = buf.Write(&ip6Header.GetDestination(), sizeof(ip6Header.GetDestination()))); + SuccessOrExit(error = aFrameBuilder.Append(ip6Header.GetDestination())); } headerDepth++; @@ -426,18 +422,18 @@ Error Lowpan::Compress(Message & aMessage, switch (nextHeader) { case Ip6::kProtoHopOpts: - SuccessOrExit(error = CompressExtensionHeader(aMessage, buf, nextHeader)); + SuccessOrExit(error = CompressExtensionHeader(aMessage, aFrameBuilder, nextHeader)); break; case Ip6::kProtoUdp: - error = CompressUdp(aMessage, buf); + error = CompressUdp(aMessage, aFrameBuilder); ExitNow(); case Ip6::kProtoIp6: // For IP-in-IP the NH bit of the LOWPAN_NHC encoding MUST be set to zero. - SuccessOrExit(error = buf.Write(kExtHdrDispatch | kExtHdrEidIp6)); + SuccessOrExit(error = aFrameBuilder.AppendUint8(kExtHdrDispatch | kExtHdrEidIp6)); - error = Compress(aMessage, aMacSource, aMacDest, buf); + error = Compress(aMessage, aMacSource, aMacDest, aFrameBuilder); OT_FALL_THROUGH; @@ -453,9 +449,7 @@ exit: if (error == kErrorNone) { - IgnoreError(aBuf.Write(hcCtl >> 8)); - IgnoreError(aBuf.Write(hcCtl & 0xff)); - aBuf = buf; + aFrameBuilder.Write(hcCtlOffset, HostSwap16(hcCtl)); } else { @@ -465,10 +459,9 @@ exit: return error; } -Error Lowpan::CompressExtensionHeader(Message &aMessage, BufferWriter &aBuf, uint8_t &aNextHeader) +Error Lowpan::CompressExtensionHeader(Message &aMessage, FrameBuilder &aFrameBuilder, uint8_t &aNextHeader) { Error error = kErrorNone; - BufferWriter buf = aBuf; uint16_t startOffset = aMessage.GetOffset(); Ip6::ExtensionHeader extHeader; uint16_t len; @@ -488,12 +481,12 @@ Error Lowpan::CompressExtensionHeader(Message &aMessage, BufferWriter &aBuf, uin break; default: - SuccessOrExit(error = buf.Write(tmpByte)); + SuccessOrExit(error = aFrameBuilder.AppendUint8(tmpByte)); tmpByte = static_cast(extHeader.GetNextHeader()); break; } - SuccessOrExit(error = buf.Write(tmpByte)); + SuccessOrExit(error = aFrameBuilder.AppendUint8(tmpByte)); len = (extHeader.GetLength() + 1) * 8 - sizeof(extHeader); @@ -540,16 +533,12 @@ Error Lowpan::CompressExtensionHeader(Message &aMessage, BufferWriter &aBuf, uin aNextHeader = static_cast(extHeader.GetNextHeader()); - SuccessOrExit(error = buf.Write(static_cast(len))); - SuccessOrExit(error = buf.Write(aMessage, static_cast(len))); + SuccessOrExit(error = aFrameBuilder.AppendUint8(static_cast(len))); + SuccessOrExit(error = aFrameBuilder.AppendBytesFromMessage(aMessage, aMessage.GetOffset(), len)); aMessage.MoveOffset(len + padLength); exit: - if (error == kErrorNone) - { - aBuf = buf; - } - else + if (error != kErrorNone) { aMessage.SetOffset(startOffset); } @@ -557,10 +546,9 @@ exit: return error; } -Error Lowpan::CompressUdp(Message &aMessage, BufferWriter &aBuf) +Error Lowpan::CompressUdp(Message &aMessage, FrameBuilder &aFrameBuilder) { Error error = kErrorNone; - BufferWriter buf = aBuf; uint16_t startOffset = aMessage.GetOffset(); Ip6::Udp::Header udpHeader; uint16_t source; @@ -573,40 +561,33 @@ Error Lowpan::CompressUdp(Message &aMessage, BufferWriter &aBuf) if ((source & 0xfff0) == 0xf0b0 && (destination & 0xfff0) == 0xf0b0) { - SuccessOrExit(error = buf.Write(kUdpDispatch | 3)); - SuccessOrExit(error = buf.Write((((source & 0xf) << 4) | (destination & 0xf)) & 0xff)); + SuccessOrExit(error = aFrameBuilder.AppendUint8(kUdpDispatch | 3)); + SuccessOrExit(error = aFrameBuilder.AppendUint8((((source & 0xf) << 4) | (destination & 0xf)) & 0xff)); } else if ((source & 0xff00) == 0xf000) { - SuccessOrExit(error = buf.Write(kUdpDispatch | 2)); - SuccessOrExit(error = buf.Write(source & 0xff)); - SuccessOrExit(error = buf.Write(destination >> 8)); - SuccessOrExit(error = buf.Write(destination & 0xff)); + SuccessOrExit(error = aFrameBuilder.AppendUint8(kUdpDispatch | 2)); + SuccessOrExit(error = aFrameBuilder.AppendUint8(source & 0xff)); + SuccessOrExit(error = aFrameBuilder.AppendBigEndianUint16(destination)); } else if ((destination & 0xff00) == 0xf000) { - SuccessOrExit(error = buf.Write(kUdpDispatch | 1)); - SuccessOrExit(error = buf.Write(source >> 8)); - SuccessOrExit(error = buf.Write(source & 0xff)); - SuccessOrExit(error = buf.Write(destination & 0xff)); + SuccessOrExit(error = aFrameBuilder.AppendUint8(kUdpDispatch | 1)); + SuccessOrExit(error = aFrameBuilder.AppendBigEndianUint16(source)); + SuccessOrExit(error = aFrameBuilder.AppendUint8(destination & 0xff)); } else { - SuccessOrExit(error = buf.Write(kUdpDispatch)); - SuccessOrExit(error = buf.Write(&udpHeader, Ip6::Udp::Header::kLengthFieldOffset)); + SuccessOrExit(error = aFrameBuilder.AppendUint8(kUdpDispatch)); + SuccessOrExit(error = aFrameBuilder.AppendBytes(&udpHeader, Ip6::Udp::Header::kLengthFieldOffset)); } - SuccessOrExit(error = - buf.Write(reinterpret_cast(&udpHeader) + Ip6::Udp::Header::kChecksumFieldOffset, 2)); + SuccessOrExit(error = aFrameBuilder.AppendBigEndianUint16(udpHeader.GetChecksum())); aMessage.MoveOffset(sizeof(udpHeader)); exit: - if (error == kErrorNone) - { - aBuf = buf; - } - else + if (error != kErrorNone) { aMessage.SetOffset(startOffset); } diff --git a/src/core/thread/lowpan.hpp b/src/core/thread/lowpan.hpp index 38b0fa30e..719360790 100644 --- a/src/core/thread/lowpan.hpp +++ b/src/core/thread/lowpan.hpp @@ -37,6 +37,7 @@ #include "openthread-core-config.h" #include "common/debug.hpp" +#include "common/frame_builder.hpp" #include "common/frame_data.hpp" #include "common/locator.hpp" #include "common/message.hpp" @@ -79,144 +80,6 @@ struct Context bool mCompressFlag; ///< The Context compression flag. }; -/** - * This class defines a buffer writer used by the 6LoWPAN compressor. - * - */ -class BufferWriter -{ -public: - /** - * This constructor initializes the buffer writer. - * - * @param[in] aBuf A pointer to the write buffer. - * @param[in] aLength The size of the write buffer. - * - */ - BufferWriter(uint8_t *aBuf, uint16_t aLength) - : mWritePointer(aBuf) - , mEndPointer(aBuf + aLength) - { - } - - /** - * This method indicates whether there is buffer space available to write @p aLength bytes. - * - * @param[in] aLength Number of bytes to write. - * - * @retval TRUE Enough buffer space is available to write the requested number of bytes. - * @retval FALSE Insufficient buffer space to write the requested number of bytes. - * - */ - bool CanWrite(uint8_t aLength) const { return (mWritePointer + aLength) <= mEndPointer; } - - /** - * This method returns the current write pointer value. - * - * @returns the current write pointer value. - * - */ - uint8_t *GetWritePointer(void) { return mWritePointer; } - - /** - * This method advances the write pointer. - * - * @param[in] aLength Number of bytes to advance. - * - * @retval kErrorNone Enough buffer space is available to advance the requested number of bytes. - * @retval kErrorNoBufs Insufficient buffer space to advance the requested number of bytes. - * - */ - Error Advance(uint8_t aLength) - { - Error error = kErrorNone; - - VerifyOrExit(CanWrite(aLength), error = kErrorNoBufs); - mWritePointer += aLength; - - exit: - return error; - } - - /** - * This method writes a byte into the buffer and updates the write pointer, if space is available. - * - * @param[in] aByte Byte to write. - * - * @retval kErrorNone Successfully wrote the byte and updated the pointer. - * @retval kErrorNoBufs Insufficient buffer space to write the byte. - * - */ - Error Write(uint8_t aByte) - { - Error error = kErrorNone; - - VerifyOrExit(CanWrite(sizeof(aByte)), error = kErrorNoBufs); - - *mWritePointer++ = aByte; - - exit: - return error; - } - - /** - * This method writes a byte sequence into the buffer and updates the write pointer, if space is available. - * - * @param[in] aBuf A pointer to the byte sequence. - * @param[in] aLength Number of bytes to write. - * - * @retval kErrorNone Successfully wrote the byte sequence and updated the pointer. - * @retval kErrorNoBufs Insufficient buffer space to write the byte sequence. - * - */ - Error Write(const void *aBuf, uint8_t aLength) - { - Error error = kErrorNone; - - VerifyOrExit(CanWrite(aLength), error = kErrorNoBufs); - - memcpy(mWritePointer, aBuf, aLength); - mWritePointer += aLength; - - exit: - return error; - } - - /** - * This method writes a byte sequence into the buffer and updates the write pointer, if space is available. - * - * The byte sequence is taken from a message buffer at the current message buffer's offset. - * - * @param[in] aMessage A message buffer. - * @param[in] aLength Number of bytes to write. - * - * @retval kErrorNone Successfully wrote the byte sequence and updated the pointer. - * @retval kErrorNoBufs Insufficient buffer space to write the byte sequence. - * - */ - Error Write(const Message &aMessage, uint8_t aLength) - { - Error error = kErrorNone; - int rval; - - OT_UNUSED_VARIABLE(rval); - - VerifyOrExit(CanWrite(aLength), error = kErrorNoBufs); - - rval = aMessage.ReadBytes(aMessage.GetOffset(), mWritePointer, aLength); - OT_ASSERT(rval == aLength); - - mWritePointer += aLength; - - exit: - return error; - } - -private: - uint8_t *mWritePointer; - uint8_t *mEndPointer; -}; - /** * This class implements LOWPAN_IPHC header compression. * @@ -261,15 +124,18 @@ public: /** * This method compresses an IPv6 header. * - * @param[in] aMessage A reference to the IPv6 message. - * @param[in] aMacSource The MAC source address. - * @param[in] aMacDest The MAC destination address. - * @param[out] aBuf A pointer where the compressed IPv6 header will be placed. + * @param[in] aMessage A reference to the IPv6 message. + * @param[in] aMacSource The MAC source address. + * @param[in] aMacDest The MAC destination address. + * @param[in] aFrameBuilder The `FrameBuilder` to use to append the compressed headers. * * @returns The size of the compressed header in bytes. * */ - Error Compress(Message &aMessage, const Mac::Address &aMacSource, const Mac::Address &aMacDest, BufferWriter &aBuf); + Error Compress(Message & aMessage, + const Mac::Address &aMacSource, + const Mac::Address &aMacDest, + FrameBuilder & aFrameBuilder); /** * This method decompresses a LOWPAN_IPHC header. @@ -405,22 +271,22 @@ private: Error Compress(Message & aMessage, const Mac::Address &aMacSource, const Mac::Address &aMacDest, - BufferWriter & aBuf, + FrameBuilder & aFrameBuilder, uint8_t & aHeaderDepth); - Error CompressExtensionHeader(Message &aMessage, BufferWriter &aBuf, uint8_t &aNextHeader); + Error CompressExtensionHeader(Message &aMessage, FrameBuilder &aFrameBuilder, uint8_t &aNextHeader); Error CompressSourceIid(const Mac::Address &aMacAddr, const Ip6::Address &aIpAddr, const Context & aContext, uint16_t & aHcCtl, - BufferWriter & aBuf); + FrameBuilder & aFrameBuilder); Error CompressDestinationIid(const Mac::Address &aMacAddr, const Ip6::Address &aIpAddr, const Context & aContext, uint16_t & aHcCtl, - BufferWriter & aBuf); - Error CompressMulticast(const Ip6::Address &aIpAddr, uint16_t &aHcCtl, BufferWriter &aBuf); - Error CompressUdp(Message &aMessage, BufferWriter &aBuf); + FrameBuilder & aFrameBuilder); + Error CompressMulticast(const Ip6::Address &aIpAddr, uint16_t &aHcCtl, FrameBuilder &aFrameBuilder); + Error CompressUdp(Message &aMessage, FrameBuilder &aFrameBuilder); Error DecompressExtensionHeader(Message &aMessage, FrameData &aFrameData); Error DecompressUdpHeader(Message &aMessage, FrameData &aFrameData, uint16_t aDatagramLength); diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp index 74a9198f0..c7f68339b 100644 --- a/src/core/thread/mesh_forwarder.cpp +++ b/src/core/thread/mesh_forwarder.cpp @@ -963,10 +963,11 @@ start: // Compress IPv6 Header if (aMessage.GetOffset() == 0) { - Lowpan::BufferWriter buffer(payload, - maxPayloadLength - headerLength - Lowpan::FragmentHeader::kFirstFragmentHeaderSize); - uint8_t hcLength; - Mac::Address meshSource, meshDest; + FrameBuilder frameBuilder; + uint8_t hcLength; + Mac::Address meshSource, meshDest; + + frameBuilder.Init(payload, maxPayloadLength - headerLength - Lowpan::FragmentHeader::kFirstFragmentHeaderSize); if (aAddMeshHeader) { @@ -979,9 +980,9 @@ start: meshDest = aMacDest; } - SuccessOrAssert(Get().Compress(aMessage, meshSource, meshDest, buffer)); + SuccessOrAssert(Get().Compress(aMessage, meshSource, meshDest, frameBuilder)); - hcLength = static_cast(buffer.GetWritePointer() - payload); + hcLength = static_cast(frameBuilder.GetLength()); headerLength += hcLength; payloadLength = aMessage.GetLength() - aMessage.GetOffset(); fragmentLength = maxPayloadLength - headerLength; diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index ae1e210af..9a68ecfcf 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -320,6 +320,27 @@ target_link_libraries(ot-test-flash add_test(NAME ot-test-flash COMMAND ot-test-flash) +add_executable(ot-test-frame-builder + test_frame_builder.cpp +) + +target_include_directories(ot-test-frame-builder + PRIVATE + ${COMMON_INCLUDES} +) + +target_compile_options(ot-test-frame-builder + PRIVATE + ${COMMON_COMPILE_OPTIONS} +) + +target_link_libraries(ot-test-frame-builder + PRIVATE + ${COMMON_LIBS} +) + +add_test(NAME ot-test-frame_builder COMMAND ot-test-frame-builder) + add_executable(ot-test-heap test_heap.cpp ) diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index a99b2d861..21316aab2 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -123,6 +123,7 @@ check_PROGRAMS += \ ot-test-dso \ ot-test-ecdsa \ ot-test-flash \ + ot-test-frame-builder \ ot-test-heap \ ot-test-heap-array \ ot-test-heap-string \ @@ -240,6 +241,10 @@ ot_test_flash_LDADD = $(COMMON_LDADD) ot_test_flash_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) ot_test_flash_SOURCES = $(COMMON_SOURCES) test_flash.cpp +ot_test_frame_builder_LDADD = $(COMMON_LDADD) +ot_test_frame_builder_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) +ot_test_frame_builder_SOURCES = $(COMMON_SOURCES) test_frame_builder.cpp + ot_test_hdlc_LDADD = $(COMMON_LDADD) ot_test_hdlc_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) ot_test_hdlc_SOURCES = $(COMMON_SOURCES) test_hdlc.cpp diff --git a/tests/unit/test_frame_builder.cpp b/tests/unit/test_frame_builder.cpp new file mode 100644 index 000000000..cbc09c01b --- /dev/null +++ b/tests/unit/test_frame_builder.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2022, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "test_platform.h" +#include "test_util.hpp" + +#include "common/frame_builder.hpp" + +namespace ot { + +void TestFrameBuilder(void) +{ + const uint8_t kData1[] = {0x01, 0x02, 0x03, 0x04, 0x05}; + const uint8_t kData2[] = {0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa}; + + static constexpr uint16_t kMaxBufferSize = sizeof(kData1) * 2 + sizeof(kData2); + + Instance * instance; + Message * message; + uint16_t offset; + uint8_t buffer[kMaxBufferSize]; + uint8_t zeroBuffer[kMaxBufferSize]; + FrameBuilder frameBuilder; + + printf("TestFrameBuilder\n"); + + instance = static_cast(testInitInstance()); + VerifyOrQuit(instance != nullptr); + + message = instance->Get().Allocate(Message::kTypeIp6); + VerifyOrQuit(message != nullptr); + + SuccessOrQuit(message->Append(kData1)); + SuccessOrQuit(message->Append(kData2)); + + memset(buffer, 0, sizeof(buffer)); + memset(zeroBuffer, 0, sizeof(zeroBuffer)); + + frameBuilder.Init(buffer, sizeof(buffer)); + + VerifyOrQuit(frameBuilder.GetBytes() == buffer); + VerifyOrQuit(frameBuilder.GetLength() == 0); + VerifyOrQuit(frameBuilder.GetMaxLength() == sizeof(buffer)); + VerifyOrQuit(memcmp(buffer, zeroBuffer, sizeof(buffer)) == 0); + VerifyOrQuit(frameBuilder.CanAppend(sizeof(buffer))); + VerifyOrQuit(!frameBuilder.CanAppend(sizeof(buffer) + 1)); + + SuccessOrQuit(frameBuilder.Append(kData1)); + VerifyOrQuit(frameBuilder.GetLength() == sizeof(kData1)); + VerifyOrQuit(frameBuilder.GetBytes() == buffer); + VerifyOrQuit(memcmp(buffer, kData1, sizeof(kData1)) == 0); + VerifyOrQuit(memcmp(buffer + sizeof(kData1), zeroBuffer, sizeof(buffer) - sizeof(kData1)) == 0); + + SuccessOrQuit(frameBuilder.AppendUint8(0x01)); + SuccessOrQuit(frameBuilder.AppendBigEndianUint16(0x0203)); + SuccessOrQuit(frameBuilder.AppendLittleEndianUint16(0x0504)); + VerifyOrQuit(frameBuilder.GetLength() == sizeof(kData1) * 2); + VerifyOrQuit(frameBuilder.GetBytes() == buffer); + VerifyOrQuit(memcmp(buffer, kData1, sizeof(kData1)) == 0); + VerifyOrQuit(memcmp(buffer + sizeof(kData1), kData1, sizeof(kData1)) == 0); + + SuccessOrQuit(frameBuilder.AppendBigEndianUint32(0x01020304)); + SuccessOrQuit(frameBuilder.AppendUint8(0x05)); + VerifyOrQuit(frameBuilder.GetLength() == sizeof(kData1) * 3); + VerifyOrQuit(frameBuilder.GetBytes() == buffer); + VerifyOrQuit(memcmp(buffer, kData1, sizeof(kData1)) == 0); + VerifyOrQuit(memcmp(buffer + sizeof(kData1), kData1, sizeof(kData1)) == 0); + VerifyOrQuit(memcmp(buffer + 2 * sizeof(kData1), kData1, sizeof(kData1)) == 0); + + frameBuilder.Init(buffer, sizeof(buffer)); + VerifyOrQuit(frameBuilder.GetBytes() == buffer); + VerifyOrQuit(frameBuilder.GetLength() == 0); + VerifyOrQuit(frameBuilder.GetMaxLength() == sizeof(buffer)); + + offset = sizeof(kData1); + SuccessOrQuit(frameBuilder.AppendBytesFromMessage(*message, offset, sizeof(kData2))); + VerifyOrQuit(frameBuilder.GetLength() == sizeof(kData2)); + VerifyOrQuit(frameBuilder.GetBytes() == buffer); + VerifyOrQuit(memcmp(buffer, kData2, sizeof(kData2)) == 0); + + frameBuilder.Init(buffer, sizeof(buffer)); + VerifyOrQuit(frameBuilder.GetBytes() == buffer); + VerifyOrQuit(frameBuilder.GetLength() == 0); + VerifyOrQuit(frameBuilder.GetMaxLength() == sizeof(buffer)); + + SuccessOrQuit(frameBuilder.AppendLittleEndianUint32(0x04030201)); + SuccessOrQuit(frameBuilder.AppendUint8(0x05)); + VerifyOrQuit(frameBuilder.GetLength() == sizeof(kData1)); + VerifyOrQuit(frameBuilder.GetBytes() == buffer); + VerifyOrQuit(memcmp(buffer, kData1, sizeof(kData1)) == 0); + + SuccessOrQuit(frameBuilder.AppendBytes(zeroBuffer, sizeof(kData2))); + SuccessOrQuit(frameBuilder.Append(kData1)); + VerifyOrQuit(frameBuilder.GetLength() == sizeof(buffer)); + VerifyOrQuit(frameBuilder.GetBytes() == buffer); + VerifyOrQuit(memcmp(buffer, kData1, sizeof(kData1)) == 0); + VerifyOrQuit(memcmp(buffer + sizeof(kData1), zeroBuffer, sizeof(kData2)) == 0); + VerifyOrQuit(memcmp(buffer + sizeof(kData1) + sizeof(kData2), kData1, sizeof(kData1)) == 0); + + VerifyOrQuit(!frameBuilder.CanAppend(1)); + VerifyOrQuit(frameBuilder.AppendUint8(0x00) == kErrorNoBufs); + + offset = sizeof(kData1); + frameBuilder.Write(offset, kData2); + VerifyOrQuit(frameBuilder.GetLength() == sizeof(buffer)); + VerifyOrQuit(frameBuilder.GetBytes() == buffer); + VerifyOrQuit(memcmp(buffer, kData1, sizeof(kData1)) == 0); + VerifyOrQuit(memcmp(buffer + sizeof(kData1), kData2, sizeof(kData2)) == 0); + VerifyOrQuit(memcmp(buffer + sizeof(kData1) + sizeof(kData2), kData1, sizeof(kData1)) == 0); + + message->Free(); + testFreeInstance(instance); +} + +} // namespace ot + +int main(void) +{ + ot::TestFrameBuilder(); + + printf("All tests passed\n"); + return 0; +} diff --git a/tests/unit/test_lowpan.cpp b/tests/unit/test_lowpan.cpp index d5311142b..beb6981fc 100644 --- a/tests/unit/test_lowpan.cpp +++ b/tests/unit/test_lowpan.cpp @@ -171,20 +171,22 @@ static void Test(TestIphcVector &aVector, bool aCompress, bool aDecompress) if (aCompress) { - Lowpan::BufferWriter buffer(result, 127); - Message * compressedMsg; - Ip6::Ecn ecn; + FrameBuilder frameBuilder; + Message * compressedMsg; + Ip6::Ecn ecn; + + frameBuilder.Init(result, 127); VerifyOrQuit((message = sInstance->Get().Allocate(Message::kTypeIp6)) != nullptr); aVector.GetUncompressedStream(*message); - VerifyOrQuit(sLowpan->Compress(*message, aVector.mMacSource, aVector.mMacDestination, buffer) == + VerifyOrQuit(sLowpan->Compress(*message, aVector.mMacSource, aVector.mMacDestination, frameBuilder) == aVector.mError); if (aVector.mError == kErrorNone) { - uint8_t compressBytes = static_cast(buffer.GetWritePointer() - result); + uint16_t compressBytes = frameBuilder.GetLength(); // Append payload to the LOWPAN_IPHC. message->ReadBytes(message->GetOffset(), result + compressBytes,