/*
 *  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 MLE functionality required by the Thread Child, Router, and Leader roles.
 */

#ifndef MLE_HPP_
#define MLE_HPP_

#include "openthread-core-config.h"

#include "common/encoding.hpp"
#include "common/locator.hpp"
#include "common/log.hpp"
#include "common/non_copyable.hpp"
#include "common/notifier.hpp"
#include "common/timer.hpp"
#include "crypto/aes_ccm.hpp"
#include "mac/mac.hpp"
#include "meshcop/joiner_router.hpp"
#include "meshcop/meshcop.hpp"
#include "net/udp6.hpp"
#include "thread/link_metrics.hpp"
#include "thread/link_metrics_tlvs.hpp"
#include "thread/mle_tlvs.hpp"
#include "thread/mle_types.hpp"
#include "thread/neighbor_table.hpp"
#include "thread/network_data_types.hpp"
#include "thread/topology.hpp"

namespace ot {

/**
 * @addtogroup core-mle MLE
 *
 * @brief
 *   This module includes definitions for the MLE protocol.
 *
 * @{
 *
 * @defgroup core-mle-core Core
 * @defgroup core-mle-router Router
 * @defgroup core-mle-tlvs TLVs
 *
 * @}
 */

/**
 * @namespace ot::Mle
 *
 * @brief
 *   This namespace includes definitions for the MLE protocol.
 */

namespace Mle {

/**
 * @addtogroup core-mle-core
 *
 * @brief
 *   This module includes definitions for MLE functionality required by the Thread Child, Router, and Leader roles.
 *
 * @{
 *
 */

/**
 * This class implements MLE functionality required by the Thread EndDevices, Router, and Leader roles.
 *
 */
class Mle : public InstanceLocator, private NonCopyable
{
    friend class DiscoverScanner;
    friend class ot::Notifier;
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
    friend class ot::LinkMetrics::LinkMetrics;
#endif

public:
    /**
     * This constructor initializes the MLE object.
     *
     * @param[in]  aInstance     A reference to the OpenThread instance.
     *
     */
    explicit Mle(Instance &aInstance);

    /**
     * This method enables MLE.
     *
     * @retval kErrorNone     Successfully enabled MLE.
     * @retval kErrorAlready  MLE was already enabled.
     *
     */
    Error Enable(void);

    /**
     * This method disables MLE.
     *
     * @retval kErrorNone     Successfully disabled MLE.
     *
     */
    Error Disable(void);

    /**
     * This method starts the MLE protocol operation.
     *
     * @retval kErrorNone           Successfully started the protocol operation.
     * @retval kErrorInvalidState   IPv6 interface is down or device is in raw-link mode.
     *
     */
    Error Start(void) { return Start(kNormalAttach); }

    /**
     * This method stops the MLE protocol operation.
     *
     */
    void Stop(void) { Stop(kUpdateNetworkDatasets); }

    /**
     * This method restores network information from non-volatile memory (if any).
     *
     */
    void Restore(void);

    /**
     * This method stores network information into non-volatile memory.
     *
     * @retval kErrorNone      Successfully store the network information.
     * @retval kErrorNoBufs    Could not store the network information due to insufficient memory space.
     *
     */
    Error Store(void);

    /**
     * This method generates an MLE Announce message.
     *
     * @param[in]  aChannel        The channel to use when transmitting.
     *
     */
    void SendAnnounce(uint8_t aChannel) { SendAnnounce(aChannel, kNormalAnnounce); }

    /**
     * This method causes the Thread interface to detach from the Thread network.
     *
     * @retval kErrorNone          Successfully detached from the Thread network.
     * @retval kErrorInvalidState  MLE is Disabled.
     *
     */
    Error BecomeDetached(void);

    /**
     * This method causes the Thread interface to attempt an MLE attach.
     *
     * @retval kErrorNone          Successfully began the attach process.
     * @retval kErrorInvalidState  MLE is Disabled.
     * @retval kErrorBusy          An attach process is in progress.
     *
     */
    Error BecomeChild(void);

    /**
     * This function notifies other nodes in the network (if any) and then stops Thread protocol operation.
     *
     * It sends an Address Release if it's a router, or sets its child timeout to 0 if it's a child.
     *
     * @param[in] aCallback A pointer to a function that is called upon finishing detaching.
     * @param[in] aContext  A pointer to callback application-specific context.
     *
     * @retval kErrorNone   Successfully started detaching.
     * @retval kErrorBusy   Detaching is already in progress.
     *
     */
    Error DetachGracefully(otDetachGracefullyCallback aCallback, void *aContext);

    /**
     * This method indicates whether or not the Thread device is attached to a Thread network.
     *
     * @retval TRUE   Attached to a Thread network.
     * @retval FALSE  Not attached to a Thread network.
     *
     */
    bool IsAttached(void) const;

    /**
     * This method indicates whether device is currently attaching or not.
     *
     * Note that an already attached device may also be in attaching state. Examples of this include a leader/router
     * trying to attach to a better partition, or a child trying to find a better parent (when feature
     * `OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE` is enabled).
     *
     * @retval TRUE   Device is currently trying to attach.
     * @retval FALSE  Device is not in middle of attach process.
     *
     */
    bool IsAttaching(void) const { return (mAttachState != kAttachStateIdle); }

    /**
     * This method returns the current Thread device role.
     *
     * @returns The current Thread device role.
     *
     */
    DeviceRole GetRole(void) const { return mRole; }

    /**
     * This method indicates whether device role is disabled.
     *
     * @retval TRUE   Device role is disabled.
     * @retval FALSE  Device role is not disabled.
     *
     */
    bool IsDisabled(void) const { return (mRole == kRoleDisabled); }

    /**
     * This method indicates whether device role is detached.
     *
     * @retval TRUE   Device role is detached.
     * @retval FALSE  Device role is not detached.
     *
     */
    bool IsDetached(void) const { return (mRole == kRoleDetached); }

    /**
     * This method indicates whether device role is child.
     *
     * @retval TRUE   Device role is child.
     * @retval FALSE  Device role is not child.
     *
     */
    bool IsChild(void) const { return (mRole == kRoleChild); }

    /**
     * This method indicates whether device role is router.
     *
     * @retval TRUE   Device role is router.
     * @retval FALSE  Device role is not router.
     *
     */
    bool IsRouter(void) const { return (mRole == kRoleRouter); }

    /**
     * This method indicates whether device role is leader.
     *
     * @retval TRUE   Device role is leader.
     * @retval FALSE  Device role is not leader.
     *
     */
    bool IsLeader(void) const { return (mRole == kRoleLeader); }

    /**
     * This method indicates whether device role is either router or leader.
     *
     * @retval TRUE   Device role is either router or leader.
     * @retval FALSE  Device role is neither router nor leader.
     *
     */
    bool IsRouterOrLeader(void) const;

    /**
     * This method returns the Device Mode as reported in the Mode TLV.
     *
     * @returns The Device Mode as reported in the Mode TLV.
     *
     */
    DeviceMode GetDeviceMode(void) const { return mDeviceMode; }

    /**
     * This method sets the Device Mode as reported in the Mode TLV.
     *
     * @param[in]  aDeviceMode  The device mode to set.
     *
     * @retval kErrorNone         Successfully set the Mode TLV.
     * @retval kErrorInvalidArgs  The mode combination specified in @p aMode is invalid.
     *
     */
    Error SetDeviceMode(DeviceMode aDeviceMode);

    /**
     * This method indicates whether or not the device is rx-on-when-idle.
     *
     * @returns TRUE if rx-on-when-idle, FALSE otherwise.
     *
     */
    bool IsRxOnWhenIdle(void) const { return mDeviceMode.IsRxOnWhenIdle(); }

    /**
     * This method indicates whether or not the device is a Full Thread Device.
     *
     * @returns TRUE if a Full Thread Device, FALSE otherwise.
     *
     */
    bool IsFullThreadDevice(void) const { return mDeviceMode.IsFullThreadDevice(); }

    /**
     * This method indicates whether or not the device is a Minimal End Device.
     *
     * @returns TRUE if the device is a Minimal End Device, FALSE otherwise.
     *
     */
    bool IsMinimalEndDevice(void) const { return mDeviceMode.IsMinimalEndDevice(); }

    /**
     * This method gets the Network Data type (full set or stable subset) that this device requests.
     *
     * @returns The Network Data type requested by this device.
     *
     */
    NetworkData::Type GetNetworkDataType(void) const { return mDeviceMode.GetNetworkDataType(); }

    /**
     * This method returns a pointer to the Mesh Local Prefix.
     *
     * @returns A reference to the Mesh Local Prefix.
     *
     */
    const Ip6::NetworkPrefix &GetMeshLocalPrefix(void) const { return mMeshLocal16.GetAddress().GetPrefix(); }

