From 19ed02842cd595cba203d3085cf1ad6485268c12 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Thu, 27 Feb 2020 13:43:21 -0800 Subject: [PATCH] [tlv] add helper read/append methods for TLVs with simple types (#4635) This commit adds helper static methods in `Tlv` class to directly read the value of simple TLVs from a `Message`, or append a simple TLV to a message. The simple TLVs are the ones with single unsigned integer value (e.g., MLE Frame Counter TLV, MeshCop Scan Duration TLV, etc), or a value that can be treated as an array of bytes (e.g., MeshCop PSKc TLV, etc). --- src/core/common/tlvs.cpp | 109 ++++++++++++++- src/core/common/tlvs.hpp | 278 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 380 insertions(+), 7 deletions(-) diff --git a/src/core/common/tlvs.cpp b/src/core/common/tlvs.cpp index 3a79f3321..6a2cc42e1 100644 --- a/src/core/common/tlvs.cpp +++ b/src/core/common/tlvs.cpp @@ -55,6 +55,15 @@ const uint8_t *Tlv::GetValue(void) const return reinterpret_cast(this) + (IsExtended() ? sizeof(ExtendedTlv) : sizeof(Tlv)); } +otError Tlv::AppendTo(Message &aMessage) const +{ + uint32_t size = GetSize(); + + assert(size <= UINT16_MAX); + + return aMessage.Append(this, static_cast(size)); +} + otError Tlv::Get(const Message &aMessage, uint8_t aType, uint16_t aMaxSize, Tlv &aTlv) { otError error; @@ -165,13 +174,105 @@ exit: return error; } -otError Tlv::AppendTo(Message &aMessage) const +otError Tlv::ReadUint8Tlv(const Message &aMessage, uint8_t aType, uint8_t &aValue) { - uint32_t size = GetSize(); + otError error = OT_ERROR_NONE; + TlvUint8 tlv8; - assert(size <= UINT16_MAX); + SuccessOrExit(error = Get(aMessage, aType, sizeof(tlv8), tlv8)); + VerifyOrExit(tlv8.IsValid(), error = OT_ERROR_PARSE); + aValue = tlv8.GetUint8Value(); - return aMessage.Append(this, static_cast(size)); +exit: + return error; +} + +otError Tlv::ReadUint16Tlv(const Message &aMessage, uint8_t aType, uint16_t &aValue) +{ + otError error = OT_ERROR_NONE; + TlvUint16 tlv16; + + SuccessOrExit(error = Get(aMessage, aType, sizeof(tlv16), tlv16)); + VerifyOrExit(tlv16.IsValid(), error = OT_ERROR_PARSE); + aValue = tlv16.GetUint16Value(); + +exit: + return error; +} + +otError Tlv::ReadUint32Tlv(const Message &aMessage, uint8_t aType, uint32_t &aValue) +{ + otError error = OT_ERROR_NONE; + TlvUint32 tlv32; + + SuccessOrExit(error = Get(aMessage, aType, sizeof(tlv32), tlv32)); + VerifyOrExit(tlv32.IsValid(), error = OT_ERROR_PARSE); + aValue = tlv32.GetUint32Value(); + +exit: + return error; +} + +otError Tlv::ReadTlv(const Message &aMessage, uint8_t aType, void *aValue, uint8_t aLength) +{ + otError error; + uint16_t offset; + uint16_t length; + + SuccessOrExit(error = GetValueOffset(aMessage, aType, offset, length)); + VerifyOrExit(length >= aLength, error = OT_ERROR_PARSE); + aMessage.Read(offset, aLength, static_cast(aValue)); + +exit: + return error; +} + +otError Tlv::AppendUint8Tlv(Message &aMessage, uint8_t aType, uint8_t aValue) +{ + TlvUint8 tlv8; + + tlv8.Init(aType); + tlv8.SetUint8Value(aValue); + + return tlv8.AppendTo(aMessage); +} + +otError Tlv::AppendUint16Tlv(Message &aMessage, uint8_t aType, uint16_t aValue) +{ + TlvUint16 tlv16; + + tlv16.Init(aType); + tlv16.SetUint16Value(aValue); + + return tlv16.AppendTo(aMessage); +} + +otError Tlv::AppendUint32Tlv(Message &aMessage, uint8_t aType, uint32_t aValue) +{ + TlvUint32 tlv32; + + tlv32.Init(aType); + tlv32.SetUint32Value(aValue); + + return tlv32.AppendTo(aMessage); +} + +otError Tlv::AppendTlv(Message &aMessage, uint8_t aType, const uint8_t *aValue, uint8_t aLength) +{ + otError error = OT_ERROR_NONE; + Tlv tlv; + + assert(aLength <= Tlv::kBaseTlvMaxLength); + + tlv.SetType(aType); + tlv.SetLength(aLength); + SuccessOrExit(error = aMessage.Append(&tlv, sizeof(tlv))); + + VerifyOrExit(aLength > 0); + error = aMessage.Append(aValue, aLength); + +exit: + return error; } } // namespace ot diff --git a/src/core/common/tlvs.hpp b/src/core/common/tlvs.hpp index 093192909..23776f97b 100644 --- a/src/core/common/tlvs.hpp +++ b/src/core/common/tlvs.hpp @@ -163,6 +163,19 @@ public: return reinterpret_cast(reinterpret_cast(this) + GetSize()); } + /** + * This method appends a TLV to the end of the message. + * + * On success, this method grows the message by the size of the TLV. + * + * @param[in] aMessage A reference to the message to append to. + * + * @retval OT_ERROR_NONE Successfully appended the TLV to the message. + * @retval OT_ERROR_NO_BUFS Insufficient available buffers to grow the message. + * + */ + otError AppendTo(Message &aMessage) const; + /** * This static method reads the requested TLV out of @p aMessage. * @@ -211,17 +224,129 @@ public: static otError GetValueOffset(const Message &aMessage, uint8_t aType, uint16_t &aOffset, uint16_t &aLength); /** - * This method appends a TLV to the end of the message. + * This static method searches for a TLV with a given type in a message and reads its value as an `uint8_t`. * - * On success, this method grows the message by the size of the TLV. + * @param[in] aMessage A reference to the message. + * @param[in] aType The TLV type to search for. + * @param[out] aValue A reference to a `uint8_t` to output the TLV's value. + * + * @retval OT_ERROR_NONE Successfully found the TLV and updated @p aValue. + * @retval OT_ERROR_NOT_FOUND Could not find the TLV with Type @p aType. + * @retval OT_ERROR_PARSE TLV was found but it was not well-formed and could not be parsed. + * + */ + static otError ReadUint8Tlv(const Message &aMessage, uint8_t aType, uint8_t &aValue); + + /** + * This static method searches for a TLV with a given type in a message and reads its value as an `uint16_t`. + * + * @param[in] aMessage A reference to the message. + * @param[in] aType The TLV type to search for. + * @param[out] aValue A reference to a `uint16_t` to output the TLV's value. + * + * @retval OT_ERROR_NONE Successfully found the TLV and updated @p aValue. + * @retval OT_ERROR_NOT_FOUND Could not find the TLV with Type @p aType. + * @retval OT_ERROR_PARSE TLV was found but it was not well-formed and could not be parsed. + * + */ + static otError ReadUint16Tlv(const Message &aMessage, uint8_t aType, uint16_t &aValue); + + /** + * This static method searches for a TLV with a given type in a message and reads its value as an `uint32_t`. + * + * @param[in] aMessage A reference to the message. + * @param[in] aType The TLV type to search for. + * @param[out] aValue A reference to a `uint32_t` to output the TLV's value. + * + * @retval OT_ERROR_NONE Successfully found the TLV and updated @p aValue. + * @retval OT_ERROR_NOT_FOUND Could not find the TLV with Type @p aType. + * @retval OT_ERROR_PARSE TLV was found but it was not well-formed and could not be parsed. + * + */ + static otError ReadUint32Tlv(const Message &aMessage, uint8_t aType, uint32_t &aValue); + + /** + * This static method searches for a TLV with a given type in a message, ensures its length is same or larger than + * an expected minimum value, and then reads its value into a given buffer. + * + * If the TLV length is smaller than the minimum length @p aLength, the TLV is considered invalid. In this case, + * this method returns `OT_ERROR_PARSE` and the @p aValue buffer is not updated. + * + * If the TLV is length is larger than @p aLength, the TLV is considered valid, but only the @aLength first bytes + * of the value are read and copied into the @p aValue buffer. + * + * @param[in] aMessage A reference to the message. + * @param[in] aType The TLV type to search for. + * @param[out] aValue A buffer to output the value (must contain at least @p aLength bytes). + * @param[in] aLength The expected (minimum) length of the TLV value. + * + * @retval OT_ERROR_NONE The TLV was found and read successfully. @p @aValue is updated. + * @retval OT_ERROR_NOT_FOUND Could not find the TLV with Type @p aType. + * @retval OT_ERROR_PARSE TLV was found but it was not well-formed and could not be parsed. + * + */ + static otError ReadTlv(const Message &aMessage, uint8_t aType, void *aValue, uint8_t aLength); + + /** + * This static method appends a simple TLV with a given type and an `uint8_t` value to a message. + * + * On success this method grows the message by the size of the TLV. * * @param[in] aMessage A reference to the message to append to. + * @param[in] aType The TLV type. + * @param[in] aValue The TLV value (`uint8_t`). * * @retval OT_ERROR_NONE Successfully appended the TLV to the message. * @retval OT_ERROR_NO_BUFS Insufficient available buffers to grow the message. * */ - otError AppendTo(Message &aMessage) const; + static otError AppendUint8Tlv(Message &aMessage, uint8_t aType, uint8_t aValue); + + /** + * This static method appends a simple TLV with a given type and an `uint16_t` value to a message. + * + * On success this method grows the message by the size of the TLV. + * + * @param[in] aMessage A reference to the message to append to. + * @param[in] aType The TLV type. + * @param[in] aValue The TLV value (`uint16_t`). + * + * @retval OT_ERROR_NONE Successfully appended the TLV to the message. + * @retval OT_ERROR_NO_BUFS Insufficient available buffers to grow the message. + * + */ + static otError AppendUint16Tlv(Message &aMessage, uint8_t aType, uint16_t aValue); + + /** + * This static method appends a (simple) TLV with a given type and an `uint32_t` value to a message. + * + * On success this method grows the message by the size of the TLV. + * + * @param[in] aMessage A reference to the message to append to. + * @param[in] aType The TLV type. + * @param[in] aValue The TLV value (`uint32_t`). + * + * @retval OT_ERROR_NONE Successfully appended the TLV to the message. + * @retval OT_ERROR_NO_BUFS Insufficient available buffers to grow the message. + * + */ + static otError AppendUint32Tlv(Message &aMessage, uint8_t aType, uint32_t aValue); + + /** + * This static method appends a TLV with a given type and value to a message. + * + * On success this method grows the message by the size of the TLV. + * + * @param[in] aMessage A reference to the message to append to. + * @param[in] aType The TLV type. + * @param[in] aValue A buffer containing the TLV value. + * @param[in] aLength The value length (in bytes). + * + * @retval OT_ERROR_NONE Successfully appended the TLV to the message. + * @retval OT_ERROR_NO_BUFS Insufficient available buffers to grow the message. + * + */ + static otError AppendTlv(Message &aMessage, uint8_t aType, const uint8_t *aValue, uint8_t aLength); protected: enum @@ -283,6 +408,153 @@ private: uint16_t mLength; } OT_TOOL_PACKED_END; +/** + * This class implements a simple TLV with a `uint8_t` value. + * + */ +OT_TOOL_PACKED_BEGIN +class TlvUint8 : public Tlv +{ +public: + /** + * This method initializes the TLV. + * + * @param[in] aType The Type value. + * + */ + void Init(uint8_t aType) + { + SetType(aType); + SetLength(sizeof(*this) - sizeof(Tlv)); + } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() >= sizeof(*this) - sizeof(Tlv); } + + /** + * This method returns the `uint8_t` value. + * + * @returns The `uint8_t` value. + * + */ + uint8_t GetUint8Value(void) const { return mValue; } + + /** + * This method sets the `uint8_t` value. + * + * @param[in] aValue The `uint8_t` value. + * + */ + void SetUint8Value(uint8_t aValue) { mValue = aValue; } + +private: + uint8_t mValue; +} OT_TOOL_PACKED_END; + +/** + * This class implements a simple TLV with a `uint16_t` value. + * + */ +OT_TOOL_PACKED_BEGIN +class TlvUint16 : public Tlv +{ +public: + /** + * This method initializes the TLV. + * + * @param[in] aType The Type value. + * + */ + void Init(uint8_t aType) + { + SetType(aType); + SetLength(sizeof(*this) - sizeof(Tlv)); + } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() >= sizeof(*this) - sizeof(Tlv); } + + /** + * This method returns the `uint16_t` value. + * + * @returns The `uint16_t` value. + * + */ + uint16_t GetUint16Value(void) const { return HostSwap16(mValue); } + + /** + * This method sets the `uint16_t` value. + * + * @param[in] aValue The `uint16_t` value. + * + */ + void SetUint16Value(uint16_t aValue) { mValue = HostSwap16(aValue); } + +private: + uint16_t mValue; +} OT_TOOL_PACKED_END; + +/** + * This class implements a simple TLV with a `uint32_t` value. + * + */ +OT_TOOL_PACKED_BEGIN +class TlvUint32 : public Tlv +{ +public: + /** + * This method initializes the TLV. + * + * @param[in] aType The Type value. + * + */ + void Init(uint8_t aType) + { + SetType(aType); + SetLength(sizeof(*this) - sizeof(Tlv)); + } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() >= sizeof(*this) - sizeof(Tlv); } + + /** + * This method returns the `uint32_t` value. + * + * @returns The `uint32_t` value. + * + */ + uint32_t GetUint32Value(void) const { return HostSwap32(mValue); } + + /** + * This method sets the `uint32_t` value. + * + * @param[in] aValue The `uint32_t` value. + * + */ + void SetUint32Value(uint32_t aValue) { mValue = HostSwap32(aValue); } + +private: + uint32_t mValue; +} OT_TOOL_PACKED_END; + } // namespace ot #endif // TLVS_HPP_