blob: a17dfdaf716400c61b9742cdbb8155d32fa82997 [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.
*/
#define WPP_NAME "network_diagnostic.tmh"
#include <openthread/config.h>
#include "network_diagnostic.hpp"
#include <openthread/platform/random.h>
#include "coap/coap_header.hpp"
#include "common/code_utils.hpp"
#include "common/debug.hpp"
#include "common/logging.hpp"
#include "common/encoding.hpp"
#include "mac/mac_frame.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/thread_uri_paths.hpp"
#include "thread/network_diagnostic_tlvs.hpp"
using ot::Encoding::BigEndian::HostSwap16;
#if OPENTHREAD_FTD || OPENTHREAD_ENABLE_MTD_NETWORK_DIAGNOSTIC
namespace ot {
namespace NetworkDiagnostic {
NetworkDiagnostic::NetworkDiagnostic(ThreadNetif &aThreadNetif) :
ThreadNetifLocator(aThreadNetif),
mDiagnosticGetRequest(OT_URI_PATH_DIAGNOSTIC_GET_REQUEST, &NetworkDiagnostic::HandleDiagnosticGetRequest, this),
mDiagnosticGetQuery(OT_URI_PATH_DIAGNOSTIC_GET_QUERY, &NetworkDiagnostic::HandleDiagnosticGetQuery, this),
mDiagnosticGetAnswer(OT_URI_PATH_DIAGNOSTIC_GET_ANSWER, &NetworkDiagnostic::HandleDiagnosticGetAnswer, this),
mDiagnosticReset(OT_URI_PATH_DIAGNOSTIC_RESET, &NetworkDiagnostic::HandleDiagnosticReset, this),
mReceiveDiagnosticGetCallback(NULL),
mReceiveDiagnosticGetCallbackContext(NULL)
{
aThreadNetif.GetCoap().AddResource(mDiagnosticGetRequest);
aThreadNetif.GetCoap().AddResource(mDiagnosticGetQuery);
aThreadNetif.GetCoap().AddResource(mDiagnosticGetAnswer);
aThreadNetif.GetCoap().AddResource(mDiagnosticReset);
}
void NetworkDiagnostic::SetReceiveDiagnosticGetCallback(otReceiveDiagnosticGetCallback aCallback,
void *aCallbackContext)
{
mReceiveDiagnosticGetCallback = aCallback;
mReceiveDiagnosticGetCallbackContext = aCallbackContext;
}
otError NetworkDiagnostic::SendDiagnosticGet(const Ip6::Address &aDestination, const uint8_t aTlvTypes[],
uint8_t aCount)
{
ThreadNetif &netif = GetNetif();
otError error;
Message *message;
Coap::Header header;
Ip6::MessageInfo messageInfo;
otCoapResponseHandler handler = NULL;
if (aDestination.IsMulticast())
{
header.Init(OT_COAP_TYPE_NON_CONFIRMABLE, OT_COAP_CODE_POST);
header.SetToken(Coap::Header::kDefaultTokenLength);
header.AppendUriPathOptions(OT_URI_PATH_DIAGNOSTIC_GET_QUERY);
}
else
{
handler = &NetworkDiagnostic::HandleDiagnosticGetResponse;
header.Init(OT_COAP_TYPE_CONFIRMABLE, OT_COAP_CODE_POST);
header.SetToken(Coap::Header::kDefaultTokenLength);
header.AppendUriPathOptions(OT_URI_PATH_DIAGNOSTIC_GET_REQUEST);
}
if (aCount > 0)
{
header.SetPayloadMarker();
}
VerifyOrExit((message = netif.GetCoap().NewMessage(header)) != NULL, error = OT_ERROR_NO_BUFS);
SuccessOrExit(error = message->Append(aTlvTypes, aCount));
messageInfo.SetPeerAddr(aDestination);
messageInfo.SetSockAddr(netif.GetMle().GetMeshLocal16());
messageInfo.SetPeerPort(kCoapUdpPort);
messageInfo.SetInterfaceId(netif.GetInterfaceId());
SuccessOrExit(error = netif.GetCoap().SendMessage(*message, messageInfo, handler, this));
otLogInfoNetDiag(GetInstance(), "Sent diagnostic get");
exit:
if (error != OT_ERROR_NONE && message != NULL)
{
message->Free();
}
return error;
}
void NetworkDiagnostic::HandleDiagnosticGetResponse(void *aContext, otCoapHeader *aHeader, otMessage *aMessage,
const otMessageInfo *aMessageInfo, otError aResult)
{
static_cast<NetworkDiagnostic *>(aContext)->HandleDiagnosticGetResponse(*static_cast<Coap::Header *>(aHeader),
*static_cast<Message *>(aMessage),
*static_cast<const Ip6::MessageInfo *>(aMessageInfo),
aResult);
}
void NetworkDiagnostic::HandleDiagnosticGetResponse(Coap::Header &aHeader, Message &aMessage,
const Ip6::MessageInfo &aMessageInfo,
otError aResult)
{
VerifyOrExit(aResult == OT_ERROR_NONE);
VerifyOrExit(aHeader.GetCode() == OT_COAP_CODE_CHANGED);
otLogInfoNetDiag(GetInstance(), "Received diagnostic get response");
if (mReceiveDiagnosticGetCallback)
{
mReceiveDiagnosticGetCallback(&aMessage, &aMessageInfo, mReceiveDiagnosticGetCallbackContext);
}
exit:
return;
}
void NetworkDiagnostic::HandleDiagnosticGetAnswer(void *aContext, otCoapHeader *aHeader, otMessage *aMessage,
const otMessageInfo *aMessageInfo)
{
static_cast<NetworkDiagnostic *>(aContext)->HandleDiagnosticGetAnswer(*static_cast<Coap::Header *>(aHeader),
*static_cast<Message *>(aMessage),
*static_cast<const Ip6::MessageInfo *>(aMessageInfo));
}
void NetworkDiagnostic::HandleDiagnosticGetAnswer(Coap::Header &aHeader, Message &aMessage,
const Ip6::MessageInfo &aMessageInfo)
{
VerifyOrExit(aHeader.GetType() == OT_COAP_TYPE_CONFIRMABLE &&
aHeader.GetCode() == OT_COAP_CODE_POST);
otLogInfoNetDiag(GetInstance(), "Diagnostic get answer received");
if (mReceiveDiagnosticGetCallback)
{
mReceiveDiagnosticGetCallback(&aMessage, &aMessageInfo, mReceiveDiagnosticGetCallbackContext);
}
SuccessOrExit(GetNetif().GetCoap().SendEmptyAck(aHeader, aMessageInfo));
otLogInfoNetDiag(GetInstance(), "Sent diagnostic answer acknowledgment");
exit:
return;
}
otError NetworkDiagnostic::AppendIp6AddressList(Message &aMessage)
{
ThreadNetif &netif = GetNetif();
otError error = OT_ERROR_NONE;
Ip6AddressListTlv tlv;
uint8_t count = 0;
tlv.Init();
for (const Ip6::NetifUnicastAddress *addr = netif.GetUnicastAddresses(); addr; addr = addr->GetNext())
{
count++;
}
tlv.SetLength(count * sizeof(Ip6::Address));
SuccessOrExit(error = aMessage.Append(&tlv, sizeof(tlv)));
for (const Ip6::NetifUnicastAddress *addr = netif.GetUnicastAddresses(); addr; addr = addr->GetNext())
{
SuccessOrExit(error = aMessage.Append(&addr->GetAddress(), sizeof(Ip6::Address)));
}
exit:
return error;
}
otError NetworkDiagnostic::AppendChildTable(Message &aMessage)
{
ThreadNetif &netif = GetNetif();
otError error = OT_ERROR_NONE;
uint8_t count = 0;
uint8_t timeout = 0;
uint8_t numChildren;
const Child *children = netif.GetMle().GetChildren(&numChildren);
ChildTableTlv tlv;
ChildTableEntry entry;
tlv.Init();
for (int i = 0; i < numChildren; i++)
{
if (children[i].GetState() == Neighbor::kStateValid)
{
count++;
}
}
tlv.SetLength(count * sizeof(ChildTableEntry));
SuccessOrExit(error = aMessage.Append(&tlv, sizeof(ChildTableTlv)));
for (int i = 0; i < numChildren; i++)
{
if (children[i].GetState() == Neighbor::kStateValid)
{
timeout = 0;
while (static_cast<uint32_t>(1 << timeout) < children[i].GetTimeout()) { timeout++; }
entry.SetReserved(0);
entry.SetTimeout(timeout + 4);
entry.SetChildId(netif.GetMle().GetChildId(children[i].GetRloc16()));
entry.SetMode(children[i].GetDeviceMode());
SuccessOrExit(error = aMessage.Append(&entry, sizeof(ChildTableEntry)));
}
}
exit:
return error;
}
otError NetworkDiagnostic::FillRequestedTlvs(Message &aRequest, Message &aResponse,
NetworkDiagnosticTlv &aNetworkDiagnosticTlv)
{
ThreadNetif &netif = GetNetif();
otError error = OT_ERROR_NONE;
uint16_t offset = 0;
uint8_t type;
offset = aRequest.GetOffset() + sizeof(NetworkDiagnosticTlv);
for (uint32_t i = 0; i < aNetworkDiagnosticTlv.GetLength(); i++)
{
VerifyOrExit(aRequest.Read(offset, sizeof(type), &type) == sizeof(type), error = OT_ERROR_DROP);
otLogInfoNetDiag(GetInstance(), "Received diagnostic get type %d", type);
switch (type)
{
case NetworkDiagnosticTlv::kExtMacAddress:
{
ExtMacAddressTlv tlv;
tlv.Init();
tlv.SetMacAddr(*netif.GetMac().GetExtAddress());
SuccessOrExit(error = aResponse.Append(&tlv, sizeof(tlv)));
break;
}
case NetworkDiagnosticTlv::kAddress16:
{
Address16Tlv tlv;
tlv.Init();
tlv.SetRloc16(netif.GetMle().GetRloc16());
SuccessOrExit(error = aResponse.Append(&tlv, sizeof(tlv)));
break;
}
case NetworkDiagnosticTlv::kMode:
{
ModeTlv tlv;
tlv.Init();
tlv.SetMode(netif.GetMle().GetDeviceMode());
SuccessOrExit(error = aResponse.Append(&tlv, sizeof(tlv)));
break;
}
case NetworkDiagnosticTlv::kTimeout:
{
if ((netif.GetMle().GetDeviceMode() & ModeTlv::kModeRxOnWhenIdle) == 0)
{
TimeoutTlv tlv;
tlv.Init();
tlv.SetTimeout(
Timer::MsecToSec(netif.GetMeshForwarder().GetDataPollManager().GetKeepAlivePollPeriod()));
SuccessOrExit(error = aResponse.Append(&tlv, sizeof(tlv)));
}
break;
}
case NetworkDiagnosticTlv::kConnectivity:
{
ConnectivityTlv tlv;
tlv.Init();
netif.GetMle().FillConnectivityTlv(*reinterpret_cast<Mle::ConnectivityTlv *>(&tlv));
SuccessOrExit(error = aResponse.Append(&tlv, sizeof(tlv)));
break;
}
case NetworkDiagnosticTlv::kRoute:
{
RouteTlv tlv;
tlv.Init();
netif.GetMle().FillRouteTlv(*reinterpret_cast<Mle::RouteTlv *>(&tlv));
SuccessOrExit(error = aResponse.Append(&tlv, tlv.GetSize()));
break;
}
case NetworkDiagnosticTlv::kLeaderData:
{
LeaderDataTlv tlv;
memcpy(&tlv, &netif.GetMle().GetLeaderDataTlv(), sizeof(tlv));
tlv.Init();
SuccessOrExit(error = aResponse.Append(&tlv, tlv.GetSize()));
break;
}
case NetworkDiagnosticTlv::kNetworkData:
{
NetworkDataTlv tlv;
tlv.Init();
netif.GetMle().FillNetworkDataTlv((*reinterpret_cast<Mle::NetworkDataTlv *>(&tlv)), true);
SuccessOrExit(error = aResponse.Append(&tlv, tlv.GetSize()));
break;
}
case NetworkDiagnosticTlv::kIp6AddressList:
{
SuccessOrExit(error = AppendIp6AddressList(aResponse));
break;
}
case NetworkDiagnosticTlv::kMacCounters:
{
MacCountersTlv tlv;
memset(&tlv, 0, sizeof(tlv));
tlv.Init();
netif.GetMac().FillMacCountersTlv(tlv);
SuccessOrExit(error = aResponse.Append(&tlv, tlv.GetSize()));
break;
}
case NetworkDiagnosticTlv::kBatteryLevel:
{
// TODO Need more api from driver
BatteryLevelTlv tlv;
tlv.Init();
tlv.SetBatteryLevel(100);
SuccessOrExit(error = aResponse.Append(&tlv, tlv.GetSize()));
break;
}
case NetworkDiagnosticTlv::kSupplyVoltage:
{
// TODO Need more api from driver
SupplyVoltageTlv tlv;
tlv.Init();
tlv.SetSupplyVoltage(0);
SuccessOrExit(error = aResponse.Append(&tlv, tlv.GetSize()));
break;
}
case NetworkDiagnosticTlv::kChildTable:
{
SuccessOrExit(error = AppendChildTable(aResponse));
break;
}
case NetworkDiagnosticTlv::kChannelPages:
{
ChannelPagesTlv tlv;
tlv.Init();
tlv.GetChannelPages()[0] = 0;
tlv.SetLength(1);
SuccessOrExit(error = aResponse.Append(&tlv, tlv.GetSize()));
break;
}
case NetworkDiagnosticTlv::kMaxChildTimeout:
{
uint32_t maxTimeout = 0;
if (netif.GetMle().GetMaxChildTimeout(maxTimeout) == OT_ERROR_NONE)
{
MaxChildTimeoutTlv tlv;
tlv.Init();
tlv.SetTimeout(maxTimeout);
SuccessOrExit(error = aResponse.Append(&tlv, sizeof(tlv)));
}
break;
}
default:
ExitNow(error = OT_ERROR_DROP);
}
offset += sizeof(type);
}
exit:
return error;
}
void NetworkDiagnostic::HandleDiagnosticGetQuery(void *aContext, otCoapHeader *aHeader, otMessage *aMessage,
const otMessageInfo *aMessageInfo)
{
static_cast<NetworkDiagnostic *>(aContext)->HandleDiagnosticGetQuery(
*static_cast<Coap::Header *>(aHeader), *static_cast<Message *>(aMessage),
*static_cast<const Ip6::MessageInfo *>(aMessageInfo));
}
void NetworkDiagnostic::HandleDiagnosticGetQuery(Coap::Header &aHeader, Message &aMessage,
const Ip6::MessageInfo &aMessageInfo)
{
ThreadNetif &netif = GetNetif();
otError error = OT_ERROR_NONE;
Message *message = NULL;
NetworkDiagnosticTlv networkDiagnosticTlv;
Coap::Header header;
Ip6::MessageInfo messageInfo;
VerifyOrExit(aHeader.GetCode() == OT_COAP_CODE_POST, error = OT_ERROR_DROP);
otLogInfoNetDiag(GetInstance(), "Received diagnostic get query");
VerifyOrExit((aMessage.Read(aMessage.GetOffset(), sizeof(NetworkDiagnosticTlv),
&networkDiagnosticTlv) == sizeof(NetworkDiagnosticTlv)), error = OT_ERROR_DROP);
VerifyOrExit(networkDiagnosticTlv.GetType() == NetworkDiagnosticTlv::kTypeList, error = OT_ERROR_DROP);
VerifyOrExit((static_cast<TypeListTlv *>(&networkDiagnosticTlv)->IsValid()), error = OT_ERROR_DROP);
// DIAG_GET.qry may be sent as a confirmable message.
if (aHeader.GetType() == OT_COAP_TYPE_CONFIRMABLE)
{
if (netif.GetCoap().SendEmptyAck(aHeader, aMessageInfo) == OT_ERROR_NONE)
{
otLogInfoNetDiag(GetInstance(), "Sent diagnostic get query acknowledgment");
}
}
header.Init(OT_COAP_TYPE_CONFIRMABLE, OT_COAP_CODE_POST);
header.SetToken(Coap::Header::kDefaultTokenLength);
header.AppendUriPathOptions(OT_URI_PATH_DIAGNOSTIC_GET_ANSWER);
if (networkDiagnosticTlv.GetLength() > 0)
{
header.SetPayloadMarker();
}
VerifyOrExit((message = netif.GetCoap().NewMessage(header)) != NULL, error = OT_ERROR_NO_BUFS);
messageInfo.SetPeerAddr(aMessageInfo.GetPeerAddr());
messageInfo.SetSockAddr(netif.GetMle().GetMeshLocal16());
messageInfo.SetPeerPort(kCoapUdpPort);
messageInfo.SetInterfaceId(netif.GetInterfaceId());
SuccessOrExit(error = FillRequestedTlvs(aMessage, *message, networkDiagnosticTlv));
SuccessOrExit(error = netif.GetCoap().SendMessage(*message, messageInfo, NULL, this));
otLogInfoNetDiag(GetInstance(), "Sent diagnostic get answer");
exit:
if (error != OT_ERROR_NONE && message != NULL)
{
message->Free();
}
}
void NetworkDiagnostic::HandleDiagnosticGetRequest(void *aContext, otCoapHeader *aHeader, otMessage *aMessage,
const otMessageInfo *aMessageInfo)
{
static_cast<NetworkDiagnostic *>(aContext)->HandleDiagnosticGetRequest(
*static_cast<Coap::Header *>(aHeader), *static_cast<Message *>(aMessage),
*static_cast<const Ip6::MessageInfo *>(aMessageInfo));
}
void NetworkDiagnostic::HandleDiagnosticGetRequest(Coap::Header &aHeader, Message &aMessage,
const Ip6::MessageInfo &aMessageInfo)
{
ThreadNetif &netif = GetNetif();
otError error = OT_ERROR_NONE;
Message *message = NULL;
NetworkDiagnosticTlv networkDiagnosticTlv;
Coap::Header header;
Ip6::MessageInfo messageInfo(aMessageInfo);
VerifyOrExit(aHeader.GetType() == OT_COAP_TYPE_CONFIRMABLE &&
aHeader.GetCode() == OT_COAP_CODE_POST, error = OT_ERROR_DROP);
otLogInfoNetDiag(GetInstance(), "Received diagnostic get request");
VerifyOrExit((aMessage.Read(aMessage.GetOffset(), sizeof(NetworkDiagnosticTlv),
&networkDiagnosticTlv) == sizeof(NetworkDiagnosticTlv)), error = OT_ERROR_DROP);
VerifyOrExit(networkDiagnosticTlv.GetType() == NetworkDiagnosticTlv::kTypeList, error = OT_ERROR_DROP);
VerifyOrExit((static_cast<TypeListTlv *>(&networkDiagnosticTlv)->IsValid()), error = OT_ERROR_DROP);
header.SetDefaultResponseHeader(aHeader);
header.SetPayloadMarker();
VerifyOrExit((message = netif.GetCoap().NewMessage(header)) != NULL, error = OT_ERROR_NO_BUFS);
SuccessOrExit(error = FillRequestedTlvs(aMessage, *message, networkDiagnosticTlv));
if (message->GetLength() == header.GetLength())
{
// Remove Payload Marker if payload is actually empty.
message->SetLength(header.GetLength() - 1);
}
SuccessOrExit(error = netif.GetCoap().SendMessage(*message, messageInfo));
otLogInfoNetDiag(GetInstance(), "Sent diagnostic get response");
exit:
if (error != OT_ERROR_NONE && message != NULL)
{
message->Free();
}
}
otError NetworkDiagnostic::SendDiagnosticReset(const Ip6::Address &aDestination, const uint8_t aTlvTypes[],
uint8_t aCount)
{
ThreadNetif &netif = GetNetif();
otError error;
Message *message;
Coap::Header header;
Ip6::MessageInfo messageInfo;
header.Init(OT_COAP_TYPE_CONFIRMABLE, OT_COAP_CODE_POST);
header.SetToken(Coap::Header::kDefaultTokenLength);
header.AppendUriPathOptions(OT_URI_PATH_DIAGNOSTIC_RESET);
if (aCount > 0)
{
header.SetPayloadMarker();
}
VerifyOrExit((message = netif.GetCoap().NewMessage(header)) != NULL, error = OT_ERROR_NO_BUFS);
SuccessOrExit(error = message->Append(aTlvTypes, aCount));
messageInfo.SetPeerAddr(aDestination);
messageInfo.SetSockAddr(netif.GetMle().GetMeshLocal16());
messageInfo.SetPeerPort(kCoapUdpPort);
messageInfo.SetInterfaceId(netif.GetInterfaceId());
SuccessOrExit(error = netif.GetCoap().SendMessage(*message, messageInfo));
otLogInfoNetDiag(GetInstance(), "Sent network diagnostic reset");
exit:
if (error != OT_ERROR_NONE && message != NULL)
{
message->Free();
}
return error;
}
void NetworkDiagnostic::HandleDiagnosticReset(void *aContext, otCoapHeader *aHeader, otMessage *aMessage,
const otMessageInfo *aMessageInfo)
{
static_cast<NetworkDiagnostic *>(aContext)->HandleDiagnosticReset(
*static_cast<Coap::Header *>(aHeader), *static_cast<Message *>(aMessage),
*static_cast<const Ip6::MessageInfo *>(aMessageInfo));
}
void NetworkDiagnostic::HandleDiagnosticReset(Coap::Header &aHeader, Message &aMessage,
const Ip6::MessageInfo &aMessageInfo)
{
ThreadNetif &netif = GetNetif();
uint16_t offset = 0;
uint8_t type;
NetworkDiagnosticTlv networkDiagnosticTlv;
otLogInfoNetDiag(GetInstance(), "Received diagnostic reset request");
VerifyOrExit(aHeader.GetType() == OT_COAP_TYPE_CONFIRMABLE &&
aHeader.GetCode() == OT_COAP_CODE_POST);
VerifyOrExit((aMessage.Read(aMessage.GetOffset(), sizeof(NetworkDiagnosticTlv),
&networkDiagnosticTlv) == sizeof(NetworkDiagnosticTlv)));
VerifyOrExit(networkDiagnosticTlv.GetType() == NetworkDiagnosticTlv::kTypeList);
VerifyOrExit((static_cast<TypeListTlv *>(&networkDiagnosticTlv)->IsValid()));
offset = aMessage.GetOffset() + sizeof(NetworkDiagnosticTlv);
for (uint8_t i = 0; i < networkDiagnosticTlv.GetLength(); i++)
{
VerifyOrExit(aMessage.Read(offset, sizeof(type), &type) == sizeof(type));
switch (type)
{
case NetworkDiagnosticTlv::kMacCounters:
netif.GetMac().ResetCounters();
otLogInfoNetDiag(GetInstance(), "Received diagnostic reset type kMacCounters(9)");
break;
default:
otLogInfoNetDiag(GetInstance(), "Received diagnostic reset other type %d not resetable", type);
break;
}
}
SuccessOrExit(netif.GetCoap().SendEmptyAck(aHeader, aMessageInfo));
otLogInfoNetDiag(GetInstance(), "Sent diagnostic reset acknowledgment");
exit:
return;
}
} // namespace NetworkDiagnostic
} // namespace ot
#endif // OPENTHREAD_FTD || OPENTHREAD_ENABLE_MTD_NETWORK_DIAGNOSTIC