From 3514458d5d4ff87a1699efffd0ff27d0bdc4bc48 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Thu, 12 Sep 2019 09:30:22 -0700 Subject: [PATCH] [tlv] handle Extended TLV and simplify searching for TLV in a message (#4152) This commit contains the following change in `Tlv` class: - Changes `GetSize()`, `GetValue()` and `GetNext()` to ensure they work correctly independent of whether the TLV is an Extended TLV or not. - Defines a new common private static method `Tlv::Find()` which is then used to simplify `Tlv::Get()` and `Tlv::GetOffset()` and `Tlv::GetValueOffset()` implementations. - This change also ensures `Tlv::Get()` (which finds and reads a TLV of a given type within a message) work correctly for Extended TLVs. --- src/core/common/tlvs.cpp | 140 +++++++++++++++++++++++---------------- src/core/common/tlvs.hpp | 72 ++++++++++++++++---- 2 files changed, 143 insertions(+), 69 deletions(-) diff --git a/src/core/common/tlvs.cpp b/src/core/common/tlvs.cpp index 49ac45419..02893c5d4 100644 --- a/src/core/common/tlvs.cpp +++ b/src/core/common/tlvs.cpp @@ -38,20 +38,36 @@ namespace ot { -otError Tlv::Get(const Message &aMessage, uint8_t aType, uint16_t aMaxLength, Tlv &aTlv) +uint16_t Tlv::GetSize(void) const { - otError error = OT_ERROR_NOT_FOUND; + return IsExtended() ? sizeof(ExtendedTlv) + static_cast(this)->GetLength() + : sizeof(Tlv) + GetLength(); +} + +uint8_t *Tlv::GetValue(void) +{ + return reinterpret_cast(this) + (IsExtended() ? sizeof(ExtendedTlv) : sizeof(Tlv)); +} + +const uint8_t *Tlv::GetValue(void) const +{ + return reinterpret_cast(this) + (IsExtended() ? sizeof(ExtendedTlv) : sizeof(Tlv)); +} + +otError Tlv::Get(const Message &aMessage, uint8_t aType, uint16_t aMaxSize, Tlv &aTlv) +{ + otError error; uint16_t offset; + uint16_t size; - SuccessOrExit(error = GetOffset(aMessage, aType, offset)); - aMessage.Read(offset, sizeof(Tlv), &aTlv); + SuccessOrExit(error = Find(aMessage, aType, &offset, &size, NULL)); - if (aMaxLength > sizeof(aTlv) + aTlv.GetLength()) + if (aMaxSize > size) { - aMaxLength = sizeof(aTlv) + aTlv.GetLength(); + aMaxSize = size; } - aMessage.Read(offset, aMaxLength, &aTlv); + aMessage.Read(offset, aMaxSize, &aTlv); exit: return error; @@ -59,77 +75,87 @@ exit: otError Tlv::GetOffset(const Message &aMessage, uint8_t aType, uint16_t &aOffset) { - otError error = OT_ERROR_NOT_FOUND; - uint16_t offset = aMessage.GetOffset(); - uint16_t end = aMessage.GetLength(); - Tlv tlv; + return Find(aMessage, aType, &aOffset, NULL, NULL); +} - while (offset + sizeof(tlv) <= end) +otError Tlv::GetValueOffset(const Message &aMessage, uint8_t aType, uint16_t &aValueOffset, uint16_t &aLength) +{ + otError error; + uint16_t offset; + uint16_t size; + bool isExtendedTlv; + + SuccessOrExit(error = Find(aMessage, aType, &offset, &size, &isExtendedTlv)); + + if (!isExtendedTlv) { - uint32_t length = sizeof(tlv); - - aMessage.Read(offset, sizeof(tlv), &tlv); - - if (tlv.GetLength() != kExtendedLength) - { - length += tlv.GetLength(); - } - else - { - uint16_t extLength; - - VerifyOrExit(sizeof(extLength) == aMessage.Read(offset + sizeof(tlv), sizeof(extLength), &extLength)); - length += sizeof(extLength) + HostSwap16(extLength); - } - - VerifyOrExit(offset + length <= end); - - if (tlv.GetType() == aType) - { - aOffset = offset; - ExitNow(error = OT_ERROR_NONE); - } - - offset += static_cast(length); + aValueOffset = offset + sizeof(Tlv); + aLength = size - sizeof(Tlv); + } + else + { + aValueOffset = offset + sizeof(ExtendedTlv); + aLength = size - sizeof(ExtendedTlv); } exit: return error; } -otError Tlv::GetValueOffset(const Message &aMessage, uint8_t aType, uint16_t &aOffset, uint16_t &aLength) +otError Tlv::Find(const Message &aMessage, uint8_t aType, uint16_t *aOffset, uint16_t *aSize, bool *aIsExtendedTlv) { - otError error = OT_ERROR_NOT_FOUND; - uint16_t offset = aMessage.GetOffset(); - uint16_t end = aMessage.GetLength(); + otError error = OT_ERROR_NOT_FOUND; + uint16_t offset = aMessage.GetOffset(); + uint16_t remainingLen = aMessage.GetLength(); Tlv tlv; + uint16_t size; - while (offset + sizeof(tlv) <= end) + VerifyOrExit(offset <= remainingLen); + remainingLen -= offset; + + while (true) { - uint16_t length; + VerifyOrExit(sizeof(Tlv) <= remainingLen); + aMessage.Read(offset, sizeof(Tlv), &tlv); - aMessage.Read(offset, sizeof(tlv), &tlv); - offset += sizeof(tlv); - length = tlv.GetLength(); - - if (length == kExtendedLength) + if (tlv.mLength != kExtendedLength) { - VerifyOrExit(offset + sizeof(length) <= end); - aMessage.Read(offset, sizeof(length), &length); - offset += sizeof(length); - length = HostSwap16(length); + size = tlv.GetSize(); + } + else + { + ExtendedTlv extTlv; + + VerifyOrExit(sizeof(ExtendedTlv) <= remainingLen); + aMessage.Read(offset, sizeof(ExtendedTlv), &extTlv); + size = extTlv.GetSize(); } - VerifyOrExit(length <= end - offset); + VerifyOrExit(size <= remainingLen); if (tlv.GetType() == aType) { - aOffset = offset; - aLength = length; - ExitNow(error = OT_ERROR_NONE); + if (aOffset != NULL) + { + *aOffset = offset; + } + + if (aSize != NULL) + { + *aSize = size; + } + + if (aIsExtendedTlv != NULL) + { + *aIsExtendedTlv = (tlv.mLength == kExtendedLength); + } + + error = OT_ERROR_NONE; + ExitNow(); } - offset += length; + offset += size; + remainingLen -= size; } exit: diff --git a/src/core/common/tlvs.hpp b/src/core/common/tlvs.hpp index c53e2a26d..b735bcd10 100644 --- a/src/core/common/tlvs.hpp +++ b/src/core/common/tlvs.hpp @@ -83,9 +83,22 @@ public: */ void SetType(uint8_t aType) { mType = aType; } + /** + * This method indicates whether the TLV is an Extended TLV. + * + * @retval TRUE If the TLV is an Extended TLV. + * @retval FALSE If the TLV is not an Extended TLV. + * + */ + bool IsExtended(void) const { return (mLength == kExtendedLength); } + /** * This method returns the Length value. * + * @note This method should be used when TLV is not an Extended TLV, otherwise the returned length from this method + * would not be correct. When TLV is an Extended TLV, the TLV should be down-casted to the `ExtendedTlv` type and + * the `ExtendedTlv::GetLength()` should be used instead. + * * @returns The Length value. * */ @@ -100,65 +113,79 @@ public: void SetLength(uint8_t aLength) { mLength = aLength; } /** - * This method returns the total size including Type, Length, and Value fields. + * This method returns the TLV's total size (number of bytes) including Type, Length, and Value fields. + * + * This method correctly returns the TLV size independent of whether the TLV is an Extended TLV or not. * * @returns The total size include Type, Length, and Value fields. * */ - uint16_t GetSize(void) const { return sizeof(Tlv) + mLength; } + uint16_t GetSize(void) const; /** * This method returns a pointer to the Value. * + * This method can be used independent of whether the TLV is an Extended TLV or not. + * * @returns A pointer to the value. * */ - uint8_t *GetValue(void) { return reinterpret_cast(this) + sizeof(Tlv); } + uint8_t *GetValue(void); /** * This method returns a pointer to the Value. * + * This method can be used independent of whether the TLV is an Extended TLV or not. + * * @returns A pointer to the value. * */ - const uint8_t *GetValue(void) const { return reinterpret_cast(this) + sizeof(Tlv); } + const uint8_t *GetValue(void) const; /** * This method returns a pointer to the next TLV. * + * This method correctly returns the next TLV independent of whether the current TLV is an Extended TLV or not. + * * @returns A pointer to the next TLV. * */ - Tlv *GetNext(void) { return reinterpret_cast(reinterpret_cast(this) + sizeof(*this) + mLength); } + Tlv *GetNext(void) { return reinterpret_cast(reinterpret_cast(this) + GetSize()); } /** * This method returns a pointer to the next TLV. * + * This method correctly returns the next TLV independent of whether the current TLV is an Extended TLV or not. + * * @returns A pointer to the next TLV. * */ const Tlv *GetNext(void) const { - return reinterpret_cast(reinterpret_cast(this) + sizeof(*this) + mLength); + return reinterpret_cast(reinterpret_cast(this) + GetSize()); } /** * This static method reads the requested TLV out of @p aMessage. * + * This method can be used independent of whether the read TLV (from message) is an Extended TLV or not. + * * @param[in] aMessage A reference to the message. * @param[in] aType The Type value to search for. - * @param[in] aMaxLength Maximum number of bytes to read. + * @param[in] aMaxSize Maximum number of bytes to read. * @param[out] aTlv A reference to the TLV that will be copied to. * * @retval OT_ERROR_NONE Successfully copied the TLV. * @retval OT_ERROR_NOT_FOUND Could not find the TLV with Type @p aType. * */ - static otError Get(const Message &aMessage, uint8_t aType, uint16_t aMaxLength, Tlv &aTlv); + static otError Get(const Message &aMessage, uint8_t aType, uint16_t aMaxSize, Tlv &aTlv); /** * This static method obtains the offset of a TLV within @p aMessage. * + * This method can be used independent of whether the read TLV (from message) is an Extended TLV or not. + * * @param[in] aMessage A reference to the message. * @param[in] aType The Type value to search for. * @param[out] aOffset A reference to the offset of the TLV. @@ -172,6 +199,8 @@ public: /** * This static method finds the offset and length of a given TLV type. * + * This method can be used independent of whether the read TLV (from message) is an Extended TLV or not. + * * @param[in] aMessage A reference to the message. * @param[in] aType The Type value to search for. * @param[out] aOffset The offset where the value starts. @@ -184,16 +213,35 @@ public: static otError GetValueOffset(const Message &aMessage, uint8_t aType, uint16_t &aOffset, uint16_t &aLength); protected: - /** - * Length values. - * - */ enum { kExtendedLength = 255, ///< Extended Length value }; private: + /** + * This private static method searches within a given message for TLV type and outputs the TLV offset, size and + * whether it is an Extended TLV. + * + * A NULL pointer can be used for output parameters @p aOffset, @p aSize, or @p aIsExtendedTlv if the parameter + * is not required. + * + * @param[in] aMessage A reference to the message to search within. + * @param[in] aType The TLV type to search for. + * @param[out] aOffset A pointer to a variable to output the offset to the start of the TLV. + * @param[out] aSize A pointer to a variable to output the size (total number of bytes) of the TLV. + * @param[out] aIsExtendedTlv A pointer to a boolean variable to output whether the found TLV is extended or not. + * + * @retval OT_ERROR_NONE Successfully found the TLV. + * @retval OT_ERROR_NOT_FOUND Could not find the TLV with Type @p aType. + * + */ + static otError Find(const Message &aMessage, + uint8_t aType, + uint16_t * aOffset, + uint16_t * aSize, + bool * aIsExtendedTlv); + uint8_t mType; uint8_t mLength; } OT_TOOL_PACKED_END;