[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.
This commit is contained in:
Abtin Keshavarzian
2025-04-01 22:06:48 -07:00
committed by GitHub
parent 7cfae1e05e
commit d2fcf539da
12 changed files with 325 additions and 98 deletions

View File

@ -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;

View File

@ -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

View File

@ -35,6 +35,7 @@
#ifndef OPENTHREAD_NETDIAG_H_
#define OPENTHREAD_NETDIAG_H_
#include <openthread/dataset.h>
#include <openthread/ip6.h>
#include <openthread/thread.h>
@ -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);
/**
* @}
*/

View File

@ -2864,13 +2864,26 @@ Done
### networkdiagnostic reset \<addr\> \<type\> ..
Send network diagnostic request to reset \<addr\>'s tlv of \<type\>s. Currently only `MAC Counters`(9) is supported.
Send network diagnostic request to reset \<addr\>'s tlv of \<type\>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.

View File

@ -7468,6 +7468,46 @@ template <> otError Interpreter::Process<Cmd("networkdiagnostic")>(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;
}

View File

@ -112,3 +112,20 @@ otError otThreadSetVendorAppUrl(otInstance *aInstance, const char *aVendorAppUrl
return AsCoreType(aInstance).Get<NetworkDiagnostic::Server>().SetVendorAppUrl(aVendorAppUrl);
}
#endif
void otThreadSetNonPreferredChannels(otInstance *aInstance, otChannelMask aChannelMask)
{
return AsCoreType(aInstance).Get<NetworkDiagnostic::Server>().SetNonPreferredChannels(aChannelMask);
}
otChannelMask otThreadGetNonPreferredChannels(otInstance *aInstance)
{
return AsCoreType(aInstance).Get<NetworkDiagnostic::Server>().GetNonPreferredChannels();
}
void otThreadSetNonPreferredChannelsResetCallback(otInstance *aInstance,
otThreadNonPreferredChannelsResetCallback aCallback,
void *aContext)
{
AsCoreType(aInstance).Get<NetworkDiagnostic::Server>().SetNonPreferredChannelsResetCallback(aCallback, aContext);
}

View File

@ -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<Entry *>(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);

View File

@ -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.

View File

@ -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<kUriDiagnosticReset>(Coap::Message &aMessage,
Get<Mle::Mle>().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<uint8_t>(tlvLength);
aMessage.ReadBytes(valueOffset, aTlvInfo.mData.mNetworkData.m8, tlvLength);
VerifyOrExit(valueOffsetRange.GetLength() <= NetworkData::NetworkData::kMaxSize, error = kErrorParse);
aTlvInfo.mData.mNetworkData.mCount = static_cast<uint8_t>(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<uint8_t>(Min(tlvLength, GetArrayLength(aTlvInfo.mData.mChannelPages.m8)));
aMessage.ReadBytes(valueOffset, aTlvInfo.mData.mChannelPages.m8, aTlvInfo.mData.mChannelPages.mCount);
aTlvInfo.mData.mChannelPages.mCount = static_cast<uint8_t>(
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<ThreadStackVersionTlv>(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;

View File

@ -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<NonPreferredChannelsResetCallback> mNonPreferredChannelsResetCallback;
};
DeclareTmfHandler(Server, kUriDiagnosticGetRequest);

View File

@ -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,
};
/**

View File

@ -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