From d2fcf539daeb68e4d77fa60183b0147edc8299f5 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 1 Apr 2025 22:06:48 -0700 Subject: [PATCH] [net-diag] implement non-preferred channels mask TLV support (#11367) This commit adds support for the "non-preferred channels" TLV in Network Diagnostics. New APIs and their related CLI commands are added to allow users to get/set this value, which is then used to respond to Diagnostic Get/Query messages requesting this TLV. This commit also introduces a mechanism to monitor and notify the caller when a Network Diagnostic Reset command is received for this TLV. The `test-020-net-diag` test is updated to validate the new TLV and its API. --- include/openthread/dataset.h | 5 +- include/openthread/instance.h | 2 +- include/openthread/netdiag.h | 105 +++++++++++++----- src/cli/README.md | 15 ++- src/cli/cli.cpp | 43 +++++++ src/core/api/netdiag_api.cpp | 17 +++ src/core/meshcop/meshcop_tlvs.cpp | 22 ++-- src/core/meshcop/meshcop_tlvs.hpp | 24 +++- src/core/thread/network_diagnostic.cpp | 61 ++++++---- src/core/thread/network_diagnostic.hpp | 32 ++++++ src/core/thread/network_diagnostic_tlvs.hpp | 61 +++++----- .../cli/test-020-net-diag-vendor-info.py | 36 +++++- 12 files changed, 325 insertions(+), 98 deletions(-) diff --git a/include/openthread/dataset.h b/include/openthread/dataset.h index 840f0d87e..1444ca59d 100644 --- a/include/openthread/dataset.h +++ b/include/openthread/dataset.h @@ -152,7 +152,10 @@ typedef struct otSecurityPolicy } otSecurityPolicy; /** - * Represents Channel Mask. + * Represents a Channel Mask. + * + * The least significant bit (LSB), also referred to as bit 0, corresponds to channel number 0, and so on. + * */ typedef uint32_t otChannelMask; diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 49501ca0d..6bc3f7a9c 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -52,7 +52,7 @@ extern "C" { * * @note This number versions both OpenThread platform and user APIs. */ -#define OPENTHREAD_API_VERSION (492) +#define OPENTHREAD_API_VERSION (493) /** * @addtogroup api-instance diff --git a/include/openthread/netdiag.h b/include/openthread/netdiag.h index 98e1450e5..392c9a441 100644 --- a/include/openthread/netdiag.h +++ b/include/openthread/netdiag.h @@ -35,6 +35,7 @@ #ifndef OPENTHREAD_NETDIAG_H_ #define OPENTHREAD_NETDIAG_H_ +#include #include #include @@ -48,36 +49,37 @@ extern "C" { * @{ */ -#define OT_NETWORK_DIAGNOSTIC_TLV_EXT_ADDRESS 0 ///< MAC Extended Address TLV -#define OT_NETWORK_DIAGNOSTIC_TLV_SHORT_ADDRESS 1 ///< Address16 TLV -#define OT_NETWORK_DIAGNOSTIC_TLV_MODE 2 ///< Mode TLV -#define OT_NETWORK_DIAGNOSTIC_TLV_TIMEOUT 3 ///< Timeout TLV (max polling time period for SEDs) -#define OT_NETWORK_DIAGNOSTIC_TLV_CONNECTIVITY 4 ///< Connectivity TLV -#define OT_NETWORK_DIAGNOSTIC_TLV_ROUTE 5 ///< Route64 TLV -#define OT_NETWORK_DIAGNOSTIC_TLV_LEADER_DATA 6 ///< Leader Data TLV -#define OT_NETWORK_DIAGNOSTIC_TLV_NETWORK_DATA 7 ///< Network Data TLV -#define OT_NETWORK_DIAGNOSTIC_TLV_IP6_ADDR_LIST 8 ///< IPv6 Address List TLV -#define OT_NETWORK_DIAGNOSTIC_TLV_MAC_COUNTERS 9 ///< MAC Counters TLV -#define OT_NETWORK_DIAGNOSTIC_TLV_BATTERY_LEVEL 14 ///< Battery Level TLV -#define OT_NETWORK_DIAGNOSTIC_TLV_SUPPLY_VOLTAGE 15 ///< Supply Voltage TLV -#define OT_NETWORK_DIAGNOSTIC_TLV_CHILD_TABLE 16 ///< Child Table TLV -#define OT_NETWORK_DIAGNOSTIC_TLV_CHANNEL_PAGES 17 ///< Channel Pages TLV -#define OT_NETWORK_DIAGNOSTIC_TLV_TYPE_LIST 18 ///< Type List TLV -#define OT_NETWORK_DIAGNOSTIC_TLV_MAX_CHILD_TIMEOUT 19 ///< Max Child Timeout TLV -#define OT_NETWORK_DIAGNOSTIC_TLV_EUI64 23 ///< EUI64 TLV -#define OT_NETWORK_DIAGNOSTIC_TLV_VERSION 24 ///< Thread Version TLV -#define OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_NAME 25 ///< Vendor Name TLV -#define OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_MODEL 26 ///< Vendor Model TLV -#define OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_SW_VERSION 27 ///< Vendor SW Version TLV -#define OT_NETWORK_DIAGNOSTIC_TLV_THREAD_STACK_VERSION 28 ///< Thread Stack Version TLV (codebase/commit version) -#define OT_NETWORK_DIAGNOSTIC_TLV_CHILD 29 ///< Child TLV -#define OT_NETWORK_DIAGNOSTIC_TLV_CHILD_IP6_ADDR_LIST 30 ///< Child IPv6 Address List TLV -#define OT_NETWORK_DIAGNOSTIC_TLV_ROUTER_NEIGHBOR 31 ///< Router Neighbor TLV -#define OT_NETWORK_DIAGNOSTIC_TLV_ANSWER 32 ///< Answer TLV -#define OT_NETWORK_DIAGNOSTIC_TLV_QUERY_ID 33 ///< Query ID TLV -#define OT_NETWORK_DIAGNOSTIC_TLV_MLE_COUNTERS 34 ///< MLE Counters TLV -#define OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_APP_URL 35 ///< Vendor App URL TLV -#define OT_NETWORK_DIAGNOSTIC_TLV_ENHANCED_ROUTE 37 ///< Enhanced Route TLV +#define OT_NETWORK_DIAGNOSTIC_TLV_EXT_ADDRESS 0 ///< MAC Extended Address TLV +#define OT_NETWORK_DIAGNOSTIC_TLV_SHORT_ADDRESS 1 ///< Address16 TLV +#define OT_NETWORK_DIAGNOSTIC_TLV_MODE 2 ///< Mode TLV +#define OT_NETWORK_DIAGNOSTIC_TLV_TIMEOUT 3 ///< Timeout TLV (max polling time period for SEDs) +#define OT_NETWORK_DIAGNOSTIC_TLV_CONNECTIVITY 4 ///< Connectivity TLV +#define OT_NETWORK_DIAGNOSTIC_TLV_ROUTE 5 ///< Route64 TLV +#define OT_NETWORK_DIAGNOSTIC_TLV_LEADER_DATA 6 ///< Leader Data TLV +#define OT_NETWORK_DIAGNOSTIC_TLV_NETWORK_DATA 7 ///< Network Data TLV +#define OT_NETWORK_DIAGNOSTIC_TLV_IP6_ADDR_LIST 8 ///< IPv6 Address List TLV +#define OT_NETWORK_DIAGNOSTIC_TLV_MAC_COUNTERS 9 ///< MAC Counters TLV +#define OT_NETWORK_DIAGNOSTIC_TLV_BATTERY_LEVEL 14 ///< Battery Level TLV +#define OT_NETWORK_DIAGNOSTIC_TLV_SUPPLY_VOLTAGE 15 ///< Supply Voltage TLV +#define OT_NETWORK_DIAGNOSTIC_TLV_CHILD_TABLE 16 ///< Child Table TLV +#define OT_NETWORK_DIAGNOSTIC_TLV_CHANNEL_PAGES 17 ///< Channel Pages TLV +#define OT_NETWORK_DIAGNOSTIC_TLV_TYPE_LIST 18 ///< Type List TLV +#define OT_NETWORK_DIAGNOSTIC_TLV_MAX_CHILD_TIMEOUT 19 ///< Max Child Timeout TLV +#define OT_NETWORK_DIAGNOSTIC_TLV_EUI64 23 ///< EUI64 TLV +#define OT_NETWORK_DIAGNOSTIC_TLV_VERSION 24 ///< Thread Version TLV +#define OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_NAME 25 ///< Vendor Name TLV +#define OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_MODEL 26 ///< Vendor Model TLV +#define OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_SW_VERSION 27 ///< Vendor SW Version TLV +#define OT_NETWORK_DIAGNOSTIC_TLV_THREAD_STACK_VERSION 28 ///< Thread Stack Version TLV (codebase/commit version) +#define OT_NETWORK_DIAGNOSTIC_TLV_CHILD 29 ///< Child TLV +#define OT_NETWORK_DIAGNOSTIC_TLV_CHILD_IP6_ADDR_LIST 30 ///< Child IPv6 Address List TLV +#define OT_NETWORK_DIAGNOSTIC_TLV_ROUTER_NEIGHBOR 31 ///< Router Neighbor TLV +#define OT_NETWORK_DIAGNOSTIC_TLV_ANSWER 32 ///< Answer TLV +#define OT_NETWORK_DIAGNOSTIC_TLV_QUERY_ID 33 ///< Query ID TLV +#define OT_NETWORK_DIAGNOSTIC_TLV_MLE_COUNTERS 34 ///< MLE Counters TLV +#define OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_APP_URL 35 ///< Vendor App URL TLV +#define OT_NETWORK_DIAGNOSTIC_TLV_NON_PREFERRED_CHANNELS 36 ///< Non-Preferred Channels Mask TLV +#define OT_NETWORK_DIAGNOSTIC_TLV_ENHANCED_ROUTE 37 ///< Enhanced Route TLV #define OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_NAME_TLV_LENGTH 32 ///< Max length of Vendor Name TLV. #define OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_MODEL_TLV_LENGTH 32 ///< Max length of Vendor Model TLV. @@ -254,6 +256,7 @@ typedef struct otNetworkDiagTlv char mVendorSwVersion[OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_SW_VERSION_TLV_LENGTH + 1]; char mThreadStackVersion[OT_NETWORK_DIAGNOSTIC_MAX_THREAD_STACK_VERSION_TLV_LENGTH + 1]; char mVendorAppUrl[OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_APP_URL_TLV_LENGTH + 1]; + otChannelMask mNonPreferredChannels; struct { uint8_t mCount; @@ -453,6 +456,48 @@ otError otThreadSetVendorSwVersion(otInstance *aInstance, const char *aVendorSwV */ otError otThreadSetVendorAppUrl(otInstance *aInstance, const char *aVendorAppUrl); +/** + * Callback function pointer to notify when a Network Diagnostic Reset request message is received for the + * `OT_NETWORK_DIAGNOSTIC_TLV_NON_PREFERRED_CHANNELS` TLV. + * + * This is used to inform the device to reevaluate the channels that are presently included in the non-preferred + * channels list and update it if needed based on the reevaluation. + * + * @param[in] aContext A pointer to application-specific context. + */ +typedef void (*otThreadNonPreferredChannelsResetCallback)(void *aContext); + +/** + * Sets the non-preferred channels value for `OT_NETWORK_DIAGNOSTIC_TLV_NON_PREFERRED_CHANNELS` TLV. + * + * This value is used to respond to a Network Diagnostic Get request for this TLV. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aChannelMask A channel mask specifying the non-preferred channels. + */ +void otThreadSetNonPreferredChannels(otInstance *aInstance, otChannelMask aChannelMask); + +/** + * Gets the non-preferred channels for `OT_NETWORK_DIAGNOSTIC_TLV_NON_PREFERRED_CHANNELS` TLV. + * + * @returns The non-preferred channels as a channel mask. + */ +otChannelMask otThreadGetNonPreferredChannels(otInstance *aInstance); + +/** + * Sets the callback to notify when a Network Diagnostic Reset request message is received for the + * `OT_NETWORK_DIAGNOSTIC_TLV_NON_PREFERRED_CHANNELS` TLV. + * + * A subsequent call to this function will replace the previously set callback. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aCallback The callback function pointer. Can be NULL. + * @param[in] aContext A pointer to application-specific context used with @p aCallback. + */ +void otThreadSetNonPreferredChannelsResetCallback(otInstance *aInstance, + otThreadNonPreferredChannelsResetCallback aCallback, + void *aContext); + /** * @} */ diff --git a/src/cli/README.md b/src/cli/README.md index 92ebf1621..51f80e8e1 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -2864,13 +2864,26 @@ Done ### networkdiagnostic reset \ \ .. -Send network diagnostic request to reset \'s tlv of \s. Currently only `MAC Counters`(9) is supported. +Send network diagnostic request to reset \'s tlv of \s. Currently `MAC Counters`(9) is supported. ```bash > diagnostic reset fd00:db8::ff:fe00:0 9 Done ``` +### networkdiagnostic nonpreferredchannels + +Get or set the non-preferred channels value as a channel mask. This is used to respond to a Network Diagnostics Get request for the corresponding TLV. The channel mask is a 32-bit unsigned integer value where the least significant bit (LSB), also referred to as bit 0, corresponds to channel number 0, and so on. + +```bash +> networkdiagnostic nonpreferredchannels 0x4000000 +Done + +> networkdiagnostic nonpreferredchannels +0x4000000 +Done +``` + ### networkidtimeout Get the NETWORK_ID_TIMEOUT parameter used in the Router role. diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 10026d717..16ed8ae66 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -7468,6 +7468,46 @@ template <> otError Interpreter::Process(Arg aArgs[]) uint8_t tlvTypes[kMaxTlvs]; uint8_t count = 0; + if (aArgs[0] == "nonpreferredchannels") + { + /** + * @cli networkdiagnostic nonpreferredchannels + * @code + * networkdiagnostic nonpreferredchannels + * 0x4000000 + * Done + * @endcode + * @par api_copy + * #otThreadGetNonPreferredChannels + */ + if (aArgs[1].IsEmpty()) + { + OutputLine("0x%lx", ToUlong(otThreadGetNonPreferredChannels(GetInstancePtr()))); + } + /** + * @cli networkdiagnostic nonpreferredchannels (set) + * @code + * networkdiagnostic nonpreferredchannels 0x4000000 + * Done + * @endcode + * @par api_copy + * #otThreadSetNonPreferredChannels + * @cparam networkdiagnostic nonprfchannelmas @ca{mask} + */ + else + { + otChannelMask mask; + + SuccessOrExit(error = aArgs[1].ParseAsUint32(mask)); + VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + otThreadSetNonPreferredChannels(GetInstancePtr(), mask); + } + + ExitNow(); + } + + // Process args for `get` and `reset` commands. + SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address)); for (Arg *arg = &aArgs[2]; !arg->IsEmpty(); arg++) @@ -7709,6 +7749,9 @@ void Interpreter::HandleDiagnosticGetResponse(otError aError, case OT_NETWORK_DIAGNOSTIC_TLV_THREAD_STACK_VERSION: OutputLine("Thread Stack Version: %s", diagTlv.mData.mThreadStackVersion); break; + case OT_NETWORK_DIAGNOSTIC_TLV_NON_PREFERRED_CHANNELS: + OutputLine("Non-preferred Channels Mask: 0x%lx", ToUlong(diagTlv.mData.mNonPreferredChannels)); + break; default: break; } diff --git a/src/core/api/netdiag_api.cpp b/src/core/api/netdiag_api.cpp index fe026aaba..623ca53ed 100644 --- a/src/core/api/netdiag_api.cpp +++ b/src/core/api/netdiag_api.cpp @@ -112,3 +112,20 @@ otError otThreadSetVendorAppUrl(otInstance *aInstance, const char *aVendorAppUrl return AsCoreType(aInstance).Get().SetVendorAppUrl(aVendorAppUrl); } #endif + +void otThreadSetNonPreferredChannels(otInstance *aInstance, otChannelMask aChannelMask) +{ + return AsCoreType(aInstance).Get().SetNonPreferredChannels(aChannelMask); +} + +otChannelMask otThreadGetNonPreferredChannels(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().GetNonPreferredChannels(); +} + +void otThreadSetNonPreferredChannelsResetCallback(otInstance *aInstance, + otThreadNonPreferredChannelsResetCallback aCallback, + void *aContext) +{ + AsCoreType(aInstance).Get().SetNonPreferredChannelsResetCallback(aCallback, aContext); +} diff --git a/src/core/meshcop/meshcop_tlvs.cpp b/src/core/meshcop/meshcop_tlvs.cpp index db54907e5..03ebef398 100644 --- a/src/core/meshcop/meshcop_tlvs.cpp +++ b/src/core/meshcop/meshcop_tlvs.cpp @@ -140,20 +140,26 @@ Error ChannelMaskTlv::ReadChannelMask(uint32_t &aChannelMask) const Error ChannelMaskTlv::FindIn(const Message &aMessage, uint32_t &aChannelMask) { Error error; - EntriesData entriesData; OffsetRange offsetRange; - entriesData.Clear(); - entriesData.mMessage = &aMessage; - SuccessOrExit(error = FindTlvValueOffsetRange(aMessage, Tlv::kChannelMask, offsetRange)); - entriesData.mOffsetRange = offsetRange; - error = entriesData.Parse(aChannelMask); + error = ParseValue(aMessage, offsetRange, aChannelMask); exit: return error; } +Error ChannelMaskTlv::ParseValue(const Message &aMessage, const OffsetRange &aOffsetRange, uint32_t &aChannelMask) +{ + EntriesData entriesData; + + entriesData.Clear(); + entriesData.mMessage = &aMessage; + entriesData.mOffsetRange = aOffsetRange; + + return entriesData.Parse(aChannelMask); +} + Error ChannelMaskTlv::EntriesData::Parse(uint32_t &aChannelMask) { // Validates and parses the Channel Mask TLV entries for each @@ -217,7 +223,7 @@ exit: return error; } -void ChannelMaskTlv::PrepareValue(Value &aValue, uint32_t aChannelMask) +void ChannelMaskTlv::PrepareValue(Value &aValue, uint32_t aChannelMask, bool aIncludeZeroPageMasks) { Entry *entry = reinterpret_cast(aValue.mData); @@ -227,7 +233,7 @@ void ChannelMaskTlv::PrepareValue(Value &aValue, uint32_t aChannelMask) { uint32_t mask = (Radio::ChannelMaskForPage(page) & aChannelMask); - if (mask != 0) + if ((mask != 0) || aIncludeZeroPageMasks) { entry->SetChannelPage(page); entry->SetMaskLength(kMaskLength); diff --git a/src/core/meshcop/meshcop_tlvs.hpp b/src/core/meshcop/meshcop_tlvs.hpp index 713384aca..91b955f6e 100644 --- a/src/core/meshcop/meshcop_tlvs.hpp +++ b/src/core/meshcop/meshcop_tlvs.hpp @@ -640,13 +640,31 @@ public: */ static Error FindIn(const Message &aMessage, uint32_t &aChannelMask); + /** + * Parses and validates the TLV value and returns the combined channel mask for all supported channel pages + * included in the TLV. + * + * The Channel Mask TLV value entries for each channel page are parsed one by one and `aChannelMask` is updated + * to return the combined mask for all channel pages that are supported by radio. Note that @p aOffsetRange + * corresponds to offset range where the TLV value resides within @p aMessage (not the full TLV). + * + * @param[in] aMessage The message to read the TLV value from. + * @param[in] aOffsetRange The offset range for the TLV value. + * @param[out] aChannelMask A reference to return the channel mask. + * + * @retval kErrorNone Successfully parsed the TLV value, @p aChannelMask is updated. + * @retval kErrorParse Failed to parse the TLV value. + */ + static Error ParseValue(const Message &aMessage, const OffsetRange &aOffsetRange, uint32_t &aChannelMask); + /** * Prepares Channel Mask TLV value for appending/writing. * - * @param[out] aValue A reference to `Value` structure to populate. - * @param[in] aChannelMask The combined channel mask for all supported channel pages. + * @param[out] aValue A reference to `Value` structure to populate. + * @param[in] aChannelMask The combined channel mask for all supported channel pages. + * @param[in] aIncludeZeroPageMasks Determine whether to include or skip a zero mask for a supported channel page. */ - static void PrepareValue(Value &aValue, uint32_t aChannelMask); + static void PrepareValue(Value &aValue, uint32_t aChannelMask, bool aIncludeZeroPageMasks = false); /** * Prepares a Channel Mask TLV value and appends the TLV to a given message. diff --git a/src/core/thread/network_diagnostic.cpp b/src/core/thread/network_diagnostic.cpp index ed3c14485..ba1c52ca4 100644 --- a/src/core/thread/network_diagnostic.cpp +++ b/src/core/thread/network_diagnostic.cpp @@ -51,6 +51,7 @@ const char Server::kVendorAppUrl[] = OPENTHREAD_CONFIG_NET_DIAG_VENDOR_APP_UR Server::Server(Instance &aInstance) : InstanceLocator(aInstance) + , mNonPreferredChannels(0) { static_assert(sizeof(kVendorName) <= sizeof(VendorNameTlv::StringType), "VENDOR_NAME is too long"); static_assert(sizeof(kVendorModel) <= sizeof(VendorModelTlv::StringType), "VENDOR_MODEL is too long"); @@ -387,6 +388,15 @@ Error Server::AppendDiagTlv(uint8_t aTlvType, Message &aMessage) break; } + case Tlv::kNonPreferredChannels: + { + MeshCoP::ChannelMaskTlv::Value value; + + MeshCoP::ChannelMaskTlv::PrepareValue(value, mNonPreferredChannels, /* aIncludeZeroPageMasks */ true); + error = Tlv::AppendTlv(aMessage, Tlv::kNonPreferredChannels, value.mData, value.mLength); + break; + } + #if OPENTHREAD_FTD case Tlv::kConnectivity: @@ -884,6 +894,10 @@ template <> void Server::HandleTmf(Coap::Message &aMessage, Get().ResetCounters(); break; + case Tlv::kNonPreferredChannels: + mNonPreferredChannelsResetCallback.InvokeIfSet(); + break; + default: break; } @@ -1110,9 +1124,8 @@ Error Client::GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator, while (offset < aMessage.GetLength()) { - bool skipTlv = false; - uint16_t valueOffset; - uint16_t tlvLength; + bool skipTlv = false; + OffsetRange valueOffsetRange; union { Tlv tlv; @@ -1124,13 +1137,11 @@ Error Client::GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator, if (tlv.IsExtended()) { SuccessOrExit(error = aMessage.Read(offset, extTlv)); - valueOffset = offset + sizeof(ExtendedTlv); - tlvLength = extTlv.GetLength(); + valueOffsetRange.Init(offset + sizeof(ExtendedTlv), extTlv.GetLength()); } else { - valueOffset = offset + sizeof(Tlv); - tlvLength = tlv.GetLength(); + valueOffsetRange.Init(offset + sizeof(Tlv), tlv.GetLength()); } VerifyOrExit(offset + tlv.GetSize() <= aMessage.GetLength(), error = kErrorParse); @@ -1201,9 +1212,9 @@ Error Client::GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator, static_assert(sizeof(aTlvInfo.mData.mNetworkData.m8) >= NetworkData::NetworkData::kMaxSize, "NetworkData array in `otNetworkDiagTlv` is too small"); - VerifyOrExit(tlvLength <= NetworkData::NetworkData::kMaxSize, error = kErrorParse); - aTlvInfo.mData.mNetworkData.mCount = static_cast(tlvLength); - aMessage.ReadBytes(valueOffset, aTlvInfo.mData.mNetworkData.m8, tlvLength); + VerifyOrExit(valueOffsetRange.GetLength() <= NetworkData::NetworkData::kMaxSize, error = kErrorParse); + aTlvInfo.mData.mNetworkData.mCount = static_cast(valueOffsetRange.GetLength()); + aMessage.ReadBytes(valueOffsetRange, aTlvInfo.mData.mNetworkData.m8); break; case Tlv::kIp6AddressList: @@ -1212,7 +1223,7 @@ Error Client::GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator, Ip6::Address *addrEntry = AsCoreTypePtr(&aTlvInfo.mData.mIp6AddrList.mList[0]); uint8_t &addrCount = aTlvInfo.mData.mIp6AddrList.mCount; - VerifyOrExit((tlvLength % Ip6::Address::kSize) == 0, error = kErrorParse); + VerifyOrExit((valueOffsetRange.GetLength() % Ip6::Address::kSize) == 0, error = kErrorParse); // `TlvInfo` has a fixed array for IPv6 addresses. If there // are more addresses in the message, we read and return as @@ -1220,13 +1231,12 @@ Error Client::GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator, addrCount = 0; - while ((tlvLength > 0) && (addrCount < addrListLength)) + while (!valueOffsetRange.IsEmpty() && (addrCount < addrListLength)) { - SuccessOrExit(error = aMessage.Read(valueOffset, *addrEntry)); + SuccessOrExit(error = aMessage.Read(valueOffsetRange, *addrEntry)); addrCount++; addrEntry++; - valueOffset += Ip6::Address::kSize; - tlvLength -= Ip6::Address::kSize; + valueOffsetRange.AdvanceOffset(Ip6::Address::kSize); } break; @@ -1266,7 +1276,7 @@ Error Client::GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator, ChildInfo *childInfo = &aTlvInfo.mData.mChildTable.mTable[0]; uint8_t &childCount = aTlvInfo.mData.mChildTable.mCount; - VerifyOrExit((tlvLength % sizeof(ChildTableEntry)) == 0, error = kErrorParse); + VerifyOrExit((valueOffsetRange.GetLength() % sizeof(ChildTableEntry)) == 0, error = kErrorParse); // `TlvInfo` has a fixed array Child Table entries. If there // are more entries in the message, we read and return as @@ -1274,11 +1284,11 @@ Error Client::GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator, childCount = 0; - while ((tlvLength > 0) && (childCount < childInfoLength)) + while (!valueOffsetRange.IsEmpty() && (childCount < childInfoLength)) { ChildTableEntry entry; - SuccessOrExit(error = aMessage.Read(valueOffset, entry)); + SuccessOrExit(error = aMessage.Read(valueOffsetRange, entry)); childInfo->mTimeout = entry.GetTimeout(); childInfo->mLinkQuality = entry.GetLinkQuality(); @@ -1287,17 +1297,17 @@ Error Client::GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator, childCount++; childInfo++; - tlvLength -= sizeof(ChildTableEntry); - valueOffset += sizeof(ChildTableEntry); + valueOffsetRange.AdvanceOffset(sizeof(ChildTableEntry)); } break; } case Tlv::kChannelPages: - aTlvInfo.mData.mChannelPages.mCount = - static_cast(Min(tlvLength, GetArrayLength(aTlvInfo.mData.mChannelPages.m8))); - aMessage.ReadBytes(valueOffset, aTlvInfo.mData.mChannelPages.m8, aTlvInfo.mData.mChannelPages.mCount); + aTlvInfo.mData.mChannelPages.mCount = static_cast( + Min(valueOffsetRange.GetLength(), GetArrayLength(aTlvInfo.mData.mChannelPages.m8))); + aMessage.ReadBytes(valueOffsetRange.GetOffset(), aTlvInfo.mData.mChannelPages.m8, + aTlvInfo.mData.mChannelPages.mCount); break; case Tlv::kMaxChildTimeout: @@ -1333,6 +1343,11 @@ Error Client::GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator, Tlv::Read(aMessage, offset, aTlvInfo.mData.mThreadStackVersion)); break; + case Tlv::kNonPreferredChannels: + SuccessOrExit(error = MeshCoP::ChannelMaskTlv::ParseValue(aMessage, valueOffsetRange, + aTlvInfo.mData.mNonPreferredChannels)); + break; + default: // Skip unrecognized TLVs. skipTlv = true; diff --git a/src/core/thread/network_diagnostic.hpp b/src/core/thread/network_diagnostic.hpp index beeab1ae6..8d75e9e63 100644 --- a/src/core/thread/network_diagnostic.hpp +++ b/src/core/thread/network_diagnostic.hpp @@ -74,6 +74,11 @@ class Server : public InstanceLocator, private NonCopyable friend class Client; public: + /** + * Callback function pointer to notify a reset request for `kNonPreferredChannels` TLV value. + */ + typedef otThreadNonPreferredChannelsResetCallback NonPreferredChannelsResetCallback; + /** * Initializes the Server. * @@ -81,6 +86,31 @@ public: */ explicit Server(Instance &aInstance); + /** + * Sets the non-preferred channels value for `kNonPreferredChannels` TLV. + * + * @param[in] aChannelMask A channel mask for non-preferred channels. + */ + void SetNonPreferredChannels(uint32_t aChannelMask) { mNonPreferredChannels = aChannelMask; } + + /** + * Gets the non-preferred channel mask value for `kNonPreferredChannels` TLV. + * + * @returns The non-preferred channels as a channel mask. + */ + uint32_t GetNonPreferredChannels(void) const { return mNonPreferredChannels; } + + /** + * Sets the callback to notify when a Diagnostic Reset request is received for `kNonPreferredChannels` TLV value. + * + * @param[in] aCallback The callback function pointer. + * @param[in] aContext An arbitrary context used with @p aCallback. + */ + void SetNonPreferredChannelsResetCallback(NonPreferredChannelsResetCallback aCallback, void *aContext) + { + mNonPreferredChannelsResetCallback.Set(aCallback, aContext); + } + #if OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE /** * Returns the vendor name string. @@ -229,6 +259,8 @@ private: #if OPENTHREAD_FTD Coap::MessageQueue mAnswerQueue; #endif + uint32_t mNonPreferredChannels; + Callback mNonPreferredChannelsResetCallback; }; DeclareTmfHandler(Server, kUriDiagnosticGetRequest); diff --git a/src/core/thread/network_diagnostic_tlvs.hpp b/src/core/thread/network_diagnostic_tlvs.hpp index 08d9e81c8..bb09a2ff4 100644 --- a/src/core/thread/network_diagnostic_tlvs.hpp +++ b/src/core/thread/network_diagnostic_tlvs.hpp @@ -66,36 +66,37 @@ public: */ enum Type : uint8_t { - 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, - kEui64 = OT_NETWORK_DIAGNOSTIC_TLV_EUI64, - kVersion = OT_NETWORK_DIAGNOSTIC_TLV_VERSION, - kVendorName = OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_NAME, - kVendorModel = OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_MODEL, - kVendorSwVersion = OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_SW_VERSION, - kThreadStackVersion = OT_NETWORK_DIAGNOSTIC_TLV_THREAD_STACK_VERSION, - kChild = OT_NETWORK_DIAGNOSTIC_TLV_CHILD, - kChildIp6AddressList = OT_NETWORK_DIAGNOSTIC_TLV_CHILD_IP6_ADDR_LIST, - kRouterNeighbor = OT_NETWORK_DIAGNOSTIC_TLV_ROUTER_NEIGHBOR, - kAnswer = OT_NETWORK_DIAGNOSTIC_TLV_ANSWER, - kQueryId = OT_NETWORK_DIAGNOSTIC_TLV_QUERY_ID, - kMleCounters = OT_NETWORK_DIAGNOSTIC_TLV_MLE_COUNTERS, - kVendorAppUrl = OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_APP_URL, - kEnhancedRoute = OT_NETWORK_DIAGNOSTIC_TLV_ENHANCED_ROUTE, + 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, + kEui64 = OT_NETWORK_DIAGNOSTIC_TLV_EUI64, + kVersion = OT_NETWORK_DIAGNOSTIC_TLV_VERSION, + kVendorName = OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_NAME, + kVendorModel = OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_MODEL, + kVendorSwVersion = OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_SW_VERSION, + kThreadStackVersion = OT_NETWORK_DIAGNOSTIC_TLV_THREAD_STACK_VERSION, + kChild = OT_NETWORK_DIAGNOSTIC_TLV_CHILD, + kChildIp6AddressList = OT_NETWORK_DIAGNOSTIC_TLV_CHILD_IP6_ADDR_LIST, + kRouterNeighbor = OT_NETWORK_DIAGNOSTIC_TLV_ROUTER_NEIGHBOR, + kAnswer = OT_NETWORK_DIAGNOSTIC_TLV_ANSWER, + kQueryId = OT_NETWORK_DIAGNOSTIC_TLV_QUERY_ID, + kMleCounters = OT_NETWORK_DIAGNOSTIC_TLV_MLE_COUNTERS, + kVendorAppUrl = OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_APP_URL, + kNonPreferredChannels = OT_NETWORK_DIAGNOSTIC_TLV_NON_PREFERRED_CHANNELS, + kEnhancedRoute = OT_NETWORK_DIAGNOSTIC_TLV_ENHANCED_ROUTE, }; /** diff --git a/tests/toranj/cli/test-020-net-diag-vendor-info.py b/tests/toranj/cli/test-020-net-diag-vendor-info.py index 463854a9c..86acd0371 100755 --- a/tests/toranj/cli/test-020-net-diag-vendor-info.py +++ b/tests/toranj/cli/test-020-net-diag-vendor-info.py @@ -70,6 +70,7 @@ VENDOR_SW_VERSION_TLV = 27 THREAD_STACK_VERSION_TLV = 28 MLE_COUNTERS_TLV = 34 VENDOR_APP_URL = 35 +NON_PREFERRED_CHANNELS_TLV = 36 #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Check setting vendor name, model, ans sw version @@ -192,9 +193,42 @@ for line in result[1:]: verify(False) result = r2.cli('networkdiagnostic get', r1_rloc, MLE_COUNTERS_TLV) -print(len(result) >= 1) verify(result[1].startswith("MLE Counters:")) +# Test Non-preferred Channels TLV + + +def verify_non_preferred_channels_query_result(mask): + r1.cli('networkdiagnostic nonpreferredchannels', mask) + result = r1.cli('networkdiagnostic nonpreferredchannels') + verify(len(result) == 1) + verify(int(result[0], 16) == mask) + + +result = r1.cli('networkdiagnostic nonpreferredchannels') +verify(len(result) == 1) +verify(int(result[0], 16) == 0) + +verify_non_preferred_channels_query_result(0) + +mask = 1 << 26 # channel 26 + +r1.cli('networkdiagnostic nonpreferredchannels', mask) +result = r1.cli('networkdiagnostic nonpreferredchannels') +verify(len(result) == 1) +verify(int(result[0], 16) == mask) + +verify_non_preferred_channels_query_result(mask) + +mask = 0 + +r1.cli('networkdiagnostic nonpreferredchannels', mask) +result = r1.cli('networkdiagnostic nonpreferredchannels') +verify(len(result) == 1) +verify(int(result[0], 16) == mask) + +verify_non_preferred_channels_query_result(mask) + # ----------------------------------------------------------------------------------------------------------------------- # Test finished