diff --git a/doc/ot_api_doc.h b/doc/ot_api_doc.h index 7d4c1c015..58e09a954 100644 --- a/doc/ot_api_doc.h +++ b/doc/ot_api_doc.h @@ -139,6 +139,7 @@ * @defgroup api-history-tracker History Tracker * @defgroup api-jam-detection Jam Detection * @defgroup api-logging Logging - Thread Stack + * @defgroup api-mesh-diag Mesh Diagnostics * @defgroup api-ncp Network Co-Processor * @defgroup api-network-time Network Time Synchronization * @defgroup api-random-group Random Number Generator diff --git a/etc/cmake/options.cmake b/etc/cmake/options.cmake index e3de0c8b2..a4fbc359c 100644 --- a/etc/cmake/options.cmake +++ b/etc/cmake/options.cmake @@ -117,6 +117,7 @@ ot_option(OT_LINK_METRICS_SUBJECT OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENA ot_option(OT_LINK_RAW OPENTHREAD_CONFIG_LINK_RAW_ENABLE "link raw service") ot_option(OT_LOG_LEVEL_DYNAMIC OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE "dynamic log level control") ot_option(OT_MAC_FILTER OPENTHREAD_CONFIG_MAC_FILTER_ENABLE "mac filter") +ot_option(OT_MESH_DIAG OPENTHREAD_CONFIG_MESH_DIAG_ENABLE "mesh diag") ot_option(OT_MESSAGE_USE_HEAP OPENTHREAD_CONFIG_MESSAGE_USE_HEAP_ENABLE "heap allocator for message buffers") ot_option(OT_MLE_LONG_ROUTES OPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE "MLE long routes extension (experimental)") ot_option(OT_MLR OPENTHREAD_CONFIG_MLR_ENABLE "Multicast Listener Registration (MLR)") diff --git a/examples/common-switches.mk b/examples/common-switches.mk index 01f5f84b4..0fd0418fb 100644 --- a/examples/common-switches.mk +++ b/examples/common-switches.mk @@ -66,6 +66,7 @@ LOG_OUTPUT ?= APP endif LINK_RAW ?= 0 MAC_FILTER ?= 0 +MESH_DIAG ?= 0 MESSAGE_USE_HEAP ?= 0 MLE_LONG_ROUTES ?= 0 MLR ?= 0 @@ -268,6 +269,10 @@ ifeq ($(MAC_FILTER),1) COMMONCFLAGS += -DOPENTHREAD_CONFIG_MAC_FILTER_ENABLE=1 endif +ifeq ($(MESH_DIAG),1) +COMMONCFLAGS += -DOPENTHREAD_CONFIG_MESH_DIAG_ENABLE=1 +endif + ifeq ($(MESSAGE_USE_HEAP),1) COMMONCFLAGS += -DOPENTHREAD_CONFIG_MESSAGE_USE_HEAP_ENABLE=1 endif diff --git a/include/Makefile.am b/include/Makefile.am index edc0b607c..6ce6a7dff 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -68,6 +68,7 @@ openthread_headers = \ openthread/link_metrics.h \ openthread/link_raw.h \ openthread/logging.h \ + openthread/mesh_diag.h \ openthread/message.h \ openthread/multi_radio.h \ openthread/nat64.h \ diff --git a/include/openthread/BUILD.gn b/include/openthread/BUILD.gn index e0088a468..fba1dfe9c 100644 --- a/include/openthread/BUILD.gn +++ b/include/openthread/BUILD.gn @@ -72,6 +72,7 @@ source_set("openthread") { "link_metrics.h", "link_raw.h", "logging.h", + "mesh_diag.h", "message.h", "multi_radio.h", "nat64.h", diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 42a93095d..2e137bc94 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (293) +#define OPENTHREAD_API_VERSION (294) /** * @addtogroup api-instance diff --git a/include/openthread/mesh_diag.h b/include/openthread/mesh_diag.h new file mode 100644 index 000000000..e4c1eb588 --- /dev/null +++ b/include/openthread/mesh_diag.h @@ -0,0 +1,223 @@ +/* + * 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 + * @brief + * This file defines the OpenThread Mesh Diagnostic APIs. + */ + +#ifndef OPENTHREAD_MESH_DIAG_H_ +#define OPENTHREAD_MESH_DIAG_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup api-mesh-diag + * + * @brief + * This module includes definitions and functions for Mesh Diagnostics. + * + * The Mesh Diagnostics APIs require `OPENTHREAD_CONFIG_MESH_DIAG_ENABLE` and `OPENTHREAD_FTD`. + * + * @{ + * + */ + +/** + * This structure represents the set of configurations used when discovering mesh topology indicating which items to + * discover. + * + */ +typedef struct otMeshDiagDiscoverConfig +{ + bool mDiscoverIp6Addresses : 1; ///< Whether or not to discover IPv6 addresses of every router. + bool mDiscoverChildTable : 1; ///< Whether or not to discover children of every router. +} otMeshDiagDiscoverConfig; + +/** + * This type is an opaque iterator to iterate over list of IPv6 addresses of a router. + * + * Pointers to instance of this type are provided in `otMeshDiagRouterInfo`. + * + */ +typedef struct otMeshDiagIp6AddrIterator otMeshDiagIp6AddrIterator; + +/** + * This type is an opaque iterator to iterate over list of children of a router. + * + * Pointers to instance of this type are provided in `otMeshDiagRouterInfo`. + * + */ +typedef struct otMeshDiagChildIterator otMeshDiagChildIterator; + +/** + * This type represents information about a router in Thread mesh. + * + */ +typedef struct otMeshDiagRouterInfo +{ + otExtAddress mExtAddress; ///< Extended MAC address. + uint16_t mRloc16; ///< RLOC16. + uint8_t mRouterId; ///< Router ID. + bool mIsThisDevice : 1; ///< Whether router is this device itself. + bool mIsThisDeviceParent : 1; ///< Whether router is parent of this device (when device is a child). + bool mIsLeader : 1; ///< Whether router is leader. + bool mIsBorderRouter : 1; ///< Whether router acts as a border router providing ext connectivity. + + /** + * This array provides the link quality from this router to other routers, also indicating whether a link is + * established between the routers. + * + * The array is indexed based on Router ID. `mLinkQualities[routerId]` indicates the incoming link quality, the + * router sees to the router with `routerId`. Link quality is a value in [0, 3]. Value zero indicates no link. + * Larger value indicate better link quality (as defined by Thread specification). + * + */ + uint8_t mLinkQualities[OT_NETWORK_MAX_ROUTER_ID + 1]; + + /** + * A pointer to an iterator to go through the list of IPv6 addresses of the router. + * + * The pointer is valid only while `otMeshDiagRouterInfo` is valid. It can be used in `otMeshDiagGetNextIp6Address` + * to iterate through the IPv6 addresses. + * + * The pointer can be NULL when there was no request to discover IPv6 addresses (in `otMeshDiagDiscoverConfig`) or + * if the router did not provide the list. + * + */ + otMeshDiagIp6AddrIterator *mIp6AddrIterator; + + /** + * A pointer to an iterator to go through the list of children of the router. + * + * The pointer is valid only while `otMeshDiagRouterInfo` is valid. It can be used in `otMeshDiagGetNextChildInfo` + * to iterate through the children of the router. + * + * The pointer can be NULL when there was no request to discover children (in `otMeshDiagDiscoverConfig`) or + * if the router did not provide the list. + * + */ + otMeshDiagChildIterator *mChildIterator; +} otMeshDiagRouterInfo; + +/** + * This type represents information about a discovered child in Thread mesh. + * + */ +typedef struct otMeshDiagChildInfo +{ + uint16_t mRloc16; ///< RLOC16. + otLinkModeConfig mMode; ///< Device mode. + uint8_t mLinkQuality; ///< Incoming link quality to child from parent. + bool mIsThisDevice : 1; ///< Whether child is this device itself. + bool mIsBorderRouter : 1; ///< Whether child acts as a border router providing ext connectivity. +} otMeshDiagChildInfo; + +/** + * This function pointer type represents the callback used by `otMeshDiagDiscoverTopology()` to provide information + * about a discovered router. + * + * When @p aError is `OT_ERROR_PENDING`, it indicates that the discovery is not yet finished and there will be more + * routers to discover and the callback will be invoked again. + * + * @param[in] aError OT_ERROR_PENDING Indicates there are more routers to be discovered. + * OT_ERROR_NONE Indicates this is the last router and mesh discovery is done. + * OT_ERROR_RESPONSE_TIMEOUT Timed out waiting for response from one or more routers. + * @param[in] aRouterInfo The discovered router info (can be null if `aError` is OT_ERROR_RESPONSE_TIMEOUT). + * @param[in] aContext Application-specific context. + * + */ +typedef void (*otMeshDiagDiscoverCallback)(otError aError, otMeshDiagRouterInfo *aRouterInfo, void *aContext); + +/** + * This function starts network topology discovery. + * + * @param[in] aInstance The OpenThread instance. + * @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 OT_ERROR_NONE The network topology discovery started successfully. + * @retval OT_ERROR_BUSY A previous discovery request is still ongoing. + * @retval OT_ERROR_INVALID_STATE Device is not attached. + * @retval OT_ERROR_NO_BUFS Could not allocate buffer to send discovery messages. + * + */ +otError otMeshDiagDiscoverTopology(otInstance *aInstance, + const otMeshDiagDiscoverConfig *aConfig, + otMeshDiagDiscoverCallback aCallback, + void *aContext); + +/** + * This function cancels an ongoing topology discovery if there is one, otherwise no action. + * + * When ongoing discovery is cancelled, the callback from `otMeshDiagDiscoverTopology()` will not be called anymore. + * + */ +void otMeshDiagCancel(otInstance *aInstance); + +/** + * This function iterates through the discovered IPv6 address of a router. + * + * @param[in,out] aIterator The address iterator to use. + * @param[out] aIp6Address A pointer to return the next IPv6 address (if any). + * + * @retval OT_ERROR_NONE Successfully retrieved the next address. @p aIp6Address and @p aIterator are updated. + * @retval OT_ERROR_NOT_FOUND No more address. Reached the end of the list. + * + */ +otError otMeshDiagGetNextIp6Address(otMeshDiagIp6AddrIterator *aIterator, otIp6Address *aIp6Address); + +/** + * This function iterates through the discovered children of a router. + * + * @param[in,out] aIterator The address iterator to use. + * @param[out] aChildInfo A pointer to return the child info (if any). + * + * @retval OT_ERROR_NONE Successfully retrieved the next child. @p aChildInfo and @p aIterator are updated. + * @retval OT_ERROR_NOT_FOUND No more child. Reached the end of the list. + * + */ +otError otMeshDiagGetNextChildInfo(otMeshDiagChildIterator *aIterator, otMeshDiagChildInfo *aChildInfo); + +/** + * @} + * + */ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // OPENTHREAD_MESH_DIAG_H_ diff --git a/script/check-scan-build b/script/check-scan-build index 13c653e12..b04b111b4 100755 --- a/script/check-scan-build +++ b/script/check-scan-build @@ -61,6 +61,7 @@ OT_BUILD_OPTIONS=( "-DOT_JOINER=ON" "-DOT_LOG_LEVEL_DYNAMIC=ON" "-DOT_MAC_FILTER=ON" + "-DOT_MESH_DIAG=ON" "-DOT_MTD_NETDIAG=ON" "-DOT_NAT64_BORDER_ROUTING=ON" "-DOT_NAT64_TRANSLATOR=ON" diff --git a/script/make-pretty b/script/make-pretty index c61c442a9..475122e07 100755 --- a/script/make-pretty +++ b/script/make-pretty @@ -120,6 +120,7 @@ OT_CLANG_TIDY_BUILD_OPTS=( '-DOT_LINK_METRICS_INITIATOR=ON' '-DOT_LINK_METRICS_SUBJECT=ON' '-DOT_MAC_FILTER=ON' + '-DOT_MESH_DIAG=ON' '-DOT_MTD_NETDIAG=ON' '-DOT_NAT64_BORDER_ROUTING=ON' '-DOT_NAT64_TRANSLATOR=ON' diff --git a/src/cli/README.md b/src/cli/README.md index 616a3ab0d..b4c6d1346 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -67,6 +67,7 @@ Done - [log](#log-filename-filename) - [mac](#mac-retries-direct) - [macfilter](#macfilter) +- [meshdiag](#meshdiag-topology) - [mliid](#mliid-iid) - [mlr](#mlr-reg-ipaddr--timeout) - [mode](#mode) @@ -1821,6 +1822,140 @@ Set the log level. Done ``` +### meshdiag topology [ip6-addrs][children] + +Discover network topology (list of routers and their connections). + +This command requires `OPENTHREAD_CONFIG_MESH_DIAG_ENABLE` and `OPENTHREAD_FTD`. + +Parameters are optional and indicate additional items to discover. Can be added in any order. + +- `ip6-addrs` to discover the list of IPv6 addresses of every router. +- `children` to discover the child table of every router. + +Output lists all discovered routers. Information per router: + +- Router ID +- RLOC16 +- Extended MAC address +- Whether the router is this device is itself (`me`) +- Whether the router is the parent of this device when device is a child (`parent`) +- Whether the router is `leader` +- Whether the router acts as a border router providing external connectivity (`br`) +- List of routers to which this router has a link: + - `3-links`: Router IDs to which this router has a incoming link with link quality 3 + - `2-links`: Router IDs to which this router has a incoming link with link quality 2 + - `1-links`: Router IDs to which this router has a incoming link with link quality 1 + - If a list if empty, it is omitted in the out. +- If `ip6-addrs`, list of IPv6 addresses of the router +- If `children`, list of all children of the router. Information per child: + - RLOC16 + - Incoming Link Quality from perspective of parent to child (zero indicates unknown) + - Child Device mode (`r` rx-on-when-idle, `d` Full Thread Device, `n` Full Network Data, `-` no flags set) + - Whether the child is this device itself (`me`) + - Whether the child acts as a border router providing external connectivity (`br`) + +Discover network topology: + +```bash +> meshdiag topology +id:02 rloc16:0x0800 ext-addr:8aa57d2c603fe16c - me - leader + 3-links:{ 46 } +id:46 rloc16:0xb800 ext-addr:fe109d277e0175cc + 3-links:{ 02 51 57 } +id:33 rloc16:0x8400 ext-addr:d2e511a146b9e54d + 3-links:{ 51 57 } +id:51 rloc16:0xcc00 ext-addr:9aab43ababf05352 + 3-links:{ 33 57 } + 2-links:{ 46 } +id:57 rloc16:0xe400 ext-addr:dae9c4c0e9da55ff + 3-links:{ 46 51 } + 1-links:{ 33 } +Done +``` + +Discover network topology with router's IPv6 addresses and children: + +```bash +> meshdiag topology children ip6-addrs +id:62 rloc16:0xf800 ext-addr:ce349873897233a5 - me - br + 3-links:{ 46 } + ip6-addrs: + fdde:ad00:beef:0:0:ff:fe00:f800 + fdde:ad00:beef:0:211d:39e9:6b2e:4ad1 + fe80:0:0:0:cc34:9873:8972:33a5 + children: none +id:02 rloc16:0x0800 ext-addr:8aa57d2c603fe16c - leader - br + 3-links:{ 46 51 } + ip6-addrs: + fdde:ad00:beef:0:0:ff:fe00:fc00 + fdde:ad00:beef:0:0:ff:fe00:800 + fdde:ad00:beef:0:8a36:a3eb:47ae:a9b0 + fe80:0:0:0:88a5:7d2c:603f:e16c + children: + rloc16:0x0803 lq:3, mode:rn + rloc16:0x0804 lq:3, mode:rdn +id:33 rloc16:0x8400 ext-addr:d2e511a146b9e54d + 3-links:{ 57 } + ip6-addrs: + fdde:ad00:beef:0:0:ff:fe00:8400 + fdde:ad00:beef:0:824:a126:cf19:a9f4 + fe80:0:0:0:d0e5:11a1:46b9:e54d + children: none +id:51 rloc16:0xcc00 ext-addr:9aab43ababf05352 + 3-links:{ 02 46 57 } + ip6-addrs: + fdde:ad00:beef:0:0:ff:fe00:cc00 + fdde:ad00:beef:0:2986:bba3:12d0:1dd2 + fe80:0:0:0:98ab:43ab:abf0:5352 + children: none +id:57 rloc16:0xe400 ext-addr:dae9c4c0e9da55ff + 3-links:{ 33 51 } + ip6-addrs: + fdde:ad00:beef:0:0:ff:fe00:e400 + fdde:ad00:beef:0:87d0:550:bc18:9920 + fe80:0:0:0:d8e9:c4c0:e9da:55ff + children: + rloc16:0xe402 lq:3, mode:rn - br + rloc16:0xe403 lq:3, mode:rn +id:46 rloc16:0xb800 ext-addr:fe109d277e0175cc + 3-links:{ 02 51 62 } + ip6-addrs: + fdde:ad00:beef:0:0:ff:fe00:b800 + fdde:ad00:beef:0:df4d:2994:d85c:c337 + fe80:0:0:0:fc10:9d27:7e01:75cc + children: none +Done +``` + +Discover network topology with children: + +```bash +> meshdiag topology children +id:02 rloc16:0x0800 ext-addr:8aa57d2c603fe16c - parent - leader - br + 3-links:{ 46 51 } + children: + rloc16:0x0803 lq:0, mode:rn + rloc16:0x0804 lq:0, mode:rdn - me +id:46 rloc16:0xb800 ext-addr:fe109d277e0175cc + 3-links:{ 02 51 62 } + children: none +id:33 rloc16:0x8400 ext-addr:d2e511a146b9e54d + 3-links:{ 57 } + children: none +id:51 rloc16:0xcc00 ext-addr:9aab43ababf05352 + 3-links:{ 02 46 57 } + children: none +id:57 rloc16:0xe400 ext-addr:dae9c4c0e9da55ff + 3-links:{ 33 51 } + children: + rloc16:0xe402 lq:3, mode:rn - br + rloc16:0xe403 lq:3, mode:rn +id:62 rloc16:0xf800 ext-addr:ce349873897233a5 - br + 3-links:{ 46 } + children: none +``` + ### mliid \ Set the Mesh Local IID. diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 018174bc3..36b40a27e 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -5434,6 +5434,215 @@ template <> otError Interpreter::Process(Arg aArgs[]) exit: return error; } + +#if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE + +template <> otError Interpreter::Process(Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + + /** + * @cli meshdiag topology + * @code + * meshdiag topology + * id:02 rloc16:0x0800 ext-addr:8aa57d2c603fe16c - me - leader + * 3-links:{ 46 } + * id:46 rloc16:0xb800 ext-addr:fe109d277e0175cc + * 3-links:{ 02 51 57 } + * id:33 rloc16:0x8400 ext-addr:d2e511a146b9e54d + * 3-links:{ 51 57 } + * id:51 rloc16:0xcc00 ext-addr:9aab43ababf05352 + * 3-links:{ 33 57 } + * 2-links:{ 46 } + * id:57 rloc16:0xe400 ext-addr:dae9c4c0e9da55ff + * 3-links:{ 46 51 } + * 1-links:{ 33 } + * Done + * @endcode + * @par + * Discover network topology (list of routers and their connections). + * Parameters are optional and indicate additional items to discover. Can be added in any order. + * * `ip6-addrs` to discover the list of IPv6 addresses of every router. + * * `children` to discover the child table of every router. + * @par + * Information per router: + * * Router ID + * * RLOC16 + * * Extended MAC address + * * Whether the router is this device is itself (`me`) + * * Whether the router is the parent of this device when device is a child (`parent`) + * * Whether the router is `leader` + * * Whether the router acts as a border router providing external connectivity (`br`) + * * List of routers to which this router has a link: + * * `3-links`: Router IDs to which this router has a incoming link with link quality 3 + * * `2-links`: Router IDs to which this router has a incoming link with link quality 2 + * * `1-links`: Router IDs to which this router has a incoming link with link quality 1 + * * If a list if empty, it is omitted in the out. + * * If `ip6-addrs`, list of IPv6 addresses of the router + * * If `children`, list of all children of the router. Information per child: + * * RLOC16 + * * Incoming Link Quality from perspective of parent to child (zero indicates unknown) + * * Child Device mode (`r` rx-on-when-idle, `d` Full Thread Device, `n` Full Network Data, `-` no flags set) + * * Whether the child is this device itself (`me`) + * * Whether the child acts as a border router providing external connectivity (`br`) + * @cparam meshdiag topology [@ca{ip6-addrs}] [@ca{children}] + * @sa otMeshDiagDiscoverTopology + */ + if (aArgs[0] == "topology") + { + otMeshDiagDiscoverConfig config; + + config.mDiscoverIp6Addresses = false; + config.mDiscoverChildTable = false; + + aArgs++; + + for (; !aArgs->IsEmpty(); aArgs++) + { + if (*aArgs == "ip6-addrs") + { + config.mDiscoverIp6Addresses = true; + } + else if (*aArgs == "children") + { + config.mDiscoverChildTable = true; + } + else + { + ExitNow(error = OT_ERROR_INVALID_ARGS); + } + } + + SuccessOrExit(error = otMeshDiagDiscoverTopology(GetInstancePtr(), &config, HandleMeshDiagDiscoverDone, this)); + error = OT_ERROR_PENDING; + } + else + { + error = OT_ERROR_INVALID_COMMAND; + } + +exit: + return error; +} + +void Interpreter::HandleMeshDiagDiscoverDone(otError aError, otMeshDiagRouterInfo *aRouterInfo, void *aContext) +{ + reinterpret_cast(aContext)->HandleMeshDiagDiscoverDone(aError, aRouterInfo); +} + +void Interpreter::HandleMeshDiagDiscoverDone(otError aError, otMeshDiagRouterInfo *aRouterInfo) +{ + VerifyOrExit(aRouterInfo != nullptr); + + OutputFormat("id:%02u rloc16:0x%04x ext-addr:", aRouterInfo->mRouterId, aRouterInfo->mRloc16); + OutputExtAddress(aRouterInfo->mExtAddress); + + if (aRouterInfo->mIsThisDevice) + { + OutputFormat(" - me"); + } + + if (aRouterInfo->mIsThisDeviceParent) + { + OutputFormat(" - parent"); + } + + if (aRouterInfo->mIsLeader) + { + OutputFormat(" - leader"); + } + + if (aRouterInfo->mIsBorderRouter) + { + OutputFormat(" - br"); + } + + OutputNewLine(); + + for (uint8_t linkQuality = 3; linkQuality > 0; linkQuality--) + { + bool hasLinkQuality = false; + + for (uint8_t entryQuality : aRouterInfo->mLinkQualities) + { + if (entryQuality == linkQuality) + { + hasLinkQuality = true; + break; + } + } + + if (hasLinkQuality) + { + OutputFormat(kIndentSize, "%u-links:{ ", linkQuality); + + for (uint8_t id = 0; id < OT_ARRAY_LENGTH(aRouterInfo->mLinkQualities); id++) + { + if (aRouterInfo->mLinkQualities[id] == linkQuality) + { + OutputFormat("%02u ", id); + } + } + + OutputLine("}"); + } + } + + if (aRouterInfo->mIp6AddrIterator != nullptr) + { + otIp6Address ip6Address; + + OutputLine(kIndentSize, "ip6-addrs:"); + + while (otMeshDiagGetNextIp6Address(aRouterInfo->mIp6AddrIterator, &ip6Address) == OT_ERROR_NONE) + { + OutputSpaces(kIndentSize * 2); + OutputIp6AddressLine(ip6Address); + } + } + + if (aRouterInfo->mChildIterator != nullptr) + { + otMeshDiagChildInfo childInfo; + char linkModeString[kLinkModeStringSize]; + bool isFirst = true; + + while (otMeshDiagGetNextChildInfo(aRouterInfo->mChildIterator, &childInfo) == OT_ERROR_NONE) + { + if (isFirst) + { + OutputLine(kIndentSize, "children:"); + isFirst = false; + } + + OutputFormat(kIndentSize * 2, "rloc16:0x%04x lq:%u, mode:%s", childInfo.mRloc16, childInfo.mLinkQuality, + LinkModeToString(childInfo.mMode, linkModeString)); + + if (childInfo.mIsThisDevice) + { + OutputFormat(" - me"); + } + + if (childInfo.mIsBorderRouter) + { + OutputFormat(" - br"); + } + + OutputNewLine(); + } + + if (isFirst) + { + OutputLine(kIndentSize, "children: none"); + } + } + +exit: + OutputResult(aError); +} + +#endif // OPENTHREAD_CONFIG_MESH_DIAG_ENABLE + #endif // OPENTHREAD_FTD template <> otError Interpreter::Process(Arg aArgs[]) @@ -7740,6 +7949,9 @@ otError Interpreter::ProcessCommand(Arg aArgs[]) #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE CmdEntry("macfilter"), #endif +#if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD + CmdEntry("meshdiag"), +#endif #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE CmdEntry("mliid"), #endif diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index de01fd7e6..bb220a8f2 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -403,6 +404,10 @@ private: uint16_t aRloc16); void HandleLocateResult(otError aError, const otIp6Address *aMeshLocalAddress, uint16_t aRloc16); #endif +#if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD + static void HandleMeshDiagDiscoverDone(otError aError, otMeshDiagRouterInfo *aRouterInfo, void *aContext); + void HandleMeshDiagDiscoverDone(otError aError, otMeshDiagRouterInfo *aRouterInfo); +#endif #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE static void HandleMlrRegResult(void *aContext, otError aError, diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index 6ec802b03..37bc9c63f 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -326,6 +326,7 @@ openthread_core_files = [ "api/link_metrics_api.cpp", "api/link_raw_api.cpp", "api/logging_api.cpp", + "api/mesh_diag_api.cpp", "api/message_api.cpp", "api/multi_radio_api.cpp", "api/nat64_api.cpp", @@ -694,6 +695,8 @@ openthread_core_files = [ "utils/history_tracker.hpp", "utils/jam_detector.cpp", "utils/jam_detector.hpp", + "utils/mesh_diag.cpp", + "utils/mesh_diag.hpp", "utils/otns.cpp", "utils/otns.hpp", "utils/parse_cmdline.cpp", @@ -782,6 +785,7 @@ source_set("libopenthread_core_config") { "config/link_raw.h", "config/logging.h", "config/mac.h", + "config/mesh_diag.h", "config/misc.h", "config/mle.h", "config/nat64.h", diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 26bfe37ea..09818c239 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -61,6 +61,7 @@ set(COMMON_SOURCES api/link_metrics_api.cpp api/link_raw_api.cpp api/logging_api.cpp + api/mesh_diag_api.cpp api/message_api.cpp api/multi_radio_api.cpp api/nat64_api.cpp @@ -238,6 +239,7 @@ set(COMMON_SOURCES utils/heap.cpp utils/history_tracker.cpp utils/jam_detector.cpp + utils/mesh_diag.cpp utils/otns.cpp utils/parse_cmdline.cpp utils/ping_sender.cpp diff --git a/src/core/Makefile.am b/src/core/Makefile.am index d88f5f7bb..776ca2095 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -151,6 +151,7 @@ SOURCES_COMMON = \ api/link_metrics_api.cpp \ api/link_raw_api.cpp \ api/logging_api.cpp \ + api/mesh_diag_api.cpp \ api/message_api.cpp \ api/multi_radio_api.cpp \ api/nat64_api.cpp \ @@ -328,6 +329,7 @@ SOURCES_COMMON = \ utils/heap.cpp \ utils/history_tracker.cpp \ utils/jam_detector.cpp \ + utils/mesh_diag.cpp \ utils/otns.cpp \ utils/parse_cmdline.cpp \ utils/ping_sender.cpp \ @@ -511,6 +513,7 @@ HEADERS_COMMON = \ config/link_raw.h \ config/logging.h \ config/mac.h \ + config/mesh_diag.h \ config/misc.h \ config/mle.h \ config/nat64.h \ @@ -652,6 +655,7 @@ HEADERS_COMMON = \ utils/heap.hpp \ utils/history_tracker.hpp \ utils/jam_detector.hpp \ + utils/mesh_diag.hpp \ utils/otns.hpp \ utils/parse_cmdline.hpp \ utils/ping_sender.hpp \ diff --git a/src/core/api/mesh_diag_api.cpp b/src/core/api/mesh_diag_api.cpp new file mode 100644 index 000000000..888476a1a --- /dev/null +++ b/src/core/api/mesh_diag_api.cpp @@ -0,0 +1,67 @@ +/* + * 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 implements the Mesh Diagnostics public APIs. + */ + +#include "openthread-core-config.h" + +#if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD + +#include + +#include "common/as_core_type.hpp" +#include "common/locator_getters.hpp" +#include "utils/mesh_diag.hpp" + +using namespace ot; + +otError otMeshDiagDiscoverTopology(otInstance *aInstance, + const otMeshDiagDiscoverConfig *aConfig, + otMeshDiagDiscoverCallback aCallback, + void *aContext) +{ + AssertPointerIsNotNull(aConfig); + return AsCoreType(aInstance).Get().DiscoverTopology(*aConfig, aCallback, aContext); +} + +void otMeshDiagCancel(otInstance *aInstance) { AsCoreType(aInstance).Get().Cancel(); } + +otError otMeshDiagGetNextIp6Address(otMeshDiagIp6AddrIterator *aIterator, otIp6Address *aIp6Address) +{ + return AsCoreType(aIterator).GetNextAddress(AsCoreType(aIp6Address)); +} + +otError otMeshDiagGetNextChildInfo(otMeshDiagChildIterator *aIterator, otMeshDiagChildInfo *aChildInfo) +{ + return AsCoreType(aIterator).GetNextChildInfo(AsCoreType(aChildInfo)); +} + +#endif // OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD diff --git a/src/core/common/instance.cpp b/src/core/common/instance.cpp index 4672468f6..384843b68 100644 --- a/src/core/common/instance.cpp +++ b/src/core/common/instance.cpp @@ -206,6 +206,9 @@ Instance::Instance(void) #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD , mChannelManager(*this) #endif +#if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD + , mMeshDiag(*this) +#endif #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE , mHistoryTracker(*this) #endif diff --git a/src/core/common/instance.hpp b/src/core/common/instance.hpp index 9fe4de1f7..14d21ca6b 100644 --- a/src/core/common/instance.hpp +++ b/src/core/common/instance.hpp @@ -127,6 +127,7 @@ #include "utils/heap.hpp" #include "utils/history_tracker.hpp" #include "utils/jam_detector.hpp" +#include "utils/mesh_diag.hpp" #include "utils/ping_sender.hpp" #include "utils/slaac_address.hpp" #include "utils/srp_client_buffers.hpp" @@ -585,6 +586,10 @@ private: Utils::ChannelManager mChannelManager; #endif +#if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD + Utils::MeshDiag mMeshDiag; +#endif + #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE Utils::HistoryTracker mHistoryTracker; #endif @@ -864,6 +869,10 @@ template <> inline Utils::ChannelMonitor &Instance::Get(void) { return mChannelM template <> inline Utils::ChannelManager &Instance::Get(void) { return mChannelManager; } #endif +#if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD +template <> inline Utils::MeshDiag &Instance::Get(void) { return mMeshDiag; } +#endif + #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE template <> inline Utils::HistoryTracker &Instance::Get(void) { return mHistoryTracker; } #endif diff --git a/src/core/config/mesh_diag.h b/src/core/config/mesh_diag.h new file mode 100644 index 000000000..4049255fe --- /dev/null +++ b/src/core/config/mesh_diag.h @@ -0,0 +1,62 @@ +/* + * 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 compile-time configurations for Mesh Diagnostic module. + * + */ + +#ifndef CONFIG_MESH_DIAG_H_ +#define CONFIG_MESH_DIAG_H_ + +#include "config/border_routing.h" + +/** + * @def OPENTHREAD_CONFIG_MESH_DIAG_ENABLE + * + * Define to 1 to enable Mesh Diagnostic module. + * + * By default this feature is enabled if device is configured to act as Border Router. + * + */ +#ifndef OPENTHREAD_CONFIG_MESH_DIAG_ENABLE +#define OPENTHREAD_CONFIG_MESH_DIAG_ENABLE OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +#endif + +/** + * @def OPENTHREAD_CONFIG_MESH_DIAG_RESPONSE_TIMEOUT + * + * Specifies the timeout interval in milliseconds waiting for response from router during discover. + * + */ +#ifndef OPENTHREAD_CONFIG_MESH_DIAG_RESPONSE_TIMEOUT +#define OPENTHREAD_CONFIG_MESH_DIAG_RESPONSE_TIMEOUT 5000 +#endif + +#endif // CONFIG_MESH_DIAG_H_ diff --git a/src/core/openthread-core-config.h b/src/core/openthread-core-config.h index 1a52d6e3b..4b6524118 100644 --- a/src/core/openthread-core-config.h +++ b/src/core/openthread-core-config.h @@ -82,6 +82,7 @@ #include "config/link_raw.h" #include "config/logging.h" #include "config/mac.h" +#include "config/mesh_diag.h" #include "config/misc.h" #include "config/mle.h" #include "config/nat64.h" diff --git a/src/core/utils/mesh_diag.cpp b/src/core/utils/mesh_diag.cpp new file mode 100644 index 000000000..40ae5358c --- /dev/null +++ b/src/core/utils/mesh_diag.cpp @@ -0,0 +1,295 @@ +/* + * 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 implements the Mesh Diag module. + */ + +#include "mesh_diag.hpp" + +#if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD + +#include "common/as_core_type.hpp" +#include "common/code_utils.hpp" +#include "common/debug.hpp" +#include "common/instance.hpp" +#include "common/locator_getters.hpp" +#include "common/log.hpp" + +namespace ot { +namespace Utils { + +using namespace ot::NetworkDiagnostic; + +RegisterLogModule("MeshDiag"); + +//--------------------------------------------------------------------------------------------------------------------- +// MeshDiag + +MeshDiag::MeshDiag(Instance &aInstance) + : InstanceLocator(aInstance) + , mTimer(aInstance) +{ +} + +Error MeshDiag::DiscoverTopology(const DiscoverConfig &aConfig, DiscoverCallback aCallback, void *aContext) +{ + Error error = kErrorNone; + + VerifyOrExit(Get().IsAttached(), error = kErrorInvalidState); + VerifyOrExit(!mTimer.IsRunning(), error = kErrorBusy); + + Get().GetRouterIdSet(mExpectedRouterIdSet); + + for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++) + { + if (mExpectedRouterIdSet.Contains(routerId)) + { + SuccessOrExit(error = SendDiagGetTo(Mle::Rloc16FromRouterId(routerId), aConfig)); + } + } + + mDiscoverCallback.Set(aCallback, aContext); + mTimer.Start(kResponseTimeout); + +exit: + return error; +} + +void MeshDiag::Cancel(void) +{ + mTimer.Stop(); + IgnoreError(Get().AbortTransaction(HandleDiagGetResponse, this)); +} + +Error MeshDiag::SendDiagGetTo(uint16_t aRloc16, const DiscoverConfig &aConfig) +{ + Error error = kErrorNone; + Coap::Message *message = nullptr; + Tmf::MessageInfo messageInfo(GetInstance()); + uint8_t tlvs[kMaxTlvsToRequest]; + uint8_t tlvsLength; + + message = Get().NewConfirmablePostMessage(kUriDiagnosticGetRequest); + VerifyOrExit(message != nullptr, error = kErrorNoBufs); + + IgnoreError(message->SetPriority(Message::kPriorityLow)); + + tlvs[0] = Address16Tlv::kType; + tlvs[1] = ExtMacAddressTlv::kType; + tlvs[2] = RouteTlv::kType; + tlvsLength = 3; + + if (aConfig.mDiscoverIp6Addresses) + { + tlvs[tlvsLength++] = Ip6AddressListTlv::kType; + } + + if (aConfig.mDiscoverChildTable) + { + tlvs[tlvsLength++] = ChildTableTlv::kType; + } + + SuccessOrExit(error = Tlv::Append(*message, tlvs, tlvsLength)); + + messageInfo.SetSockAddrToRlocPeerAddrTo(aRloc16); + error = Get().SendMessage(*message, messageInfo, HandleDiagGetResponse, this); + +exit: + FreeMessageOnError(message, error); + return error; +} + +void MeshDiag::HandleDiagGetResponse(void *aContext, + otMessage *aMessage, + const otMessageInfo *aMessageInfo, + Error aResult) +{ + static_cast(aContext)->HandleDiagGetResponse(AsCoapMessagePtr(aMessage), AsCoreTypePtr(aMessageInfo), + aResult); +} + +void MeshDiag::HandleDiagGetResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult) +{ + OT_UNUSED_VARIABLE(aMessageInfo); + + Error error; + RouterInfo routerInfo; + Ip6AddrIterator ip6AddrIterator; + ChildIterator childIterator; + + SuccessOrExit(aResult); + VerifyOrExit((aMessage != nullptr) && mTimer.IsRunning()); + + SuccessOrExit(routerInfo.ParseFrom(*aMessage)); + + if (ip6AddrIterator.InitFrom(*aMessage) == kErrorNone) + { + routerInfo.mIp6AddrIterator = &ip6AddrIterator; + } + + if (childIterator.InitFrom(*aMessage, routerInfo.mRloc16) == kErrorNone) + { + routerInfo.mChildIterator = &childIterator; + } + + mExpectedRouterIdSet.Remove(routerInfo.mRouterId); + + if (mExpectedRouterIdSet.GetNumberOfAllocatedIds() == 0) + { + error = kErrorNone; + mTimer.Stop(); + } + else + { + error = kErrorPending; + } + + mDiscoverCallback.InvokeIfSet(error, &routerInfo); + +exit: + return; +} + +void MeshDiag::HandleTimer(void) +{ + // Timed out waiting for response from one or more routers. + + IgnoreError(Get().AbortTransaction(HandleDiagGetResponse, this)); + + mDiscoverCallback.InvokeIfSet(kErrorResponseTimeout, nullptr); +} + +//--------------------------------------------------------------------------------------------------------------------- +// MeshDiag::RouterInfo + +Error MeshDiag::RouterInfo::ParseFrom(const Message &aMessage) +{ + Error error = kErrorNone; + Mle::Mle &mle = aMessage.Get(); + RouteTlv routeTlv; + + Clear(); + + SuccessOrExit(error = Tlv::Find(aMessage, mRloc16)); + SuccessOrExit(error = Tlv::Find(aMessage, AsCoreType(&mExtAddress))); + SuccessOrExit(error = Tlv::FindTlv(aMessage, routeTlv)); + + mRouterId = Mle::RouterIdFromRloc16(mRloc16); + mIsThisDevice = (mRloc16 == mle.GetRloc16()); + mIsThisDeviceParent = mle.IsChild() && (mRloc16 == mle.GetParent().GetRloc16()); + mIsLeader = (mRouterId == mle.GetLeaderId()); + mIsBorderRouter = aMessage.Get().ContainsBorderRouterWithRloc(mRloc16); + + for (uint8_t id = 0, index = 0; id <= Mle::kMaxRouterId; id++) + { + if (routeTlv.IsRouterIdSet(id)) + { + mLinkQualities[id] = routeTlv.GetLinkQualityIn(index); + index++; + } + } + +exit: + return error; +} + +//--------------------------------------------------------------------------------------------------------------------- +// MeshDiag::Ip6AddrIterator + +Error MeshDiag::Ip6AddrIterator::InitFrom(const Message &aMessage) +{ + Error error; + uint16_t tlvLength; + + SuccessOrExit(error = Tlv::FindTlvValueOffset(aMessage, Ip6AddressListTlv::kType, mCurOffset, tlvLength)); + mEndOffset = mCurOffset + tlvLength; + mMessage = &aMessage; + +exit: + return error; +} + +Error MeshDiag::Ip6AddrIterator::GetNextAddress(Ip6::Address &aAddress) +{ + Error error = kErrorNone; + + VerifyOrExit(mMessage != nullptr, error = kErrorNotFound); + VerifyOrExit(mCurOffset + sizeof(Ip6::Address) <= mEndOffset, error = kErrorNotFound); + + IgnoreError(mMessage->Read(mCurOffset, aAddress)); + mCurOffset += sizeof(Ip6::Address); + +exit: + return error; +} + +//--------------------------------------------------------------------------------------------------------------------- +// MeshDiag::ChildIterator + +Error MeshDiag::ChildIterator::InitFrom(const Message &aMessage, uint16_t aParentRloc16) +{ + Error error; + uint16_t tlvLength; + + SuccessOrExit(error = Tlv::FindTlvValueOffset(aMessage, ChildTableTlv::kType, mCurOffset, tlvLength)); + mEndOffset = mCurOffset + tlvLength; + mMessage = &aMessage; + mParentRloc16 = aParentRloc16; + +exit: + return error; +} + +Error MeshDiag::ChildIterator::GetNextChildInfo(ChildInfo &aChildInfo) +{ + Error error = kErrorNone; + ChildTableEntry entry; + + VerifyOrExit(mMessage != nullptr, error = kErrorNotFound); + VerifyOrExit(mCurOffset + sizeof(ChildTableEntry) <= mEndOffset, error = kErrorNotFound); + + IgnoreError(mMessage->Read(mCurOffset, entry)); + mCurOffset += sizeof(ChildTableEntry); + + aChildInfo.mRloc16 = mParentRloc16 + entry.GetChildId(); + entry.GetMode().Get(aChildInfo.mMode); + aChildInfo.mLinkQuality = entry.GetLinkQuality(); + + aChildInfo.mIsThisDevice = (aChildInfo.mRloc16 == mMessage->Get().GetRloc16()); + aChildInfo.mIsBorderRouter = mMessage->Get().ContainsBorderRouterWithRloc(aChildInfo.mRloc16); + +exit: + return error; +} + +} // namespace Utils +} // namespace ot + +#endif // #if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD diff --git a/src/core/utils/mesh_diag.hpp b/src/core/utils/mesh_diag.hpp new file mode 100644 index 000000000..3de8e22a2 --- /dev/null +++ b/src/core/utils/mesh_diag.hpp @@ -0,0 +1,213 @@ +/* + * 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 + +#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_tlvs.hpp" + +struct otMeshDiagIp6AddrIterator +{ +}; + +struct otMeshDiagChildIterator +{ +}; + +namespace ot { +namespace Utils { + +/** + * This class implements the Mesh Diagnostics. + * + */ +class MeshDiag : public InstanceLocator +{ +public: + typedef otMeshDiagDiscoverConfig DiscoverConfig; ///< The discovery configuration. + typedef otMeshDiagDiscoverCallback DiscoverCallback; ///< The discovery callback function pointer type. + + /** + * This type represents an iterator to go over list of IPv6 addresses of a router. + * + */ + class Ip6AddrIterator : public otMeshDiagIp6AddrIterator + { + friend class MeshDiag; + + public: + /** + * This method iterates through the discovered IPv6 address of a router. + * + * @param[out] aIp6Address A reference to return the next IPv6 address (if any). + * + * @retval kErrorNone Successfully retrieved the next address. @p aIp6Address 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; + uint16_t mCurOffset; + uint16_t mEndOffset; + }; + + /** + * This type represents information about a router in Thread mesh. + * + */ + class RouterInfo : public otMeshDiagRouterInfo, public Clearable + { + friend class MeshDiag; + + private: + Error ParseFrom(const Message &aMessage); + }; + + /** + * This type represents information about a child in Thread mesh. + * + */ + class ChildInfo : public otMeshDiagChildInfo, public Clearable + { + }; + + /** + * This type represents an iterator to go over list of IPv6 addresses of a router. + * + */ + class ChildIterator : public otMeshDiagChildIterator + { + friend class MeshDiag; + + public: + /** + * This method 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; + uint16_t mCurOffset; + uint16_t mEndOffset; + uint16_t mParentRloc16; + }; + + /** + * This constructor initializes the `MeshDiag` instance. + * + * @param[in] aInstance The OpenThread instance. + * + */ + explicit MeshDiag(Instance &aInstance); + + /** + * This method 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 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); + + /** + * This method cancels an ongoing topology discovery if there one, otherwise no action. + * + * When ongoing discovery is cancelled, the callback from `DiscoverTopology()` 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; + static constexpr uint8_t kMaxTlvsToRequest = 5; + + Error SendDiagGetTo(uint16_t aRloc16, const DiscoverConfig &aConfig); + void HandleTimer(void); + void HandleDiagGetResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult); + + static void HandleDiagGetResponse(void *aContext, + otMessage *aMessage, + const otMessageInfo *aMessageInfo, + Error aResult); + + using TimeoutTimer = TimerMilliIn; + + Callback mDiscoverCallback; + Mle::RouterIdSet mExpectedRouterIdSet; + TimeoutTimer mTimer; +}; + +} // 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_ diff --git a/tests/toranj/openthread-core-toranj-config.h b/tests/toranj/openthread-core-toranj-config.h index 3a6e802a0..5ae92b26d 100644 --- a/tests/toranj/openthread-core-toranj-config.h +++ b/tests/toranj/openthread-core-toranj-config.h @@ -83,6 +83,14 @@ */ #define OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE 1 +/** + * @def OPENTHREAD_CONFIG_MESH_DIAG_ENABLE + * + * Define as 1 to enable Mesh Diagnostics module. + * + */ +#define OPENTHREAD_CONFIG_MESH_DIAG_ENABLE 1 + /** * @def OPENTHREAD_CONFIG_COMMISSIONER_ENABLE *