blob: 738e904abb8505c3f420bc72090ce2b2b5caaf41 [file] [log] [blame]
/*
* Copyright (c) 2016, 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 Thread's Network Diagnostic processing.
*/
#include "network_diagnostic.hpp"
#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE
#include "coap/coap_message.hpp"
#include "common/array.hpp"
#include "common/as_core_type.hpp"
#include "common/code_utils.hpp"
#include "common/debug.hpp"
#include "common/encoding.hpp"
#include "common/instance.hpp"
#include "common/locator_getters.hpp"
#include "common/log.hpp"
#include "mac/mac.hpp"
#include "net/netif.hpp"
#include "thread/mesh_forwarder.hpp"
#include "thread/mle_router.hpp"
#include "thread/thread_netif.hpp"
#include "thread/thread_tlvs.hpp"
#include "thread/uri_paths.hpp"
namespace ot {
RegisterLogModule("NetDiag");
namespace NetworkDiagnostic {
NetworkDiagnostic::NetworkDiagnostic(Instance &aInstance)
: InstanceLocator(aInstance)
, mDiagnosticGetRequest(UriPath::kDiagnosticGetRequest, &NetworkDiagnostic::HandleDiagnosticGetRequest, this)
, mDiagnosticGetQuery(UriPath::kDiagnosticGetQuery, &NetworkDiagnostic::HandleDiagnosticGetQuery, this)
, mDiagnosticGetAnswer(UriPath::kDiagnosticGetAnswer, &NetworkDiagnostic::HandleDiagnosticGetAnswer, this)
, mDiagnosticReset(UriPath::kDiagnosticReset, &NetworkDiagnostic::HandleDiagnosticReset, this)
, mReceiveDiagnosticGetCallback(nullptr)
, mReceiveDiagnosticGetCallbackContext(nullptr)
{
Get<Tmf::Agent>().AddResource(mDiagnosticGetRequest);
Get<Tmf::Agent>().AddResource(mDiagnosticGetQuery);
Get<Tmf::Agent>().AddResource(mDiagnosticGetAnswer);
Get<Tmf::Agent>().AddResource(mDiagnosticReset);
}
Error NetworkDiagnostic::SendDiagnosticGet(const Ip6::Address & aDestination,
const uint8_t aTlvTypes[],
uint8_t aCount,
otReceiveDiagnosticGetCallback aCallback,
void * aCallbackContext)
{
Error error;
Coap::Message * message = nullptr;
Tmf::MessageInfo messageInfo(GetInstance());
otCoapResponseHandler handler = nullptr;
if (aDestination.IsMulticast())
{
message = Get<Tmf::Agent>().NewNonConfirmablePostMessage(UriPath::kDiagnosticGetQuery);
messageInfo.SetMulticastLoop(true);
}
else
{
handler = &NetworkDiagnostic::HandleDiagnosticGetResponse;
message = Get<Tmf::Agent>().NewConfirmablePostMessage(UriPath::kDiagnosticGetRequest);
}
VerifyOrExit(message != nullptr, error = kErrorNoBufs);
if (aCount > 0)
{
SuccessOrExit(error = Tlv::Append<TypeListTlv>(*message, aTlvTypes, aCount));
}
if (aDestination.IsLinkLocal() || aDestination.IsLinkLocalMulticast())
{
messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetLinkLocalAddress());
}
else
{
messageInfo.SetSockAddrToRloc();
}
messageInfo.SetPeerAddr(aDestination);
SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, handler, this));
mReceiveDiagnosticGetCallback = aCallback;
mReceiveDiagnosticGetCallbackContext = aCallbackContext;
LogInfo("Sent diagnostic get");
exit:
FreeMessageOnError(message, error);
return error;
}
void NetworkDiagnostic::HandleDiagnosticGetResponse(void * aContext,
otMessage * aMessage,
const otMessageInfo *aMessageInfo,
Error aResult)
{
static_cast<NetworkDiagnostic *>(aContext)->HandleDiagnosticGetResponse(AsCoapMessagePtr(aMessage),
AsCoreTypePtr(aMessageInfo), aResult);
}
void NetworkDiagnostic::HandleDiagnosticGetResponse(Coap::Message * aMessage,
const Ip6::MessageInfo *aMessageInfo,
Error aResult)
{
SuccessOrExit(aResult);
VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged, aResult = kErrorFailed);
exit:
if (mReceiveDiagnosticGetCallback)
{
mReceiveDiagnosticGetCallback(aResult, aMessage, aMessageInfo, mReceiveDiagnosticGetCallbackContext);
}
else
{
LogDebg("Received diagnostic get response, error = %s", ErrorToString(aResult));
}
return;
}
void NetworkDiagnostic::HandleDiagnosticGetAnswer(void * aContext,
otMessage * aMessage,
const otMessageInfo *aMessageInfo)
{
static_cast<NetworkDiagnostic *>(aContext)->HandleDiagnosticGetAnswer(AsCoapMessage(aMessage),
AsCoreType(aMessageInfo));
}
void NetworkDiagnostic::HandleDiagnosticGetAnswer(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
VerifyOrExit(aMessage.IsConfirmablePostRequest());
LogInfo("Diagnostic get answer received");
if (mReceiveDiagnosticGetCallback)
{
mReceiveDiagnosticGetCallback(kErrorNone, &aMessage, &aMessageInfo, mReceiveDiagnosticGetCallbackContext);
}
SuccessOrExit(Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo));
LogInfo("Sent diagnostic answer acknowledgment");
exit:
return;
}
Error NetworkDiagnostic::AppendIp6AddressList(Message &aMessage)
{
Error error = kErrorNone;
Ip6AddressListTlv tlv;
uint8_t count = 0;
tlv.Init();
for (const Ip6::Netif::UnicastAddress &addr : Get<ThreadNetif>().GetUnicastAddresses())
{
OT_UNUSED_VARIABLE(addr);
count++;
}
tlv.SetLength(count * sizeof(Ip6::Address));
SuccessOrExit(error = aMessage.Append(tlv));
for (const Ip6::Netif::UnicastAddress &addr : Get<ThreadNetif>().GetUnicastAddresses())
{
SuccessOrExit(error = aMessage.Append(addr.GetAddress()));
}
exit:
return error;
}
#if OPENTHREAD_FTD
Error NetworkDiagnostic::AppendChildTable(Message &aMessage)
{
Error error = kErrorNone;
uint16_t count = 0;
uint8_t timeout = 0;
ChildTableTlv tlv;
ChildTableEntry entry;
tlv.Init();
count = Get<ChildTable>().GetNumChildren(Child::kInStateValid);
// The length of the Child Table TLV may exceed the outgoing link's MTU (1280B).
// As a workaround we limit the number of entries in the Child Table TLV,
// also to avoid using extended TLV format. The issue is processed by the
// Thread Group (SPEC-894).
if (count > (Tlv::kBaseTlvMaxLength / sizeof(ChildTableEntry)))
{
count = Tlv::kBaseTlvMaxLength / sizeof(ChildTableEntry);
}
tlv.SetLength(static_cast<uint8_t>(count * sizeof(ChildTableEntry)));
SuccessOrExit(error = aMessage.Append(tlv));
for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
{
VerifyOrExit(count--);
timeout = 0;
while (static_cast<uint32_t>(1 << timeout) < child.GetTimeout())
{
timeout++;
}
entry.SetReserved(0);
entry.SetTimeout(timeout + 4);
entry.SetChildId(Mle::Mle::ChildIdFromRloc16(child.GetRloc16()));
entry.SetMode(child.GetDeviceMode());
SuccessOrExit(error = aMessage.Append(entry));
}
exit:
return error;
}
#endif // OPENTHREAD_FTD
void NetworkDiagnostic::FillMacCountersTlv(MacCountersTlv &aMacCountersTlv)
{
const otMacCounters &macCounters = Get<Mac::Mac>().GetCounters();
aMacCountersTlv.SetIfInUnknownProtos(macCounters.mRxOther);
aMacCountersTlv.SetIfInErrors(macCounters.mRxErrNoFrame + macCounters.mRxErrUnknownNeighbor +
macCounters.mRxErrInvalidSrcAddr + macCounters.mRxErrSec + macCounters.mRxErrFcs +
macCounters.mRxErrOther);
aMacCountersTlv.SetIfOutErrors(macCounters.mTxErrCca);
aMacCountersTlv.SetIfInUcastPkts(macCounters.mRxUnicast);
aMacCountersTlv.SetIfInBroadcastPkts(macCounters.mRxBroadcast);
aMacCountersTlv.SetIfInDiscards(macCounters.mRxAddressFiltered + macCounters.mRxDestAddrFiltered +
macCounters.mRxDuplicated);
aMacCountersTlv.SetIfOutUcastPkts(macCounters.mTxUnicast);
aMacCountersTlv.SetIfOutBroadcastPkts(macCounters.mTxBroadcast);
aMacCountersTlv.SetIfOutDiscards(macCounters.mTxErrBusyChannel);
}
Error NetworkDiagnostic::FillRequestedTlvs(const Message & aRequest,
Message & aResponse,
NetworkDiagnosticTlv &aNetworkDiagnosticTlv)
{
Error error = kErrorNone;
uint16_t offset = 0;
uint8_t type;
offset = aRequest.GetOffset() + sizeof(NetworkDiagnosticTlv);
for (uint32_t i = 0; i < aNetworkDiagnosticTlv.GetLength(); i++)
{
SuccessOrExit(error = aRequest.Read(offset, type));
LogInfo("Type %d", type);
switch (type)
{
case NetworkDiagnosticTlv::kExtMacAddress:
SuccessOrExit(error = Tlv::Append<ExtMacAddressTlv>(aResponse, Get<Mac::Mac>().GetExtAddress()));
break;
case NetworkDiagnosticTlv::kAddress16:
SuccessOrExit(error = Tlv::Append<Address16Tlv>(aResponse, Get<Mle::MleRouter>().GetRloc16()));
break;
case NetworkDiagnosticTlv::kMode:
SuccessOrExit(error = Tlv::Append<ModeTlv>(aResponse, Get<Mle::MleRouter>().GetDeviceMode().Get()));
break;
case NetworkDiagnosticTlv::kTimeout:
if (!Get<Mle::MleRouter>().IsRxOnWhenIdle())
{
SuccessOrExit(error = Tlv::Append<TimeoutTlv>(aResponse, Get<Mle::MleRouter>().GetTimeout()));
}
break;
#if OPENTHREAD_FTD
case NetworkDiagnosticTlv::kConnectivity:
{
ConnectivityTlv tlv;
tlv.Init();
Get<Mle::MleRouter>().FillConnectivityTlv(reinterpret_cast<Mle::ConnectivityTlv &>(tlv));
SuccessOrExit(error = tlv.AppendTo(aResponse));
break;
}
case NetworkDiagnosticTlv::kRoute:
{
RouteTlv tlv;
tlv.Init();
Get<Mle::MleRouter>().FillRouteTlv(reinterpret_cast<Mle::RouteTlv &>(tlv));
SuccessOrExit(error = tlv.AppendTo(aResponse));
break;
}
#endif
case NetworkDiagnosticTlv::kLeaderData:
{
LeaderDataTlv tlv;
const Mle::LeaderData &leaderData = Get<Mle::MleRouter>().GetLeaderData();
tlv.Init();
tlv.SetPartitionId(leaderData.GetPartitionId());
tlv.SetWeighting(leaderData.GetWeighting());
tlv.SetDataVersion(leaderData.GetDataVersion(NetworkData::kFullSet));
tlv.SetStableDataVersion(leaderData.GetDataVersion(NetworkData::kStableSubset));
tlv.SetLeaderRouterId(leaderData.GetLeaderRouterId());
SuccessOrExit(error = tlv.AppendTo(aResponse));
break;
}
case NetworkDiagnosticTlv::kNetworkData:
{
NetworkData::NetworkData &netData = Get<NetworkData::Leader>();
SuccessOrExit(error = Tlv::Append<NetworkDataTlv>(aResponse, netData.GetBytes(), netData.GetLength()));
break;
}
case NetworkDiagnosticTlv::kIp6AddressList:
{
SuccessOrExit(error = AppendIp6AddressList(aResponse));
break;
}
case NetworkDiagnosticTlv::kMacCounters:
{
MacCountersTlv tlv;
memset(&tlv, 0, sizeof(tlv));
tlv.Init();
FillMacCountersTlv(tlv);
SuccessOrExit(error = tlv.AppendTo(aResponse));
break;
}
case NetworkDiagnosticTlv::kBatteryLevel:
{
// Thread 1.1.1 Specification Section 10.11.4.2:
// Omitted if the battery level is not measured, is unknown or the device does not
// operate on battery power.
break;
}
case NetworkDiagnosticTlv::kSupplyVoltage:
{
// Thread 1.1.1 Specification Section 10.11.4.3:
// Omitted if the supply voltage is not measured, is unknown.
break;
}
#if OPENTHREAD_FTD
case NetworkDiagnosticTlv::kChildTable:
{
// Thread 1.1.1 Specification Section 10.11.2.2:
// If a Thread device is unable to supply a specific Diagnostic TLV, that TLV is omitted.
// Here only Leader or Router may have children.
if (Get<Mle::MleRouter>().IsRouterOrLeader())
{
SuccessOrExit(error = AppendChildTable(aResponse));
}
break;
}
#endif
case NetworkDiagnosticTlv::kChannelPages:
{
uint8_t length = 0;
uint32_t pageMask = Radio::kSupportedChannelPages;
ChannelPagesTlv tlv;
tlv.Init();
for (uint8_t page = 0; page < sizeof(pageMask) * 8; page++)
{
if (pageMask & (1 << page))
{
tlv.GetChannelPages()[length++] = page;
}
}
tlv.SetLength(length);
SuccessOrExit(error = tlv.AppendTo(aResponse));
break;
}
#if OPENTHREAD_FTD
case NetworkDiagnosticTlv::kMaxChildTimeout:
{
uint32_t maxTimeout;
if (Get<Mle::MleRouter>().GetMaxChildTimeout(maxTimeout) == kErrorNone)
{
SuccessOrExit(error = Tlv::Append<MaxChildTimeoutTlv>(aResponse, maxTimeout));
}
break;
}
#endif
default:
// Skip unrecognized TLV type.
break;
}
offset += sizeof(type);
}
exit:
return error;
}
void NetworkDiagnostic::HandleDiagnosticGetQuery(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
{
static_cast<NetworkDiagnostic *>(aContext)->HandleDiagnosticGetQuery(AsCoapMessage(aMessage),
AsCoreType(aMessageInfo));
}
void NetworkDiagnostic::HandleDiagnosticGetQuery(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
Error error = kErrorNone;
Coap::Message * message = nullptr;
NetworkDiagnosticTlv networkDiagnosticTlv;
Tmf::MessageInfo messageInfo(GetInstance());
VerifyOrExit(aMessage.IsPostRequest(), error = kErrorDrop);
LogInfo("Received diagnostic get query");
SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), networkDiagnosticTlv));
VerifyOrExit(networkDiagnosticTlv.GetType() == NetworkDiagnosticTlv::kTypeList, error = kErrorParse);
// DIAG_GET.qry may be sent as a confirmable message.
if (aMessage.IsConfirmable())
{
if (Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo) == kErrorNone)
{
LogInfo("Sent diagnostic get query acknowledgment");
}
}
message = Get<Tmf::Agent>().NewConfirmablePostMessage(UriPath::kDiagnosticGetAnswer);
VerifyOrExit(message != nullptr, error = kErrorNoBufs);
if (aMessageInfo.GetPeerAddr().IsLinkLocal())
{
messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetLinkLocalAddress());
}
else
{
messageInfo.SetSockAddrToRloc();
}
messageInfo.SetPeerAddr(aMessageInfo.GetPeerAddr());
SuccessOrExit(error = FillRequestedTlvs(aMessage, *message, networkDiagnosticTlv));
SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, nullptr, this));
LogInfo("Sent diagnostic get answer");
exit:
FreeMessageOnError(message, error);
}
void NetworkDiagnostic::HandleDiagnosticGetRequest(void * aContext,
otMessage * aMessage,
const otMessageInfo *aMessageInfo)
{
static_cast<NetworkDiagnostic *>(aContext)->HandleDiagnosticGetRequest(AsCoapMessage(aMessage),
AsCoreType(aMessageInfo));
}
void NetworkDiagnostic::HandleDiagnosticGetRequest(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
Error error = kErrorNone;
Coap::Message * message = nullptr;
NetworkDiagnosticTlv networkDiagnosticTlv;
Ip6::MessageInfo messageInfo(aMessageInfo);
VerifyOrExit(aMessage.IsConfirmablePostRequest(), error = kErrorDrop);
LogInfo("Received diagnostic get request");
SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), networkDiagnosticTlv));
VerifyOrExit(networkDiagnosticTlv.GetType() == NetworkDiagnosticTlv::kTypeList, error = kErrorParse);
message = Get<Tmf::Agent>().NewResponseMessage(aMessage);
VerifyOrExit(message != nullptr, error = kErrorNoBufs);
SuccessOrExit(error = FillRequestedTlvs(aMessage, *message, networkDiagnosticTlv));
SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
LogInfo("Sent diagnostic get response");
exit:
FreeMessageOnError(message, error);
}
Error NetworkDiagnostic::SendDiagnosticReset(const Ip6::Address &aDestination,
const uint8_t aTlvTypes[],
uint8_t aCount)
{
Error error;
Coap::Message * message = nullptr;
Tmf::MessageInfo messageInfo(GetInstance());
message = Get<Tmf::Agent>().NewConfirmablePostMessage(UriPath::kDiagnosticReset);
VerifyOrExit(message != nullptr, error = kErrorNoBufs);
if (aCount > 0)
{
SuccessOrExit(error = Tlv::Append<TypeListTlv>(*message, aTlvTypes, aCount));
}
if (aDestination.IsLinkLocal() || aDestination.IsLinkLocalMulticast())
{
messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetLinkLocalAddress());
}
else
{
messageInfo.SetSockAddrToRloc();
}
messageInfo.SetPeerAddr(aDestination);
SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
LogInfo("Sent network diagnostic reset");
exit:
FreeMessageOnError(message, error);
return error;
}
void NetworkDiagnostic::HandleDiagnosticReset(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
{
static_cast<NetworkDiagnostic *>(aContext)->HandleDiagnosticReset(AsCoapMessage(aMessage),
AsCoreType(aMessageInfo));
}
void NetworkDiagnostic::HandleDiagnosticReset(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
uint16_t offset = 0;
uint8_t type;
NetworkDiagnosticTlv tlv;
LogInfo("Received diagnostic reset request");
VerifyOrExit(aMessage.IsConfirmablePostRequest());
SuccessOrExit(aMessage.Read(aMessage.GetOffset(), tlv));
VerifyOrExit(tlv.GetType() == NetworkDiagnosticTlv::kTypeList);
offset = aMessage.GetOffset() + sizeof(NetworkDiagnosticTlv);
for (uint8_t i = 0; i < tlv.GetLength(); i++)
{
SuccessOrExit(aMessage.Read(offset + i, type));
switch (type)
{
case NetworkDiagnosticTlv::kMacCounters:
Get<Mac::Mac>().ResetCounters();
LogInfo("Received diagnostic reset type kMacCounters(9)");
break;
default:
LogInfo("Received diagnostic reset other type %d not resetable", type);
break;
}
}
SuccessOrExit(Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo));
LogInfo("Sent diagnostic reset acknowledgment");
exit:
return;
}
static inline void ParseMode(const Mle::DeviceMode &aMode, otLinkModeConfig &aLinkModeConfig)
{
aLinkModeConfig.mRxOnWhenIdle = aMode.IsRxOnWhenIdle();
aLinkModeConfig.mDeviceType = aMode.IsFullThreadDevice();
aLinkModeConfig.mNetworkData = (aMode.GetNetworkDataType() == NetworkData::kFullSet);
}
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);
}
Error NetworkDiagnostic::GetNextDiagTlv(const Coap::Message &aMessage,
Iterator & aIterator,
otNetworkDiagTlv & aNetworkDiagTlv)
{
Error error = kErrorNone;
uint16_t offset = aMessage.GetOffset() + aIterator;
NetworkDiagnosticTlv tlv;
while (true)
{
uint16_t tlvTotalLength;
VerifyOrExit(aMessage.Read(offset, tlv) == kErrorNone, error = kErrorNotFound);
switch (tlv.GetType())
{
case NetworkDiagnosticTlv::kExtMacAddress:
SuccessOrExit(
error = Tlv::Read<ExtMacAddressTlv>(aMessage, offset, AsCoreType(&aNetworkDiagTlv.mData.mExtAddress)));
break;
case NetworkDiagnosticTlv::kAddress16:
SuccessOrExit(error = Tlv::Read<Address16Tlv>(aMessage, offset, aNetworkDiagTlv.mData.mAddr16));
break;
case NetworkDiagnosticTlv::kMode:
{
uint8_t mode;
SuccessOrExit(error = Tlv::Read<ModeTlv>(aMessage, offset, mode));
ParseMode(Mle::DeviceMode(mode), aNetworkDiagTlv.mData.mMode);
break;
}
case NetworkDiagnosticTlv::kTimeout:
SuccessOrExit(error = Tlv::Read<TimeoutTlv>(aMessage, offset, aNetworkDiagTlv.mData.mTimeout));
break;
case NetworkDiagnosticTlv::kConnectivity:
{
ConnectivityTlv connectivity;
SuccessOrExit(error = aMessage.Read(offset, connectivity));
VerifyOrExit(connectivity.IsValid(), error = kErrorParse);
ParseConnectivity(connectivity, aNetworkDiagTlv.mData.mConnectivity);
break;
}
case NetworkDiagnosticTlv::kRoute:
{
RouteTlv route;
tlvTotalLength = sizeof(tlv) + tlv.GetLength();
VerifyOrExit(tlvTotalLength <= sizeof(route), error = kErrorParse);
SuccessOrExit(error = aMessage.Read(offset, &route, tlvTotalLength));
VerifyOrExit(route.IsValid(), error = kErrorParse);
ParseRoute(route, aNetworkDiagTlv.mData.mRoute);
break;
}
case NetworkDiagnosticTlv::kLeaderData:
{
LeaderDataTlv leaderData;
SuccessOrExit(error = aMessage.Read(offset, leaderData));
VerifyOrExit(leaderData.IsValid(), error = kErrorParse);
ParseLeaderData(leaderData, aNetworkDiagTlv.mData.mLeaderData);
break;
}
case NetworkDiagnosticTlv::kNetworkData:
{
NetworkDataTlv networkData;
tlvTotalLength = sizeof(tlv) + tlv.GetLength();
VerifyOrExit(tlvTotalLength <= sizeof(networkData), error = kErrorParse);
SuccessOrExit(error = aMessage.Read(offset, &networkData, tlvTotalLength));
VerifyOrExit(networkData.IsValid(), error = kErrorParse);
VerifyOrExit(sizeof(aNetworkDiagTlv.mData.mNetworkData.m8) >= networkData.GetLength(), error = kErrorParse);
memcpy(aNetworkDiagTlv.mData.mNetworkData.m8, networkData.GetNetworkData(), networkData.GetLength());
aNetworkDiagTlv.mData.mNetworkData.mCount = networkData.GetLength();
break;
}
case NetworkDiagnosticTlv::kIp6AddressList:
{
Ip6AddressListTlv &ip6AddrList = As<Ip6AddressListTlv>(tlv);
VerifyOrExit(ip6AddrList.IsValid(), error = kErrorParse);
VerifyOrExit(sizeof(aNetworkDiagTlv.mData.mIp6AddrList.mList) >= ip6AddrList.GetLength(),
error = kErrorParse);
SuccessOrExit(error = aMessage.Read(offset + sizeof(ip6AddrList), aNetworkDiagTlv.mData.mIp6AddrList.mList,
ip6AddrList.GetLength()));
aNetworkDiagTlv.mData.mIp6AddrList.mCount = ip6AddrList.GetLength() / OT_IP6_ADDRESS_SIZE;
break;
}
case NetworkDiagnosticTlv::kMacCounters:
{
MacCountersTlv macCounters;
SuccessOrExit(error = aMessage.Read(offset, macCounters));
VerifyOrExit(macCounters.IsValid(), error = kErrorParse);
ParseMacCounters(macCounters, aNetworkDiagTlv.mData.mMacCounters);
break;
}
case NetworkDiagnosticTlv::kBatteryLevel:
SuccessOrExit(error = Tlv::Read<BatteryLevelTlv>(aMessage, offset, aNetworkDiagTlv.mData.mBatteryLevel));
break;
case NetworkDiagnosticTlv::kSupplyVoltage:
SuccessOrExit(error = Tlv::Read<SupplyVoltageTlv>(aMessage, offset, aNetworkDiagTlv.mData.mSupplyVoltage));
break;
case NetworkDiagnosticTlv::kChildTable:
{
ChildTableTlv &childTable = As<ChildTableTlv>(tlv);
VerifyOrExit(childTable.IsValid(), error = kErrorParse);
VerifyOrExit(childTable.GetNumEntries() <= GetArrayLength(aNetworkDiagTlv.mData.mChildTable.mTable),
error = kErrorParse);
for (uint8_t i = 0; i < childTable.GetNumEntries(); ++i)
{
ChildTableEntry childEntry;
VerifyOrExit(childTable.ReadEntry(childEntry, aMessage, offset, i) == kErrorNone, error = kErrorParse);
ParseChildEntry(childEntry, aNetworkDiagTlv.mData.mChildTable.mTable[i]);
}
aNetworkDiagTlv.mData.mChildTable.mCount = childTable.GetNumEntries();
break;
}
case NetworkDiagnosticTlv::kChannelPages:
{
VerifyOrExit(sizeof(aNetworkDiagTlv.mData.mChannelPages.m8) >= tlv.GetLength(), error = kErrorParse);
SuccessOrExit(
error = aMessage.Read(offset + sizeof(tlv), aNetworkDiagTlv.mData.mChannelPages.m8, tlv.GetLength()));
aNetworkDiagTlv.mData.mChannelPages.mCount = tlv.GetLength();
break;
}
case NetworkDiagnosticTlv::kMaxChildTimeout:
SuccessOrExit(error =
Tlv::Read<MaxChildTimeoutTlv>(aMessage, offset, aNetworkDiagTlv.mData.mMaxChildTimeout));
break;
default:
// Ignore unrecognized Network Diagnostic TLV silently and
// continue to top of the `while(true)` loop.
offset += tlv.GetSize();
continue;
}
// Exit if a TLV is recognized and parsed successfully.
aNetworkDiagTlv.mType = tlv.GetType();
aIterator = static_cast<uint16_t>(offset - aMessage.GetOffset() + tlv.GetSize());
ExitNow();
}
exit:
return error;
}
} // namespace NetworkDiagnostic
} // namespace ot
#endif // OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE