/* * Copyright (c) 2023, 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 Mesh Diagnostic module. */ #ifndef MESH_DIAG_HPP_ #define MESH_DIAG_HPP_ #include "openthread-core-config.h" #if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD #if !OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE #error "OPENTHREAD_CONFIG_MESH_DIAG_ENABLE requires OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE" #endif #include #include "coap/coap.hpp" #include "common/callback.hpp" #include "common/locator.hpp" #include "common/message.hpp" #include "common/timer.hpp" #include "net/ip6_address.hpp" #include "thread/network_diagnostic.hpp" #include "thread/network_diagnostic_tlvs.hpp" struct otMeshDiagIp6AddrIterator { }; struct otMeshDiagChildIterator { }; namespace ot { namespace Utils { /** * Implements the Mesh Diagnostics. */ class MeshDiag : public InstanceLocator { friend class ot::NetworkDiagnostic::Client; public: static constexpr uint16_t kVersionUnknown = OT_MESH_DIAG_VERSION_UNKNOWN; ///< Unknown version. typedef otMeshDiagDiscoverConfig DiscoverConfig; ///< Discovery configuration. typedef otMeshDiagDiscoverCallback DiscoverCallback; ///< Discovery callback. typedef otMeshDiagQueryChildTableCallback QueryChildTableCallback; ///< Query Child Table callback. typedef otMeshDiagChildIp6AddrsCallback ChildIp6AddrsCallback; ///< Child IPv6 addresses callback. typedef otMeshDiagQueryRouterNeighborTableCallback RouterNeighborTableCallback; ///< Neighbor table callback. /** * Represents an iterator to go over list of IPv6 addresses of a router or an MTD child. */ class Ip6AddrIterator : public otMeshDiagIp6AddrIterator { friend class MeshDiag; public: /** * Iterates through the discovered IPv6 address of a router. * * @param[out] aAddress A reference to return the next IPv6 address (if any). * * @retval kErrorNone Successfully retrieved the next address. @p aAddress is updated. * @retval kErrorNotFound No more address. Reached the end of the list. */ Error GetNextAddress(Ip6::Address &aAddress); private: Error InitFrom(const Message &aMessage); const Message *mMessage; OffsetRange mOffsetRange; }; /** * Represents information about a router in Thread mesh. */ class RouterInfo : public otMeshDiagRouterInfo, public Clearable { friend class MeshDiag; private: Error ParseFrom(const Message &aMessage); }; /** * Represents information about a child in Thread mesh. */ class ChildInfo : public otMeshDiagChildInfo, public Clearable { }; /** * Represents an iterator to go over list of IPv6 addresses of a router. */ class ChildIterator : public otMeshDiagChildIterator { friend class MeshDiag; public: /** * Iterates through the discovered children of a router. * * @param[out] aChildInfo A reference to return the info for the next child (if any). * * @retval kErrorNone Successfully retrieved the next child info. @p aChildInfo is updated. * @retval kErrorNotFound No more child entry. Reached the end of the list. */ Error GetNextChildInfo(ChildInfo &aChildInfo); private: Error InitFrom(const Message &aMessage, uint16_t aParentRloc16); const Message *mMessage; OffsetRange mOffsetRange; uint16_t mParentRloc16; }; /** * Initializes the `MeshDiag` instance. * * @param[in] aInstance The OpenThread instance. */ explicit MeshDiag(Instance &aInstance); /** * Starts network topology discovery. * * @param[in] aConfig The configuration to use for discovery (e.g., which items to discover). * @param[in] aCallback The callback to report the discovered routers. * @param[in] aContext A context to pass in @p aCallback. * * @retval kErrorNone The network topology discovery started successfully. * @retval kErrorBusy A previous discovery or query request is still ongoing. * @retval kErrorInvalidState Device is not attached. * @retval kErrorNoBufs Could not allocate buffer to send discovery messages. */ Error DiscoverTopology(const DiscoverConfig &aConfig, DiscoverCallback aCallback, void *aContext); /** * Starts query for child table for a given router. * * @param[in] aRloc16 The RLOC16 of router to query. * @param[in] aCallback The callback to report the queried child table. * @param[in] aContext A context to pass in @p aCallback. * * @retval kErrorNone The query started successfully. * @retval kErrorBusy A previous discovery or query request is still ongoing. * @retval kErrorInvalidArgs The @p aRloc16 is not a valid router RLOC16. * @retval kErrorInvalidState Device is not attached. * @retval kErrorNoBufs Could not allocate buffer to send query messages. */ Error QueryChildTable(uint16_t aRloc16, QueryChildTableCallback aCallback, void *aContext); /** * Sends a query to a parent to retrieve the IPv6 addresses of all its MTD children. * * @param[in] aRloc16 The RLOC16 of parent to query. * @param[in] aCallback The callback to report the queried child IPv6 address list. * @param[in] aContext A context to pass in @p aCallback. * * @retval kErrorNone The query started successfully. * @retval kErrorBusy A previous discovery or query request is still ongoing. * @retval kErrorInvalidArgs The @p aRloc16 is not a valid RLOC16. * @retval kErrorInvalidState Device is not attached. * @retval kErrorNoBufs Could not allocate buffer to send query messages. */ Error QueryChildrenIp6Addrs(uint16_t aRloc16, ChildIp6AddrsCallback aCallback, void *aContext); /** * Starts query for router neighbor table for a given router. * * @param[in] aRloc16 The RLOC16 of router to query. * @param[in] aCallback The callback to report the queried table. * @param[in] aContext A context to pass in @p aCallback. * * @retval kErrorNone The query started successfully. * @retval kErrorBusy A previous discovery or query request is still ongoing. * @retval kErrorInvalidArgs The @p aRloc16 is not a valid router RLOC16. * @retval kErrorInvalidState Device is not attached. * @retval kErrorNoBufs Could not allocate buffer to send query messages. */ Error QueryRouterNeighborTable(uint16_t aRloc16, RouterNeighborTableCallback aCallback, void *aContext); /** * Cancels an ongoing discovery or query operation if there one, otherwise no action. * * When ongoing discovery is cancelled, the callback from `DiscoverTopology()` or `QueryChildTable()` will not be * called anymore. */ void Cancel(void); private: typedef ot::NetworkDiagnostic::Tlv Tlv; static constexpr uint32_t kResponseTimeout = OPENTHREAD_CONFIG_MESH_DIAG_RESPONSE_TIMEOUT; enum State : uint8_t { kStateIdle, kStateDiscoverTopology, kStateQueryChildTable, kStateQueryChildrenIp6Addrs, kStateQueryRouterNeighborTable, }; struct DiscoverInfo { Callback mCallback; Mle::RouterIdSet mExpectedRouterIdSet; }; struct QueryChildTableInfo { Callback mCallback; uint16_t mRouterRloc16; }; struct QueryChildrenIp6AddrsInfo { Callback mCallback; uint16_t mParentRloc16; }; struct QueryRouterNeighborTableInfo { Callback mCallback; uint16_t mRouterRloc16; }; class ChildEntry : public otMeshDiagChildEntry { friend class MeshDiag; private: void SetFrom(const NetworkDiagnostic::ChildTlv &aChildTlv); }; class RouterNeighborEntry : public otMeshDiagRouterNeighborEntry { friend class MeshDiag; private: void SetFrom(const NetworkDiagnostic::RouterNeighborTlv &aTlv); }; Error SendQuery(uint16_t aRloc16, const uint8_t *aTlvs, uint8_t aTlvsLength); void Finalize(Error aError); void HandleTimer(void); bool HandleDiagnosticGetAnswer(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); Error ProcessMessage(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo, uint16_t aSenderRloc16); bool ProcessChildTableAnswer(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); bool ProcessChildrenIp6AddrsAnswer(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); bool ProcessRouterNeighborTableAnswer(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); void HandleDiagGetResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult); static void HandleDiagGetResponse(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aResult); using TimeoutTimer = TimerMilliIn; State mState; uint16_t mExpectedQueryId; uint16_t mExpectedAnswerIndex; TimeoutTimer mTimer; union { DiscoverInfo mDiscover; QueryChildTableInfo mQueryChildTable; QueryChildrenIp6AddrsInfo mQueryChildrenIp6Addrs; QueryRouterNeighborTableInfo mQueryRouterNeighborTable; }; }; } // namespace Utils DefineCoreType(otMeshDiagIp6AddrIterator, Utils::MeshDiag::Ip6AddrIterator); DefineCoreType(otMeshDiagRouterInfo, Utils::MeshDiag::RouterInfo); DefineCoreType(otMeshDiagChildInfo, Utils::MeshDiag::ChildInfo); DefineCoreType(otMeshDiagChildIterator, Utils::MeshDiag::ChildIterator); } // namespace ot #endif // OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD #endif // MESH_DIAG_HPP_