    /**
     * This method sets the Mesh Local Prefix.
     *
     * @param[in]  aMeshLocalPrefix  A reference to the Mesh Local Prefix.
     *
     */
    void SetMeshLocalPrefix(const Ip6::NetworkPrefix &aMeshLocalPrefix);

#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
    /**
     * This method sets the Mesh Local IID.
     *
     * Available only when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled.
     *
     * @param[in] aMlIid  The Mesh Local IID.
     *
     * @retval kErrorNone           Successfully configured Mesh Local IID.
     * @retval kErrorInvalidState   If the Thread stack is already enabled.
     *
     */
    Error SetMeshLocalIid(const Ip6::InterfaceIdentifier &aMlIid);
#endif

    /**
     * This method applies the Mesh Local Prefix.
     *
     */
    void ApplyMeshLocalPrefix(void);

    /**
     * This method returns a reference to the Thread link-local address.
     *
     * The Thread link local address is derived using IEEE802.15.4 Extended Address as Interface Identifier.
     *
     * @returns A reference to the Thread link local address.
     *
     */
    const Ip6::Address &GetLinkLocalAddress(void) const { return mLinkLocal64.GetAddress(); }

    /**
     * This method updates the link local address.
     *
     * Call this method when the IEEE 802.15.4 Extended Address has changed.
     *
     */
    void UpdateLinkLocalAddress(void);

    /**
     * This method returns a reference to the link-local all Thread nodes multicast address.
     *
     * @returns A reference to the link-local all Thread nodes multicast address.
     *
     */
    const Ip6::Address &GetLinkLocalAllThreadNodesAddress(void) const { return mLinkLocalAllThreadNodes.GetAddress(); }

    /**
     * This method returns a reference to the realm-local all Thread nodes multicast address.
     *
     * @returns A reference to the realm-local all Thread nodes multicast address.
     *
     */
    const Ip6::Address &GetRealmLocalAllThreadNodesAddress(void) const
    {
        return mRealmLocalAllThreadNodes.GetAddress();
    }

    /**
     * This method gets the parent when operating in End Device mode.
     *
     * @returns A reference to the parent.
     *
     */
    Router &GetParent(void) { return mParent; }

    /**
     * This method get the parent candidate.
     *
     * The parent candidate is valid when attempting to attach to a new parent.
     *
     */
    Router &GetParentCandidate(void) { return mParentCandidate; }

    /**
     * This method indicates whether or not an IPv6 address is an RLOC.
     *
     * @retval TRUE   If @p aAddress is an RLOC.
     * @retval FALSE  If @p aAddress is not an RLOC.
     *
     */
    bool IsRoutingLocator(const Ip6::Address &aAddress) const;

    /**
     * This method indicates whether or not an IPv6 address is an ALOC.
     *
     * @retval TRUE   If @p aAddress is an ALOC.
     * @retval FALSE  If @p aAddress is not an ALOC.
     *
     */
    bool IsAnycastLocator(const Ip6::Address &aAddress) const;

    /**
     * This method indicates whether or not an IPv6 address is a Mesh Local Address.
     *
     * @retval TRUE   If @p aAddress is a Mesh Local Address.
     * @retval FALSE  If @p aAddress is not a Mesh Local Address.
     *
     */
    bool IsMeshLocalAddress(const Ip6::Address &aAddress) const;

    /**
     * This method returns the MLE Timeout value.
     *
     * @returns The MLE Timeout value in seconds.
     *
     */
    uint32_t GetTimeout(void) const { return mTimeout; }

    /**
     * This method sets the MLE Timeout value.
     *
     * @param[in]  aTimeout  The Timeout value in seconds.
     *
     */
    void SetTimeout(uint32_t aTimeout);

    /**
     * This method returns the RLOC16 assigned to the Thread interface.
     *
     * @returns The RLOC16 assigned to the Thread interface.
     *
     */
    uint16_t GetRloc16(void) const;

    /**
     * This method returns a reference to the RLOC assigned to the Thread interface.
     *
     * @returns A reference to the RLOC assigned to the Thread interface.
     *
     */
    const Ip6::Address &GetMeshLocal16(void) const { return mMeshLocal16.GetAddress(); }

    /**
     * This method returns a reference to the ML-EID assigned to the Thread interface.
     *
     * @returns A reference to the ML-EID assigned to the Thread interface.
     *
     */
    const Ip6::Address &GetMeshLocal64(void) const { return mMeshLocal64.GetAddress(); }

    /**
     * This method returns the Router ID of the Leader.
     *
     * @returns The Router ID of the Leader.
     *
     */
    uint8_t GetLeaderId(void) const { return mLeaderData.GetLeaderRouterId(); }

    /**
     * This method retrieves the Leader's RLOC.
     *
     * @param[out]  aAddress  A reference to the Leader's RLOC.
     *
     * @retval kErrorNone      Successfully retrieved the Leader's RLOC.
     * @retval kErrorDetached  The Thread interface is not currently attached to a Thread Partition.
     *
     */
    Error GetLeaderAddress(Ip6::Address &aAddress) const;

    /**
     * This method retrieves the Leader's ALOC.
     *
     * @param[out]  aAddress  A reference to the Leader's ALOC.
     *
     * @retval kErrorNone      Successfully retrieved the Leader's ALOC.
     * @retval kErrorDetached  The Thread interface is not currently attached to a Thread Partition.
     *
     */
    Error GetLeaderAloc(Ip6::Address &aAddress) const { return GetLocatorAddress(aAddress, kAloc16Leader); }

    /**
     * This method computes the Commissioner's ALOC.
     *
     * @param[out]  aAddress        A reference to the Commissioner's ALOC.
     * @param[in]   aSessionId      Commissioner session id.
     *
     * @retval kErrorNone      Successfully retrieved the Commissioner's ALOC.
     * @retval kErrorDetached  The Thread interface is not currently attached to a Thread Partition.
     *
     */
    Error GetCommissionerAloc(Ip6::Address &aAddress, uint16_t aSessionId) const
    {
        return GetLocatorAddress(aAddress, CommissionerAloc16FromId(aSessionId));
    }

    /**
     * This method retrieves the Service ALOC for given Service ID.
     *
     * @param[in]   aServiceId Service ID to get ALOC for.
     * @param[out]  aAddress   A reference to the Service ALOC.
     *
     * @retval kErrorNone      Successfully retrieved the Service ALOC.
     * @retval kErrorDetached  The Thread interface is not currently attached to a Thread Partition.
     *
     */
    Error GetServiceAloc(uint8_t aServiceId, Ip6::Address &aAddress) const;

    /**
     * This method returns the most recently received Leader Data.
     *
     * @returns  A reference to the most recently received Leader Data.
     *
     */
    const LeaderData &GetLeaderData(void);

    /**
     * This method derives the Child ID from a given RLOC16.
     *
     * @param[in]  aRloc16  The RLOC16 value.
     *
     * @returns The Child ID portion of an RLOC16.
     *
     */
    static uint16_t ChildIdFromRloc16(uint16_t aRloc16) { return aRloc16 & kMaxChildId; }

    /**
     * This method derives the Router ID portion from a given RLOC16.
     *
     * @param[in]  aRloc16  The RLOC16 value.
     *
     * @returns The Router ID portion of an RLOC16.
     *
     */
    static uint8_t RouterIdFromRloc16(uint16_t aRloc16) { return aRloc16 >> kRouterIdOffset; }

    /**
     * This method returns whether the two RLOC16 have the same Router ID.
     *
     * @param[in]  aRloc16A  The first RLOC16 value.
     * @param[in]  aRloc16B  The second RLOC16 value.
     *
     * @returns true if the two RLOC16 have the same Router ID, false otherwise.
     *
     */
    static bool RouterIdMatch(uint16_t aRloc16A, uint16_t aRloc16B)
    {
        return RouterIdFromRloc16(aRloc16A) == RouterIdFromRloc16(aRloc16B);
    }

    /**
     * This method returns the Service ID corresponding to a Service ALOC16.
     *
     * @param[in]  aAloc16  The Service ALOC16 value.
     *
     * @returns The Service ID corresponding to given ALOC16.
     *
     */
    static uint8_t ServiceIdFromAloc(uint16_t aAloc16) { return static_cast<uint8_t>(aAloc16 - kAloc16ServiceStart); }

    /**
     * This method returns the Service ALOC16 corresponding to a Service ID.
     *
     * @param[in]  aServiceId  The Service ID value.
     *
     * @returns The Service ALOC16 corresponding to given ID.
     *
     */
    static uint16_t ServiceAlocFromId(uint8_t aServiceId)
    {
        return static_cast<uint16_t>(aServiceId + kAloc16ServiceStart);
    }

    /**
     * This method returns the Commissioner Aloc corresponding to a Commissioner Session ID.
     *
     * @param[in]  aSessionId   The Commissioner Session ID value.
     *
     * @returns The Commissioner ALOC16 corresponding to given ID.
     *
     */
    static uint16_t CommissionerAloc16FromId(uint16_t aSessionId)
    {
        return static_cast<uint16_t>((aSessionId & kAloc16CommissionerMask) + kAloc16CommissionerStart);
    }

