/* * Copyright (c) 2016, 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 includes definitions for generating and processing MLE TLVs. */ #ifndef TLVS_HPP_ #define TLVS_HPP_ #include "openthread-core-config.h" #include #include #include "common/encoding.hpp" #include "common/error.hpp" #include "common/type_traits.hpp" namespace ot { using ot::Encoding::BigEndian::HostSwap16; class Message; /** * This class implements TLV generation and parsing. * */ OT_TOOL_PACKED_BEGIN class Tlv { public: /** * The maximum length of the Base TLV format. * */ static constexpr uint8_t kBaseTlvMaxLength = OT_NETWORK_BASE_TLV_MAX_LENGTH; /** * This method returns the Type value. * * @returns The Type value. * */ uint8_t GetType(void) const { return mType; } /** * This method sets the Type value. * * @param[in] aType The Type value. * */ 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. * */ uint8_t GetLength(void) const { return mLength; } /** * This method sets the Length value. * * @param[in] aLength The Length value. * */ void SetLength(uint8_t aLength) { mLength = aLength; } /** * 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. * */ uint32_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); /** * 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; /** * 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) + 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) + 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 kErrorNone Successfully appended the TLV to the message. * @retval kErrorNoBufs Insufficient available buffers to grow the message. * */ Error AppendTo(Message &aMessage) const; /** * This static method reads a TLV's value in a message at a given offset expecting a minimum length for the value. * * This method can be used independent of whether the read TLV (from the message) is an Extended TLV or not. * * @param[in] aMessage The message to read from. * @param[in] aOffset The offset into the message pointing to the start of the TLV. * @param[out] aValue A buffer to output the TLV's value, must contain (at least) @p aMinLength bytes. * @param[in] aMinLength The minimum expected length of TLV and number of bytes to copy into @p aValue buffer. * * @retval kErrorNone Successfully read the TLV and copied @p aMinLength into @p aValue. * @retval kErrorParse The TLV was not well-formed and could not be parsed. * */ static Error ReadTlvValue(const Message &aMessage, uint16_t aOffset, void *aValue, uint8_t aMinLength); /** * This static method reads a simple TLV with a single non-integral value in a message at a given offset. * * @tparam SimpleTlvType The simple TLV type to read (must be a sub-class of `SimpleTlvInfo`). * * @param[in] aMessage The message to read from. * @param[in] aOffset The offset into the message pointing to the start of the TLV. * @param[out] aValue A reference to the value object to output the read value. * * @retval kErrorNone Successfully read the TLV and updated the @p aValue. * @retval kErrorParse The TLV was not well-formed and could not be parsed. * */ template static Error Read(const Message &aMessage, uint16_t aOffset, typename SimpleTlvType::ValueType &aValue) { return ReadTlvValue(aMessage, aOffset, &aValue, sizeof(aValue)); } /** * This static method reads a simple TLV with a single integral value in a message at a given offset. * * @tparam UintTlvType The simple TLV type to read (must be a sub-class of `UintTlvInfo`). * * @param[in] aMessage The message to read from. * @param[in] aOffset The offset into the message pointing to the start of the TLV. * @param[out] aValue A reference to an unsigned int to output the read value. * * @retval kErrorNone Successfully read the TLV and updated the @p aValue. * @retval kErrorParse The TLV was not well-formed and could not be parsed. * */ template static Error Read(const Message &aMessage, uint16_t aOffset, typename UintTlvType::UintValueType &aValue) { return ReadUintTlv(aMessage, aOffset, aValue); } /** * This static method reads a simple TLV with a UTF-8 string value in a message at a given offset. * * @tparam StringTlvType The simple TLV type to read (must be a sub-class of `StringTlvInfo`). * * @param[in] aMessage The message to read from. * @param[in] aOffset The offset into the message pointing to the start of the TLV. * @param[out] aValue A reference to the string buffer to output the read value. * * @retval kErrorNone Successfully read the TLV and updated the @p aValue. * @retval kErrorParse The TLV was not well-formed and could not be parsed. * */ template static Error Read(const Message &aMessage, uint16_t aOffset, typename StringTlvType::StringType &aValue) { return ReadStringTlv(aMessage, aOffset, StringTlvType::kMaxStringLength, aValue); } /** * This static method searches for and reads a requested TLV out of a given message. * * 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] aMaxSize Maximum number of bytes to read. * @param[out] aTlv A reference to the TLV that will be copied to. * * @retval kErrorNone Successfully copied the TLV. * @retval kErrorNotFound Could not find the TLV with Type @p aType. * */ static Error FindTlv(const Message &aMessage, uint8_t aType, uint16_t aMaxSize, Tlv &aTlv); /** * This static method searches for and reads a requested TLV out of a given message. * * This method can be used independent of whether the read TLV (from message) is an Extended TLV or not. * * @tparam TlvType The TlvType to search for (must be a sub-class of `Tlv`). * * @param[in] aMessage A reference to the message. * @param[out] aTlv A reference to the TLV that will be copied to. * * @retval kErrorNone Successfully copied the TLV. * @retval kErrorNotFound Could not find the TLV with Type @p aType. * */ template static Error FindTlv(const Message &aMessage, TlvType &aTlv) { return FindTlv(aMessage, TlvType::kType, sizeof(TlvType), 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. * * @retval kErrorNone Successfully copied the TLV. * @retval kErrorNotFound Could not find the TLV with Type @p aType. * */ static Error FindTlvOffset(const Message &aMessage, uint8_t aType, uint16_t &aOffset); /** * 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] aValueOffset The offset where the value starts. * @param[out] aLength The length of the value. * * @retval kErrorNone Successfully found the TLV. * @retval kErrorNotFound Could not find the TLV with Type @p aType. * */ static Error FindTlvValueOffset(const Message &aMessage, uint8_t aType, uint16_t &aValueOffset, uint16_t &aLength); /** * 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 `kErrorParse` and the @p aValue buffer is not updated. * * If the TLV length is larger than @p aLength, the TLV is considered valid, but only the first @p aLength bytes * of the value are read and copied into the @p aValue buffer. * * @tparam TlvType The TLV type to find. * * @param[in] aMessage A reference to the message. * @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 kErrorNone The TLV was found and read successfully. @p aValue is updated. * @retval kErrorNotFound Could not find the TLV with Type @p aType. * @retval kErrorParse TLV was found but it was not well-formed and could not be parsed. * */ template static Error Find(const Message &aMessage, void *aValue, uint8_t aLength) { return FindTlv(aMessage, TlvType::kType, aValue, aLength); } /** * This static method searches for a simple TLV with a single non-integral value in a message, ensures its length is * same or larger than the expected `ValueType` object size, and then reads its value into a value object reference. * * If the TLV length is smaller than the size of @p aValue, the TLV is considered invalid. In this case, this * method returns `kErrorParse` and the @p aValue is not updated. * * If the TLV length is larger than the size of @p aValue, the TLV is considered valid, but the size of * `ValueType` bytes are read and copied into the @p aValue. * * @tparam SimpleTlvType The simple TLV type to find (must be a sub-class of `SimpleTlvInfo`) * * @param[in] aMessage A reference to the message. * @param[out] aValue A reference to the value object to output the read value. * * @retval kErrorNone The TLV was found and read successfully. @p aValue is updated. * @retval kErrorNotFound Could not find the TLV with Type @p aType. * @retval kErrorParse TLV was found but it was not well-formed and could not be parsed. * */ template static Error Find(const Message &aMessage, typename SimpleTlvType::ValueType &aValue) { return FindTlv(aMessage, SimpleTlvType::kType, &aValue, sizeof(aValue)); } /** * This static method searches for a simple TLV with a single integral value in a message, and then reads its value * into a given `uint` reference variable. * * If the TLV length is smaller than size of integral value, the TLV is considered invalid. In this case, this * method returns `kErrorParse` and the @p aValue is not updated. * * @tparam UintTlvType The simple TLV type to find (must be a sub-class of `UintTlvInfo`) * * @param[in] aMessage A reference to the message. * @param[out] aValue A reference to an unsigned int value to output the TLV's value. * * @retval kErrorNone The TLV was found and read successfully. @p aValue is updated. * @retval kErrorNotFound Could not find the TLV with Type @p aType. * @retval kErrorParse TLV was found but it was not well-formed and could not be parsed. * */ template static Error Find(const Message &aMessage, typename UintTlvType::UintValueType &aValue) { return FindUintTlv(aMessage, UintTlvType::kType, aValue); } /** * This static method searches for a simple TLV with a UTF-8 string value in a message, and then reads its value * into a given string buffer. * * If the TLV length is longer than maximum string length specified by `StringTlvType::kMaxStringLength` then * only up to maximum length is read and returned. In this case `kErrorNone` is returned. * * The returned string in @p aValue is always null terminated.`StringTlvType::StringType` MUST have at least * `kMaxStringLength + 1` chars. * * @tparam StringTlvType The simple TLV type to find (must be a sub-class of `StringTlvInfo`) * * @param[in] aMessage A reference to the message. * @param[out] aValue A reference to a string buffer to output the TLV's value. * * @retval kErrorNone The TLV was found and read successfully. @p aValue is updated. * @retval kErrorNotFound Could not find the TLV with Type @p aType. * @retval kErrorParse TLV was found but it was not well-formed and could not be parsed. * */ template static Error Find(const Message &aMessage, typename StringTlvType::StringType &aValue) { return FindStringTlv(aMessage, StringTlvType::kType, StringTlvType::kMaxStringLength, 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. * * @tparam TlvType The TLV type to append. * * @param[in] aMessage A reference to the message to append to. * @param[in] aValue A buffer containing the TLV value. * @param[in] aLength The value length (in bytes). * * @retval kErrorNone Successfully appended the TLV to the message. * @retval kErrorNoBufs Insufficient available buffers to grow the message. * */ template static Error Append(Message &aMessage, const void *aValue, uint8_t aLength) { return AppendTlv(aMessage, TlvType::kType, aValue, aLength); } /** * This static method appends a simple TLV with a single (non-integral) value to a message. * * On success this method grows the message by the size of the TLV. * * @tparam SimpleTlvType The simple TLV type to append (must be a sub-class of `SimpleTlvInfo`) * * @param[in] aMessage A reference to the message to append to. * @param[in] aValue A reference to the object containing TLV's value. * * @retval kErrorNone Successfully appended the TLV to the message. * @retval kErrorNoBufs Insufficient available buffers to grow the message. * */ template static Error Append(Message &aMessage, const typename SimpleTlvType::ValueType &aValue) { return AppendTlv(aMessage, SimpleTlvType::kType, &aValue, sizeof(aValue)); } /** * This static method appends a simple TLV with a single integral value to a message. * * On success this method grows the message by the size of the TLV. * * @tparam UintTlvType The simple TLV type to append (must be a sub-class of `UintTlvInfo`) * * @param[in] aMessage A reference to the message to append to. * @param[in] aValue An unsigned int value to use as TLV's value. * * @retval kErrorNone Successfully appended the TLV to the message. * @retval kErrorNoBufs Insufficient available buffers to grow the message. * */ template static Error Append(Message &aMessage, typename UintTlvType::UintValueType aValue) { return AppendUintTlv(aMessage, UintTlvType::kType, aValue); } /** * This static method appends a simple TLV with a single UTF-8 string value to a message. * * On success this method grows the message by the size of the TLV. * * If the passed in @p aValue string length is longer than the maximum allowed length for the TLV as specified by * `StringTlvType::kMaxStringLength`, the first maximum length chars are appended. * * The @p aValue can be `nullptr` in which case it is treated as an empty string. * * @tparam StringTlvType The simple TLV type to append (must be a sub-class of `StringTlvInfo`) * * @param[in] aMessage A reference to the message to append to. * @param[in] aValue A pointer to a C string to append as TLV's value. * * @retval kErrorNone Successfully appended the TLV to the message. * @retval kErrorNoBufs Insufficient available buffers to grow the message. * */ template static Error Append(Message &aMessage, const char *aValue) { return AppendStringTlv(aMessage, StringTlvType::kType, StringTlvType::kMaxStringLength, aValue); } protected: static const uint8_t kExtendedLength = 255; // Extended Length value. private: struct ParsedInfo { Error ParseFrom(const Message &aMessage, uint16_t aOffset); Error FindIn(const Message &aMessage, uint8_t aType); uint8_t mType; uint16_t mLength; uint16_t mOffset; uint16_t mValueOffset; uint16_t mSize; }; static Error FindTlv(const Message &aMessage, uint8_t aType, void *aValue, uint8_t aLength); static Error AppendTlv(Message &aMessage, uint8_t aType, const void *aValue, uint8_t aLength); static Error ReadStringTlv(const Message &aMessage, uint16_t aOffset, uint8_t aMaxStringLength, char *aValue); static Error FindStringTlv(const Message &aMessage, uint8_t aType, uint8_t aMaxStringLength, char *aValue); static Error AppendStringTlv(Message &aMessage, uint8_t aType, uint8_t aMaxStringLength, const char *aValue); template static Error ReadUintTlv(const Message &aMessage, uint16_t aOffset, UintType &aValue); template static Error FindUintTlv(const Message &aMessage, uint8_t aType, UintType &aValue); template static Error AppendUintTlv(Message &aMessage, uint8_t aType, UintType aValue); uint8_t mType; uint8_t mLength; } OT_TOOL_PACKED_END; OT_TOOL_PACKED_BEGIN class ExtendedTlv : public Tlv { public: /** * This method returns the Length value. * */ uint16_t GetLength(void) const { return HostSwap16(mLength); } /** * This method sets the Length value. * * @param[in] aLength The Length value. * */ void SetLength(uint16_t aLength) { Tlv::SetLength(kExtendedLength); mLength = HostSwap16(aLength); } private: uint16_t mLength; } OT_TOOL_PACKED_END; /** * This template method casts a `Tlv` pointer to a given subclass `TlvType` pointer. * * @tparam TlvType The TLV type to cast into. MUST be a subclass of `Tlv`. * * @param[in] aTlv A pointer to a `Tlv` to convert/cast to a `TlvType`. * * @returns A `TlvType` pointer to `aTlv`. * */ template TlvType *As(Tlv *aTlv) { return static_cast(aTlv); } /** * This template method casts a `Tlv` pointer to a given subclass `TlvType` pointer. * * @tparam TlvType The TLV type to cast into. MUST be a subclass of `Tlv`. * * @param[in] aTlv A pointer to a `Tlv` to convert/cast to a `TlvType`. * * @returns A `TlvType` pointer to `aTlv`. * */ template const TlvType *As(const Tlv *aTlv) { return static_cast(aTlv); } /** * This template method casts a `Tlv` reference to a given subclass `TlvType` reference. * * @tparam TlvType The TLV type to cast into. MUST be a subclass of `Tlv`. * * @param[in] aTlv A reference to a `Tlv` to convert/cast to a `TlvType`. * * @returns A `TlvType` reference to `aTlv`. * */ template TlvType &As(Tlv &aTlv) { return static_cast(aTlv); } /** * This template method casts a `Tlv` reference to a given subclass `TlvType` reference. * * @tparam TlvType The TLV type to cast into. MUST be a subclass of `Tlv`. * * @param[in] aTlv A reference to a `Tlv` to convert/cast to a `TlvType`. * * @returns A `TlvType` reference to `aTlv`. * */ template const TlvType &As(const Tlv &aTlv) { return static_cast(aTlv); } /** * This class defines constants for a TLV. * * @tparam kTlvTypeValue The TLV Type value. * */ template class TlvInfo { public: static constexpr uint8_t kType = kTlvTypeValue; ///< The TLV Type value. }; /** * This class defines constants and types for a simple TLV with an unsigned int value type. * * This class and its sub-classes are intended to be used as the template type in `Tlv::Append()`, and * the related `Tlv::Find()` and `Tlv::Read()`. * * @tparam kTlvTypeValue The TLV Type value. * @tparam UintType The TLV Value's type (must be an unsigned int, i.e. uint8_t, uint16_t, or uint32_t). * */ template class UintTlvInfo : public TlvInfo { public: static_assert(TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue, "UintTlv must be used used with unsigned int value type"); typedef UintType UintValueType; ///< The TLV Value unsigned int type. }; /** * This class defines constants and types for a simple TLV with a single value. * * This class and its sub-classes are intended to be used as the template type in `Tlv::Append()`, * and the related `Tlv::Find()` and `Tlv::Read()`. * * @tparam kTlvTypeValue The TLV Type value. * @tparam TlvValueType The TLV Value's type (must not be an integral type). * */ template class SimpleTlvInfo : public TlvInfo { public: static_assert(!TypeTraits::IsPointer::kValue, "TlvValueType must not be a pointer"); static_assert(!TypeTraits::IsSame::kValue, "SimpleTlv must not use int value type"); static_assert(!TypeTraits::IsSame::kValue, "SimpleTlv must not use int value type"); static_assert(!TypeTraits::IsSame::kValue, "SimpleTlv must not use int value type"); static_assert(!TypeTraits::IsSame::kValue, "SimpleTlv must not use int value type"); static_assert(!TypeTraits::IsSame::kValue, "SimpleTlv must not use int value type"); static_assert(!TypeTraits::IsSame::kValue, "SimpleTlv must not use int value type"); typedef TlvValueType ValueType; ///< The TLV Value type. }; /** * This class defines constants and types for a simple TLV with a UTF-8 string value. * * This class and its sub-classes are intended to be used as the template type in `Tlv::Append()`, * and the related `Tlv::Find()` and `Tlv::Read()`. * * @tparam kTlvTypeValue The TLV Type value. * @tparam kTlvMaxValueLength The maximum allowed string length (as TLV value). * */ template class StringTlvInfo : public TlvInfo { public: static constexpr uint8_t kMaxStringLength = kTlvMaxValueLength; ///< Maximum string length. typedef char StringType[kMaxStringLength + 1]; ///< String buffer for TLV value. }; } // namespace ot #endif // TLVS_HPP_