diff --git a/Android.mk b/Android.mk index 40b72f52d..3c7fe9979 100644 --- a/Android.mk +++ b/Android.mk @@ -148,6 +148,7 @@ LOCAL_SRC_FILES := \ src/core/api/logging_api.cpp \ src/core/api/message_api.cpp \ src/core/api/netdata_api.cpp \ + src/core/api/netdiag_api.cpp \ src/core/api/random_crypto_api.cpp \ src/core/api/random_noncrypto_api.cpp \ src/core/api/server_api.cpp \ diff --git a/include/openthread/Makefile.am b/include/openthread/Makefile.am index b32ca95f3..448d8e03d 100644 --- a/include/openthread/Makefile.am +++ b/include/openthread/Makefile.am @@ -71,6 +71,7 @@ openthread_headers = \ message.h \ ncp.h \ netdata.h \ + netdiag.h \ network_time.h \ random_crypto.h \ random_noncrypto.h \ diff --git a/include/openthread/netdiag.h b/include/openthread/netdiag.h new file mode 100644 index 000000000..1ff14953d --- /dev/null +++ b/include/openthread/netdiag.h @@ -0,0 +1,350 @@ +/* + * Copyright (c) 2020, 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 Network Diagnostic API. + */ + +#ifndef OPENTHREAD_NETDIAG_H_ +#define OPENTHREAD_NETDIAG_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup api-thread-general + * + * @{ + * + */ + +/** + * Maximum Number of Network Diagnostic TLV Types to Request or Reset. + */ +#define OT_NETWORK_DIAGNOSTIC_TYPELIST_MAX_ENTRIES 19 + +/** + * Size of Network Diagnostic Child Table entry. + */ +#define OT_NETWORK_DIAGNOSTIC_CHILD_TABLE_ENTRY_SIZE 3 + +/** + * Initializer for otNetworkDiagIterator. + */ +#define OT_NETWORK_DIAGNOSTIC_ITERATOR_INIT 0 + +enum +{ + OT_NETWORK_DIAGNOSTIC_TLV_EXT_ADDRESS = 0, ///< MAC Extended Address TLV + OT_NETWORK_DIAGNOSTIC_TLV_SHORT_ADDRESS = 1, ///< Address16 TLV + OT_NETWORK_DIAGNOSTIC_TLV_MODE = 2, ///< Mode TLV + OT_NETWORK_DIAGNOSTIC_TLV_TIMEOUT = 3, ///< Timeout TLV (the maximum polling time period for SEDs) + OT_NETWORK_DIAGNOSTIC_TLV_CONNECTIVITY = 4, ///< Connectivity TLV + OT_NETWORK_DIAGNOSTIC_TLV_ROUTE = 5, ///< Route64 TLV + OT_NETWORK_DIAGNOSTIC_TLV_LEADER_DATA = 6, ///< Leader Data TLV + OT_NETWORK_DIAGNOSTIC_TLV_NETWORK_DATA = 7, ///< Network Data TLV + OT_NETWORK_DIAGNOSTIC_TLV_IP6_ADDR_LIST = 8, ///< IPv6 Address List TLV + OT_NETWORK_DIAGNOSTIC_TLV_MAC_COUNTERS = 9, ///< MAC Counters TLV + OT_NETWORK_DIAGNOSTIC_TLV_BATTERY_LEVEL = 14, ///< Battery Level TLV + OT_NETWORK_DIAGNOSTIC_TLV_SUPPLY_VOLTAGE = 15, ///< Supply Voltage TLV + OT_NETWORK_DIAGNOSTIC_TLV_CHILD_TABLE = 16, ///< Child Table TLV + OT_NETWORK_DIAGNOSTIC_TLV_CHANNEL_PAGES = 17, ///< Channel Pages TLV + OT_NETWORK_DIAGNOSTIC_TLV_TYPE_LIST = 18, ///< Type List TLV + OT_NETWORK_DIAGNOSTIC_TLV_MAX_CHILD_TIMEOUT = 19, ///< Max Child Timeout TLV +}; + +typedef uint16_t otNetworkDiagIterator; ///< Used to iterate through Network Diagnostic TLV. + +/** + * This structure represents a Network Diagnostic Connectivity value. + * + */ +typedef struct otNetworkDiagConnectivity +{ + /** + * The priority of the sender as a parent. + */ + int8_t mParentPriority; + + /** + * The number of neighboring devices with which the sender shares a link of quality 3. + */ + uint8_t mLinkQuality3; + + /** + * The number of neighboring devices with which the sender shares a link of quality 2. + */ + uint8_t mLinkQuality2; + + /** + * The number of neighboring devices with which the sender shares a link of quality 1. + */ + uint8_t mLinkQuality1; + + /** + * The sender's routing cost to the Leader. + */ + uint8_t mLeaderCost; + + /** + * The most recent ID sequence number received by the sender. + */ + uint8_t mIdSequence; + + /** + * The number of active Routers in the sender's Thread Network Partition. + */ + uint8_t mActiveRouters; + + /** + * The guaranteed buffer capacity in octets for all IPv6 datagrams destined to a given SED. Optional. + */ + uint16_t mSedBufferSize; + + /** + * The guaranteed queue capacity in number of IPv6 datagrams destined to a given SED. Optional. + */ + uint8_t mSedDatagramCount; +} otNetworkDiagConnectivity; + +/** + * This structure represents a Network Diagnostic Route data. + * + */ +typedef struct otNetworkDiagRouteData +{ + uint8_t mRouterId; ///< The Assigned Router ID. + uint8_t mLinkQualityOut : 2; ///< Link Quality Out. + uint8_t mLinkQualityIn : 2; ///< Link Quality In. + uint8_t mRouteCost : 4; ///< Routing Cost. Infinite routing cost is represented by value 0. +} otNetworkDiagRouteData; + +/** + * This structure represents a Network Diagnostic Route TLV value. + * + */ +typedef struct otNetworkDiagRoute +{ + /** + * The sequence number associated with the set of Router ID assignments in #mRouteData. + */ + uint8_t mIdSequence; + + /** + * Number of elements in #mRouteData. + */ + uint8_t mRouteCount; + + /** + * Link Quality and Routing Cost data. + */ + otNetworkDiagRouteData mRouteData[OT_NETWORK_MAX_ROUTER_ID + 1]; +} otNetworkDiagRoute; + +/** + * This structure represents a Network Diagnostic Mac Counters value. + * + * See RFC 2863 for definitions of member fields. + * + */ +typedef struct otNetworkDiagMacCounters +{ + uint32_t mIfInUnknownProtos; + uint32_t mIfInErrors; + uint32_t mIfOutErrors; + uint32_t mIfInUcastPkts; + uint32_t mIfInBroadcastPkts; + uint32_t mIfInDiscards; + uint32_t mIfOutUcastPkts; + uint32_t mIfOutBroadcastPkts; + uint32_t mIfOutDiscards; +} otNetworkDiagMacCounters; + +/** + * This structure represents a Network Diagnostic Child Table Entry. + * + */ +typedef struct otNetworkDiagChildEntry +{ + /** + * Expected poll time expressed as 2^(Timeout-4) seconds. + */ + uint16_t mTimeout : 5; + + /** + * Child ID from which an RLOC can be generated. + */ + uint16_t mChildId : 9; + + /** + * Link mode bits. + */ + otLinkModeConfig mMode; +} otNetworkDiagChildEntry; + +/** + * This structure represents a Network Diagnostic TLV. + * + */ +typedef struct otNetworkDiagTlv +{ + /** + * The Network Diagnostic TLV type. + */ + uint8_t mType; + + union + { + otExtAddress mExtAddress; + uint16_t mAddr16; + otLinkModeConfig mMode; + uint32_t mTimeout; + otNetworkDiagConnectivity mConnectivity; + otNetworkDiagRoute mRoute; + otLeaderData mLeaderData; + otNetworkDiagMacCounters mMacCounters; + uint8_t mBatteryLevel; + uint16_t mSupplyVoltage; + uint32_t mMaxChildTimeout; + struct + { + uint8_t mNetworkDataCount; + uint8_t mNetworkData[OT_NETWORK_BASE_TLV_MAX_LENGTH]; + }; + struct + { + uint8_t mIp6AddrCount; + otIp6Address mIp6AddrList[OT_NETWORK_BASE_TLV_MAX_LENGTH / OT_IP6_ADDRESS_SIZE]; + }; + struct + { + uint8_t mChildCount; + otNetworkDiagChildEntry + mChildTable[OT_NETWORK_BASE_TLV_MAX_LENGTH / OT_NETWORK_DIAGNOSTIC_CHILD_TABLE_ENTRY_SIZE]; + }; + struct + { + uint8_t mChannelPageCount; + uint8_t mChannelPages[OT_NETWORK_BASE_TLV_MAX_LENGTH]; + }; + }; +} otNetworkDiagTlv; + +/** + * This function gets the next Network Diagnostic TLV in the message. + * + * @param[in] aMessage A pointer to a message. + * @param[inout] aIterator A pointer to the Network Diagnostic iterator context. To get the first + * Network Diagnostic TLV it should be set to OT_NETWORK_DIAGNOSTIC_ITERATOR_INIT. + * @param[out] aNetworkDiagTlv A pointer to where the Network Diagnostic TLV information will be placed. + * + * @retval OT_ERROR_NONE Successfully found the next Network Diagnostic TLV. + * @retval OT_ERROR_NOT_FOUND No subsequent Network Diagnostic TLV exists in the message. + * @retval OT_ERROR_PARSE Parsing the next Network Diagnostic failed. + * + * @Note A subsequent call to this function is allowed only when current return value is OT_ERROR_NONE. + * + */ +otError otThreadGetNextDiagnosticTlv(const otMessage * aMessage, + otNetworkDiagIterator *aIterator, + otNetworkDiagTlv * aNetworkDiagTlv); + +/** + * This function pointer is called when Network Diagnostic Get response is received. + * + * @param[in] aMessage A pointer to the message buffer containing the received Network Diagnostic + * Get response payload. + * @param[in] aMessageInfo A pointer to the message info for @p aMessage. + * @param[in] aContext A pointer to application-specific context. + * + */ +typedef void (*otReceiveDiagnosticGetCallback)(otMessage *aMessage, const otMessageInfo *aMessageInfo, void *aContext); + +/** + * This function registers a callback to provide received raw Network Diagnostic Get response payload. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aCallback A pointer to a function that is called when Network Diagnostic Get response + * is received or NULL to disable the callback. + * @param[in] aCallbackContext A pointer to application-specific context. + * + */ +void otThreadSetReceiveDiagnosticGetCallback(otInstance * aInstance, + otReceiveDiagnosticGetCallback aCallback, + void * aCallbackContext); + +/** + * Send a Network Diagnostic Get request. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aDestination A pointer to destination address. + * @param[in] aTlvTypes An array of Network Diagnostic TLV types. + * @param[in] aCount Number of types in aTlvTypes. + * + * @retval OT_ERROR_NONE Successfully queued the DIAG_GET.req. + * @retval OT_ERROR_NO_BUFS Insufficient message buffers available to send DIAG_GET.req. + * + */ +otError otThreadSendDiagnosticGet(otInstance * aInstance, + const otIp6Address *aDestination, + const uint8_t aTlvTypes[], + uint8_t aCount); + +/** + * Send a Network Diagnostic Reset request. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aDestination A pointer to destination address. + * @param[in] aTlvTypes An array of Network Diagnostic TLV types. Currently only Type 9 is allowed. + * @param[in] aCount Number of types in aTlvTypes + * + * @retval OT_ERROR_NONE Successfully queued the DIAG_RST.ntf. + * @retval OT_ERROR_NO_BUFS Insufficient message buffers available to send DIAG_RST.ntf. + * + */ +otError otThreadSendDiagnosticReset(otInstance * aInstance, + const otIp6Address *aDestination, + const uint8_t aTlvTypes[], + uint8_t aCount); + +/** + * @} + * + */ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // OPENTHREAD_NETDIAG_H_ diff --git a/include/openthread/thread.h b/include/openthread/thread.h index 80d54acea..aab1af9ea 100644 --- a/include/openthread/thread.h +++ b/include/openthread/thread.h @@ -54,9 +54,11 @@ extern "C" { */ /** - * Maximum Number of Network Diagnostic TLV Types to Request or Reset. + * Maximum value length of Thread Base TLV. */ -#define OT_NETWORK_DIAGNOSTIC_TYPELIST_MAX_ENTRIES 19 +#define OT_NETWORK_BASE_TLV_MAX_LENGTH 254 + +#define OT_NETWORK_MAX_ROUTER_ID 62 ///< Maximum Router ID /** * Represents a Thread device role. @@ -662,58 +664,6 @@ otError otThreadGetParentAverageRssi(otInstance *aInstance, int8_t *aParentRssi) */ otError otThreadGetParentLastRssi(otInstance *aInstance, int8_t *aLastRssi); -/** - * This function pointer is called when Network Diagnostic Get response is received. - * - * @param[in] aMessage A pointer to the message buffer containing the received Network Diagnostic - * Get response payload. - * @param[in] aMessageInfo A pointer to the message info for @p aMessage. - * @param[in] aContext A pointer to application-specific context. - * - */ -typedef void (*otReceiveDiagnosticGetCallback)(otMessage *aMessage, const otMessageInfo *aMessageInfo, void *aContext); - -/** - * This function registers a callback to provide received raw Network Diagnostic Get response payload. - * - * @param[in] aInstance A pointer to an OpenThread instance. - * @param[in] aCallback A pointer to a function that is called when Network Diagnostic Get response - * is received or NULL to disable the callback. - * @param[in] aCallbackContext A pointer to application-specific context. - * - */ -void otThreadSetReceiveDiagnosticGetCallback(otInstance * aInstance, - otReceiveDiagnosticGetCallback aCallback, - void * aCallbackContext); - -/** - * Send a Network Diagnostic Get request. - * - * @param[in] aInstance A pointer to an OpenThread instance. - * @param[in] aDestination A pointer to destination address. - * @param[in] aTlvTypes An array of Network Diagnostic TLV types. - * @param[in] aCount Number of types in aTlvTypes - * - */ -otError otThreadSendDiagnosticGet(otInstance * aInstance, - const otIp6Address *aDestination, - const uint8_t aTlvTypes[], - uint8_t aCount); - -/** - * Send a Network Diagnostic Reset request. - * - * @param[in] aInstance A pointer to an OpenThread instance. - * @param[in] aDestination A pointer to destination address. - * @param[in] aTlvTypes An array of Network Diagnostic TLV types. Currently only Type 9 is allowed. - * @param[in] aCount Number of types in aTlvTypes - * - */ -otError otThreadSendDiagnosticReset(otInstance * aInstance, - const otIp6Address *aDestination, - const uint8_t aTlvTypes[], - uint8_t aCount); - /** * Get the IPv6 counters. * diff --git a/src/cli/README.md b/src/cli/README.md index ca4028d80..b57e74f40 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -972,12 +972,27 @@ If \ is unicast address, `Diagnostic Get` will be sent. if \ is multicast address, `Diagnostic Query` will be sent. ```bash -> networkdiagnostic get fdde:ad00:beef:0:0:ff:fe00:f400 0 1 6 -DIAG_GET.rsp: 00088e18ad17a24b0b740102f400060841dcb82d40bac63d +> networkdiagnostic get fdde:ad00:beef:0:0:ff:fe00:fc00 0 1 6 +> DIAG_GET.rsp/ans: 00080e336e1c41494e1c01020c000608640b0f674074c503 +Ext Address: '0e336e1c41494e1c' +Rloc16: 0x0c00 +Leader Data: + PartitionId: 0x640b0f67 + Weighting: 64 + DataVersion: 116 + StableDataVersion: 197 + LeaderRouterId: 0x03 +Done > networkdiagnostic get ff02::1 0 1 -DIAG_GET.rsp: 0008567e31a79667a8cc0102f000 -DIAG_GET.rsp: 0008aaa7e584759e4e6401025400 +> DIAG_GET.rsp/ans: 00080e336e1c41494e1c01020c00 +Ext Address: '0e336e1c41494e1c' +Rloc16: 0x0c00 +Done +DIAG_GET.rsp/ans: 00083efcdb7e3f9eb0f201021800 +Ext Address: '3efcdb7e3f9eb0f2' +Rloc16: 0x1800 +Done ``` ### networkdiagnostic reset \ \ .. diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 4da6887c6..67ed74095 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -94,6 +94,8 @@ using ot::Encoding::BigEndian::HostSwap16; using ot::Encoding::BigEndian::HostSwap32; +#define INDENT_SIZE (4) + namespace ot { namespace Cli { @@ -3865,27 +3867,29 @@ exit: AppendResult(error); } } -#endif // OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE void Interpreter::HandleDiagnosticGetResponse(otMessage *aMessage, const otMessageInfo *aMessageInfo, void *aContext) { static_cast(aContext)->HandleDiagnosticGetResponse( - *static_cast(aMessage), *static_cast(aMessageInfo)); + *aMessage, *static_cast(aMessageInfo)); } -void Interpreter::HandleDiagnosticGetResponse(Message &aMessage, const Ip6::MessageInfo &) +void Interpreter::HandleDiagnosticGetResponse(const otMessage &aMessage, const Ip6::MessageInfo &) { - uint8_t buf[16]; - uint16_t bytesToPrint; - uint16_t bytesPrinted = 0; - uint16_t length = aMessage.GetLength() - aMessage.GetOffset(); + uint8_t buf[16]; + uint16_t bytesToPrint; + uint16_t bytesPrinted = 0; + uint16_t length = otMessageGetLength(&aMessage) - otMessageGetOffset(&aMessage); + otNetworkDiagTlv diagTlv; + otNetworkDiagIterator iterator = OT_NETWORK_DIAGNOSTIC_ITERATOR_INIT; + otError error = OT_ERROR_NONE; mServer->OutputFormat("DIAG_GET.rsp/ans: "); while (length > 0) { bytesToPrint = (length < sizeof(buf)) ? length : sizeof(buf); - aMessage.Read(aMessage.GetOffset() + bytesPrinted, bytesToPrint, buf); + otMessageRead(&aMessage, otMessageGetOffset(&aMessage) + bytesPrinted, buf, bytesToPrint); OutputBytes(buf, static_cast(bytesToPrint)); @@ -3895,9 +3899,241 @@ void Interpreter::HandleDiagnosticGetResponse(Message &aMessage, const Ip6::Mess mServer->OutputFormat("\r\n"); - AppendResult(OT_ERROR_NONE); + // Output Network Diagnostic TLV values in standard YAML format. + while ((error = otThreadGetNextDiagnosticTlv(&aMessage, &iterator, &diagTlv)) == OT_ERROR_NONE) + { + uint16_t column = 0; + switch (diagTlv.mType) + { + case OT_NETWORK_DIAGNOSTIC_TLV_EXT_ADDRESS: + mServer->OutputFormat("Ext Address: '"); + OutputBytes(diagTlv.mExtAddress.m8, sizeof(diagTlv.mExtAddress.m8)); + mServer->OutputFormat("'\r\n"); + break; + case OT_NETWORK_DIAGNOSTIC_TLV_SHORT_ADDRESS: + mServer->OutputFormat("Rloc16: 0x%04x\r\n", diagTlv.mAddr16); + break; + case OT_NETWORK_DIAGNOSTIC_TLV_MODE: + mServer->OutputFormat("Mode:\r\n"); + OutputMode(diagTlv.mMode, column + INDENT_SIZE); + break; + case OT_NETWORK_DIAGNOSTIC_TLV_TIMEOUT: + mServer->OutputFormat("Timeout: %u\r\n", diagTlv.mTimeout); + break; + case OT_NETWORK_DIAGNOSTIC_TLV_CONNECTIVITY: + mServer->OutputFormat("Connectivity:\r\n"); + OutputConnectivity(diagTlv.mConnectivity, column + INDENT_SIZE); + break; + case OT_NETWORK_DIAGNOSTIC_TLV_ROUTE: + mServer->OutputFormat("Route:\r\n"); + OutputRoute(diagTlv.mRoute, column + INDENT_SIZE); + break; + case OT_NETWORK_DIAGNOSTIC_TLV_LEADER_DATA: + mServer->OutputFormat("Leader Data:\r\n"); + OutputLeaderData(diagTlv.mLeaderData, column + INDENT_SIZE); + break; + case OT_NETWORK_DIAGNOSTIC_TLV_NETWORK_DATA: + mServer->OutputFormat("Network Data: '"); + OutputBytes(diagTlv.mNetworkData, diagTlv.mNetworkDataCount); + mServer->OutputFormat("'\r\n"); + break; + case OT_NETWORK_DIAGNOSTIC_TLV_IP6_ADDR_LIST: + mServer->OutputFormat("IP6 Address List:\r\n"); + for (uint16_t i = 0; i < diagTlv.mIp6AddrCount; ++i) + { + OutputSpaces(column + INDENT_SIZE); + mServer->OutputFormat("- "); + OutputIp6Address(diagTlv.mIp6AddrList[i]); + mServer->OutputFormat("\r\n"); + } + break; + case OT_NETWORK_DIAGNOSTIC_TLV_MAC_COUNTERS: + mServer->OutputFormat("MAC Counters:\r\n"); + OutputNetworkDiagMacCounters(diagTlv.mMacCounters, column + INDENT_SIZE); + break; + case OT_NETWORK_DIAGNOSTIC_TLV_BATTERY_LEVEL: + mServer->OutputFormat("Battery Level: %u%%\r\n", diagTlv.mBatteryLevel); + break; + case OT_NETWORK_DIAGNOSTIC_TLV_SUPPLY_VOLTAGE: + mServer->OutputFormat("Supply Voltage: %umV\r\n", diagTlv.mSupplyVoltage); + break; + case OT_NETWORK_DIAGNOSTIC_TLV_CHILD_TABLE: + mServer->OutputFormat("Child Table:\r\n"); + for (uint16_t i = 0; i < diagTlv.mChildCount; ++i) + { + OutputSpaces(column + INDENT_SIZE); + mServer->OutputFormat("- "); + OutputChildTableEntry(diagTlv.mChildTable[i], column + INDENT_SIZE + 2); + } + break; + case OT_NETWORK_DIAGNOSTIC_TLV_CHANNEL_PAGES: + mServer->OutputFormat("Channel Pages: '"); + OutputBytes(diagTlv.mChannelPages, diagTlv.mChannelPageCount); + mServer->OutputFormat("'\r\n"); + break; + case OT_NETWORK_DIAGNOSTIC_TLV_MAX_CHILD_TIMEOUT: + mServer->OutputFormat("Max Child Timeout: %u\r\n", diagTlv.mMaxChildTimeout); + break; + } + } + + AppendResult(error == OT_ERROR_NOT_FOUND ? OT_ERROR_NONE : error); } +void Interpreter::OutputSpaces(uint16_t aCount) +{ + static const uint16_t kSpaceStrLen = 16; + char spaceStr[kSpaceStrLen + 1]; + + memset(spaceStr, ' ', kSpaceStrLen); + spaceStr[kSpaceStrLen] = '\0'; + + for (uint16_t i = 0; i < aCount; i += kSpaceStrLen) + { + uint16_t idx = (i + kSpaceStrLen <= aCount) ? 0 : (i + kSpaceStrLen - aCount); + mServer->OutputFormat(&spaceStr[idx]); + } +} + +void Interpreter::OutputMode(const otLinkModeConfig &aMode, uint16_t aColumn) +{ + OutputSpaces(aColumn); + mServer->OutputFormat("RxOnWhenIdle: %d\r\n", aMode.mRxOnWhenIdle); + + OutputSpaces(aColumn); + mServer->OutputFormat("SecureDataRequests: %d\r\n", aMode.mSecureDataRequests); + + OutputSpaces(aColumn); + mServer->OutputFormat("DeviceType: %d\r\n", aMode.mDeviceType); + + OutputSpaces(aColumn); + mServer->OutputFormat("NetworkData: %d\r\n", aMode.mNetworkData); +} + +void Interpreter::OutputConnectivity(const otNetworkDiagConnectivity &aConnectivity, uint16_t aColumn) +{ + OutputSpaces(aColumn); + mServer->OutputFormat("ParentPriority: %d\r\n", aConnectivity.mParentPriority); + + OutputSpaces(aColumn); + mServer->OutputFormat("LinkQuality3: %u\r\n", aConnectivity.mLinkQuality3); + + OutputSpaces(aColumn); + mServer->OutputFormat("LinkQuality2: %u\r\n", aConnectivity.mLinkQuality2); + + OutputSpaces(aColumn); + mServer->OutputFormat("LinkQuality1: %u\r\n", aConnectivity.mLinkQuality1); + + OutputSpaces(aColumn); + mServer->OutputFormat("LeaderCost: %u\r\n", aConnectivity.mLeaderCost); + + OutputSpaces(aColumn); + mServer->OutputFormat("IdSequence: %u\r\n", aConnectivity.mIdSequence); + + OutputSpaces(aColumn); + mServer->OutputFormat("ActiveRouters: %u\r\n", aConnectivity.mActiveRouters); + + OutputSpaces(aColumn); + mServer->OutputFormat("SedBufferSize: %u\r\n", aConnectivity.mSedBufferSize); + + OutputSpaces(aColumn); + mServer->OutputFormat("SedDatagramCount: %u\r\n", aConnectivity.mSedDatagramCount); +} + +void Interpreter::OutputRoute(const otNetworkDiagRoute &aRoute, uint16_t aColumn) +{ + OutputSpaces(aColumn); + mServer->OutputFormat("IdSequence: %u\r\n", aRoute.mIdSequence); + + OutputSpaces(aColumn); + mServer->OutputFormat("RouteData:\r\n"); + + aColumn += INDENT_SIZE; + for (uint16_t i = 0; i < aRoute.mRouteCount; ++i) + { + OutputSpaces(aColumn); + mServer->OutputFormat("- "); + + OutputRouteData(aRoute.mRouteData[i], aColumn + 2); + } +} + +void Interpreter::OutputRouteData(const otNetworkDiagRouteData &aRouteData, uint16_t aColumn) +{ + mServer->OutputFormat("RouteId: 0x%02x\r\n", aRouteData.mRouterId); + + OutputSpaces(aColumn); + mServer->OutputFormat("LinkQualityOut: %u\r\n", aRouteData.mLinkQualityOut); + + OutputSpaces(aColumn); + mServer->OutputFormat("LinkQualityIn: %u\r\n", aRouteData.mLinkQualityIn); + + OutputSpaces(aColumn); + mServer->OutputFormat("RouteCost: %u\r\n", aRouteData.mRouteCost); +} + +void Interpreter::OutputLeaderData(const otLeaderData &aLeaderData, uint16_t aColumn) +{ + OutputSpaces(aColumn); + mServer->OutputFormat("PartitionId: 0x%08x\r\n", aLeaderData.mPartitionId); + + OutputSpaces(aColumn); + mServer->OutputFormat("Weighting: %u\r\n", aLeaderData.mWeighting); + + OutputSpaces(aColumn); + mServer->OutputFormat("DataVersion: %u\r\n", aLeaderData.mDataVersion); + + OutputSpaces(aColumn); + mServer->OutputFormat("StableDataVersion: %u\r\n", aLeaderData.mStableDataVersion); + + OutputSpaces(aColumn); + mServer->OutputFormat("LeaderRouterId: 0x%02x\r\n", aLeaderData.mLeaderRouterId); +} + +void Interpreter::OutputNetworkDiagMacCounters(const otNetworkDiagMacCounters &aMacCounters, uint16_t aColumn) +{ + OutputSpaces(aColumn); + mServer->OutputFormat("IfInUnknownProtos: %u\r\n", aMacCounters.mIfInUnknownProtos); + + OutputSpaces(aColumn); + mServer->OutputFormat("IfInErrors: %u\r\n", aMacCounters.mIfInErrors); + + OutputSpaces(aColumn); + mServer->OutputFormat("IfOutErrors: %u\r\n", aMacCounters.mIfOutErrors); + + OutputSpaces(aColumn); + mServer->OutputFormat("IfInUcastPkts: %u\r\n", aMacCounters.mIfInUcastPkts); + + OutputSpaces(aColumn); + mServer->OutputFormat("IfInBroadcastPkts: %u\r\n", aMacCounters.mIfInBroadcastPkts); + + OutputSpaces(aColumn); + mServer->OutputFormat("IfInDiscards: %u\r\n", aMacCounters.mIfInDiscards); + + OutputSpaces(aColumn); + mServer->OutputFormat("IfOutUcastPkts: %u\r\n", aMacCounters.mIfOutUcastPkts); + + OutputSpaces(aColumn); + mServer->OutputFormat("IfOutBroadcastPkts: %u\r\n", aMacCounters.mIfOutBroadcastPkts); + + OutputSpaces(aColumn); + mServer->OutputFormat("IfOutDiscards: %u\r\n", aMacCounters.mIfOutDiscards); +} + +void Interpreter::OutputChildTableEntry(const otNetworkDiagChildEntry &aChildEntry, uint16_t aColumn) +{ + mServer->OutputFormat("ChildId: 0x%04x\r\n", aChildEntry.mChildId); + + OutputSpaces(aColumn); + mServer->OutputFormat("Timeout: %u\r\n", aChildEntry.mTimeout); + + OutputSpaces(aColumn); + mServer->OutputFormat("Mode:\r\n"); + + OutputMode(aChildEntry.mMode, aColumn + INDENT_SIZE); +} +#endif // OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE + void Interpreter::SetUserCommands(const otCliCommand *aCommands, uint8_t aLength) { mUserCommands = aCommands; diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index 35f0fa449..42b90ca02 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -359,7 +359,19 @@ private: static void HandleActiveScanResult(otActiveScanResult *aResult, void *aContext); static void HandleEnergyScanResult(otEnergyScanResult *aResult, void *aContext); static void HandleLinkPcapReceive(const otRadioFrame *aFrame, bool aIsTx, void *aContext); + +#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE + void HandleDiagnosticGetResponse(const otMessage &aMessage, const Ip6::MessageInfo &aMessageInfo); static void HandleDiagnosticGetResponse(otMessage *aMessage, const otMessageInfo *aMessageInfo, void *aContext); + void OutputSpaces(uint16_t aCount); + void OutputMode(const otLinkModeConfig &aMode, uint16_t aColumn); + void OutputConnectivity(const otNetworkDiagConnectivity &aConnectivity, uint16_t aColumn); + void OutputRoute(const otNetworkDiagRoute &aRoute, uint16_t aColumn); + void OutputRouteData(const otNetworkDiagRouteData &aRouteData, uint16_t aColumn); + void OutputLeaderData(const otLeaderData &aLeaderData, uint16_t aColumn); + void OutputNetworkDiagMacCounters(const otNetworkDiagMacCounters &aMacCounters, uint16_t aColumn); + void OutputChildTableEntry(const otNetworkDiagChildEntry &aChildEntry, uint16_t aColumn); +#endif // OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE static void HandleDnsResponse(void * aContext, @@ -378,7 +390,6 @@ private: void HandleActiveScanResult(otActiveScanResult *aResult); void HandleEnergyScanResult(otEnergyScanResult *aResult); void HandleLinkPcapReceive(const otRadioFrame *aFrame, bool aIsTx); - void HandleDiagnosticGetResponse(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE void HandleDnsResponse(const char *aHostname, const Ip6::Address *aAddress, uint32_t aTtl, otError aResult); #endif diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 49fc372b6..3228eb8e3 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -96,6 +96,7 @@ set(COMMON_SOURCES api/logging_api.cpp api/message_api.cpp api/netdata_api.cpp + api/netdiag_api.cpp api/network_time_api.cpp api/random_crypto_api.cpp api/random_noncrypto_api.cpp diff --git a/src/core/Makefile.am b/src/core/Makefile.am index c85b8f2f1..472133fbe 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -134,6 +134,7 @@ SOURCES_COMMON = \ api/logging_api.cpp \ api/message_api.cpp \ api/netdata_api.cpp \ + api/netdiag_api.cpp \ api/network_time_api.cpp \ api/random_crypto_api.cpp \ api/random_noncrypto_api.cpp \ diff --git a/src/core/api/netdiag_api.cpp b/src/core/api/netdiag_api.cpp new file mode 100644 index 000000000..5b75e0eb5 --- /dev/null +++ b/src/core/api/netdiag_api.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2020, 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 OpenThread Network Diagnostic API. + */ + +#include "openthread-core-config.h" + +#include + +#include "common/instance.hpp" + +using namespace ot; + +#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE + +otError otThreadGetNextDiagnosticTlv(const otMessage * aMessage, + otNetworkDiagIterator *aIterator, + otNetworkDiagTlv * aNetworkDiagTlv) +{ + return NetworkDiagnostic::NetworkDiagnostic::GetNextDiagTlv(*static_cast(aMessage), + *aIterator, *aNetworkDiagTlv); +} + +void otThreadSetReceiveDiagnosticGetCallback(otInstance * aInstance, + otReceiveDiagnosticGetCallback aCallback, + void * aCallbackContext) +{ + Instance &instance = *static_cast(aInstance); + + instance.Get().SetReceiveDiagnosticGetCallback(aCallback, aCallbackContext); +} + +otError otThreadSendDiagnosticGet(otInstance * aInstance, + const otIp6Address *aDestination, + const uint8_t aTlvTypes[], + uint8_t aCount) +{ + Instance &instance = *static_cast(aInstance); + + return instance.Get().SendDiagnosticGet( + *static_cast(aDestination), aTlvTypes, aCount); +} + +otError otThreadSendDiagnosticReset(otInstance * aInstance, + const otIp6Address *aDestination, + const uint8_t aTlvTypes[], + uint8_t aCount) +{ + Instance &instance = *static_cast(aInstance); + + return instance.Get().SendDiagnosticReset( + *static_cast(aDestination), aTlvTypes, aCount); +} + +#endif // OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE diff --git a/src/core/api/thread_api.cpp b/src/core/api/thread_api.cpp index fee06f294..586f5160c 100644 --- a/src/core/api/thread_api.cpp +++ b/src/core/api/thread_api.cpp @@ -38,7 +38,6 @@ #include "common/debug.hpp" #include "common/instance.hpp" #include "common/locator-getters.hpp" -#include "common/logging.hpp" #include "common/settings.hpp" using namespace ot; @@ -363,39 +362,6 @@ exit: return error; } -#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE -void otThreadSetReceiveDiagnosticGetCallback(otInstance * aInstance, - otReceiveDiagnosticGetCallback aCallback, - void * aCallbackContext) -{ - Instance &instance = *static_cast(aInstance); - - instance.Get().SetReceiveDiagnosticGetCallback(aCallback, aCallbackContext); -} - -otError otThreadSendDiagnosticGet(otInstance * aInstance, - const otIp6Address *aDestination, - const uint8_t aTlvTypes[], - uint8_t aCount) -{ - Instance &instance = *static_cast(aInstance); - - return instance.Get().SendDiagnosticGet( - *static_cast(aDestination), aTlvTypes, aCount); -} - -otError otThreadSendDiagnosticReset(otInstance * aInstance, - const otIp6Address *aDestination, - const uint8_t aTlvTypes[], - uint8_t aCount) -{ - Instance &instance = *static_cast(aInstance); - - return instance.Get().SendDiagnosticReset( - *static_cast(aDestination), aTlvTypes, aCount); -} -#endif // OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE - otError otThreadSetEnabled(otInstance *aInstance, bool aEnabled) { otError error = OT_ERROR_NONE; diff --git a/src/core/common/tlvs.hpp b/src/core/common/tlvs.hpp index 23776f97b..91cf6625a 100644 --- a/src/core/common/tlvs.hpp +++ b/src/core/common/tlvs.hpp @@ -37,6 +37,7 @@ #include "openthread-core-config.h" #include +#include #include #include "common/encoding.hpp" @@ -62,7 +63,7 @@ public: */ enum { - kBaseTlvMaxLength = 254, ///< The maximum length of the Base TLV format. + kBaseTlvMaxLength = OT_NETWORK_BASE_TLV_MAX_LENGTH, ///< The maximum length of the Base TLV format. }; /** diff --git a/src/core/thread/mle_types.hpp b/src/core/thread/mle_types.hpp index 298599bdd..5664e5b41 100644 --- a/src/core/thread/mle_types.hpp +++ b/src/core/thread/mle_types.hpp @@ -142,7 +142,7 @@ enum #else kMaxRouteCost = 16, ///< MAX_ROUTE_COST #endif - kMaxRouterId = 62, ///< MAX_ROUTER_ID + kMaxRouterId = OT_NETWORK_MAX_ROUTER_ID, ///< MAX_ROUTER_ID kInvalidRouterId = kMaxRouterId + 1, ///< Value indicating incorrect Router Id kMaxRouters = OPENTHREAD_CONFIG_MLE_MAX_ROUTERS, ///< MAX_ROUTERS kMinDowngradeNeighbors = 7, ///< MIN_DOWNGRADE_NEIGHBORS diff --git a/src/core/thread/network_diagnostic.cpp b/src/core/thread/network_diagnostic.cpp index 81205d1c8..b8407a1f0 100644 --- a/src/core/thread/network_diagnostic.cpp +++ b/src/core/thread/network_diagnostic.cpp @@ -720,6 +720,317 @@ exit: return; } +static inline void ParseMode(const Mle::DeviceMode &aMode, otLinkModeConfig &aLinkModeConfig) +{ + aLinkModeConfig.mRxOnWhenIdle = aMode.IsRxOnWhenIdle(); + aLinkModeConfig.mSecureDataRequests = aMode.IsSecureDataRequest(); + aLinkModeConfig.mDeviceType = aMode.IsFullThreadDevice(); + aLinkModeConfig.mNetworkData = aMode.IsFullNetworkData(); +} + +static inline void ParseConnectivity(const ConnectivityTlv & aConnectivityTlv, + otNetworkDiagConnectivity &aNetworkDiagConnectivity) +{ + aNetworkDiagConnectivity.mParentPriority = aConnectivityTlv.GetParentPriority(); + aNetworkDiagConnectivity.mLinkQuality3 = aConnectivityTlv.GetLinkQuality3(); + aNetworkDiagConnectivity.mLinkQuality2 = aConnectivityTlv.GetLinkQuality2(); + aNetworkDiagConnectivity.mLinkQuality1 = aConnectivityTlv.GetLinkQuality1(); + aNetworkDiagConnectivity.mLeaderCost = aConnectivityTlv.GetLeaderCost(); + aNetworkDiagConnectivity.mIdSequence = aConnectivityTlv.GetIdSequence(); + aNetworkDiagConnectivity.mActiveRouters = aConnectivityTlv.GetActiveRouters(); + aNetworkDiagConnectivity.mSedBufferSize = aConnectivityTlv.GetSedBufferSize(); + aNetworkDiagConnectivity.mSedDatagramCount = aConnectivityTlv.GetSedDatagramCount(); +} + +static void ParseRoute(const RouteTlv &aRouteTlv, otNetworkDiagRoute &aNetworkDiagRoute) +{ + uint8_t routeCount = 0; + + for (uint8_t i = 0; i <= Mle::kMaxRouterId; ++i) + { + if (!aRouteTlv.IsRouterIdSet(i)) + { + continue; + } + aNetworkDiagRoute.mRouteData[routeCount].mRouterId = i; + aNetworkDiagRoute.mRouteData[routeCount].mRouteCost = aRouteTlv.GetRouteCost(routeCount); + aNetworkDiagRoute.mRouteData[routeCount].mLinkQualityIn = aRouteTlv.GetLinkQualityIn(routeCount); + aNetworkDiagRoute.mRouteData[routeCount].mLinkQualityOut = aRouteTlv.GetLinkQualityOut(routeCount); + ++routeCount; + } + aNetworkDiagRoute.mRouteCount = routeCount; + aNetworkDiagRoute.mIdSequence = aRouteTlv.GetRouterIdSequence(); +} + +static inline void ParseLeaderData(const LeaderDataTlv &aLeaderDataTlv, otLeaderData &aLeaderData) +{ + aLeaderData.mPartitionId = aLeaderDataTlv.GetPartitionId(); + aLeaderData.mWeighting = aLeaderDataTlv.GetWeighting(); + aLeaderData.mDataVersion = aLeaderDataTlv.GetDataVersion(); + aLeaderData.mStableDataVersion = aLeaderDataTlv.GetStableDataVersion(); + aLeaderData.mLeaderRouterId = aLeaderDataTlv.GetLeaderRouterId(); +} + +static inline void ParseMacCounters(const MacCountersTlv &aMacCountersTlv, otNetworkDiagMacCounters &aMacCounters) +{ + aMacCounters.mIfInUnknownProtos = aMacCountersTlv.GetIfInUnknownProtos(); + aMacCounters.mIfInErrors = aMacCountersTlv.GetIfInErrors(); + aMacCounters.mIfOutErrors = aMacCountersTlv.GetIfOutErrors(); + aMacCounters.mIfInUcastPkts = aMacCountersTlv.GetIfInUcastPkts(); + aMacCounters.mIfInBroadcastPkts = aMacCountersTlv.GetIfInBroadcastPkts(); + aMacCounters.mIfInDiscards = aMacCountersTlv.GetIfInDiscards(); + aMacCounters.mIfOutUcastPkts = aMacCountersTlv.GetIfOutUcastPkts(); + aMacCounters.mIfOutBroadcastPkts = aMacCountersTlv.GetIfOutBroadcastPkts(); + aMacCounters.mIfOutDiscards = aMacCountersTlv.GetIfOutDiscards(); +} + +static inline void ParseChildEntry(const ChildTableEntry &aChildTableTlvEntry, otNetworkDiagChildEntry &aChildEntry) +{ + aChildEntry.mTimeout = aChildTableTlvEntry.GetTimeout(); + aChildEntry.mChildId = aChildTableTlvEntry.GetChildId(); + ParseMode(aChildTableTlvEntry.GetMode(), aChildEntry.mMode); +} + +otError NetworkDiagnostic::GetNextDiagTlv(const otMessage & aMessage, + otNetworkDiagIterator &aIterator, + otNetworkDiagTlv & aNetworkDiagTlv) +{ + otError error = OT_ERROR_PARSE; + const Coap::Message &message = static_cast(aMessage); + uint16_t offset = message.GetOffset(); + NetworkDiagnosticTlv tlv; + + offset += aIterator; + + while (true) + { + uint16_t tlvTotalLength; + + VerifyOrExit(message.Read(offset, sizeof(tlv), &tlv) == sizeof(tlv), error = OT_ERROR_NOT_FOUND); + + switch (tlv.GetType()) + { + case NetworkDiagnosticTlv::kExtMacAddress: + { + ExtMacAddressTlv extMacAddr; + + tlvTotalLength = sizeof(extMacAddr); + VerifyOrExit(message.Read(offset, tlvTotalLength, &extMacAddr) == tlvTotalLength); + VerifyOrExit(extMacAddr.IsValid()); + + aNetworkDiagTlv.mExtAddress = *extMacAddr.GetMacAddr(); + ExitNow(error = OT_ERROR_NONE); + break; + } + + case NetworkDiagnosticTlv::kAddress16: + { + Address16Tlv addr16; + + tlvTotalLength = sizeof(addr16); + VerifyOrExit(message.Read(offset, tlvTotalLength, &addr16) == tlvTotalLength); + VerifyOrExit(addr16.IsValid()); + + aNetworkDiagTlv.mAddr16 = addr16.GetRloc16(); + ExitNow(error = OT_ERROR_NONE); + break; + } + + case NetworkDiagnosticTlv::kMode: + { + ModeTlv linkMode; + + tlvTotalLength = sizeof(linkMode); + VerifyOrExit(message.Read(offset, tlvTotalLength, &linkMode) == tlvTotalLength); + VerifyOrExit(linkMode.IsValid()); + + ParseMode(linkMode.GetMode(), aNetworkDiagTlv.mMode); + ExitNow(error = OT_ERROR_NONE); + break; + } + + case NetworkDiagnosticTlv::kTimeout: + { + TimeoutTlv timeout; + + tlvTotalLength = sizeof(timeout); + VerifyOrExit(message.Read(offset, tlvTotalLength, &timeout) == tlvTotalLength); + VerifyOrExit(timeout.IsValid()); + + aNetworkDiagTlv.mTimeout = timeout.GetTimeout(); + ExitNow(error = OT_ERROR_NONE); + break; + } + + case NetworkDiagnosticTlv::kConnectivity: + { + ConnectivityTlv connectivity; + + tlvTotalLength = sizeof(connectivity); + VerifyOrExit(message.Read(offset, tlvTotalLength, &connectivity) == tlvTotalLength); + VerifyOrExit(connectivity.IsValid()); + + ParseConnectivity(connectivity, aNetworkDiagTlv.mConnectivity); + ExitNow(error = OT_ERROR_NONE); + break; + } + + case NetworkDiagnosticTlv::kRoute: + { + RouteTlv route; + + tlvTotalLength = sizeof(tlv) + tlv.GetLength(); + VerifyOrExit(tlvTotalLength <= sizeof(route)); + VerifyOrExit(message.Read(offset, tlvTotalLength, &route) == tlvTotalLength); + VerifyOrExit(route.IsValid()); + + ParseRoute(route, aNetworkDiagTlv.mRoute); + ExitNow(error = OT_ERROR_NONE); + break; + } + + case NetworkDiagnosticTlv::kLeaderData: + { + LeaderDataTlv leaderData; + + tlvTotalLength = sizeof(leaderData); + VerifyOrExit(message.Read(offset, tlvTotalLength, &leaderData) == tlvTotalLength); + VerifyOrExit(leaderData.IsValid()); + + ParseLeaderData(leaderData, aNetworkDiagTlv.mLeaderData); + ExitNow(error = OT_ERROR_NONE); + break; + } + + case NetworkDiagnosticTlv::kNetworkData: + { + NetworkDataTlv networkData; + + tlvTotalLength = sizeof(tlv) + tlv.GetLength(); + VerifyOrExit(tlvTotalLength <= sizeof(networkData)); + VerifyOrExit(message.Read(offset, tlvTotalLength, &networkData) == tlvTotalLength); + VerifyOrExit(networkData.IsValid()); + VerifyOrExit(sizeof(aNetworkDiagTlv.mNetworkData) >= networkData.GetLength()); + + memcpy(aNetworkDiagTlv.mNetworkData, networkData.GetNetworkData(), networkData.GetLength()); + aNetworkDiagTlv.mNetworkDataCount = networkData.GetLength(); + ExitNow(error = OT_ERROR_NONE); + break; + } + + case NetworkDiagnosticTlv::kIp6AddressList: + { + Ip6AddressListTlv &ip6AddrList = static_cast(tlv); + + VerifyOrExit(ip6AddrList.IsValid()); + VerifyOrExit(sizeof(aNetworkDiagTlv.mIp6AddrList) >= ip6AddrList.GetLength()); + VerifyOrExit(message.Read(offset + sizeof(ip6AddrList), ip6AddrList.GetLength(), + aNetworkDiagTlv.mIp6AddrList) == ip6AddrList.GetLength()); + + aNetworkDiagTlv.mIp6AddrCount = ip6AddrList.GetLength() / OT_IP6_ADDRESS_SIZE; + ExitNow(error = OT_ERROR_NONE); + break; + } + + case NetworkDiagnosticTlv::kMacCounters: + { + MacCountersTlv macCounters; + + tlvTotalLength = sizeof(MacCountersTlv); + VerifyOrExit(message.Read(offset, tlvTotalLength, &macCounters) == tlvTotalLength); + VerifyOrExit(macCounters.IsValid()); + + ParseMacCounters(macCounters, aNetworkDiagTlv.mMacCounters); + ExitNow(error = OT_ERROR_NONE); + break; + } + + case NetworkDiagnosticTlv::kBatteryLevel: + { + BatteryLevelTlv batteryLevel; + + tlvTotalLength = sizeof(BatteryLevelTlv); + VerifyOrExit(message.Read(offset, tlvTotalLength, &batteryLevel) == tlvTotalLength); + VerifyOrExit(batteryLevel.IsValid()); + + aNetworkDiagTlv.mBatteryLevel = batteryLevel.GetBatteryLevel(); + ExitNow(error = OT_ERROR_NONE); + break; + } + + case NetworkDiagnosticTlv::kSupplyVoltage: + { + SupplyVoltageTlv supplyVoltage; + + tlvTotalLength = sizeof(SupplyVoltageTlv); + VerifyOrExit(message.Read(offset, tlvTotalLength, &supplyVoltage) == tlvTotalLength); + VerifyOrExit(supplyVoltage.IsValid()); + + aNetworkDiagTlv.mSupplyVoltage = supplyVoltage.GetSupplyVoltage(); + ExitNow(error = OT_ERROR_NONE); + break; + } + + case NetworkDiagnosticTlv::kChildTable: + { + ChildTableTlv &childTable = static_cast(tlv); + + VerifyOrExit(childTable.IsValid()); + VerifyOrExit(childTable.GetNumEntries() <= OT_ARRAY_LENGTH(aNetworkDiagTlv.mChildTable)); + + for (uint8_t i = 0; i < childTable.GetNumEntries(); ++i) + { + ChildTableEntry childEntry; + VerifyOrExit(childTable.ReadEntry(childEntry, message, offset, i) == OT_ERROR_NONE); + ParseChildEntry(childEntry, aNetworkDiagTlv.mChildTable[i]); + } + aNetworkDiagTlv.mChildCount = childTable.GetNumEntries(); + ExitNow(error = OT_ERROR_NONE); + break; + } + + case NetworkDiagnosticTlv::kChannelPages: + { + VerifyOrExit(sizeof(aNetworkDiagTlv.mChannelPages) >= tlv.GetLength()); + VerifyOrExit(message.Read(offset + sizeof(tlv), tlv.GetLength(), aNetworkDiagTlv.mChannelPages) == + tlv.GetLength()); + + aNetworkDiagTlv.mChannelPageCount = tlv.GetLength(); + ExitNow(error = OT_ERROR_NONE); + break; + } + + case NetworkDiagnosticTlv::kMaxChildTimeout: + { + MaxChildTimeoutTlv maxChildTimeout; + + tlvTotalLength = sizeof(maxChildTimeout); + VerifyOrExit(message.Read(offset, tlvTotalLength, &maxChildTimeout) == tlvTotalLength); + VerifyOrExit(maxChildTimeout.IsValid()); + + aNetworkDiagTlv.mMaxChildTimeout = maxChildTimeout.GetTimeout(); + ExitNow(error = OT_ERROR_NONE); + break; + } + + default: + // Ignore unrecognized Network Diagnostic TLV silently. + break; + } + + // The actual TLV size may exceeds tlvTotalLength. + offset += tlv.GetSize(); + } + +exit: + if (error == OT_ERROR_NONE) + { + aNetworkDiagTlv.mType = tlv.GetType(); + aIterator = offset - message.GetOffset(); + } + return error; +} + } // namespace NetworkDiagnostic } // namespace ot diff --git a/src/core/thread/network_diagnostic.hpp b/src/core/thread/network_diagnostic.hpp index 4d0500c04..be64350f0 100644 --- a/src/core/thread/network_diagnostic.hpp +++ b/src/core/thread/network_diagnostic.hpp @@ -36,6 +36,8 @@ #include "openthread-core-config.h" +#include + #include "coap/coap.hpp" #include "common/locator.hpp" #include "net/udp6.hpp" @@ -98,6 +100,10 @@ public: */ otError SendDiagnosticReset(const Ip6::Address &aDestination, const uint8_t aTlvTypes[], uint8_t aCount); + static otError GetNextDiagTlv(const otMessage & aMessage, + otNetworkDiagIterator &aIterator, + otNetworkDiagTlv & aNetworkDiagTlv); + private: otError AppendIp6AddressList(Message &aMessage); otError AppendChildTable(Message &aMessage); diff --git a/src/core/thread/network_diagnostic_tlvs.hpp b/src/core/thread/network_diagnostic_tlvs.hpp index 5b742f969..6953e110a 100644 --- a/src/core/thread/network_diagnostic_tlvs.hpp +++ b/src/core/thread/network_diagnostic_tlvs.hpp @@ -82,23 +82,22 @@ public: */ enum Type { - kExtMacAddress = 0, ///< Source Address TLV - kAddress16 = 1, ///< Address16 TLV - kMode = 2, ///< Mode TLV - kTimeout = 3, ///< Timeout TLV - kConnectivity = 4, ///< Connectivity TLV - kRoute = 5, ///< Routing-Table TLV - kLeaderData = 6, ///< Leader Data TLV - kNetworkData = 7, ///< Network Data TLV - kIp6AddressList = 8, ///< Ip6 Address List TLV - kMacCounters = 9, ///< Mac Counters TLV - kBatteryLevel = 14, ///< Battery Level TLV - kSupplyVoltage = 15, ///< Supply Voltage TLV - kChildTable = 16, ///< Child Table TLV - kChannelPages = 17, ///< Channel Pages TLV - kTypeList = 18, ///< Type List TLV - kMaxChildTimeout = 19, ///< Max Child Timeout TLV - kInvalid = 255, + kExtMacAddress = OT_NETWORK_DIAGNOSTIC_TLV_EXT_ADDRESS, + kAddress16 = OT_NETWORK_DIAGNOSTIC_TLV_SHORT_ADDRESS, + kMode = OT_NETWORK_DIAGNOSTIC_TLV_MODE, + kTimeout = OT_NETWORK_DIAGNOSTIC_TLV_TIMEOUT, + kConnectivity = OT_NETWORK_DIAGNOSTIC_TLV_CONNECTIVITY, + kRoute = OT_NETWORK_DIAGNOSTIC_TLV_ROUTE, + kLeaderData = OT_NETWORK_DIAGNOSTIC_TLV_LEADER_DATA, + kNetworkData = OT_NETWORK_DIAGNOSTIC_TLV_NETWORK_DATA, + kIp6AddressList = OT_NETWORK_DIAGNOSTIC_TLV_IP6_ADDR_LIST, + kMacCounters = OT_NETWORK_DIAGNOSTIC_TLV_MAC_COUNTERS, + kBatteryLevel = OT_NETWORK_DIAGNOSTIC_TLV_BATTERY_LEVEL, + kSupplyVoltage = OT_NETWORK_DIAGNOSTIC_TLV_SUPPLY_VOLTAGE, + kChildTable = OT_NETWORK_DIAGNOSTIC_TLV_CHILD_TABLE, + kChannelPages = OT_NETWORK_DIAGNOSTIC_TLV_CHANNEL_PAGES, + kTypeList = OT_NETWORK_DIAGNOSTIC_TLV_TYPE_LIST, + kMaxChildTimeout = OT_NETWORK_DIAGNOSTIC_TLV_MAX_CHILD_TIMEOUT, }; /** @@ -919,6 +918,15 @@ public: SetLength(sizeof(*this) - sizeof(NetworkDiagnosticTlv)); } + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return !IsExtended() && (GetLength() % OT_IP6_ADDRESS_SIZE == 0); } + /** * This method returns a pointer to the IPv6 address entry. * @@ -1388,7 +1396,7 @@ public: * @retval OT_ERROR_NOT_FOUND No such entry is found. * @retval OT_ERROR_NONE Successfully read the entry. */ - otError ReadEntry(ChildTableEntry &aEntry, Message &aMessage, uint16_t aOffset, uint8_t aIndex) const + otError ReadEntry(ChildTableEntry &aEntry, const Message &aMessage, uint16_t aOffset, uint8_t aIndex) const { return (aIndex < GetNumEntries() && aMessage.Read(aOffset + sizeof(ChildTableTlv) + (aIndex * sizeof(ChildTableEntry)), @@ -1417,6 +1425,19 @@ public: SetLength(sizeof(*this) - sizeof(NetworkDiagnosticTlv)); } + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const + { + // At least one channel page must be included. + return GetLength() >= 1; + } + /** * This method returns a pointer to the list of Channel Pages. *