    /**
     * This method derives RLOC16 from a given Router ID.
     *
     * @param[in]  aRouterId  The Router ID value.
     *
     * @returns The RLOC16 corresponding to the given Router ID.
     *
     */
    static uint16_t Rloc16FromRouterId(uint8_t aRouterId)
    {
        return static_cast<uint16_t>(aRouterId << kRouterIdOffset);
    }

    /**
     * This method indicates whether or not @p aRloc16 refers to an active router.
     *
     * @param[in]  aRloc16  The RLOC16 value.
     *
     * @retval TRUE   If @p aRloc16 refers to an active router.
     * @retval FALSE  If @p aRloc16 does not refer to an active router.
     *
     */
    static bool IsActiveRouter(uint16_t aRloc16) { return ChildIdFromRloc16(aRloc16) == 0; }

    /**
     * This method returns a reference to the send queue.
     *
     * @returns A reference to the send queue.
     *
     */
    const MessageQueue &GetMessageQueue(void) const { return mDelayedResponses; }

    /**
     * This method frees multicast MLE Data Response from Delayed Message Queue if any.
     *
     */
    void RemoveDelayedDataResponseMessage(void);

    /**
     * This method converts a device role into a human-readable string.
     *
     */
    static const char *RoleToString(DeviceRole aRole);

    /**
     * This method gets the MLE counters.
     *
     * @returns A reference to the MLE counters.
     *
     */
    const otMleCounters &GetCounters(void) const { return mCounters; }

    /**
     * This method resets the MLE counters.
     *
     */
    void ResetCounters(void) { memset(&mCounters, 0, sizeof(mCounters)); }

    /**
     * This function registers the client callback that is called when processing an MLE Parent Response message.
     *
     * @param[in]  aCallback A pointer to a function that is called to deliver MLE Parent Response data.
     * @param[in]  aContext  A pointer to application-specific context.
     *
     */
    void RegisterParentResponseStatsCallback(otThreadParentResponseCallback aCallback, void *aContext);

    /**
     * This method requests MLE layer to prepare and send a shorter version of Child ID Request message by only
     * including the mesh-local IPv6 address in the Address Registration TLV.
     *
     * This method should be called when a previous MLE Child ID Request message would require fragmentation at 6LoWPAN
     * layer.
     *
     */
    void RequestShorterChildIdRequest(void);

    /**
     * This method gets the RLOC or ALOC of a given RLOC16 or ALOC16.
     *
     * @param[out]  aAddress  A reference to the RLOC or ALOC.
     * @param[in]   aLocator  RLOC16 or ALOC16.
     *
     * @retval kErrorNone      If got the RLOC or ALOC successfully.
     * @retval kErrorDetached  If device is detached.
     *
     */
    Error GetLocatorAddress(Ip6::Address &aAddress, uint16_t aLocator) const;

    /**
     * This method schedules a Child Update Request.
     *
     */
    void ScheduleChildUpdateRequest(void);

    /*
     * This method indicates whether or not the device has restored the network information from
     * non-volatile settings after boot.
     *
     * @retval true  Successfully restored the network information.
     * @retval false No valid network information was found.
     *
     */
    bool HasRestored(void) const { return mHasRestored; }

#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
    /**
     * This method gets the CSL timeout.
     *
     * @returns CSL timeout
     *
     */
    uint32_t GetCslTimeout(void) const { return mCslTimeout; }

    /**
     * This method sets the CSL timeout.
     *
     * @param[in]  aTimeout  The CSL timeout in seconds.
     *
     */
    void SetCslTimeout(uint32_t aTimeout);

    /**
     * This method calculates CSL metric of parent.
     *
     * @param[in] aCslClockAccuracy The CSL Clock Accuracy.
     * @param[in] aCslUncertainty The CSL Uncertainty.
     *
     * @returns CSL metric.
     */
    uint64_t CalcParentCslMetric(uint8_t aCslClockAccuracy, uint8_t aCslUncertainty);

#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE

protected:
    /**
     * MLE Command Types.
     *
     */
    enum Command : uint8_t
    {
        kCommandLinkRequest                   = 0,  ///< Link Request
        kCommandLinkAccept                    = 1,  ///< Link Accept
        kCommandLinkAcceptAndRequest          = 2,  ///< Link Accept and Reject
        kCommandLinkReject                    = 3,  ///< Link Reject
        kCommandAdvertisement                 = 4,  ///< Advertisement
        kCommandUpdate                        = 5,  ///< Update
        kCommandUpdateRequest                 = 6,  ///< Update Request
        kCommandDataRequest                   = 7,  ///< Data Request
        kCommandDataResponse                  = 8,  ///< Data Response
        kCommandParentRequest                 = 9,  ///< Parent Request
        kCommandParentResponse                = 10, ///< Parent Response
        kCommandChildIdRequest                = 11, ///< Child ID Request
        kCommandChildIdResponse               = 12, ///< Child ID Response
        kCommandChildUpdateRequest            = 13, ///< Child Update Request
        kCommandChildUpdateResponse           = 14, ///< Child Update Response
        kCommandAnnounce                      = 15, ///< Announce
        kCommandDiscoveryRequest              = 16, ///< Discovery Request
        kCommandDiscoveryResponse             = 17, ///< Discovery Response
        kCommandLinkMetricsManagementRequest  = 18, ///< Link Metrics Management Request
        kCommandLinkMetricsManagementResponse = 19, ///< Link Metrics Management Response
        kCommandLinkProbe                     = 20, ///< Link Probe
        kCommandTimeSync                      = 99, ///< Time Sync (when OPENTHREAD_CONFIG_TIME_SYNC_ENABLE enabled)
    };

    /**
     * Attach mode.
     *
     */
    enum AttachMode : uint8_t
    {
        kAnyPartition,       ///< Attach to any Thread partition.
        kSamePartition,      ///< Attach to the same Thread partition (attempt 1 when losing connectivity).
        kSamePartitionRetry, ///< Attach to the same Thread partition (attempt 2 when losing connectivity).
        kBetterPartition,    ///< Attach to a better (i.e. higher weight/partition id) Thread partition.
        kDowngradeToReed,    ///< Attach to the same Thread partition during downgrade process.
        kBetterParent,       ///< Attach to a better parent.
    };

    /**
     * States during attach (when searching for a parent).
     *
     */
    enum AttachState : uint8_t
    {
        kAttachStateIdle,            ///< Not currently searching for a parent.
        kAttachStateProcessAnnounce, ///< Waiting to process a received Announce (to switch channel/pan-id).
        kAttachStateStart,           ///< Starting to look for a parent.
        kAttachStateParentRequest,   ///< Send Parent Request (current number tracked by `mParentRequestCounter`).
        kAttachStateAnnounce,        ///< Send Announce messages
        kAttachStateChildIdRequest,  ///< Sending a Child ID Request message.
    };

    /**
     * States when reattaching network using stored dataset
     *
     */
    enum ReattachState : uint8_t
    {
        kReattachStop,    ///< Reattach process is disabled or finished
        kReattachStart,   ///< Start reattach process
        kReattachActive,  ///< Reattach using stored Active Dataset
        kReattachPending, ///< Reattach using stored Pending Dataset
    };

    static constexpr uint16_t kMleMaxResponseDelay = 1000u; ///< Max delay before responding to a multicast request.

    /**
     * This enumeration type is used in `AppendAddressRegistrationTlv()` to determine which addresses to include in the
     * appended Address Registration TLV.
     *
     */
    enum AddressRegistrationMode : uint8_t
    {
        kAppendAllAddresses,  ///< Append all addresses (unicast/multicast) in Address Registration TLV.
        kAppendMeshLocalOnly, ///< Only append the Mesh Local (ML-EID) address in Address Registration TLV.
    };

    /**
     * This enumeration represents the message actions used in `Log()` methods.
     *
     */
    enum MessageAction : uint8_t
    {
        kMessageSend,
        kMessageReceive,
        kMessageDelay,
        kMessageRemoveDelayed,
    };

    /**
     * This enumeration represents message types used in `Log()` methods.
     *
     */
    enum MessageType : uint8_t
    {
        kTypeAdvertisement,
        kTypeAnnounce,
        kTypeChildIdRequest,
        kTypeChildIdRequestShort,
        kTypeChildIdResponse,
        kTypeChildUpdateRequestOfParent,
        kTypeChildUpdateResponseOfParent,
        kTypeDataRequest,
        kTypeDataResponse,
        kTypeDiscoveryRequest,
        kTypeDiscoveryResponse,
        kTypeGenericDelayed,
        kTypeGenericUdp,
        kTypeParentRequestToRouters,
        kTypeParentRequestToRoutersReeds,
        kTypeParentResponse,
#if OPENTHREAD_FTD
        kTypeAddressRelease,
        kTypeAddressReleaseReply,
        kTypeAddressReply,
        kTypeAddressSolicit,
        kTypeChildUpdateRequestOfChild,
        kTypeChildUpdateResponseOfChild,
        kTypeChildUpdateResponseOfUnknownChild,
        kTypeLinkAccept,
        kTypeLinkAcceptAndRequest,
        kTypeLinkReject,
        kTypeLinkRequest,
        kTypeParentRequest,
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
        kTypeTimeSync,
#endif
#endif
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
        kTypeLinkMetricsManagementRequest,
        kTypeLinkMetricsManagementResponse,
        kTypeLinkProbe,
#endif
    };

