/* * 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().AddResource(mDiagnosticGetRequest); Get().AddResource(mDiagnosticGetQuery); Get().AddResource(mDiagnosticGetAnswer); Get().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().NewNonConfirmablePostMessage(UriPath::kDiagnosticGetQuery); messageInfo.SetMulticastLoop(true); } else { handler = &NetworkDiagnostic::HandleDiagnosticGetResponse; message = Get().NewConfirmablePostMessage(UriPath::kDiagnosticGetRequest); } VerifyOrExit(message != nullptr, error = kErrorNoBufs); if (aCount > 0) { SuccessOrExit(error = Tlv::Append(*message, aTlvTypes, aCount)); } if (aDestination.IsLinkLocal() || aDestination.IsLinkLocalMulticast()) { messageInfo.SetSockAddr(Get().GetLinkLocalAddress()); } else { messageInfo.SetSockAddrToRloc(); } messageInfo.SetPeerAddr(aDestination); SuccessOrExit(error = Get().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(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(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().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().GetUnicastAddresses()) { OT_UNUSED_VARIABLE(addr); count++; } tlv.SetLength(count * sizeof(Ip6::Address)); SuccessOrExit(error = aMessage.Append(tlv)); for (const Ip6::Netif::UnicastAddress &addr : Get().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().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(count * sizeof(ChildTableEntry))); SuccessOrExit(error = aMessage.Append(tlv)); for (Child &child : Get().Iterate(Child::kInStateValid)) { VerifyOrExit(count--); timeout = 0; while (static_cast(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().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(aResponse, Get().GetExtAddress())); break; case NetworkDiagnosticTlv::kAddress16: SuccessOrExit(error = Tlv::Append(aResponse, Get().GetRloc16())); break; case NetworkDiagnosticTlv::kMode: SuccessOrExit(error = Tlv::Append(aResponse, Get().GetDeviceMode().Get())); break; case NetworkDiagnosticTlv::kTimeout: if (!Get().IsRxOnWhenIdle()) { SuccessOrExit(error = Tlv::Append(aResponse, Get().GetTimeout())); } break; #if OPENTHREAD_FTD case NetworkDiagnosticTlv::kConnectivity: { ConnectivityTlv tlv; tlv.Init(); Get().FillConnectivityTlv(reinterpret_cast(tlv)); SuccessOrExit(error = tlv.AppendTo(aResponse)); break; } case NetworkDiagnosticTlv::kRoute: { RouteTlv tlv; tlv.Init(); Get().FillRouteTlv(reinterpret_cast(tlv)); SuccessOrExit(error = tlv.AppendTo(aResponse)); break; } #endif case NetworkDiagnosticTlv::kLeaderData: { LeaderDataTlv tlv; const Mle::LeaderData &leaderData = Get().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(); SuccessOrExit(error = Tlv::Append(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().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().GetMaxChildTimeout(maxTimeout) == kErrorNone) { SuccessOrExit(error = Tlv::Append(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(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().SendEmptyAck(aMessage, aMessageInfo) == kErrorNone) { LogInfo("Sent diagnostic get query acknowledgment"); } } message = Get().NewConfirmablePostMessage(UriPath::kDiagnosticGetAnswer); VerifyOrExit(message != nullptr, error = kErrorNoBufs); if (aMessageInfo.GetPeerAddr().IsLinkLocal()) { messageInfo.SetSockAddr(Get().GetLinkLocalAddress()); } else { messageInfo.SetSockAddrToRloc(); } messageInfo.SetPeerAddr(aMessageInfo.GetPeerAddr()); SuccessOrExit(error = FillRequestedTlvs(aMessage, *message, networkDiagnosticTlv)); SuccessOrExit(error = Get().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(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().NewResponseMessage(aMessage); VerifyOrExit(message != nullptr, error = kErrorNoBufs); SuccessOrExit(error = FillRequestedTlvs(aMessage, *message, networkDiagnosticTlv)); SuccessOrExit(error = Get().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().NewConfirmablePostMessage(UriPath::kDiagnosticReset); VerifyOrExit(message != nullptr, error = kErrorNoBufs); if (aCount > 0) { SuccessOrExit(error = Tlv::Append(*message, aTlvTypes, aCount)); } if (aDestination.IsLinkLocal() || aDestination.IsLinkLocalMulticast()) { messageInfo.SetSockAddr(Get().GetLinkLocalAddress()); } else { messageInfo.SetSockAddrToRloc(); } messageInfo.SetPeerAddr(aDestination); SuccessOrExit(error = Get().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(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().ResetCounters(); LogInfo("Received diagnostic reset type kMacCounters(9)"); break; default: LogInfo("Received diagnostic reset other type %d not resetable", type); break; } } SuccessOrExit(Get().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(aMessage, offset, AsCoreType(&aNetworkDiagTlv.mData.mExtAddress))); break; case NetworkDiagnosticTlv::kAddress16: SuccessOrExit(error = Tlv::Read(aMessage, offset, aNetworkDiagTlv.mData.mAddr16)); break; case NetworkDiagnosticTlv::kMode: { uint8_t mode; SuccessOrExit(error = Tlv::Read(aMessage, offset, mode)); ParseMode(Mle::DeviceMode(mode), aNetworkDiagTlv.mData.mMode); break; } case NetworkDiagnosticTlv::kTimeout: SuccessOrExit(error = Tlv::Read(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(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(aMessage, offset, aNetworkDiagTlv.mData.mBatteryLevel)); break; case NetworkDiagnosticTlv::kSupplyVoltage: SuccessOrExit(error = Tlv::Read(aMessage, offset, aNetworkDiagTlv.mData.mSupplyVoltage)); break; case NetworkDiagnosticTlv::kChildTable: { ChildTableTlv &childTable = As(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(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(offset - aMessage.GetOffset() + tlv.GetSize()); ExitNow(); } exit: return error; } } // namespace NetworkDiagnostic } // namespace ot #endif // OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE