/* * 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 the message buffer pool and message buffers. */ #ifndef MESSAGE_HPP_ #define MESSAGE_HPP_ #include "openthread-core-config.h" #include #include #include #include "common/code_utils.hpp" #include "common/encoding.hpp" #include "common/linked_list.hpp" #include "common/locator.hpp" #include "common/non_copyable.hpp" #include "common/pool.hpp" #include "common/type_traits.hpp" #include "mac/mac_types.hpp" #include "thread/child_mask.hpp" #include "thread/link_quality.hpp" /** * This struct represents an opaque (and empty) type for an OpenThread message buffer. * */ struct otMessage { }; namespace ot { namespace Crypto { class Sha256; class HmacSha256; } // namespace Crypto /** * @addtogroup core-message * * @brief * This module includes definitions for the message buffer pool and message buffers. * * @{ * */ /** * This macro frees a given message buffer if not nullptr. * * This macro and the ones that follow contain small but common code patterns used in many of the core modules. They * are intentionally defined as macros instead of inline methods/functions to ensure that they are fully inlined. * Note that an `inline` method/function is not necessarily always inlined by the toolchain and not inlining such * small implementations can add a rather large code-size overhead. * * @param[in] aMessage A pointer to a `Message` to free (can be nullptr). * */ #define FreeMessage(aMessage) \ do \ { \ if ((aMessage) != nullptr) \ { \ (aMessage)->Free(); \ } \ } while (false) /** * This macro frees a given message buffer if a given `Error` indicates an error. * * The parameter @p aMessage can be nullptr in which case this macro does nothing. * * @param[in] aMessage A pointer to a `Message` to free (can be nullptr). * @param[in] aError The `Error` to check. * */ #define FreeMessageOnError(aMessage, aError) \ do \ { \ if (((aError) != kErrorNone) && ((aMessage) != nullptr)) \ { \ (aMessage)->Free(); \ } \ } while (false) /** * This macro frees a given message buffer if a given `Error` indicates an error and sets the `aMessage` to `nullptr`. * * @param[in] aMessage A pointer to a `Message` to free (can be nullptr). * @param[in] aError The `Error` to check. * */ #define FreeAndNullMessageOnError(aMessage, aError) \ do \ { \ if (((aError) != kErrorNone) && ((aMessage) != nullptr)) \ { \ (aMessage)->Free(); \ (aMessage) = nullptr; \ } \ } while (false) constexpr uint16_t kNumBuffers = OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS; constexpr uint16_t kBufferSize = OPENTHREAD_CONFIG_MESSAGE_BUFFER_SIZE; class Message; class MessagePool; class MessageQueue; class PriorityQueue; class ThreadLinkInfo; /** * This structure contains metadata about a Message. * */ struct MessageMetadata { Message * mNext; ///< A pointer to the next Message in a doubly linked list. Message * mPrev; ///< A pointer to the previous Message in a doubly linked list. MessagePool *mMessagePool; ///< Identifies the message pool for this message. union { MessageQueue * mMessage; ///< Identifies the message queue (if any) where this message is queued. PriorityQueue *mPriority; ///< Identifies the priority queue (if any) where this message is queued. } mQueue; ///< Identifies the queue (if any) where this message is queued. uint32_t mDatagramTag; ///< The datagram tag used for 6LoWPAN fragmentation or identification used for IPv6 ///< fragmentation. uint16_t mReserved; ///< Number of header bytes reserved for the message. uint16_t mLength; ///< Number of bytes within the message. uint16_t mOffset; ///< A byte offset within the message. RssAverager mRssAverager; ///< The averager maintaining the received signal strength (RSS) average. #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE LqiAverager mLqiAverager; ///< The averager maintaining the Link quality indicator (LQI) average. #endif ChildMask mChildMask; ///< A ChildMask to indicate which sleepy children need to receive this. uint16_t mMeshDest; ///< Used for unicast non-link-local messages. uint8_t mTimeout; ///< Seconds remaining before dropping the message. union { uint16_t mPanId; ///< Used for MLE Discover Request and Response messages. uint8_t mChannel; ///< Used for MLE Announce. } mPanIdChannel; ///< Used for MLE Discover Request, Response, and Announce messages. uint8_t mType : 3; ///< Identifies the type of message. uint8_t mSubType : 4; ///< Identifies the message sub type. bool mDirectTx : 1; ///< Used to indicate whether a direct transmission is required. bool mLinkSecurity : 1; ///< Indicates whether or not link security is enabled. uint8_t mPriority : 2; ///< Identifies the message priority level (higher value is higher priority). bool mInPriorityQ : 1; ///< Indicates whether the message is queued in normal or priority queue. bool mTxSuccess : 1; ///< Indicates whether the direct tx of the message was successful. bool mDoNotEvict : 1; ///< Indicates whether or not this message may be evicted. bool mMulticastLoop : 1; ///< Indicates whether or not this multicast message may be looped back. #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE bool mTimeSync : 1; ///< Indicates whether the message is also used for time sync purpose. int64_t mNetworkTimeOffset; ///< The time offset to the Thread network time, in microseconds. uint8_t mTimeSyncSeq; ///< The time sync sequence. #endif #if OPENTHREAD_CONFIG_MULTI_RADIO uint8_t mRadioType : 2; ///< The radio link type the message was received on, or should be sent on. bool mIsRadioTypeSet : 1; ///< Indicates whether the radio type is set. static_assert(Mac::kNumRadioTypes <= (1 << 2), "mRadioType bitfield cannot store all radio type values"); #endif }; /** * This class represents a Message buffer. * */ class Buffer : public otMessageBuffer, public LinkedListEntry { friend class Message; friend class LinkedListEntry; public: /** * This method returns a pointer to the next message buffer. * * @returns A pointer to the next message buffer. * */ Buffer *GetNextBuffer(void) { return GetNext(); } /** * This method returns a pointer to the next message buffer. * * @returns A pointer to the next message buffer. * */ const Buffer *GetNextBuffer(void) const { return GetNext(); } /** * This method sets the pointer to the next message buffer. * * @param[in] aNext A pointer to the next buffer. * */ void SetNextBuffer(Buffer *aNext) { SetNext(aNext); } private: /** * This method returns the message metadata in the first message buffer. * * @returns The message metadata structure. * */ MessageMetadata &GetMetadata(void) { return mBuffer.mHead.mMetadata; } /** * This method returns the message metadata in the first message buffer. * * @returns The message metadata structure. * */ const MessageMetadata &GetMetadata(void) const { return mBuffer.mHead.mMetadata; } /** * This method returns a pointer to the first byte of data in the first message buffer. * * @returns A pointer to the first data byte. * */ uint8_t *GetFirstData(void) { return mBuffer.mHead.mData; } /** * This method returns a pointer to the first byte of data in the first message buffer. * * @returns A pointer to the first data byte. * */ const uint8_t *GetFirstData(void) const { return mBuffer.mHead.mData; } /** * This method returns a pointer to the first data byte of a subsequent message buffer. * * @returns A pointer to the first data byte. * */ uint8_t *GetData(void) { return mBuffer.mData; } /** * This method returns a pointer to the first data byte of a subsequent message buffer. * * @returns A pointer to the first data byte. * */ const uint8_t *GetData(void) const { return mBuffer.mData; } static constexpr uint16_t kBufferDataSize = kBufferSize - sizeof(otMessageBuffer); static constexpr uint16_t kHeadBufferDataSize = kBufferDataSize - sizeof(MessageMetadata); protected: union { struct { MessageMetadata mMetadata; uint8_t mData[kHeadBufferDataSize]; } mHead; uint8_t mData[kBufferDataSize]; } mBuffer; }; /** * This class represents a message. * */ class Message : public otMessage, public Buffer { friend class Checksum; friend class Crypto::HmacSha256; friend class Crypto::Sha256; friend class MessagePool; friend class MessageQueue; friend class PriorityQueue; public: /** * This enumeration represents the message type. * */ enum Type : uint8_t { kTypeIp6 = 0, ///< A full uncompressed IPv6 packet kType6lowpan = 1, ///< A 6lowpan frame kTypeSupervision = 2, ///< A child supervision frame. kTypeMacEmptyData = 3, ///< An empty MAC data frame. kTypeOther = 4, ///< Other (data) message. }; /** * This enumeration represents the message sub-type. * */ enum SubType : uint8_t { kSubTypeNone = 0, ///< None kSubTypeMleAnnounce = 1, ///< MLE Announce kSubTypeMleDiscoverRequest = 2, ///< MLE Discover Request kSubTypeMleDiscoverResponse = 3, ///< MLE Discover Response kSubTypeJoinerEntrust = 4, ///< Joiner Entrust kSubTypeMplRetransmission = 5, ///< MPL next retransmission message kSubTypeMleGeneral = 6, ///< General MLE kSubTypeJoinerFinalizeResponse = 7, ///< Joiner Finalize Response kSubTypeMleChildUpdateRequest = 8, ///< MLE Child Update Request kSubTypeMleDataResponse = 9, ///< MLE Data Response kSubTypeMleChildIdRequest = 10, ///< MLE Child ID Request }; enum Priority : uint8_t { kPriorityLow = OT_MESSAGE_PRIORITY_LOW, ///< Low priority level. kPriorityNormal = OT_MESSAGE_PRIORITY_NORMAL, ///< Normal priority level. kPriorityHigh = OT_MESSAGE_PRIORITY_HIGH, ///< High priority level. kPriorityNet = OT_MESSAGE_PRIORITY_HIGH + 1, ///< Network Control priority level. }; static constexpr uint8_t kNumPriorities = 4; ///< Number of priority levels. /** * This enumeration represents the link security mode (used by `Settings` constructor). * */ enum LinkSecurityMode : uint8_t { kNoLinkSecurity, ///< Link security disabled (no link security). kWithLinkSecurity, ///< Link security enabled. }; /** * This enumeration represents the message ownership model when a `Message` instance is passed to a method/function. * */ enum Ownership : uint8_t { /** * This value indicates that the method/function receiving a `Message` instance should take custody of the * message (e.g., the method should `Free()` the message if no longer needed). * */ kTakeCustody, /** * This value indicates that the method/function receiving a `Message` instance does not own the message (e.g., * it should not `Free()` or `Enqueue()` it in a queue). The receiving method/function should create a * copy/clone of the message to keep (if/when needed). * */ kCopyToUse, }; /** * This class represents settings used for creating a new message. * */ class Settings { public: /** * This constructor initializes the Settings object. * * @param[in] aSecurityMode A link security mode. * @param[in] aPriority A message priority. * */ Settings(LinkSecurityMode aSecurityMode, Priority aPriority); /** * This constructor initializes the `Settings` object from a given `otMessageSettings`. * * @param[in] aSettings A pointer to `otMessageSettings` to covert from. If nullptr default settings (link * security enabled with `kPriorityNormal` priority) would be used. * */ explicit Settings(const otMessageSettings *aSettings); /** * This method gets the message priority. * * @returns The message priority. * */ Priority GetPriority(void) const { return mPriority; } /** * This method indicates whether the link security should be enabled. * * @returns TRUE if link security should be enabled, FALSE otherwise. * */ bool IsLinkSecurityEnabled(void) const { return mLinkSecurityEnabled; } /** * This static method returns the default settings with link security enabled and `kPriorityNormal` priority. * * @returns A reference to the default settings (link security enable and `kPriorityNormal` priority). * */ static const Settings &GetDefault(void) { return kDefault; } private: static const Settings kDefault; bool mLinkSecurityEnabled; Priority mPriority; }; /** * This method frees this message buffer. * */ void Free(void); /** * This method returns a pointer to the next message. * * @returns A pointer to the next message in the list or nullptr if at the end of the list. * */ Message *GetNext(void) const; /** * This method returns the number of bytes in the message. * * @returns The number of bytes in the message. * */ uint16_t GetLength(void) const { return GetMetadata().mLength; } /** * This method sets the number of bytes in the message. * * @param[in] aLength Requested number of bytes in the message. * * @retval kErrorNone Successfully set the length of the message. * @retval kErrorNoBufs Failed to grow the size of the message because insufficient buffers were available. * */ Error SetLength(uint16_t aLength); /** * This method returns the number of buffers in the message. * */ uint8_t GetBufferCount(void) const; /** * This method returns the byte offset within the message. * * @returns A byte offset within the message. * */ uint16_t GetOffset(void) const { return GetMetadata().mOffset; } /** * This method moves the byte offset within the message. * * @param[in] aDelta The number of bytes to move the current offset, which may be positive or negative. * */ void MoveOffset(int aDelta); /** * This method sets the byte offset within the message. * * @param[in] aOffset The byte offset within the message. * */ void SetOffset(uint16_t aOffset); /** * This method returns the type of the message. * * @returns The type of the message. * */ Type GetType(void) const { return static_cast(GetMetadata().mType); } /** * This method sets the message type. * * @param[in] aType The message type. * */ void SetType(Type aType) { GetMetadata().mType = aType; } /** * This method returns the sub type of the message. * * @returns The sub type of the message. * */ SubType GetSubType(void) const { return static_cast(GetMetadata().mSubType); } /** * This method sets the message sub type. * * @param[in] aSubType The message sub type. * */ void SetSubType(SubType aSubType) { GetMetadata().mSubType = aSubType; } /** * This method returns whether or not the message is of MLE subtype. * * @retval TRUE If message is of MLE subtype. * @retval FALSE If message is not of MLE subtype. * */ bool IsSubTypeMle(void) const; /** * This method checks whether this multicast message may be looped back. * * @retval TRUE If message may be looped back. * @retval FALSE If message must not be looped back. * */ bool GetMulticastLoop(void) const { return GetMetadata().mMulticastLoop; } /** * This method sets whether multicast may be looped back. * * @param[in] aMulticastLoop Whether allow looping back multicast. * */ void SetMulticastLoop(bool aMulticastLoop) { GetMetadata().mMulticastLoop = aMulticastLoop; } /** * This method returns the message priority level. * * @returns The priority level associated with this message. * */ Priority GetPriority(void) const { return static_cast(GetMetadata().mPriority); } /** * This method sets the messages priority. * If the message is already queued in a priority queue, changing the priority ensures to * update the message in the associated queue. * * @param[in] aPriority The message priority level. * * @retval kErrorNone Successfully set the priority for the message. * @retval kErrorInvalidArgs Priority level is not invalid. * */ Error SetPriority(Priority aPriority); /** * This static method convert a `Priority` to a string. * * @param[in] aPriority The priority level. * * @returns A string representation of @p aPriority. * */ static const char *PriorityToString(Priority aPriority); /** * This method prepends bytes to the front of the message. * * On success, this method grows the message by @p aLength bytes. * * @param[in] aBuf A pointer to a data buffer (can be `nullptr` to grow message without writing bytes). * @param[in] aLength The number of bytes to prepend. * * @retval kErrorNone Successfully prepended the bytes. * @retval kErrorNoBufs Not enough reserved bytes in the message. * */ Error PrependBytes(const void *aBuf, uint16_t aLength); /** * This method prepends an object to the front of the message. * * On success, this method grows the message by the size of the object. * * @tparam ObjectType The object type to prepend to the message. * * @param[in] aObject A reference to the object to prepend to the message. * * @retval kErrorNone Successfully prepended the object. * @retval kErrorNoBufs Not enough reserved bytes in the message. * */ template Error Prepend(const ObjectType &aObject) { static_assert(!TypeTraits::IsPointer::kValue, "ObjectType must not be a pointer"); return PrependBytes(&aObject, sizeof(ObjectType)); } /** * This method removes header bytes from the message. * * @param[in] aLength Number of header bytes to remove. * */ void RemoveHeader(uint16_t aLength); /** * This method appends bytes to the end of the message. * * On success, this method grows the message by @p aLength bytes. * * @param[in] aBuf A pointer to a data buffer (MUST not be `nullptr`). * @param[in] aLength The number of bytes to append. * * @retval kErrorNone Successfully appended the bytes. * @retval kErrorNoBufs Insufficient available buffers to grow the message. * */ Error AppendBytes(const void *aBuf, uint16_t aLength); /** * This method appends bytes read from another or potentially the same message to the end of the current message. * * On success, this method grows the message by @p aLength bytes. * * @param[in] aMessage The message to read the bytes from (it can be the same as the current message). * @param[in] aOffset The offset in @p aMessage to start reading the bytes from. * @param[in] aLength The number of bytes to read from @p aMessage and append. * * @retval kErrorNone Successfully appended the bytes. * @retval kErrorNoBufs Insufficient available buffers to grow the message. * @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 end of the message. * * On success, this method grows the message by the size of the appended object * * @tparam ObjectType The object type to append to the message. * * @param[in] aObject A reference to the object to append to the message. * * @retval kErrorNone Successfully appended the object. * @retval kErrorNoBufs Insufficient available buffers to grow the message. * */ template Error Append(const ObjectType &aObject) { static_assert(!TypeTraits::IsPointer::kValue, "ObjectType must not be a pointer"); return AppendBytes(&aObject, sizeof(ObjectType)); } /** * This method reads bytes from the message. * * @param[in] aOffset Byte offset within the message to begin reading. * @param[out] aBuf A pointer to a data buffer to copy the read bytes into. * @param[in] aLength Number of bytes to read. * * @returns The number of bytes read. * */ uint16_t ReadBytes(uint16_t aOffset, void *aBuf, uint16_t aLength) const; /** * This method reads a given number of bytes from the message. * * If there are fewer bytes available in the message than the requested read length, the available bytes will be * read and copied into @p aBuf. In this case `kErrorParse` will be returned. * * @param[in] aOffset Byte offset within the message to begin reading. * @param[out] aBuf A pointer to a data buffer to copy the read bytes into. * @param[in] aLength Number of bytes to read. * * @retval kErrorNone @p aLength bytes were successfully read from message. * @retval kErrorParse Not enough bytes remaining in message to read the entire object. * */ Error Read(uint16_t aOffset, void *aBuf, uint16_t aLength) const; /** * This method reads an object from the message. * * If there are fewer bytes available in the message than the requested object size, the available bytes will be * read and copied into @p aObject (@p aObject will be read partially). In this case `kErrorParse` will * be returned. * * @tparam ObjectType The object type to read from the message. * * @param[in] aOffset Byte offset within the message to begin reading. * @param[out] aObject A reference to the object to read into. * * @retval kErrorNone Object @p aObject was successfully read from message. * @retval kErrorParse Not enough bytes remaining in message to read the entire object. * */ template Error Read(uint16_t aOffset, ObjectType &aObject) const { static_assert(!TypeTraits::IsPointer::kValue, "ObjectType must not be a pointer"); return Read(aOffset, &aObject, sizeof(ObjectType)); } /** * This method compares the bytes in the message at a given offset with a given byte array. * * If there are fewer bytes available in the message than the requested @p aLength, the comparison is treated as * failure (returns FALSE). * * @param[in] aOffset Byte offset within the message to read from for the comparison. * @param[in] aBuf A pointer to a data buffer to compare with the bytes from message. * @param[in] aLength Number of bytes in @p aBuf. * * @returns TRUE if there are enough bytes available in @p aMessage and they match the bytes from @p aBuf, * FALSE otherwise. * */ bool CompareBytes(uint16_t aOffset, const void *aBuf, uint16_t aLength) const; /** * This method compares the bytes in the message at a given offset with bytes read from another message. * * If either message has fewer bytes available than the requested @p aLength, the comparison is treated as failure * (returns FALSE). * * @param[in] aOffset Byte offset within the message to read from for the comparison. * @param[in] aOtherMessage The other message to compare with. * @param[in] aOtherOffset Byte offset within @p aOtherMessage to read from for the comparison. * @param[in] aLength Number of bytes to compare. * * @returns TRUE if there are enough bytes available in both messages and they all match. FALSE otherwise. * */ bool CompareBytes(uint16_t aOffset, const Message &aOtherMessage, uint16_t aOtherOffset, uint16_t aLength) const; /** * This method compares the bytes in the message at a given offset with an object. * * The bytes in the message are compared with the bytes in @p aObject. If there are fewer bytes available in the * message than the requested object size, it is treated as failed comparison (returns FALSE). * * @tparam ObjectType The object type to compare with the bytes in message. * * @param[in] aOffset Byte offset within the message to read from for the comparison. * @param[in] aObject A reference to the object to compare with the message bytes. * * @returns TRUE if there are enough bytes available in @p aMessage and they match the bytes in @p aObject, * FALSE otherwise. * */ template bool Compare(uint16_t aOffset, const ObjectType &aObject) const { static_assert(!TypeTraits::IsPointer::kValue, "ObjectType must not be a pointer"); return CompareBytes(aOffset, &aObject, sizeof(ObjectType)); } /** * This method writes bytes to the message. * * This method will not resize the message. The given data to write (with @p aLength bytes) MUST fit within the * existing message buffer (from the given offset @p aOffset up to the message's length). * * @param[in] aOffset Byte offset within the message to begin writing. * @param[in] aBuf A pointer to a data buffer. * @param[in] aLength Number of bytes to write. * */ void WriteBytes(uint16_t aOffset, const void *aBuf, uint16_t aLength); /** * This methods writes an object to the message. * * This method will not resize the message. The entire given object (all its bytes) MUST fit within the existing * message buffer (from the given offset @p aOffset up to the message's length). * * @tparam ObjectType The object type to write to the message. * * @param[in] aOffset Byte offset within the message 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)); } /** * This method copies bytes from one message to another. * * If source and destination messages are the same, `CopyTo()` can be used to perform a backward copy, but * it MUST not be used to forward copy within the same message (i.e., when source and destination messages are the * same and source offset is smaller than the destination offset). * * @param[in] aSourceOffset Byte offset within the source message to begin reading. * @param[in] aDestinationOffset Byte offset within the destination message to begin writing. * @param[in] aLength Number of bytes to copy. * @param[in] aMessage Message to copy to. * * @returns The number of bytes copied. * */ uint16_t CopyTo(uint16_t aSourceOffset, uint16_t aDestinationOffset, uint16_t aLength, Message &aMessage) const; /** * This method creates a copy of the message. * * It allocates the new message from the same message pool as the original one and copies @p aLength octets * of the payload. The `Type`, `SubType`, `LinkSecurity`, `Offset`, `InterfaceId`, and `Priority` fields on the * cloned message are also copied from the original one. * * @param[in] aLength Number of payload bytes to copy. * * @returns A pointer to the message or nullptr if insufficient message buffers are available. * */ Message *Clone(uint16_t aLength) const; /** * This method creates a copy of the message. * * It allocates the new message from the same message pool as the original one and copies the entire payload. The * `Type`, `SubType`, `LinkSecurity`, `Offset`, `InterfaceId`, and `Priority` fields on the cloned message are also * copied from the original one. * * @returns A pointer to the message or nullptr if insufficient message buffers are available. * */ Message *Clone(void) const { return Clone(GetLength()); } /** * This method returns the datagram tag used for 6LoWPAN fragmentation or the identification used for IPv6 * fragmentation. * * @returns The 6LoWPAN datagram tag or the IPv6 fragment identification. * */ uint32_t GetDatagramTag(void) const { return GetMetadata().mDatagramTag; } /** * This method sets the datagram tag used for 6LoWPAN fragmentation. * * @param[in] aTag The 6LoWPAN datagram tag. * */ void SetDatagramTag(uint32_t aTag) { GetMetadata().mDatagramTag = aTag; } /** * This method returns whether or not the message forwarding is scheduled for the child. * * @param[in] aChildIndex The index into the child table. * * @retval TRUE If the message is scheduled to be forwarded to the child. * @retval FALSE If the message is not scheduled to be forwarded to the child. * */ bool GetChildMask(uint16_t aChildIndex) const; /** * This method unschedules forwarding of the message to the child. * * @param[in] aChildIndex The index into the child table. * */ void ClearChildMask(uint16_t aChildIndex); /** * This method schedules forwarding of the message to the child. * * @param[in] aChildIndex The index into the child table. * */ void SetChildMask(uint16_t aChildIndex); /** * This method returns whether or not the message forwarding is scheduled for at least one child. * * @retval TRUE If message forwarding is scheduled for at least one child. * @retval FALSE If message forwarding is not scheduled for any child. * */ bool IsChildPending(void) const; /** * This method returns the RLOC16 of the mesh destination. * * @note Only use this for non-link-local unicast messages. * * @returns The IEEE 802.15.4 Destination PAN ID. * */ uint16_t GetMeshDest(void) const { return GetMetadata().mMeshDest; } /** * This method sets the RLOC16 of the mesh destination. * * @note Only use this when sending non-link-local unicast messages. * * @param[in] aMeshDest The IEEE 802.15.4 Destination PAN ID. * */ void SetMeshDest(uint16_t aMeshDest) { GetMetadata().mMeshDest = aMeshDest; } /** * This method returns the IEEE 802.15.4 Destination PAN ID. * * @note Only use this when sending MLE Discover Request or Response messages. * * @returns The IEEE 802.15.4 Destination PAN ID. * */ uint16_t GetPanId(void) const { return GetMetadata().mPanIdChannel.mPanId; } /** * This method sets the IEEE 802.15.4 Destination PAN ID. * * @note Only use this when sending MLE Discover Request or Response messages. * * @param[in] aPanId The IEEE 802.15.4 Destination PAN ID. * */ void SetPanId(uint16_t aPanId) { GetMetadata().mPanIdChannel.mPanId = aPanId; } /** * This method returns the IEEE 802.15.4 Channel to use for transmission. * * @note Only use this when sending MLE Announce messages. * * @returns The IEEE 802.15.4 Channel to use for transmission. * */ uint8_t GetChannel(void) const { return GetMetadata().mPanIdChannel.mChannel; } /** * This method sets the IEEE 802.15.4 Channel to use for transmission. * * @note Only use this when sending MLE Announce messages. * * @param[in] aChannel The IEEE 802.15.4 Channel to use for transmission. * */ void SetChannel(uint8_t aChannel) { GetMetadata().mPanIdChannel.mChannel = aChannel; } /** * This method returns the timeout used for 6LoWPAN reassembly. * * @returns The time remaining in seconds. * */ uint8_t GetTimeout(void) const { return GetMetadata().mTimeout; } /** * This method sets the timeout used for 6LoWPAN reassembly. * * @param[in] aTimeout The timeout value. * */ void SetTimeout(uint8_t aTimeout) { GetMetadata().mTimeout = aTimeout; } /** * This method decrements the timeout. * */ void DecrementTimeout(void) { GetMetadata().mTimeout--; } /** * This method returns whether or not message forwarding is scheduled for direct transmission. * * @retval TRUE If message forwarding is scheduled for direct transmission. * @retval FALSE If message forwarding is not scheduled for direct transmission. * */ bool GetDirectTransmission(void) const { return GetMetadata().mDirectTx; } /** * This method unschedules forwarding using direct transmission. * */ void ClearDirectTransmission(void) { GetMetadata().mDirectTx = false; } /** * This method schedules forwarding using direct transmission. * */ void SetDirectTransmission(void) { GetMetadata().mDirectTx = true; } /** * This method indicates whether the direct transmission of message was successful. * * @retval TRUE If direct transmission of message was successful (all fragments were delivered and acked). * @retval FALSE If direct transmission of message failed (at least one fragment failed). * */ bool GetTxSuccess(void) const { return GetMetadata().mTxSuccess; } /** * This method sets whether the direct transmission of message was successful. * * @param[in] aTxSuccess TRUE if the direct transmission is successful, FALSE otherwise (i.e., at least one * fragment transmission failed). * */ void SetTxSuccess(bool aTxSuccess) { GetMetadata().mTxSuccess = aTxSuccess; } /** * This method indicates whether the message may be evicted. * * @retval TRUE If the message must not be evicted. * @retval FALSE If the message may be evicted. * */ bool GetDoNotEvict(void) const { return GetMetadata().mDoNotEvict; } /** * This method sets whether the message may be evicted. * * @param[in] aDoNotEvict TRUE if the message may not be evicted, FALSE otherwise. * */ void SetDoNotEvict(bool aDoNotEvict) { GetMetadata().mDoNotEvict = aDoNotEvict; } /** * This method indicates whether or not link security is enabled for the message. * * @retval TRUE If link security is enabled. * @retval FALSE If link security is not enabled. * */ bool IsLinkSecurityEnabled(void) const { return GetMetadata().mLinkSecurity; } /** * This method sets whether or not link security is enabled for the message. * * @param[in] aEnabled TRUE if link security is enabled, FALSE otherwise. * */ void SetLinkSecurityEnabled(bool aEnabled) { GetMetadata().mLinkSecurity = aEnabled; } /** * This method updates the average RSS (Received Signal Strength) associated with the message by adding the given * RSS value to the average. Note that a message can be composed of multiple 802.15.4 data frame fragments each * received with a different signal strength. * * @param[in] aRss A new RSS value (in dBm) to be added to average. * */ void AddRss(int8_t aRss) { IgnoreError(GetMetadata().mRssAverager.Add(aRss)); } /** * This method returns the average RSS (Received Signal Strength) associated with the message. * * @returns The current average RSS value (in dBm) or OT_RADIO_RSSI_INVALID if no average is available. * */ int8_t GetAverageRss(void) const { return GetMetadata().mRssAverager.GetAverage(); } /** * This method returns a const reference to RssAverager of the message. * * @returns A const reference to the RssAverager of the message. * */ const RssAverager &GetRssAverager(void) const { return GetMetadata().mRssAverager; } #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE /** * This method updates the average LQI (Link Quality Indicator) associated with the message. * * The given LQI value would be added to the average. Note that a message can be composed of multiple 802.15.4 * frame fragments each received with a different signal strength. * * @param[in] aLQI A new LQI value (has no unit) to be added to average. * */ void AddLqi(uint8_t aLqi) { GetMetadata().mLqiAverager.Add(aLqi); } /** * This method returns the average LQI (Link Quality Indicator) associated with the message. * * @returns The current average LQI value (in dBm) or OT_RADIO_LQI_NONE if no average is available. * */ uint8_t GetAverageLqi(void) const { return GetMetadata().mLqiAverager.GetAverage(); } /** * This mehod returns the count of frames counted so far. * * @retruns The count of frames that have been counted. * */ uint8_t GetPsduCount(void) const { return GetMetadata().mLqiAverager.GetCount(); } #endif /** * This method sets the message's link info properties (PAN ID, link security, RSS) from a given `ThreadLinkInfo`. * * @param[in] aLinkInfo The `ThreadLinkInfo` instance from which to set message's related properties. * */ void SetLinkInfo(const ThreadLinkInfo &aLinkInfo); /** * This method returns a pointer to the message queue (if any) where this message is queued. * * @returns A pointer to the message queue or nullptr if not in any message queue. * */ MessageQueue *GetMessageQueue(void) const { return (!GetMetadata().mInPriorityQ) ? GetMetadata().mQueue.mMessage : nullptr; } /** * This method returns a pointer to the priority message queue (if any) where this message is queued. * * @returns A pointer to the priority queue or nullptr if not in any priority queue. * */ PriorityQueue *GetPriorityQueue(void) const { return (GetMetadata().mInPriorityQ) ? GetMetadata().mQueue.mPriority : nullptr; } /** * This method indicates whether or not the message is also used for time sync purpose. * * When OPENTHREAD_CONFIG_TIME_SYNC_ENABLE is 0, this method always return false. * * @retval TRUE If the message is also used for time sync purpose. * @retval FALSE If the message is not used for time sync purpose. * */ bool IsTimeSync(void) const; #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE /** * This method sets whether or not the message is also used for time sync purpose. * * @param[in] aEnabled TRUE if the message is also used for time sync purpose, FALSE otherwise. * */ void SetTimeSync(bool aEnabled) { GetMetadata().mTimeSync = aEnabled; } /** * This method sets the offset to network time. * * @param[in] aNetworkTimeOffset The offset to network time. * */ void SetNetworkTimeOffset(int64_t aNetworkTimeOffset) { GetMetadata().mNetworkTimeOffset = aNetworkTimeOffset; } /** * This method gets the offset to network time. * * @returns The offset to network time. * */ int64_t GetNetworkTimeOffset(void) const { return GetMetadata().mNetworkTimeOffset; } /** * This method sets the time sync sequence. * * @param[in] aTimeSyncSeq The time sync sequence. * */ void SetTimeSyncSeq(uint8_t aTimeSyncSeq) { GetMetadata().mTimeSyncSeq = aTimeSyncSeq; } /** * This method gets the time sync sequence. * * @returns The time sync sequence. * */ uint8_t GetTimeSyncSeq(void) const { return GetMetadata().mTimeSyncSeq; } #endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE #if OPENTHREAD_CONFIG_MULTI_RADIO /** * This method indicates whether the radio type is set. * * @retval TRUE If the radio type is set. * @retval FALSE If the radio type is not set. * */ bool IsRadioTypeSet(void) const { return GetMetadata().mIsRadioTypeSet; } /** * This method gets the radio link type the message was received on, or should be sent on. * * This method should be used only when `IsRadioTypeSet()` returns `true`. * * @returns The radio link type of the message. * */ Mac::RadioType GetRadioType(void) const { return static_cast(GetMetadata().mRadioType); } /** * This method sets the radio link type the message was received on, or should be sent on. * * @param[in] aRadioType A radio link type of the message. * */ void SetRadioType(Mac::RadioType aRadioType) { GetMetadata().mIsRadioTypeSet = true; GetMetadata().mRadioType = aRadioType; } /** * This method clears any previously set radio type on the message. * * After calling this method, `IsRadioTypeSet()` returns false until radio type is set (`SetRadioType()`). * */ void ClearRadioType(void) { GetMetadata().mIsRadioTypeSet = false; } #endif // #if OPENTHREAD_CONFIG_MULTI_RADIO private: /** * This method returns a pointer to the message pool to which this message belongs * * @returns A pointer to the message pool. * */ MessagePool *GetMessagePool(void) const { return GetMetadata().mMessagePool; } /** * This method sets the message pool this message to which this message belongs. * * @param[in] aMessagePool A pointer to the message pool * */ void SetMessagePool(MessagePool *aMessagePool) { GetMetadata().mMessagePool = aMessagePool; } /** * This method returns `true` if the message is enqueued in any queue (`MessageQueue` or `PriorityQueue`). * * @returns `true` if the message is in any queue, `false` otherwise. * */ bool IsInAQueue(void) const { return (GetMetadata().mQueue.mMessage != nullptr); } /** * This method sets the message queue information for the message. * * @param[in] aMessageQueue A pointer to the message queue where this message is queued. * */ void SetMessageQueue(MessageQueue *aMessageQueue); /** * This method sets the message queue information for the message. * * @param[in] aPriorityQueue A pointer to the priority queue where this message is queued. * */ void SetPriorityQueue(PriorityQueue *aPriorityQueue); /** * This method returns a reference to the `mNext` pointer. * * @returns A reference to the mNext pointer. * */ Message *&Next(void) { return GetMetadata().mNext; } /** * This method returns a reference to the `mNext` pointer (const pointer). * * * @returns A reference to the mNext pointer. * */ Message *const &Next(void) const { return GetMetadata().mNext; } /** * This method returns a reference to the `mPrev` pointer. * * @returns A reference to the mPrev pointer. * */ Message *&Prev(void) { return GetMetadata().mPrev; } /** * This method returns the number of reserved header bytes. * * @returns The number of reserved header bytes. * */ uint16_t GetReserved(void) const { return GetMetadata().mReserved; } /** * This method sets the number of reserved header bytes. * * @param[in] aReservedHeader The number of header bytes to reserve. * */ void SetReserved(uint16_t aReservedHeader) { GetMetadata().mReserved = aReservedHeader; } /** * This method adds or frees message buffers to meet the requested length. * * @param[in] aLength The number of bytes that the message buffer needs to handle. * * @retval kErrorNone Successfully resized the message. * @retval kErrorNoBufs Could not grow the message due to insufficient available message buffers. * */ Error ResizeMessage(uint16_t aLength); private: struct Chunk { const uint8_t *GetData(void) const { return mData; } uint16_t GetLength(void) const { return mLength; } const uint8_t *mData; // Pointer to start of chunk data buffer. uint16_t mLength; // Length of chunk data (in bytes). const Buffer * mBuffer; // Buffer containing the chunk }; struct WritableChunk : public Chunk { uint8_t *GetData(void) const { return const_cast(mData); } }; void GetFirstChunk(uint16_t aOffset, uint16_t &aLength, Chunk &chunk) const; void GetNextChunk(uint16_t &aLength, Chunk &aChunk) const; void GetFirstChunk(uint16_t aOffset, uint16_t &aLength, WritableChunk &aChunk) { const_cast(this)->GetFirstChunk(aOffset, aLength, static_cast(aChunk)); } void GetNextChunk(uint16_t &aLength, WritableChunk &aChunk) { const_cast(this)->GetNextChunk(aLength, static_cast(aChunk)); } }; /** * This class implements a message queue. * */ class MessageQueue : public otMessageQueue { friend class Message; friend class PriorityQueue; public: /** * This enumeration represents a position (head or tail) in the queue. This is used to specify where a new message * should be added in the queue. * */ enum QueuePosition : uint8_t { kQueuePositionHead, ///< Indicates the head (front) of the list. kQueuePositionTail, ///< Indicates the tail (end) of the list. }; /** * This constructor initializes the message queue. * */ MessageQueue(void); /** * This method returns a pointer to the first message. * * @returns A pointer to the first message. * */ Message *GetHead(void) const; /** * This method adds a message to the end of the list. * * @param[in] aMessage The message to add. * */ void Enqueue(Message &aMessage) { Enqueue(aMessage, kQueuePositionTail); } /** * This method adds a message at a given position (head/tail) of the list. * * @param[in] aMessage The message to add. * @param[in] aPosition The position (head or tail) where to add the message. * */ void Enqueue(Message &aMessage, QueuePosition aPosition); /** * This method removes a message from the list. * * @param[in] aMessage The message to remove. * */ void Dequeue(Message &aMessage); /** * This method removes a message from the queue and frees it. * * @param[in] aMessage The message to remove and free. * */ void DequeueAndFree(Message &aMessage); /** * This method removes and frees all messages from the queue. * */ void DequeueAndFreeAll(void); /** * This method returns the number of messages and buffers enqueued. * * @param[out] aMessageCount Returns the number of messages enqueued. * @param[out] aBufferCount Returns the number of buffers enqueued. * */ void GetInfo(uint16_t &aMessageCount, uint16_t &aBufferCount) const; private: /** * This method returns the tail of the list (last message in the list) * * @returns A pointer to the tail of the list. * */ Message *GetTail(void) const { return static_cast(mData); } /** * This method set the tail of the list. * * @param[in] aMessage A pointer to the message to set as new tail. * */ void SetTail(Message *aMessage) { mData = aMessage; } }; /** * This class implements a priority queue. * */ class PriorityQueue { friend class Message; friend class MessageQueue; friend class MessagePool; public: /** * This constructor initializes the priority queue. * */ PriorityQueue(void); /** * This method returns a pointer to the first message. * * @returns A pointer to the first message. * */ Message *GetHead(void) const; /** * This method returns a pointer to the first message for a given priority level. * * @param[in] aPriority Priority level. * * @returns A pointer to the first message with given priority level or nullptr if there is no messages with * this priority level. * */ Message *GetHeadForPriority(Message::Priority aPriority) const; /** * This method adds a message to the queue. * * @param[in] aMessage The message to add. * */ void Enqueue(Message &aMessage); /** * This method removes a message from the list. * * @param[in] aMessage The message to remove. * */ void Dequeue(Message &aMessage); /** * This method removes a message from the queue and frees it. * * @param[in] aMessage The message to remove and free. * */ void DequeueAndFree(Message &aMessage); /** * This method removes and frees all messages from the queue. * */ void DequeueAndFreeAll(void); /** * This method returns the number of messages and buffers enqueued. * * @param[out] aMessageCount Returns the number of messages enqueued. * @param[out] aBufferCount Returns the number of buffers enqueued. * */ void GetInfo(uint16_t &aMessageCount, uint16_t &aBufferCount) const; /** * This method returns the tail of the list (last message in the list) * * @returns A pointer to the tail of the list. * */ Message *GetTail(void) const; private: /** * This method increases (moves forward) the given priority while ensuring to wrap from * priority value `kNumPriorities` -1 back to 0. * * @param[in] aPriority A given priority level * * @returns Increased/Moved forward priority level */ uint8_t PrevPriority(uint8_t aPriority) const { return (aPriority == Message::kNumPriorities - 1) ? 0 : (aPriority + 1); } /** * This private method finds the first non-nullptr tail starting from the given priority level and moving forward. * It wraps from priority value `kNumPriorities` -1 back to 0. * * aStartPriorityLevel Starting priority level. * * @returns The first non-nullptr tail pointer, or nullptr if all the * */ Message *FindFirstNonNullTail(Message::Priority aStartPriorityLevel) const; private: Message *mTails[Message::kNumPriorities]; ///< Tail pointers associated with different priority levels. }; /** * This class represents a message pool * */ class MessagePool : public InstanceLocator, private NonCopyable { friend class Message; friend class MessageQueue; friend class PriorityQueue; public: /** * This constructor initializes the object. * */ explicit MessagePool(Instance &aInstance); /** * This method is used to obtain a new message. * * The link security is enabled by default on the newly obtained message. * * @param[in] aType The message type. * @param[in] aReserveHeader The number of header bytes to reserve. * @param[in] aPriority The priority level of the message. * * @returns A pointer to the message or nullptr if no message buffers are available. * */ Message *New(Message::Type aType, uint16_t aReserveHeader, Message::Priority aPriority); public: /** * This method is used to obtain a new message with specified settings. * * @param[in] aType The message type. * @param[in] aReserveHeader The number of header bytes to reserve. * @param[in] aSettings The message settings. * * @returns A pointer to the message or nullptr if no message buffers are available. * */ Message *New(Message::Type aType, uint16_t aReserveHeader, const Message::Settings &aSettings = Message::Settings::GetDefault()); /** * This method is used to free a message and return all message buffers to the buffer pool. * * @param[in] aMessage The message to free. * */ void Free(Message *aMessage); /** * This method returns the number of free buffers. * * @returns The number of free buffers. * */ uint16_t GetFreeBufferCount(void) const; /** * This method returns the total number of buffers. * * @returns The total number of buffers. * */ uint16_t GetTotalBufferCount(void) const; private: Buffer *NewBuffer(Message::Priority aPriority); void FreeBuffers(Buffer *aBuffer); Error ReclaimBuffers(Message::Priority aPriority); #if !OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT && !OPENTHREAD_CONFIG_MESSAGE_USE_HEAP_ENABLE uint16_t mNumFreeBuffers; Pool mBufferPool; #endif }; /** * @} * */ } // namespace ot #endif // MESSAGE_HPP_