    static constexpr uint8_t kMaxTlvListSize = 16; ///< Maximum number of TLVs in a `TlvList`.

    /**
     * This type represents a list of TLVs (array of TLV types).
     *
     */
    class TlvList : public Array<uint8_t, kMaxTlvListSize>
    {
    public:
        /**
         * This constructor initializes the `TlvList` as empty.
         *
         */
        TlvList(void) = default;

        /**
         * This method checks if a given TLV type is not already present in the list and adds it in the list.
         *
         * If the list is full, this method logs it as a warning.
         *
         * @param[in] aTlvType   The TLV type to add to the list.
         *
         */
        void Add(uint8_t aTlvType);

        /**
         * This method adds elements from a given list to this TLV list (if not already present in the list).
         *
         * @param[in] aTlvList   The TLV list to add elements from.
         *
         */
        void AddElementsFrom(const TlvList &aTlvList);
    };

    /**
     * This type represents a Challenge (or Response) data.
     *
     */
    struct Challenge
    {
        uint8_t mBuffer[kMaxChallengeSize]; ///< Buffer containing the challenge/response byte sequence.
        uint8_t mLength;                    ///< Challenge length (in bytes).

        /**
         * This method generates a cryptographically secure random sequence to populate the challenge data.
         *
         */
        void GenerateRandom(void);

        /**
         * This method indicates whether the Challenge matches a given buffer.
         *
         * @param[in] aBuffer   A pointer to a buffer to compare with the Challenge.
         * @param[in] aLength   Length of @p aBuffer (in bytes).
         *
         * @retval TRUE  If the Challenge matches the given buffer.
         * @retval FALSE If the Challenge does not match the given buffer.
         *
         */
        bool Matches(const uint8_t *aBuffer, uint8_t aLength) const;

        /**
         * This method indicates whether two Challenge data byte sequences are equal or not.
         *
         * @param[in] aOther   Another Challenge data to compare.
         *
         * @retval TRUE  If the two Challenges match.
         * @retval FALSE If the two Challenges do not match.
         *
         */
        bool operator==(const Challenge &aOther) const { return Matches(aOther.mBuffer, aOther.mLength); }
    };

    /**
     * This class represents an MLE Tx message.
     *
     */
    class TxMessage : public Message
    {
    public:
        /**
         * This method appends a Source Address TLV to the message.
         *
         * @retval kErrorNone    Successfully appended the Source Address TLV.
         * @retval kErrorNoBufs  Insufficient buffers available to append the Source Address TLV.
         *
         */
        Error AppendSourceAddressTlv(void);

        /**
         * This method appends a Mode TLV to the message.
         *
         * @param[in]  aMode     The Device Mode.
         *
         * @retval kErrorNone    Successfully appended the Mode TLV.
         * @retval kErrorNoBufs  Insufficient buffers available to append the Mode TLV.
         *
         */
        Error AppendModeTlv(DeviceMode aMode);

        /**
         * This method appends a Timeout TLV to the message.
         *
         * @param[in]  aTimeout  The Timeout value.
         *
         * @retval kErrorNone    Successfully appended the Timeout TLV.
         * @retval kErrorNoBufs  Insufficient buffers available to append the Timeout TLV.
         *
         */
        Error AppendTimeoutTlv(uint32_t aTimeout);

        /**
         * This method appends a Challenge TLV to the message.
         *
         * @param[in]  aChallenge        A pointer to the Challenge value.
         * @param[in]  aChallengeLength  The length of the Challenge value in bytes.
         *
         * @retval kErrorNone    Successfully appended the Challenge TLV.
         * @retval kErrorNoBufs  Insufficient buffers available to append the Challenge TLV.
         *
         */
        Error AppendChallengeTlv(const uint8_t *aChallenge, uint8_t aChallengeLength);

        /**
         * This method appends a Challenge TLV to the message.
         *
         * @param[in] aChallenge A reference to the Challenge data.
         *
         * @retval kErrorNone    Successfully appended the Challenge TLV.
         * @retval kErrorNoBufs  Insufficient buffers available to append the Challenge TLV.
         *
         */
        Error AppendChallengeTlv(const Challenge &aChallenge);

        /**
         * This method appends a Response TLV to the message.
         *
         * @param[in] aResponse  A reference to the Response data.
         *
         * @retval kErrorNone    Successfully appended the Response TLV.
         * @retval kErrorNoBufs  Insufficient buffers available to append the Response TLV.
         *
         */
        Error AppendResponseTlv(const Challenge &aResponse);

        /**
         * This method appends a Link Frame Counter TLV to the message.
         *
         * @retval kErrorNone     Successfully appended the Link Frame Counter TLV.
         * @retval kErrorNoBufs   Insufficient buffers available to append the Link Frame Counter TLV.
         *
         */
        Error AppendLinkFrameCounterTlv(void);

        /**
         * This method appends an MLE Frame Counter TLV to the message.
         *
         * @retval kErrorNone     Successfully appended the Frame Counter TLV.
         * @retval kErrorNoBufs   Insufficient buffers available to append the MLE Frame Counter TLV.
         *
         */
        Error AppendMleFrameCounterTlv(void);

        /**
         * This method appends an Address16 TLV to the message.
         *
         * @param[in]  aRloc16    The RLOC16 value.
         *
         * @retval kErrorNone     Successfully appended the Address16 TLV.
         * @retval kErrorNoBufs   Insufficient buffers available to append the Address16 TLV.
         *
         */
        Error AppendAddress16Tlv(uint16_t aRloc16);

        /**
         * This method appends a Network Data TLV to the message.
         *
         * @param[in]  aType      The Network Data type to append, full set or stable subset.
         *
         * @retval kErrorNone     Successfully appended the Network Data TLV.
         * @retval kErrorNoBufs   Insufficient buffers available to append the Network Data TLV.
         *
         */
        Error AppendNetworkDataTlv(NetworkData::Type aType);

        /**
         * This method appends a TLV Request TLV to the message.
         *
         * @param[in]  aTlvs        A pointer to the list of TLV types.
         * @param[in]  aTlvsLength  The number of TLV types in @p aTlvs
         *
         * @retval kErrorNone     Successfully appended the TLV Request TLV.
         * @retval kErrorNoBufs   Insufficient buffers available to append the TLV Request TLV.
         *
         */
        Error AppendTlvRequestTlv(const uint8_t *aTlvs, uint8_t aTlvsLength);

        /**
         * This method appends a TLV Request TLV to the message.
         *
         * @tparam kArrayLength     The TLV array length.
         *
         * @param[in]  aTlvArray    A reference to an array of TLV types of @p kArrayLength length.
         *
         * @retval kErrorNone     Successfully appended the TLV Request TLV.
         * @retval kErrorNoBufs   Insufficient buffers available to append the TLV Request TLV.
         *
         */
        template <uint8_t kArrayLength> Error AppendTlvRequestTlv(const uint8_t (&aTlvArray)[kArrayLength])
        {
            return AppendTlvRequestTlv(aTlvArray, kArrayLength);
        }

        /**
         * This method appends a Leader Data TLV to the message.
         *
         * @retval kErrorNone     Successfully appended the Leader Data TLV.
         * @retval kErrorNoBufs   Insufficient buffers available to append the Leader Data TLV.
         *
         */
        Error AppendLeaderDataTlv(void);

        /**
         * This method appends a Scan Mask TLV to th message.
         *
         * @param[in]  aScanMask  The Scan Mask value.
         *
         * @retval kErrorNone     Successfully appended the Scan Mask TLV.
         * @retval kErrorNoBufs   Insufficient buffers available to append the Scan Mask TLV.
         *
         */
        Error AppendScanMaskTlv(uint8_t aScanMask);

        /**
         * This method appends a Status TLV to the message.
         *
         * @param[in] aStatus     The Status value.
         *
         * @retval kErrorNone     Successfully appended the Status TLV.
         * @retval kErrorNoBufs   Insufficient buffers available to append the Status TLV.
         *
         */
        Error AppendStatusTlv(StatusTlv::Status aStatus);

        /**
         * This method appends a Link Margin TLV to the message.
         *
         * @param[in] aLinkMargin The Link Margin value.
         *
         * @retval kErrorNone     Successfully appended the Link Margin TLV.
         * @retval kErrorNoBufs   Insufficient buffers available to append the Link Margin TLV.
         *
         */
        Error AppendLinkMarginTlv(uint8_t aLinkMargin);

