| /* |
| * 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 |