| /* |
| * 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 <openthread/thread.h> |
| #include <openthread/platform/toolchain.h> |
| |
| #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: |
| /** |
| * Length values. |
| * |
| */ |
| enum |
| { |
| kBaseTlvMaxLength = OT_NETWORK_BASE_TLV_MAX_LENGTH, ///< The maximum length of the Base TLV format. |
| }; |
| |
| /** |
| * 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<Tlv *>(reinterpret_cast<uint8_t *>(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<const Tlv *>(reinterpret_cast<const uint8_t *>(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 in a message at a given offset expecting a minimum length for the value. |
| * |
| * @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 ReadTlv(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 <typename SimpleTlvType> |
| static Error Read(const Message &aMessage, uint16_t aOffset, typename SimpleTlvType::ValueType &aValue) |
| { |
| return ReadTlv(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 `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 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 <typename UintTlvType> |
| static Error Read(const Message &aMessage, uint16_t aOffset, typename UintTlvType::UintValueType &aValue) |
| { |
| return ReadUintTlv(aMessage, aOffset, 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 <typename TlvType> 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[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 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 <typename TlvType> 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[in] aType The TLV type to search for. |
| * @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 <typename SimpleTlvType> |
| 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 <typename UintTlvType> |
| static Error Find(const Message &aMessage, typename UintTlvType::UintValueType &aValue) |
| { |
| return FindUintTlv(aMessage, UintTlvType::kType, 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 <typename TlvType> 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 <typename SimpleTlvType> |
| 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 <typename UintTlvType> static Error Append(Message &aMessage, typename UintTlvType::UintValueType aValue) |
| { |
| return AppendUintTlv(aMessage, UintTlvType::kType, aValue); |
| } |
| |
| protected: |
| 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 nullptr 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 kErrorNone Successfully found the TLV. |
| * @retval kErrorNotFound Could not find the TLV with Type @p aType. |
| * |
| */ |
| static Error Find(const Message &aMessage, uint8_t aType, uint16_t *aOffset, uint16_t *aSize, bool *aIsExtendedTlv); |
| |
| 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); |
| template <typename UintType> static Error ReadUintTlv(const Message &aMessage, uint16_t aOffset, UintType &aValue); |
| template <typename UintType> static Error FindUintTlv(const Message &aMessage, uint8_t aType, UintType &aValue); |
| template <typename UintType> 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 class defines constants for a TLV. |
| * |
| * @tparam kTlvTypeValue The TLV Type value. |
| * |
| */ |
| template <uint8_t kTlvTypeValue> class TlvInfo |
| { |
| public: |
| enum : 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<UintTlvType>()`, and |
| * the related `Tlv::FindTlv()` and `Tlv::ReadTlv()`. |
| * |
| * @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 <uint8_t kTlvTypeValue, typename UintType> class UintTlvInfo : public TlvInfo<kTlvTypeValue> |
| { |
| public: |
| static_assert(TypeTraits::IsSame<UintType, uint8_t>::kValue || TypeTraits::IsSame<UintType, uint16_t>::kValue || |
| TypeTraits::IsSame<UintType, uint32_t>::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<SimpleTlvType>()`, |
| * and the related `Tlv::FindTlv()` and `Tlv::ReadTlv()`. |
| * |
| * @tparam kTlvTypeValue The TLV Type value. |
| * @tparam TlvValueType The TLV Value's type (must not be an integral type). |
| * |
| */ |
| template <uint8_t kTlvTypeValue, typename TlvValueType> class SimpleTlvInfo : public TlvInfo<kTlvTypeValue> |
| { |
| public: |
| static_assert(!TypeTraits::IsPointer<TlvValueType>::kValue, "TlvValueType must not be a pointer"); |
| static_assert(!TypeTraits::IsSame<TlvValueType, uint8_t>::kValue, "SimpleTlv must not use int value type"); |
| static_assert(!TypeTraits::IsSame<TlvValueType, uint16_t>::kValue, "SimpleTlv must not use int value type"); |
| static_assert(!TypeTraits::IsSame<TlvValueType, uint32_t>::kValue, "SimpleTlv must not use int value type"); |
| static_assert(!TypeTraits::IsSame<TlvValueType, int8_t>::kValue, "SimpleTlv must not use int value type"); |
| static_assert(!TypeTraits::IsSame<TlvValueType, int16_t>::kValue, "SimpleTlv must not use int value type"); |
| static_assert(!TypeTraits::IsSame<TlvValueType, int32_t>::kValue, "SimpleTlv must not use int value type"); |
| |
| typedef TlvValueType ValueType; ///< The TLV Value type. |
| }; |
| |
| } // namespace ot |
| |
| #endif // TLVS_HPP_ |