        /**
         * This method appends a Version TLV to the message.
         *
         * @retval kErrorNone     Successfully appended the Version TLV.
         * @retval kErrorNoBufs   Insufficient buffers available to append the Version TLV.
         *
         */
        Error AppendVersionTlv(void);

        /**
         * This method appends an Address Registration TLV to the message.
         *
         * @param[in]  aMode      Determines which addresses to include in the TLV (see `AddressRegistrationMode`).
         *
         * @retval kErrorNone     Successfully appended the Address Registration TLV.
         * @retval kErrorNoBufs   Insufficient buffers available to append the Address Registration TLV.
         *
         */
        Error AppendAddressRegistrationTlv(AddressRegistrationMode aMode = kAppendAllAddresses);

#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
        /**
         * This method appends a Time Request TLV to the message.
         *
         * @retval kErrorNone     Successfully appended the Time Request TLV.
         * @retval kErrorNoBufs   Insufficient buffers available to append the Time Request TLV.
         *
         */
        Error AppendTimeRequestTlv(void);

        /**
         * This method appends a Time Parameter TLV to the message.
         *
         * @retval kErrorNone     Successfully appended the Time Parameter TLV.
         * @retval kErrorNoBufs   Insufficient buffers available to append the Time Parameter TLV.
         *
         */
        Error AppendTimeParameterTlv(void);
#endif
        /**
         * This method appends a XTAL Accuracy TLV to the message.
         *
         * @retval kErrorNone     Successfully appended the XTAL Accuracy TLV.
         * @retval kErrorNoBufs   Insufficient buffers available to append the XTAl Accuracy TLV.
         *
         */
        Error AppendXtalAccuracyTlv(void);

#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
        /**
         * This method appends a CSL Channel TLV to the message.
         *
         * @retval kErrorNone     Successfully appended the CSL Channel TLV.
         * @retval kErrorNoBufs   Insufficient buffers available to append the CSL Channel TLV.
         *
         */
        Error AppendCslChannelTlv(void);

        /**
         * This method appends a CSL Sync Timeout TLV to the message.
         *
         * @retval kErrorNone     Successfully appended the CSL Timeout TLV.
         * @retval kErrorNoBufs   Insufficient buffers available to append the CSL Timeout TLV.
         *
         */
        Error AppendCslTimeoutTlv(void);
#endif

#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
        /**
         * This method appends a CSL Clock Accuracy TLV to the message.
         *
         * @retval kErrorNone     Successfully appended the CSL Accuracy TLV.
         * @retval kErrorNoBufs   Insufficient buffers available to append the CSL Accuracy TLV.
         *
         */
        Error AppendCslClockAccuracyTlv(void);
#endif

        /**
         * This method appends a Active Timestamp TLV to the message.
         *
         * @retval kErrorNone     Successfully appended the Active Timestamp TLV.
         * @retval kErrorNoBufs   Insufficient buffers available to append the Active Timestamp TLV.
         *
         */
        Error AppendActiveTimestampTlv(void);

        /**
         * This method appends a Pending Timestamp TLV to the message.
         *
         * @retval kErrorNone     Successfully appended the Pending Timestamp TLV.
         * @retval kErrorNoBufs   Insufficient buffers available to append the Pending Timestamp TLV.
         *
         */
        Error AppendPendingTimestampTlv(void);

#if OPENTHREAD_FTD
        /**
         * This method appends a Route TLV to the message.
         *
         * @param[in] aNeighbor   A pointer to the intended destination  (can be `nullptr`).
         *
         * @retval kErrorNone     Successfully appended the Route TLV.
         * @retval kErrorNoBufs   Insufficient buffers available to append the Route TLV.
         *
         */
        Error AppendRouteTlv(Neighbor *aNeighbor = nullptr);

        /**
         * This method appends a Active Dataset TLV to the message.
         *
         * @retval kErrorNone     Successfully appended the Active Dataset TLV.
         * @retval kErrorNoBufs   Insufficient buffers available to append the Active Dataset TLV.
         *
         */
        Error AppendActiveDatasetTlv(void);

        /**
         * This method appends a Pending Dataset TLV to the message.
         *
         * @retval kErrorNone     Successfully appended the Pending Dataset TLV.
         * @retval kErrorNoBufs   Insufficient buffers available to append the Pending Dataset TLV.
         *
         */
        Error AppendPendingDatasetTlv(void);

        /**
         * This method appends a Connectivity TLV to the message.
         *
         * @retval kErrorNone     Successfully appended the Connectivity TLV.
         * @retval kErrorNoBufs   Insufficient buffers available to append the Connectivity TLV.
         *
         */
        Error AppendConnectivityTlv(void);

        /**
         * This method appends a Address Registration TLV to the message with addresses from a given child.
         *
         * @param[in] aChild  The child to include its list of addresses in the Address Registration TLV.
         *
         * @retval kErrorNone     Successfully appended the Connectivity TLV.
         * @retval kErrorNoBufs   Insufficient buffers available to append the Connectivity TLV.
         *
         */
        Error AppendAddresseRegisterationTlv(Child &aChild);
#endif // OPENTHREAD_FTD

        /**
         * This method submits the MLE message to the UDP socket to be sent.
         *
         * @param[in]  aDestination  A reference to the IPv6 address of the destination.
         *
         * @retval kErrorNone     Successfully submitted the MLE message.
         * @retval kErrorNoBufs   Insufficient buffers to form the rest of the MLE message.
         *
         */
        Error SendTo(const Ip6::Address &aDestination);

        /**
         * This method enqueues the message to be sent after a given delay.
         *
         * @param[in]  aDestination         The IPv6 address of the recipient of the message.
         * @param[in]  aDelay               The delay in milliseconds before transmission of the message.
         *
         * @retval kErrorNone     Successfully queued the message to transmit after the delay.
         * @retval kErrorNoBufs   Insufficient buffers to queue the message.
         *
         */
        Error SendAfterDelay(const Ip6::Address &aDestination, uint16_t aDelay);
    };

    /**
     * This class represents an MLE Rx message.
     *
     */
    class RxMessage : public Message
    {
    public:
        /**
         * This method reads Challenge TLV from the message.
         *
         * @param[out] aChallenge        A reference to the Challenge data where to output the read value.
         *
         * @retval kErrorNone       Successfully read the Challenge TLV.
         * @retval kErrorNotFound   Challenge TLV was not found in the message.
         * @retval kErrorParse      Challenge TLV was found but could not be parsed.
         *
         */
        Error ReadChallengeTlv(Challenge &aChallenge) const;

        /**
         * This method reads Response TLV from the message.
         *
         * @param[out] aResponse        A reference to the Response data where to output the read value.
         *
         * @retval kErrorNone       Successfully read the Response TLV.
         * @retval kErrorNotFound   Response TLV was not found in the message.
         * @retval kErrorParse      Response TLV was found but could not be parsed.
         *
         */
        Error ReadResponseTlv(Challenge &aResponse) const;

        /**
         * This method reads Link and MLE Frame Counters from the message.
         *
         * Link Frame Counter TLV must be present in the message and its value is read into @p aLinkFrameCounter. If MLE
         * Frame Counter TLV is present in the message, its value is read into @p aMleFrameCounter. If the MLE Frame
         * Counter TLV is not present in the message, then @p aMleFrameCounter is set to the same value as
         * @p aLinkFrameCounter.
         *
         * @param[out] aLinkFrameCounter  A reference to an `uint32_t` to output the Link Frame Counter.
         * @param[out] aMleFrameCounter   A reference to an `uint32_t` to output the MLE Frame Counter.
         *
         * @retval kErrorNone       Successfully read the counters.
         * @retval kErrorNotFound   Link Frame Counter TLV was not found in the message.
         * @retval kErrorParse      TLVs are not well-formed.
         *
         */
        Error ReadFrameCounterTlvs(uint32_t &aLinkFrameCounter, uint32_t &aMleFrameCounter) const;

        /**
         * This method reads TLV Request TLV from the message.
         *
         * @param[out] aTlvList     A reference to output the read list of requested TLVs.
         *
         * @retval kErrorNone       Successfully read the TLV.
         * @retval kErrorNotFound   TLV was not found in the message.
         * @retval kErrorParse      TLV was found but could not be parsed.
         *
         */
        Error ReadTlvRequestTlv(TlvList &aTlvList) const;

        /**
         * This method reads Leader Data TLV from a message.
         *
         * @param[out] aLeaderData     A reference to output the Leader Data.
         *
         * @retval kErrorNone       Successfully read the TLV.
         * @retval kErrorNotFound   TLV was not found in the message.
         * @retval kErrorParse      TLV was found but could not be parsed.
         *
         */
        Error ReadLeaderDataTlv(LeaderData &aLeaderData) const;

    private:
        Error ReadChallengeOrResponse(uint8_t aTlvType, Challenge &aBuffer) const;
    };

    /**
     * This structure represents a received MLE message containing additional information about the message (e.g.
     * key sequence, neighbor from which it was received).
     *
     */
    struct RxInfo
    {
        /**
         * This enumeration represents a received MLE message class.
         *
         */
        enum Class : uint8_t
        {
            kUnknown,              ///< Unknown (default value, also indicates MLE message parse error).
            kAuthoritativeMessage, ///< Authoritative message (larger received key seq MUST be adopted).
            kPeerMessage,          ///< Peer message (adopt only if from a known neighbor and is greater by one).
        };

        /**
         * This constructor initializes the `RxInfo`.
         *
         * @param[in] aMessage       The received MLE message.
         * @param[in] aMessageInfo   The `Ip6::MessageInfo` associated with message.
         *
         */
        RxInfo(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
            : mMessage(static_cast<RxMessage &>(aMessage))
            , mMessageInfo(aMessageInfo)
            , mFrameCounter(0)
            , mKeySequence(0)
            , mNeighbor(nullptr)
            , mClass(kUnknown)
        {
        }

        RxMessage &             mMessage;      ///< The MLE message.
        const Ip6::MessageInfo &mMessageInfo;  ///< The `MessageInfo` associated with the message.
        uint32_t                mFrameCounter; ///< The frame counter from aux security header.
        uint32_t                mKeySequence;  ///< The key sequence from aux security header.
        Neighbor *              mNeighbor;     ///< Neighbor from which message was received (can be `nullptr`).
        Class                   mClass;        ///< The message class (authoritative, peer, or unknown).
    };

    /**
     * This method allocates and initializes new MLE message for a given command.
     *
     * @param[in] aCommand   The MLE command.
     *
     * @returns A pointer to the message or `nullptr` if insufficient message buffers are available.
     *
     */
    TxMessage *NewMleMessage(Command aCommand);

    /**
     * This method sets the device role.
     *
     * @param[in] aRole A device role.
     *
     */
    void SetRole(DeviceRole aRole);

    /**
     * This method causes the Thread interface to attempt an MLE attach.
     *
     * @param[in]  aMode  Indicates what partitions to attach to.
     *
     */
    void Attach(AttachMode aMode);

    /**
     * This method sets the attach state
     *
     * @param[in] aState An attach state
     *
     */
    void SetAttachState(AttachState aState);

    /**
     * This method checks if the destination is reachable.
     *
     * @param[in]  aMeshDest   The RLOC16 of the destination.
     * @param[in]  aIp6Header  The IPv6 header of the message.
     *
     * @retval kErrorNone      The destination is reachable.
     * @retval kErrorNoRoute   The destination is not reachable and the message should be dropped.
     *
     */
    Error CheckReachability(uint16_t aMeshDest, const Ip6::Header &aIp6Header);

    /**
     * This method returns the next hop towards an RLOC16 destination.
     *
     * @param[in]  aDestination  The RLOC16 of the destination.
     *
     * @returns A RLOC16 of the next hop if a route is known, kInvalidRloc16 otherwise.
     *
     */
    Mac::ShortAddress GetNextHop(uint16_t aDestination) const;

    /**
     * This method generates an MLE Data Request message.
     *
     * @param[in]  aDestination      A reference to the IPv6 address of the destination.
     * @param[in]  aTlvs             A pointer to requested TLV types.
     * @param[in]  aTlvsLength       The number of TLV types in @p aTlvs.
     * @param[in]  aDelay            Delay in milliseconds before the Data Request message is sent.
     * @param[in]  aExtraTlvs        A pointer to extra TLVs.
     * @param[in]  aExtraTlvsLength  Length of extra TLVs.
     *
     * @retval kErrorNone     Successfully generated an MLE Data Request message.
     * @retval kErrorNoBufs   Insufficient buffers to generate the MLE Data Request message.
     *
     */
    Error SendDataRequest(const Ip6::Address &aDestination,
                          const uint8_t *     aTlvs,
                          uint8_t             aTlvsLength,
                          uint16_t            aDelay,
                          const uint8_t *     aExtraTlvs,
                          uint8_t             aExtraTlvsLength);

    /**
     * This method generates an MLE Data Request message.
     *
     * @tparam kArrayLength          The TLV array length.
     *
     * @param[in]  aDestination      A reference to the IPv6 address of the destination.
     * @param[in]  aTlvs             An array of requested TLVs.
     * @param[in]  aDelay            Delay in milliseconds before the Data Request message is sent.
     *
     * @retval kErrorNone     Successfully generated an MLE Data Request message.
     * @retval kErrorNoBufs   Insufficient buffers to generate the MLE Data Request message.
     *
     */
    template <uint8_t kArrayLength>
    Error SendDataRequest(const Ip6::Address &aDestination, const uint8_t (&aTlvs)[kArrayLength], uint16_t aDelay = 0)
    {
        return SendDataRequest(aDestination, aTlvs, kArrayLength, aDelay, nullptr, 0);
    }

    /**
     * This method generates an MLE Child Update Request message.
     *
     * @param[in] aAppendChallenge   Indicates whether or not to include a Challenge TLV (even when already attached).
     *
     * @retval kErrorNone    Successfully generated an MLE Child Update Request message.
     * @retval kErrorNoBufs  Insufficient buffers to generate the MLE Child Update Request message.
     *
     */
    Error SendChildUpdateRequest(bool aAppendChallenge = false);

    /**
     * This method generates an MLE Child Update Response message.
     *
     * @param[in]  aTlvList      A list of requested TLV types.
     * @param[in]  aChallenge    The Challenge for the response.
     *
     * @retval kErrorNone     Successfully generated an MLE Child Update Response message.
     * @retval kErrorNoBufs   Insufficient buffers to generate the MLE Child Update Response message.
     *
     */
    Error SendChildUpdateResponse(const TlvList &aTlvList, const Challenge &aChallenge);

    /**
     * This method sets the RLOC16 assigned to the Thread interface.
     *
     * @param[in]  aRloc16  The RLOC16 to set.
     *
     */
    void SetRloc16(uint16_t aRloc16);

    /**
     * This method sets the Device State to Detached.
     *
     */
    void SetStateDetached(void);

    /**
     * This method sets the Device State to Child.
     *
     */
    void SetStateChild(uint16_t aRloc16);

    /**
     * This method sets the Leader's Partition ID, Weighting, and Router ID values.
     *
     * @param[in]  aPartitionId     The Leader's Partition ID value.
     * @param[in]  aWeighting       The Leader's Weighting value.
     * @param[in]  aLeaderRouterId  The Leader's Router ID value.
     *
     */
    void SetLeaderData(uint32_t aPartitionId, uint8_t aWeighting, uint8_t aLeaderRouterId);

#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
    /**
     * This static method emits a log message with an IPv6 address.
     *
     * @param[in]  aAction     The message action (send/receive/delay, etc).
     * @param[in]  aType       The message type.
     * @param[in]  aAddress    The IPv6 address of the peer.
     *
     */
    static void Log(MessageAction aAction, MessageType aType, const Ip6::Address &aAddress);

    /**
     * This static method emits a log message with an IPv6 address and RLOC16.
     *
     * @param[in]  aAction     The message action (send/receive/delay, etc).
     * @param[in]  aType       The message type.
     * @param[in]  aAddress    The IPv6 address of the peer.
     * @param[in]  aRloc       The RLOC16.
     *
     */
    static void Log(MessageAction aAction, MessageType aType, const Ip6::Address &aAddress, uint16_t aRloc);
#else
    static void Log(MessageAction, MessageType, const Ip6::Address &) {}
    static void Log(MessageAction, MessageType, const Ip6::Address &, uint16_t) {}
#endif // #if OT_SHOULD_LOG_AT( OT_LOG_LEVEL_INFO)

#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
    /**
     * This static method emits a log message indicating an error in processing of a message.
     *
     * Note that log message is emitted only if there is an error, i.e., @p aError is not `kErrorNone`. The log
     * message will have the format "Failed to process {aMessageString} : {ErrorString}".
     *
     * @param[in]  aType      The message type.
     * @param[in]  aError     The error in processing of the message.
     *
     */
    static void LogProcessError(MessageType aType, Error aError);

    /**
     * This static method emits a log message indicating an error when sending a message.
     *
     * Note that log message is emitted only if there is an error, i.e. @p aError is not `kErrorNone`. The log
     * message will have the format "Failed to send {Message Type} : {ErrorString}".
     *
     * @param[in]  aType    The message type.
     * @param[in]  aError   The error in sending the message.
     *
     */
    static void LogSendError(MessageType aType, Error aError);
#else
    static void LogProcessError(MessageType, Error) {}
    static void LogSendError(MessageType, Error) {}
#endif // #if OT_SHOULD_LOG_AT( OT_LOG_LEVEL_WARN)

    /**
     * This method triggers MLE Announce on previous channel after the Thread device successfully
     * attaches and receives the new Active Commissioning Dataset if needed.
     *
     * MTD would send Announce immediately after attached.
     * FTD would delay to send Announce after tried to become Router or decided to stay in REED role.
     *
     */
    void InformPreviousChannel(void);

    /**
     * This method indicates whether or not in announce attach process.
     *
     * @retval true if attaching/attached on the announced parameters, false otherwise.
     *
     */
    bool IsAnnounceAttach(void) const { return mAlternatePanId != Mac::kPanIdBroadcast; }

#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE)
    /**
     * This method converts an `AttachMode` enumeration value into a human-readable string.
     *
     * @param[in] aMode An attach mode
     *
     * @returns A human-readable string corresponding to the attach mode.
     *
     */
    static const char *AttachModeToString(AttachMode aMode);

    /**
     * This method converts an `AttachState` enumeration value into a human-readable string.
     *
     * @param[in] aState An attach state
     *
     * @returns A human-readable string corresponding to the attach state.
     *
     */
    static const char *AttachStateToString(AttachState aState);

    /**
     * This method converts a `ReattachState` enumeration value into a human-readable string.
     *
     * @param[in] aState A reattach state
     *
     * @returns A human-readable string corresponding to the reattach state.
     *
     */
    static const char *ReattachStateToString(ReattachState aState);
#endif

#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
    /**
     * This method sends a Link Metrics Management Request message.
     *
     * @param[in]  aDestination  A reference to the IPv6 address of the destination.
     * @param[in]  aSubTlvs      A pointer to the buffer of the sub-TLVs in the message.
     * @param[in]  aLength       The overall length of @p aSubTlvs.
     *
     * @retval kErrorNone     Successfully sent a Link Metrics Management Request.
     * @retval kErrorNoBufs   Insufficient buffers to generate the MLE Link Metrics Management Request message.
     *
     */
    Error SendLinkMetricsManagementRequest(const Ip6::Address &aDestination, const uint8_t *aSubTlvs, uint8_t aLength);

    /**
     * This method sends an MLE Link Probe message.
     *
     * @param[in]  aDestination  A reference to the IPv6 address of the destination.
     * @param[in]  aSeriesId     The Series ID [1, 254] which the Probe message targets at.
     * @param[in]  aBuf          A pointer to the data payload.
     * @param[in]  aLength       The length of the data payload in Link Probe TLV, [0, 64].
     *
     * @retval kErrorNone         Successfully sent a Link Metrics Management Request.
     * @retval kErrorNoBufs       Insufficient buffers to generate the MLE Link Metrics Management Request message.
     * @retval kErrorInvalidArgs  Series ID is not a valid value, not within range [1, 254].
     *
     */
    Error SendLinkProbe(const Ip6::Address &aDestination, uint8_t aSeriesId, uint8_t *aBuf, uint8_t aLength);

#endif

    /**
     * This method indicates whether the device is detaching gracefully.
     *
     * @retval TRUE  Detaching is in progress.
     * @retval FALSE Not detaching.
     *
     */
    bool IsDetachingGracefully(void) { return mDetachGracefullyTimer.IsRunning(); }

    Ip6::Netif::UnicastAddress mLeaderAloc; ///< Leader anycast locator

    LeaderData    mLeaderData;               ///< Last received Leader Data TLV.
    bool          mRetrieveNewNetworkData;   ///< Indicating new Network Data is needed if set.
    DeviceRole    mRole;                     ///< Current Thread role.
    Router        mParent;                   ///< Parent information.
    Router        mParentCandidate;          ///< Parent candidate information.
    NeighborTable mNeighborTable;            ///< The neighbor table.
    DeviceMode    mDeviceMode;               ///< Device mode setting.
    AttachState   mAttachState;              ///< The attach state.
    uint8_t       mParentRequestCounter;     ///< Number of parent requests while in `kAttachStateParentRequest`.
    ReattachState mReattachState;            ///< Reattach state
    uint16_t      mAttachCounter;            ///< Attach attempt counter.
    uint16_t      mAnnounceDelay;            ///< Delay in between sending Announce messages during attach.
    TimerMilli    mAttachTimer;              ///< The timer for driving the attach process.
    TimerMilli    mDelayedResponseTimer;     ///< The timer to delay MLE responses.
    TimerMilli    mMessageTransmissionTimer; ///< The timer for (re-)sending of MLE messages (e.g. Child Update).
    TimerMilli    mDetachGracefullyTimer;
    uint8_t       mParentLeaderCost;

    otDetachGracefullyCallback mDetachGracefullyCallback;
    void *                     mDetachGracefullyContext;

    static constexpr uint32_t kDetachGracefullyTimeout = 1000;

private:
    static constexpr uint8_t kMleHopLimit        = 255;
    static constexpr uint8_t kMleSecurityTagSize = 4; // Security tag size in bytes.

    // Parameters related to "periodic parent search" feature (CONFIG_ENABLE_PERIODIC_PARENT_SEARCH).
    // All timer intervals are converted to milliseconds.
    static constexpr uint32_t kParentSearchCheckInterval   = (OPENTHREAD_CONFIG_PARENT_SEARCH_CHECK_INTERVAL * 1000u);
    static constexpr uint32_t kParentSearchBackoffInterval = (OPENTHREAD_CONFIG_PARENT_SEARCH_BACKOFF_INTERVAL * 1000u);
    static constexpr uint32_t kParentSearchJitterInterval  = (15 * 1000u);
    static constexpr int8_t   kParentSearchRssThreadhold   = OPENTHREAD_CONFIG_PARENT_SEARCH_RSS_THRESHOLD;

    // Parameters for "attach backoff" feature (CONFIG_ENABLE_ATTACH_BACKOFF) - Intervals are in milliseconds.
    static constexpr uint32_t kAttachBackoffMinInterval = OPENTHREAD_CONFIG_MLE_ATTACH_BACKOFF_MINIMUM_INTERVAL;
    static constexpr uint32_t kAttachBackoffMaxInterval = OPENTHREAD_CONFIG_MLE_ATTACH_BACKOFF_MAXIMUM_INTERVAL;
    static constexpr uint32_t kAttachBackoffJitter      = OPENTHREAD_CONFIG_MLE_ATTACH_BACKOFF_JITTER_INTERVAL;
    static constexpr uint32_t kAttachBackoffDelayToResetCounter =
        OPENTHREAD_CONFIG_MLE_ATTACH_BACKOFF_DELAY_TO_RESET_BACKOFF_INTERVAL;

#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_3
    // First attach cycle includes two Parent Requests to routers, followed by four to routers and REEDs.
    static constexpr uint8_t kFirstAttachCycleTotalParentRequests       = 6;
    static constexpr uint8_t kFirstAttachCycleNumParentRequestToRouters = 2;
#else
    // First attach cycle in Thread 1.1/1.2 includes a Parent Requests to routers, followed by one to routers and REEDs.
    static constexpr uint8_t kFirstAttachCycleTotalParentRequests       = 2;
    static constexpr uint8_t kFirstAttachCycleNumParentRequestToRouters = 1;
#endif

    // Next attach cycles includes one Parent Request to routers, followed by one to routers and REEDs.
    static constexpr uint8_t kNextAttachCycleTotalParentRequests       = 2;
    static constexpr uint8_t kNextAttachCycleNumParentRequestToRouters = 1;

    enum StartMode : uint8_t // Used in `Start()`.
    {
        kNormalAttach,
        kAnnounceAttach, // Try to attach on the announced thread network with newer active timestamp.
    };

    enum StopMode : uint8_t // Used in `Stop()`.
    {
        kKeepNetworkDatasets,
        kUpdateNetworkDatasets,
    };

    enum AnnounceMode : uint8_t // Used in `SendAnnounce()`
    {
        kNormalAnnounce,
        kOrphanAnnounce,
    };

    enum ParentRequestType : uint8_t
    {
        kToRouters,         // Parent Request to routers only.
        kToRoutersAndReeds, // Parent Request to all routers and REEDs.
    };

    enum ChildUpdateRequestState : uint8_t
    {
        kChildUpdateRequestNone,    // No pending or active Child Update Request.
        kChildUpdateRequestPending, // Pending Child Update Request due to relative OT_CHANGED event.
        kChildUpdateRequestActive,  // Child Update Request has been sent and Child Update Response is expected.
    };

    enum DataRequestState : uint8_t
    {
        kDataRequestNone,   // Not waiting for a Data Response.
        kDataRequestActive, // Data Request has been sent, Data Response is expected.
    };

    enum SecuritySuite : uint8_t
    {
        k154Security = 0,   // Security suite value indicating that MLE message is not secured.
        kNoSecurity  = 255, // Security suite value indicating that MLE message is secured.
    };

    struct DelayedResponseMetadata
    {
        Error AppendTo(Message &aMessage) const { return aMessage.Append(*this); }
        void  ReadFrom(const Message &aMessage);
        void  RemoveFrom(Message &aMessage) const;

        Ip6::Address mDestination; // IPv6 address of the message destination.
        TimeMilli    mSendTime;    // Time when the message shall be sent.
    };

    OT_TOOL_PACKED_BEGIN
    class SecurityHeader
    {
    public:
        void InitSecurityControl(void) { mSecurityControl = kKeyIdMode2Mic32; }
        bool IsSecurityControlValid(void) const { return (mSecurityControl == kKeyIdMode2Mic32); }

        uint32_t GetFrameCounter(void) const { return Encoding::LittleEndian::HostSwap32(mFrameCounter); }
        void     SetFrameCounter(uint32_t aCounter) { mFrameCounter = Encoding::LittleEndian::HostSwap32(aCounter); }

        uint32_t GetKeyId(void) const { return Encoding::BigEndian::HostSwap32(mKeySource); }
        void     SetKeyId(uint32_t aKeySequence)
        {
            mKeySource = Encoding::BigEndian::HostSwap32(aKeySequence);
            mKeyIndex  = (aKeySequence & 0x7f) + 1;
        }

    private:
        static constexpr uint8_t kKeyIdMode2Mic32 = (Mac::Frame::kKeyIdMode2 | Mac::Frame::kSecEncMic32);

        uint8_t  mSecurityControl;
        uint32_t mFrameCounter;
        uint32_t mKeySource;
        uint8_t  mKeyIndex;
    } OT_TOOL_PACKED_END;

#if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
    class ServiceAloc : public Ip6::Netif::UnicastAddress
    {
    public:
        static constexpr uint16_t kNotInUse = Mac::kShortAddrInvalid;

        ServiceAloc(void);

        bool     IsInUse(void) const { return GetAloc16() != kNotInUse; }
        void     MarkAsNotInUse(void) { SetAloc16(kNotInUse); }
        uint16_t GetAloc16(void) const { return GetAddress().GetIid().GetLocator(); }
        void     SetAloc16(uint16_t aAloc16) { GetAddress().GetIid().SetLocator(aAloc16); }
        void     ApplyMeshLocalPrefix(const Ip6::NetworkPrefix &aPrefix) { GetAddress().SetPrefix(aPrefix); }
    };
#endif

    Error       Start(StartMode aMode);
    void        Stop(StopMode aMode);
    void        HandleNotifierEvents(Events aEvents);
    static void HandleAttachTimer(Timer &aTimer);
    void        HandleAttachTimer(void);
    static void HandleDelayedResponseTimer(Timer &aTimer);
    void        HandleDelayedResponseTimer(void);
    void        SendDelayedResponse(TxMessage &aMessage, const DelayedResponseMetadata &aMetadata);
    static void HandleMessageTransmissionTimer(Timer &aTimer);
    void        HandleMessageTransmissionTimer(void);
    static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo);
    void        HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
    void        ScheduleMessageTransmissionTimer(void);
    void        ReestablishLinkWithNeighbor(Neighbor &aNeighbor);
    static void HandleDetachGracefullyTimer(Timer &aTimer);
    void        HandleDetachGracefullyTimer(void);
    Error       SendChildUpdateRequest(bool aAppendChallenge, uint32_t aTimeout);

#if OPENTHREAD_FTD
    static void HandleDetachGracefullyAddressReleaseResponse(void *               aContext,
                                                             otMessage *          aMessage,
                                                             const otMessageInfo *aMessageInfo,
                                                             Error                aResult);
    void        HandleDetachGracefullyAddressReleaseResponse(void);
#endif

    void HandleAdvertisement(RxInfo &aRxInfo);
    void HandleChildIdResponse(RxInfo &aRxInfo);
    void HandleChildUpdateRequest(RxInfo &aRxInfo);
    void HandleChildUpdateResponse(RxInfo &aRxInfo);
    void HandleDataResponse(RxInfo &aRxInfo);
    void HandleParentResponse(RxInfo &aRxInfo);
    void HandleAnnounce(RxInfo &aRxInfo);
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
    void HandleLinkMetricsManagementRequest(RxInfo &aRxInfo);
#endif
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
    void HandleLinkMetricsManagementResponse(RxInfo &aRxInfo);
#endif
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
    void HandleLinkProbe(RxInfo &aRxInfo);
#endif
    Error HandleLeaderData(RxInfo &aRxInfo);
    void  ProcessAnnounce(void);
    bool  HasUnregisteredAddress(void);

    uint32_t GetAttachStartDelay(void) const;
    void     SendParentRequest(ParentRequestType aType);
    Error    SendChildIdRequest(void);
    Error    GetNextAnnouceChannel(uint8_t &aChannel) const;
    bool     HasMoreChannelsToAnnouce(void) const;
    bool     PrepareAnnounceState(void);
    void     SendAnnounce(uint8_t aChannel, AnnounceMode aMode);
    void     SendAnnounce(uint8_t aChannel, const Ip6::Address &aDestination, AnnounceMode aMode = kNormalAnnounce);
    void RemoveDelayedMessage(Message::SubType aSubType, MessageType aMessageType, const Ip6::Address *aDestination);
    void RemoveDelayedDataRequestMessage(const Ip6::Address &aDestination);
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
    Error SendLinkMetricsManagementResponse(const Ip6::Address &aDestination, LinkMetrics::Status aStatus);
#endif
    uint32_t Reattach(void);
    bool     HasAcceptableParentCandidate(void) const;
    Error    DetermineParentRequestType(ParentRequestType &aType) const;

    bool IsBetterParent(uint16_t               aRloc16,
                        LinkQuality            aLinkQuality,
                        uint8_t                aLinkMargin,
                        const ConnectivityTlv &aConnectivityTlv,
                        uint8_t                aVersion,
                        uint8_t                aCslClockAccuracy,
                        uint8_t                aCslUncertainty);
    bool IsNetworkDataNewer(const LeaderData &aLeaderData);

    Error ProcessMessageSecurity(Crypto::AesCcm::Mode    aMode,
                                 Message &               aMessage,
                                 const Ip6::MessageInfo &aMessageInfo,
                                 uint16_t                aCmdOffset,
                                 const SecurityHeader &  aHeader);

#if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
    ServiceAloc *FindInServiceAlocs(uint16_t aAloc16);
    void         UpdateServiceAlocs(void);
#endif

#if OPENTHREAD_CONFIG_MLE_INFORM_PREVIOUS_PARENT_ON_REATTACH
    void InformPreviousParent(void);
#endif

#if OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE
    static void HandleParentSearchTimer(Timer &aTimer);
    void        HandleParentSearchTimer(void);
    void        StartParentSearchTimer(void);
    void        UpdateParentSearchState(void);
#endif

#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
    static void        LogError(MessageAction aAction, MessageType aType, Error aError);
    static const char *MessageActionToString(MessageAction aAction);
    static const char *MessageTypeToString(MessageType aType);
    static const char *MessageTypeActionToSuffixString(MessageType aType, MessageAction aAction);
#endif

    MessageQueue mDelayedResponses;

    Challenge mParentRequestChallenge;

    AttachMode mAttachMode;
    int8_t     mParentPriority;
    uint8_t    mParentLinkQuality3;
    uint8_t    mParentLinkQuality2;
    uint8_t    mParentLinkQuality1;
    uint16_t   mParentSedBufferSize;
    uint8_t    mParentSedDatagramCount;

    uint8_t                 mChildUpdateAttempts;
    ChildUpdateRequestState mChildUpdateRequestState;
    uint8_t                 mDataRequestAttempts;
    DataRequestState        mDataRequestState;

    AddressRegistrationMode mAddressRegistrationMode;

    bool       mHasRestored;
    uint8_t    mParentLinkMargin;
    bool       mParentIsSingleton;
    bool       mReceivedResponseFromParent;
    LeaderData mParentLeaderData;

    Challenge mParentCandidateChallenge;

    Ip6::Udp::Socket mSocket;
    uint32_t         mTimeout;
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
    uint32_t mCslTimeout;
#endif

    uint16_t mPreviousParentRloc;

#if OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE
    bool       mParentSearchIsInBackoff : 1;
    bool       mParentSearchBackoffWasCanceled : 1;
    bool       mParentSearchRecentlyDetached : 1;
    TimeMilli  mParentSearchBackoffCancelTime;
    TimerMilli mParentSearchTimer;
#endif

    uint8_t  mAnnounceChannel;
    uint8_t  mAlternateChannel;
    uint16_t mAlternatePanId;
    uint64_t mAlternateTimestamp;

#if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
    ServiceAloc mServiceAlocs[kMaxServiceAlocs];
#endif

    otMleCounters mCounters;

    static const otMeshLocalPrefix sMeshLocalPrefixInit;

    Ip6::Netif::UnicastAddress   mLinkLocal64;
    Ip6::Netif::UnicastAddress   mMeshLocal64;
    Ip6::Netif::UnicastAddress   mMeshLocal16;
    Ip6::Netif::MulticastAddress mLinkLocalAllThreadNodes;
    Ip6::Netif::MulticastAddress mRealmLocalAllThreadNodes;

    otThreadParentResponseCallback mParentResponseCb;
    void *                         mParentResponseCbContext;
};

} // namespace Mle

/**
 * @}
 *
 */

} // namespace ot

#endif // MLE_HPP_
