diff --git a/examples/platforms/posix/logging.c b/examples/platforms/posix/logging.c index baf5b2178..74a73d990 100644 --- a/examples/platforms/posix/logging.c +++ b/examples/platforms/posix/logging.c @@ -126,6 +126,9 @@ void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat case kLogRegionMeshCoP: LOG_PRINTF("MCOP "); break; + + case kLogRegionNetDiag: + LOG_PRINTF("NDG "); } va_start(args, aFormat); diff --git a/include/openthread-types.h b/include/openthread-types.h index f6884ebf2..e6eecd3b3 100644 --- a/include/openthread-types.h +++ b/include/openthread-types.h @@ -139,6 +139,8 @@ typedef enum ThreadError #define OT_MASTER_KEY_SIZE 16 ///< Size of the Thread Master Key (bytes) +#define OT_NUM_NETDIAG_TLV_TYPES 18 ///< Number of Network Diagnostic TLV types + /** * This structure represents a Thread Master Key. * diff --git a/include/openthread.h b/include/openthread.h index eb9b3d92d..93324db0d 100644 --- a/include/openthread.h +++ b/include/openthread.h @@ -2155,6 +2155,26 @@ ThreadError otBindUdpSocket(otUdpSocket *aSocket, otSockAddr *aSockName); */ ThreadError otSendUdp(otUdpSocket *aSocket, otMessage aMessage, const otMessageInfo *aMessageInfo); +/** + * Send a Network Diagnostic Get request + * + * @param[in] aDestination A pointer to destination address. + * @param[in] aTlvTypes An array of Network Diagnostic TLV types. + * @param[in] aCount Number of types in aTlvTypes + */ +ThreadError otSendDiagnosticGet(otInstance *aInstance, otIp6Address *aDestination, uint8_t aTlvTypes[], uint8_t aCount); + +/** + * Send a Network Diagnostic Reset request + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aDestination A pointer to destination address. + * @param[in] aTlvTypes An array of Network Diagnostic TLV types. Currently only Type 9 is allowed. + * @param[in] aCount Number of types in aTlvTypes + */ +ThreadError otSendDiagnosticReset(otInstance *aInstance, otIp6Address *aDestination, uint8_t aTlvTypes[], + uint8_t aCount); + /** * @} * diff --git a/include/platform/logging.h b/include/platform/logging.h index 0ff8a8027..dcb936467 100644 --- a/include/platform/logging.h +++ b/include/platform/logging.h @@ -87,6 +87,7 @@ typedef enum otLogRegion kLogRegionMem = 8, ///< Memory kLogRegionNcp = 9, ///< NCP kLogRegionMeshCoP = 10, ///< Mesh Commissioning Protocol + kLogRegionNetDiag = 11, ///< Network Diagnostic } otLogRegion; /** diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 2223480b7..6d3e343df 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -103,6 +103,7 @@ const struct Command Interpreter::sCommands[] = { "masterkey", &Interpreter::ProcessMasterKey }, { "mode", &Interpreter::ProcessMode }, { "netdataregister", &Interpreter::ProcessNetworkDataRegister }, + { "networkdiagnostic", &Interpreter::ProcessNetworkDiagnostic }, { "networkidtimeout", &Interpreter::ProcessNetworkIdTimeout }, { "networkname", &Interpreter::ProcessNetworkName }, { "panid", &Interpreter::ProcessPanId }, @@ -2449,5 +2450,38 @@ exit: return; } +void Interpreter::ProcessNetworkDiagnostic(int argc, char *argv[]) +{ + ThreadError error = kThreadError_None; + struct otIp6Address address; + uint8_t index = 2; + uint8_t tlvTypes[OT_NUM_NETDIAG_TLV_TYPES]; + uint8_t count = 0; + + VerifyOrExit(argc > 1 + 1, error = kThreadError_Parse); + + SuccessOrExit(error = otIp6AddressFromString(argv[1], &address)); + + while (index < argc && count < sizeof(tlvTypes)) + { + long value; + SuccessOrExit(error = ParseLong(argv[index], value)); + tlvTypes[count++] = static_cast(value); + index++; + } + + if (strcmp(argv[0], "get") == 0) + { + otSendDiagnosticGet(mInstance, &address, tlvTypes, count); + } + else if (strcmp(argv[0], "reset") == 0) + { + otSendDiagnosticReset(mInstance, &address, tlvTypes, count); + } + +exit: + AppendResult(error); +} + } // namespace Cli } // namespace Thread diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index b706ddb44..ee8235224 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -175,6 +175,7 @@ private: void ProcessMasterKey(int argc, char *argv[]); void ProcessMode(int argc, char *argv[]); void ProcessNetworkDataRegister(int argc, char *argv[]); + void ProcessNetworkDiagnostic(int argc, char *argv[]); void ProcessNetworkIdTimeout(int argc, char *argv[]); void ProcessNetworkName(int argc, char *argv[]); void ProcessPanId(int argc, char *argv[]); diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 4306443a3..c383c2d84 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -79,6 +79,8 @@ libopenthread_a_SOURCES = \ thread/network_data_local.cpp \ thread/network_data_leader.cpp \ thread/panid_query_server.cpp \ + thread/network_diag.cpp \ + thread/network_diag_tlvs.cpp \ thread/thread_netif.cpp \ thread/thread_tlvs.cpp \ $(NULL) @@ -163,6 +165,8 @@ noinst_HEADERS = \ thread/network_data_local.hpp \ thread/network_data_tlvs.hpp \ thread/panid_query_server.hpp \ + thread/network_diag.hpp \ + thread/network_diag_tlvs.hpp \ thread/thread_netif.hpp \ thread/thread_tlvs.hpp \ thread/thread_uris.hpp \ diff --git a/src/core/common/logging.hpp b/src/core/common/logging.hpp index cf23a1d82..96a24b66c 100644 --- a/src/core/common/logging.hpp +++ b/src/core/common/logging.hpp @@ -1021,6 +1021,57 @@ extern "C" { #define otDumpDebgMem(aId, aBuf, aLength) #endif +/** + * @def otLogCritNetDiag + * + * This method generates a log with level critical for the NETDIAG region. + * + * @param[in] aFormat A pointer to the format string. + * @param[in] ... Arguments for the format specification. + * + */ + +/** + * @def otLogWarnNetDiag + * + * This method generates a log with level warning for the NETDIAG region. + * + * @param[in] aFormat A pointer to the format string. + * @param[in] ... Arguments for the format specification. + * + */ + +/** + * @def otLogInfoNetDiag + * + * This method generates a log with level info for the NETDIAG region. + * + * @param[in] aFormat A pointer to the format string. + * @param[in] ... Arguments for the format specification. + * + */ + +/** + * @def otLogDebgNetDiag + * + * This method generates a log with level debug for the NETDIAG region. + * + * @param[in] aFormat A pointer to the format string. + * @param[in] ... Arguments for the format specification. + * + */ +#ifdef OPENTHREAD_CONFIG_LOG_NETDIAG +#define otLogCritNetDiag(aFormat, ...) otLogCrit(kLogRegionNetDiag, aFormat, ## __VA_ARGS__) +#define otLogWarnNetDiag(aFormat, ...) otLogWarn(kLogRegionNetDiag, aFormat, ## __VA_ARGS__) +#define otLogInfoNetDiag(aFormat, ...) otLogInfo(kLogRegionNetDiag, aFormat, ## __VA_ARGS__) +#define otLogDebgNetDiag(aFormat, ...) otLogDebg(kLogRegionNetDiag, aFormat, ## __VA_ARGS__) +#else +#define otLogCritNetDiag(aFormat, ...) +#define otLogWarnNetDiag(aFormat, ...) +#define otLogInfoNetDiag(aFormat, ...) +#define otLogDebgNetDiag(aFormat, ...) +#endif + /** * This method dumps bytes to the log in a human-readable fashion. * diff --git a/src/core/openthread-core-default-config.h b/src/core/openthread-core-default-config.h index 4a8580a84..68ac16153 100644 --- a/src/core/openthread-core-default-config.h +++ b/src/core/openthread-core-default-config.h @@ -249,5 +249,14 @@ */ #define OPENTHREAD_CONFIG_LOG_MEM + +/** + * @def OPENTHREAD_CONFIG_LOG_NETDIAG + * + * Define to enable network diagnostic logging. + * + */ +#define OPENTHREAD_CONFIG_LOG_NETDIAG + #endif // OPENTHREAD_CORE_DEFAULT_CONFIG_H_ diff --git a/src/core/openthread.cpp b/src/core/openthread.cpp index fca6307df..dea474e8f 100644 --- a/src/core/openthread.cpp +++ b/src/core/openthread.cpp @@ -957,6 +957,21 @@ exit: #endif +ThreadError otSendDiagnosticGet(otInstance *aInstance, otIp6Address *aDestination, uint8_t aTlvTypes[], uint8_t aCount) +{ + (void)aInstance; + return sThreadNetif->GetNetworkDiagnostic().SendDiagnosticGet(*static_cast(aDestination), aTlvTypes, + aCount); +} + +ThreadError otSendDiagnosticReset(otInstance *aInstance, otIp6Address *aDestination, uint8_t aTlvTypes[], + uint8_t aCount) +{ + (void)aInstance; + return sThreadNetif->GetNetworkDiagnostic().SendDiagnosticReset(*static_cast(aDestination), aTlvTypes, + aCount); +} + void otInstanceFinalize(otInstance *aInstance) { // Ensure we are disabled diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index 9127eaf06..c7db567ea 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -792,15 +792,20 @@ ThreadError Mle::AppendLeaderData(Message &aMessage) return aMessage.Append(&mLeaderData, sizeof(mLeaderData)); } +void Mle::FillNetworkDataTlv(NetworkDataTlv &aTlv, bool aStableOnly) +{ + uint8_t length; + mNetworkData.GetNetworkData(aStableOnly, aTlv.GetNetworkData(), length); + aTlv.SetLength(length); +} + ThreadError Mle::AppendNetworkData(Message &aMessage, bool aStableOnly) { ThreadError error = kThreadError_None; NetworkDataTlv tlv; - uint8_t length; tlv.Init(); - mNetworkData.GetNetworkData(aStableOnly, tlv.GetNetworkData(), length); - tlv.SetLength(length); + FillNetworkDataTlv(tlv, aStableOnly); SuccessOrExit(error = aMessage.Append(&tlv, sizeof(Tlv) + tlv.GetLength())); diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp index e8439dff5..891c65e63 100644 --- a/src/core/thread/mle.hpp +++ b/src/core/thread/mle.hpp @@ -662,6 +662,15 @@ public: */ static bool IsActiveRouter(uint16_t aRloc16) { return GetChildId(aRloc16) == 0; } + /** + * This method fills the NetworkDataTlv. + * + * @param[out] aTlv The NetworkDataTlv. + * @param[in] aStableOnly TRUE to append stable data, FALSE otherwise. + * + */ + void FillNetworkDataTlv(NetworkDataTlv &aTlv, bool aStableOnly); + protected: /** * This method appends an MLE header to a message. diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index ac2b867db..81cff7507 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -3295,16 +3295,13 @@ exit: } } -ThreadError MleRouter::AppendConnectivity(Message &aMessage) +void MleRouter::FillConnectivityTlv(ConnectivityTlv &aTlv) { - ThreadError error; - ConnectivityTlv tlv; + ConnectivityTlv &tlv = aTlv; uint8_t cost; uint8_t lqi; uint8_t numChildren = 0; - tlv.Init(); - for (int i = 0; i < mMaxChildrenAllowed; i++) { if (mChildren[i].mState == Neighbor::kStateValid) @@ -3405,6 +3402,15 @@ ThreadError MleRouter::AppendConnectivity(Message &aMessage) tlv.SetIdSequence(mRouterIdSequence); tlv.SetSedBufferSize(1280); tlv.SetSedDatagramCount(1); +} + +ThreadError MleRouter::AppendConnectivity(Message &aMessage) +{ + ThreadError error; + ConnectivityTlv tlv; + + tlv.Init(); + FillConnectivityTlv(tlv); SuccessOrExit(error = aMessage.Append(&tlv, sizeof(tlv))); @@ -3455,14 +3461,11 @@ exit: return error; } -ThreadError MleRouter::AppendRoute(Message &aMessage) +void MleRouter::FillRouteTlv(RouteTlv &tlv) { - ThreadError error; - RouteTlv tlv; uint8_t routeCount = 0; uint8_t cost; - tlv.Init(); tlv.SetRouterIdSequence(mRouterIdSequence); tlv.ClearRouterIdMask(); @@ -3515,8 +3518,15 @@ ThreadError MleRouter::AppendRoute(Message &aMessage) } tlv.SetRouteDataLength(routeCount); - SuccessOrExit(error = aMessage.Append(&tlv, sizeof(Tlv) + tlv.GetLength())); +} +ThreadError MleRouter::AppendRoute(Message &aMessage) +{ + ThreadError error; + RouteTlv tlv; + tlv.Init(); + FillRouteTlv(tlv); + SuccessOrExit(error = aMessage.Append(&tlv, sizeof(Tlv) + tlv.GetLength())); exit: return error; } diff --git a/src/core/thread/mle_router.hpp b/src/core/thread/mle_router.hpp index f7cb3c176..6a562dd93 100644 --- a/src/core/thread/mle_router.hpp +++ b/src/core/thread/mle_router.hpp @@ -481,6 +481,22 @@ public: */ static bool IsRouterIdValid(uint8_t aRouterId) { return aRouterId <= kMaxRouterId; } + /** + * This method fills an ConnectivityTlv. + * + * @param[out] aTlv A reference to the tlv to be filled. + * + */ + void FillConnectivityTlv(ConnectivityTlv &aTlv); + + /** + * This method fills an RouteTlv. + * + * @param[out] aTlv A reference to the tlv to be filled. + * + */ + void FillRouteTlv(RouteTlv &aTlv); + private: enum { diff --git a/src/core/thread/network_diag.cpp b/src/core/thread/network_diag.cpp new file mode 100644 index 000000000..00293a472 --- /dev/null +++ b/src/core/thread/network_diag.cpp @@ -0,0 +1,526 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using Thread::Encoding::BigEndian::HostSwap16; + +namespace Thread { + +namespace NetworkDiagnostic { + +NetworkDiagnostic::NetworkDiagnostic(ThreadNetif &aThreadNetif) : + mDiagnosticGet(OPENTHREAD_URI_DIAGNOSTIC_GET, &HandleDiagnosticGet, this), + mDiagnosticReset(OPENTHREAD_URI_DIAGNOSTIC_RESET, &HandleDiagnosticReset, this), + mSocket(aThreadNetif.GetIp6().mUdp), + mCoapServer(aThreadNetif.GetCoapServer()), + mMle(aThreadNetif.GetMle()), + mNetif(aThreadNetif) +{ + mCoapServer.AddResource(mDiagnosticGet); + mCoapServer.AddResource(mDiagnosticReset); + mCoapMessageId = static_cast(otPlatRandomGet()); +} + +ThreadError NetworkDiagnostic::SendDiagnosticGet(const Ip6::Address &aDestination, uint8_t aTlvTypes[], uint8_t aCount) +{ + ThreadError error; + Ip6::SockAddr sockaddr; + Message *message; + Coap::Header header; + Ip6::MessageInfo messageInfo; + + sockaddr.mPort = kCoapUdpPort; + mSocket.Open(&HandleUdpReceive, this); + mSocket.Bind(sockaddr); + + VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, error = kThreadError_NoBufs); + + for (size_t i = 0; i < sizeof(mCoapToken); i++) + { + mCoapToken[i] = static_cast(otPlatRandomGet()); + } + + header.Init(); + header.SetType(Coap::Header::kTypeConfirmable); + header.SetCode(Coap::Header::kCodeGet); + header.SetMessageId(++mCoapMessageId); + header.SetToken(mCoapToken, sizeof(mCoapToken)); + header.AppendUriPathOptions(OPENTHREAD_URI_DIAGNOSTIC_GET); + header.Finalize(); + + SuccessOrExit(error = message->Append(header.GetBytes(), header.GetLength())); + + SuccessOrExit(error = message->Append(aTlvTypes, aCount)); + + memset(&messageInfo, 0, sizeof(messageInfo)); + messageInfo.GetPeerAddr() = aDestination; + messageInfo.GetSockAddr() = *mMle.GetMeshLocal16(); + messageInfo.mPeerPort = kCoapUdpPort; + messageInfo.mInterfaceId = mNetif.GetInterfaceId(); + + SuccessOrExit(error = mSocket.SendTo(*message, messageInfo)); + + otLogInfoNetDiag("Sent diagnostic get\n"); + +exit: + + if (error != kThreadError_None && message != NULL) + { + message->Free(); + } + + return error; +} + +void NetworkDiagnostic::HandleUdpReceive(void *aContext, otMessage aMessage, const otMessageInfo *aMessageInfo) +{ + NetworkDiagnostic *obj = static_cast(aContext); + obj->HandleUdpReceive(*static_cast(aMessage), *static_cast(aMessageInfo)); +} + +void NetworkDiagnostic::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + Coap::Header header; + + SuccessOrExit(header.FromMessage(aMessage)); + VerifyOrExit(header.GetType() == Coap::Header::kTypeAcknowledgment && + header.GetCode() == Coap::Header::kCodeChanged && + header.GetMessageId() == mCoapMessageId && + header.GetTokenLength() == sizeof(mCoapToken) && + memcmp(mCoapToken, header.GetToken(), sizeof(mCoapToken)) == 0, ;); + + otLogInfoNetDiag("Network Diagnostic message acknowledged\n"); + +exit: + (void)aMessageInfo; +} + +ThreadError NetworkDiagnostic::SendDiagnosticReset(const Ip6::Address &aDestination, uint8_t aTlvTypes[], + uint8_t aCount) +{ + ThreadError error; + Ip6::SockAddr sockaddr; + Message *message; + Coap::Header header; + Ip6::MessageInfo messageInfo; + + sockaddr.mPort = kCoapUdpPort; + mSocket.Open(&HandleUdpReceive, this); + mSocket.Bind(sockaddr); + + VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, error = kThreadError_NoBufs); + + for (size_t i = 0; i < sizeof(mCoapToken); i++) + { + mCoapToken[i] = static_cast(otPlatRandomGet()); + } + + header.Init(); + header.SetType(Coap::Header::kTypeConfirmable); + header.SetCode(Coap::Header::kCodePost); + header.SetMessageId(++mCoapMessageId); + header.SetToken(mCoapToken, sizeof(mCoapToken)); + header.AppendUriPathOptions(OPENTHREAD_URI_DIAGNOSTIC_RESET); + header.Finalize(); + + SuccessOrExit(error = message->Append(header.GetBytes(), header.GetLength())); + + SuccessOrExit(error = message->Append(aTlvTypes, aCount)); + + memset(&messageInfo, 0, sizeof(messageInfo)); + messageInfo.GetPeerAddr() = aDestination; + messageInfo.GetSockAddr() = *mMle.GetMeshLocal16(); + messageInfo.mPeerPort = kCoapUdpPort; + messageInfo.mInterfaceId = mNetif.GetInterfaceId(); + + SuccessOrExit(error = mSocket.SendTo(*message, messageInfo)); + + otLogInfoNetDiag("Sent network diagnostic reset\n"); + +exit: + + if (error != kThreadError_None && message != NULL) + { + message->Free(); + } + + return error; +} + +void NetworkDiagnostic::HandleDiagnosticGet(void *aContext, Coap::Header &aHeader, + Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + NetworkDiagnostic *obj = reinterpret_cast(aContext); + obj->HandleDiagnosticGet(aHeader, aMessage, aMessageInfo); +} + +ThreadError NetworkDiagnostic::AppendIPv6AddressList(Message &aMessage) +{ + ThreadError error = kThreadError_None; + IPv6AddressListTlv tlv; + uint8_t count = 0; + + tlv.Init(); + + for (const Ip6::NetifUnicastAddress *addr = mNetif.GetUnicastAddresses(); addr; addr = addr->GetNext()) + { + count++; + } + + tlv.SetLength(count * sizeof(Ip6::Address)); + SuccessOrExit(error = aMessage.Append(&tlv, sizeof(tlv))); + + for (const Ip6::NetifUnicastAddress *addr = mNetif.GetUnicastAddresses(); addr; addr = addr->GetNext()) + { + SuccessOrExit(error = aMessage.Append(&addr->GetAddress(), sizeof(Ip6::Address))); + } + +exit: + + return error; +} + +ThreadError NetworkDiagnostic::AppendChildTable(Message &aMessage) +{ + ThreadError error = kThreadError_None; + uint8_t numChildren; + const Child *children = mMle.GetChildren(&numChildren); + + { + uint8_t count = 0; + + for (int i = 0; i < numChildren; i++) + { + if (children[i].mState != Neighbor::kStateInvalid) + { + continue; + } + + count++; + } + + ChildTableTlv tlv; + tlv.Init(); + tlv.SetLength(count * sizeof(ChildTableEntry)); + + SuccessOrExit(error = aMessage.Append(&tlv, sizeof(ChildTableTlv))); + } + + for (int i = 0; i < numChildren; i++) + { + if (children[i].mState != Neighbor::kStateInvalid) + { + continue; + } + + const Child &child = children[i]; + ChildTableEntry entry; + uint8_t timeout = 0; + + while (static_cast(1 << timeout) < child.mTimeout) { timeout++; } + + entry.SetTimeout(timeout + 4); + entry.SetChildId(child.mValid.mRloc16); + entry.SetMode(child.mMode); + + SuccessOrExit(error = aMessage.Append(&entry, sizeof(ChildTableEntry))); + } + +exit: + + return error; +} + +void NetworkDiagnostic::HandleDiagnosticGet(Coap::Header &aHeader, Message &aMessage, + const Ip6::MessageInfo &aMessageInfo) +{ + uint8_t tlvTypeSet[kNumTlvTypes]; + uint16_t numTlvTypes; + ThreadError error = kThreadError_None; + Message *message = NULL; + Coap::Header header; + Ip6::MessageInfo messageInfo; + + VerifyOrExit(aHeader.GetType() == Coap::Header::kTypeConfirmable && + aHeader.GetCode() == Coap::Header::kCodeGet, error = kThreadError_Drop); + + otLogInfoNetDiag("Received diagnostic get request\n"); + + header.Init(); + header.SetType(Coap::Header::kTypeAcknowledgment); + header.SetCode(Coap::Header::kCodeChanged); + header.SetMessageId(aHeader.GetMessageId()); + header.SetToken(aHeader.GetToken(), aHeader.GetTokenLength()); + header.Finalize(); + + VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, error = kThreadError_NoBufs); + SuccessOrExit(error = message->Append(header.GetBytes(), header.GetLength())); + + numTlvTypes = aMessage.Read(aMessage.GetOffset(), kNumTlvTypes, tlvTypeSet); + + for (uint8_t i = 0; i < numTlvTypes; i++) + { + otLogInfoNetDiag("Received diagnostic get type %d\n", tlvTypeSet[i]); + + switch (tlvTypeSet[i]) + { + case NetworkDiagnosticTlv::kExtMacAddress: + { + ExtMacAddressTlv tlv; + tlv.Init(); + tlv.SetMacAddr(*mNetif.GetMac().GetExtAddress()); + SuccessOrExit(error = message->Append(&tlv, sizeof(tlv))); + break; + } + + case NetworkDiagnosticTlv::kAddress16: + { + Address16Tlv tlv; + tlv.Init(); + tlv.SetRloc16(mMle.GetRloc16()); + SuccessOrExit(error = message->Append(&tlv, sizeof(tlv))); + break; + } + + case NetworkDiagnosticTlv::kMode: + { + ModeTlv tlv; + tlv.Init(); + tlv.SetMode(mMle.GetDeviceMode()); + SuccessOrExit(error = message->Append(&tlv, sizeof(tlv))); + break; + } + + case NetworkDiagnosticTlv::kTimeout: + { + if ((mMle.GetDeviceMode() & ModeTlv::kModeRxOnWhenIdle) == 0) + { + TimeoutTlv tlv; + tlv.Init(); + tlv.SetTimeout(mMle.GetTimeout()); + SuccessOrExit(error = message->Append(&tlv, sizeof(tlv))); + } + + break; + } + + case NetworkDiagnosticTlv::kConnectivity: + { + ConnectivityTlv tlv; + tlv.Init(); + mMle.FillConnectivityTlv(*reinterpret_cast(&tlv)); + SuccessOrExit(error = message->Append(&tlv, sizeof(tlv))); + break; + } + + case NetworkDiagnosticTlv::kRoute: + { + RouteTlv tlv; + tlv.Init(); + mMle.FillRouteTlv(*reinterpret_cast(&tlv)); + SuccessOrExit(error = message->Append(&tlv, tlv.GetSize())); + break; + } + + case NetworkDiagnosticTlv::kLeaderData: + { + LeaderDataTlv tlv; + memcpy(&tlv, &mMle.GetLeaderDataTlv(), sizeof(tlv)); + tlv.Init(); + SuccessOrExit(error = message->Append(&tlv, tlv.GetSize())); + break; + } + + case NetworkDiagnosticTlv::kNetworkData: + { + NetworkDataTlv tlv; + tlv.Init(); + mMle.FillNetworkDataTlv((*reinterpret_cast(&tlv)), true); + SuccessOrExit(error = message->Append(&tlv, tlv.GetSize())); + break; + } + + case NetworkDiagnosticTlv::kIPv6AddressList: + { + SuccessOrExit(error = AppendIPv6AddressList(*message)); + break; + } + + case NetworkDiagnosticTlv::kMacCounters: + { + MacCountersTlv tlv; + memset(&tlv, 0, sizeof(tlv)); + tlv.Init(); + SuccessOrExit(error = message->Append(&tlv, tlv.GetSize())); + break; + } + + case NetworkDiagnosticTlv::kBatteryLevel: + { + // TODO Need more api from driver + BatteryLevelTlv tlv; + tlv.Init(); + tlv.SetBatteryLevel(100); + SuccessOrExit(error = message->Append(&tlv, tlv.GetSize())); + break; + } + + case NetworkDiagnosticTlv::kSupplyVoltage: + { + // TODO Need more api from driver + SupplyVoltageTlv tlv; + tlv.Init(); + tlv.SetSupplyVoltage(0); + SuccessOrExit(error = message->Append(&tlv, tlv.GetSize())); + break; + } + + case NetworkDiagnosticTlv::kChildTable: + { + SuccessOrExit(error = AppendChildTable(*message)); + break; + } + + case NetworkDiagnosticTlv::kChannelPages: + { + ChannelPagesTlv tlv; + tlv.Init(); + tlv.GetChannelPages()[0] = 0; + tlv.SetLength(1); + SuccessOrExit(error = message->Append(&tlv, tlv.GetSize())); + break; + } + + default: + ExitNow(); + } + } + + memcpy(&messageInfo, &aMessageInfo, sizeof(messageInfo)); + memset(&messageInfo.mSockAddr, 0, sizeof(messageInfo.mSockAddr)); + otLogInfoNetDiag("Sending diagnostic get acknowledgment\n"); + SuccessOrExit(error = mCoapServer.SendMessage(*message, messageInfo)); + + otLogInfoNetDiag("Sent diagnostic get acknowledgment\n"); + +exit: + + if (error != kThreadError_None && message != NULL) + { + message->Free(); + } +} + +void NetworkDiagnostic::HandleDiagnosticReset(void *aContext, Coap::Header &aHeader, Message &aMessage, + const Ip6::MessageInfo &aMessageInfo) +{ + NetworkDiagnostic *obj = reinterpret_cast(aContext); + obj->HandleDiagnosticReset(aHeader, aMessage, aMessageInfo); +} + +void NetworkDiagnostic::HandleDiagnosticReset(Coap::Header &aHeader, Message &aMessage, + const Ip6::MessageInfo &aMessageInfo) +{ + ThreadError error = kThreadError_None; + uint8_t tlvTypeSet[kNumResetTlvTypes]; + uint16_t numTlvTypes; + Message *message = NULL; + Coap::Header header; + Ip6::MessageInfo messageInfo; + otLogInfoNetDiag("Received diagnostic reset request\n"); + + VerifyOrExit(aHeader.GetType() == Coap::Header::kTypeConfirmable && + aHeader.GetCode() == Coap::Header::kCodePost, error = kThreadError_Drop); + + otLogInfoNetDiag("Received diagnostic reset request\n"); + numTlvTypes = aMessage.Read(aMessage.GetOffset(), kNumResetTlvTypes, tlvTypeSet); + + otLogInfoNetDiag("Received diagnostic reset request\n"); + + for (uint8_t i = 0; i < numTlvTypes; i++) + { + switch (tlvTypeSet[i]) + { + case NetworkDiagnosticTlv::kMacCounters: + break; + + default: + ExitNow(); + } + } + + otLogInfoNetDiag("Received diagnostic reset request\n"); + VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, error = kThreadError_NoBufs); + + otLogInfoNetDiag("Received diagnostic reset request\n"); + header.Init(); + header.SetType(Coap::Header::kTypeAcknowledgment); + header.SetCode(Coap::Header::kCodeChanged); + header.SetMessageId(aHeader.GetMessageId()); + header.SetToken(aHeader.GetToken(), aHeader.GetTokenLength()); + header.Finalize(); + SuccessOrExit(error = message->Append(header.GetBytes(), header.GetLength())); + + memcpy(&messageInfo, &aMessageInfo, sizeof(messageInfo)); + memset(&messageInfo.mSockAddr, 0, sizeof(messageInfo.mSockAddr)); + + SuccessOrExit(error = mCoapServer.SendMessage(*message, messageInfo)); + + otLogInfoNetDiag("Sent diagnostic reset acknowledgment\n"); + +exit: + + if (error != kThreadError_None && message != NULL) + { + message->Free(); + } +} + +} // namespace NetworkDiagnostic + +} // namespace Thread diff --git a/src/core/thread/network_diag.hpp b/src/core/thread/network_diag.hpp new file mode 100644 index 000000000..813ad9f8a --- /dev/null +++ b/src/core/thread/network_diag.hpp @@ -0,0 +1,142 @@ +/* + * 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 includes definitions for handle network diagnostic. + */ + +#ifndef NETWORK_DIAGNOSTIC_HPP_ +#define NETWORK_DIAGNOSTIC_HPP_ + +#include +#include +#include +#include + +namespace Thread { + +class ThreadNetif; +using namespace Coap; + +namespace NetworkDiagnostic { + +class IPv6AddressListTlv; +class ChildTableTlv; + +/** + * @addtogroup core-netdiag + * + * @brief + * This module includes definitions for sending and handling Network Diagnostic Commands. + * + * @{ + */ + +/** + * This class implements the Network Diagnostic processing. + * + */ +class NetworkDiagnostic +{ +public: + /** + * This constructor initializes the object. + * + */ + explicit NetworkDiagnostic(ThreadNetif &aThreadNetif); + + /** + * This method sends Diagnostic Get request. + * + * @param[in] aDestination A reference to the destination address. + * @param[in] aTlvTypes An array of Network Diagnostic TLV types. + * @param[in] aCount Number of types in aTlvTypes + * + */ + ThreadError SendDiagnosticGet(const Ip6::Address &aDestination, uint8_t aTlvTypes[], uint8_t aCount); + + /** + * This method sends Diagnostic Reset request. + * + * @param[in] aDestination A reference to the destination address. + * @param[in] aTlvTypes An array of Network Diagnostic TLV types. + * @param[in] aCount Number of types in aTlvTypes + * + */ + ThreadError SendDiagnosticReset(const Ip6::Address &aDestination, uint8_t aTlvTypes[], uint8_t aCount); + + /** + * This method fills IPv6AddressListTlv. + * + * @param[out] aTlv A reference to the tlv. + * + */ + ThreadError AppendIPv6AddressList(Message &aMessage); + + /** + * This method fills ChildTableTlv. + * + * @param[out] aTlv A reference to the tlv. + * + */ + ThreadError AppendChildTable(Message &aMessage); + +private: + static void HandleUdpReceive(void *aContext, otMessage aMessage, const otMessageInfo *aMessageInfo); + void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + + static void HandleDiagnosticGet(void *aContext, Coap::Header &aHeader, + Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + void HandleDiagnosticGet(Thread::Coap::Header &aHeader, Thread::Message &aMessage, + const Thread::Ip6::MessageInfo &aMessageInfo); + + static void HandleDiagnosticReset(void *aContext, Thread::Coap::Header &aHeader, Thread::Message &, + const Thread::Ip6::MessageInfo &aMessageInfo); + void HandleDiagnosticReset(Coap::Header &aHeader, Message &aMessage, + const Ip6::MessageInfo &aMessageInfo); + + Coap::Resource mDiagnosticGet; + Coap::Resource mDiagnosticReset; + Ip6::UdpSocket mSocket; + + uint8_t mCoapToken[2]; + uint16_t mCoapMessageId; + Coap::Server &mCoapServer; + Mle::MleRouter &mMle; + ThreadNetif &mNetif; +}; + +/** + * @} + */ +} // namespace NetworkDiagnostic + +} // namespace Thread + +#endif // NETWORK_DIAGNOSTIC_HPP_ diff --git a/src/core/thread/network_diag_tlvs.cpp b/src/core/thread/network_diag_tlvs.cpp new file mode 100644 index 000000000..145e0dd4a --- /dev/null +++ b/src/core/thread/network_diag_tlvs.cpp @@ -0,0 +1,86 @@ +/* + * 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 common methods for manipulating Network Diagnostic TLVs. + */ + +#include +#include +#include + +namespace Thread { +namespace NetworkDiagnostic { + +ThreadError NetworkDiagnosticTlv::GetTlv(const Message &aMessage, Type aType, uint16_t aMaxLength, + NetworkDiagnosticTlv &aTlv) +{ + ThreadError error = kThreadError_Parse; + uint16_t offset; + + SuccessOrExit(error = GetOffset(aMessage, aType, offset)); + aMessage.Read(offset, sizeof(NetworkDiagnosticTlv), &aTlv); + + if (aMaxLength > sizeof(aTlv) + aTlv.GetLength()) + { + aMaxLength = sizeof(aTlv) + aTlv.GetLength(); + } + + aMessage.Read(offset, aMaxLength, &aTlv); + +exit: + return error; +} + +ThreadError NetworkDiagnosticTlv::GetOffset(const Message &aMessage, Type aType, uint16_t &aOffset) +{ + ThreadError error = kThreadError_Parse; + uint16_t offset = aMessage.GetOffset(); + uint16_t end = aMessage.GetLength(); + NetworkDiagnosticTlv tlv; + + while (offset < end) + { + aMessage.Read(offset, sizeof(NetworkDiagnosticTlv), &tlv); + + if (tlv.GetType() == aType && (offset + sizeof(tlv) + tlv.GetLength()) <= end) + { + aOffset = offset; + ExitNow(error = kThreadError_None); + } + + offset += sizeof(tlv) + tlv.GetLength(); + } + +exit: + return error; +} + +} // namespace NetworkDiagnostic +} // namespace Thread diff --git a/src/core/thread/network_diag_tlvs.hpp b/src/core/thread/network_diag_tlvs.hpp new file mode 100644 index 000000000..1d9b2715d --- /dev/null +++ b/src/core/thread/network_diag_tlvs.hpp @@ -0,0 +1,1315 @@ +/* + * 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 includes definitions for generating and processing MLE TLVs. + */ + +#ifndef NETWORK_DIAGNOSTIC_TLVS_HPP_ +#define NETWORK_DIAGNOSTIC_TLVS_HPP_ + +#include + +#include +#include +#include +#include +#include +#include + +using Thread::Encoding::BigEndian::HostSwap16; +using Thread::Encoding::BigEndian::HostSwap32; + +namespace Thread { + +namespace NetworkDiagnostic { + +/** + * @addtogroup core-mle-tlvs + * + * @brief + * This module includes definitions for generating and processing MLE TLVs. + * + * @{ + * + */ + +enum +{ + kNumResetTlvTypes = 1, + kNumTlvTypes = 18, +}; + +/** + * This class implements MLE TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class NetworkDiagnosticTlv +{ +public: + /** + * MLE TLV Types. + * + */ + enum Type + { + kExtMacAddress = 0, ///< Source Address TLV + kAddress16 = 1, ///< Address16 TLV + kMode = 2, ///< Mode TLV + kTimeout = 3, ///< Timeout TLV + kConnectivity = 4, ///< Connectivity TLV + kRoute = 5, ///< Routing-Table TLV + kLeaderData = 6, ///< Leader Data TLV + kNetworkData = 7, ///< Network Data TLV + kIPv6AddressList = 8, ///< IPv6 Address List TLV + kMacCounters = 9, ///< Mac Counters TLV + kBatteryLevel = 14, ///< Battery Level TLV + kSupplyVoltage = 15, ///< Supply Voltage TLV + kChildTable = 16, ///< Child Table TLV + kChannelPages = 17, ///< Channel Pages TLV + kInvalid = 255, + }; + + /** + * This method returns the Type value. + * + * @returns The Type value. + * + */ + Type GetType(void) const { return static_cast(mType); } + + /** + * This method sets the Type value. + * + * @param[in] aType The Type value. + * + */ + void SetType(Type aType) { mType = static_cast(aType); } + + /** + * This method returns the Length value. + * + */ + uint8_t GetLength(void) const { return mLength; } + + /** + * This method returns the length to be sent + */ + uint8_t GetSize(void) const { return mLength + sizeof(NetworkDiagnosticTlv); } + + /** + * This method sets the Length value. + * + * @param[in] aLength The Length value. + * + */ + void SetLength(uint8_t aLength) { mLength = aLength; } + + /** + * This static method reads the requested TLV out of @p aMessage. + * + * @param[in] aMessage A reference to the message. + * @param[in] aType The Type value to search for. + * @param[in] aMaxLength Maximum number of bytes to read. + * @param[out] aTlv A reference to the TLV that will be copied to. + * + * @retval kThreadError_None Successfully copied the TLV. + * @retval kThreadError_NotFound Could not find the TLV with Type @p aType. + * + */ + static ThreadError GetTlv(const Message &aMessage, Type aType, uint16_t aMaxLength, NetworkDiagnosticTlv &aTlv); + + /** + * This static method obtains the offset of a TLV within @p aMessage. + * + * @param[in] aMessage A reference to the message. + * @param[in] aType The Type value to search for. + * @param[out] aOffset A reference to the offset of the TLV. + * + * @retval kThreadError_None Successfully copied the TLV. + * @retval kThreadError_NotFound Could not find the TLV with Type @p aType. + * + */ + static ThreadError GetOffset(const Message &aMessage, Type aType, uint16_t &aOffset); + +private: + uint8_t mType; + uint8_t mLength; +} OT_TOOL_PACKED_END; + +/** + * This class implements Extended Address TLV generation and parsing. + * + */ +class ExtMacAddressTlv: public NetworkDiagnosticTlv +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) { SetType(kExtMacAddress); SetLength(sizeof(*this) - sizeof(NetworkDiagnosticTlv)); } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() == sizeof(*this) - sizeof(NetworkDiagnosticTlv); } + + /** + * This method returns a pointer to the Extended MAC Address. + * + * @returns A pointer to the Extended MAC Address. + * + */ + const Mac::ExtAddress *GetMacAddr(void) const { return &mMacAddr; } + + /** + * This method sets the Extended MAC Address. + * + * @param[in] aAddress A reference to the Extended MAC Address. + * + */ + void SetMacAddr(const Mac::ExtAddress &aAddress) { mMacAddr = aAddress; } + +private: + Mac::ExtAddress mMacAddr; +}; + +/** + * This class implements Source Address TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class Address16Tlv: public NetworkDiagnosticTlv +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) { SetType(kAddress16); SetLength(sizeof(*this) - sizeof(NetworkDiagnosticTlv)); } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() == sizeof(*this) - sizeof(NetworkDiagnosticTlv); } + + /** + * This method returns the RLOC16 value. + * + * @returns The RLOC16 value. + * + */ + uint16_t GetRloc16(void) const { return HostSwap16(mRloc16); } + + /** + * This method sets the RLOC16 value. + * + * @param[in] aRloc16 The RLOC16 value. + * + */ + void SetRloc16(uint16_t aRloc16) { mRloc16 = HostSwap16(aRloc16); } + +private: + uint16_t mRloc16; +} OT_TOOL_PACKED_END; + +/** + * This class implements Mode TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class ModeTlv: public NetworkDiagnosticTlv +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) { SetType(kMode); SetLength(sizeof(*this) - sizeof(NetworkDiagnosticTlv)); } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() == sizeof(*this) - sizeof(NetworkDiagnosticTlv); } + + enum + { + kModeRxOnWhenIdle = 1 << 3, + kModeSecureDataRequest = 1 << 2, + kModeFFD = 1 << 1, + kModeFullNetworkData = 1 << 0, + }; + + /** + * This method returns the Mode value. + * + * @returns The Mode value. + * + */ + uint8_t GetMode(void) const { return mMode; } + + /** + * This method sets the Mode value. + * + * @param[in] aMode The Mode value. + * + */ + void SetMode(uint8_t aMode) { mMode = aMode; } + +private: + uint8_t mMode; +} OT_TOOL_PACKED_END; + +/** + * This class implements Timeout TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class TimeoutTlv: public NetworkDiagnosticTlv +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) { SetType(kTimeout); SetLength(sizeof(*this) - sizeof(NetworkDiagnosticTlv)); } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() == sizeof(*this) - sizeof(NetworkDiagnosticTlv); } + + /** + * This method returns the Timeout value. + * + * @returns The Timeout value. + * + */ + uint32_t GetTimeout(void) const { return HostSwap32(mTimeout); } + + /** + * This method sets the Timeout value. + * + * @param[in] aTimeout The Timeout value. + * + */ + void SetTimeout(uint32_t aTimeout) { mTimeout = HostSwap32(aTimeout); } + +private: + uint32_t mTimeout; +} OT_TOOL_PACKED_END; + +/** + * This class implements Connectivity TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class ConnectivityTlv: public NetworkDiagnosticTlv +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) { SetType(kConnectivity); SetLength(sizeof(*this) - sizeof(NetworkDiagnosticTlv)); } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() == sizeof(*this) - sizeof(NetworkDiagnosticTlv); } + + /** + * This method returns the Parent Priority value. + * + * @returns The Parent Priority value. + * + */ + int8_t GetParentPriority(void) const { return (mParentPriority & kParentPriorityOffset) >> kParentPriorityOffset; } + + /** + * This method sets the Parent Priority value. + * + * @param[in] aParentPriority The Parent Priority value. + * + */ + void SetParentPriority(int8_t aParentPriority) { + mParentPriority = (aParentPriority << kParentPriorityOffset) & kParentPriorityMask; + } + + /** + * This method returns the Link Quality 3 value. + * + * @returns The Link Quality 3 value. + * + */ + uint8_t GetLinkQuality3(void) const { return mLinkQuality3; } + + /** + * This method sets the Link Quality 3 value. + * + * @param[in] aLinkQuality The Link Quality 3 value. + * + */ + void SetLinkQuality3(uint8_t aLinkQuality) { mLinkQuality3 = aLinkQuality; } + + /** + * This method returns the Link Quality 2 value. + * + * @returns The Link Quality 2 value. + * + */ + uint8_t GetLinkQuality2(void) const { return mLinkQuality2; } + + /** + * This method sets the Link Quality 2 value. + * + * @param[in] aLinkQuality The Link Quality 2 value. + * + */ + void SetLinkQuality2(uint8_t aLinkQuality) { mLinkQuality2 = aLinkQuality; } + + /** + * This method sets the Link Quality 1 value. + * + * @returns The Link Quality 1 value. + * + */ + uint8_t GetLinkQuality1(void) const { return mLinkQuality1; } + + /** + * This method sets the Link Quality 1 value. + * + * @param[in] aLinkQuality The Link Quality 1 value. + * + */ + void SetLinkQuality1(uint8_t aLinkQuality) { mLinkQuality1 = aLinkQuality; } + + /** + * This method sets the Active Routers value. + * + * @returns The Active Routers value. + * + */ + uint8_t GetActiveRouters(void) const { return mActiveRouters; } + + /** + * This method sets the Active Routers value. + * + * @param[in] aActiveRouters The Active Routers value. + * + */ + void SetActiveRouters(uint8_t aActiveRouters) { mActiveRouters = aActiveRouters; } + + /** + * This method returns the Leader Cost value. + * + * @returns The Leader Cost value. + * + */ + uint8_t GetLeaderCost(void) const { return mLeaderCost; } + + /** + * This method sets the Leader Cost value. + * + * @param[in] aCost The Leader Cost value. + * + */ + void SetLeaderCost(uint8_t aCost) { mLeaderCost = aCost; } + + /** + * This method returns the ID Sequence value. + * + * @returns The ID Sequence value. + * + */ + uint8_t GetIdSequence(void) const { return mIdSequence; } + + /** + * This method sets the ID Sequence value. + * + * @param[in] aSequence The ID Sequence value. + * + */ + void SetIdSequence(uint8_t aSequence) { mIdSequence = aSequence; } + + /** + * This method returns the SED Buffer Size value. + * + * @returns The SED Buffer Size value. + * + */ + uint16_t GetSedBufferSize(void) const { return HostSwap16(mSedBufferSize); } + + /** + * This method sets the SED Buffer Size value. + * + * @param[in] aSedBufferSize The SED Buffer Size value. + * + */ + void SetSedBufferSize(uint16_t aSedBufferSize) { mSedBufferSize = HostSwap16(aSedBufferSize); } + + /** + * This method returns the SED Datagram Count value. + * + * @returns The SED Datagram Count value. + * + */ + uint8_t GetSedDatagramCount(void) const { return mSedDatagramCount; } + + /** + * This method sets the SED Datagram Count value. + * + * @param[in] aSedDatagramCount The SED Datagram Count value. + * + */ + void SetSedDatagramCount(uint8_t aSedDatagramCount) { mSedDatagramCount = aSedDatagramCount; } + +private: + enum + { + kParentPriorityOffset = 6, + kParentPriorityMask = 3 << kParentPriorityOffset, + }; + + uint8_t mParentPriority; + uint8_t mLinkQuality3; + uint8_t mLinkQuality2; + uint8_t mLinkQuality1; + uint8_t mLeaderCost; + uint8_t mIdSequence; + uint8_t mActiveRouters; + uint16_t mSedBufferSize; + uint8_t mSedDatagramCount; +} OT_TOOL_PACKED_END; + +/** + * This class implements Route TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class RouteTlv: public NetworkDiagnosticTlv +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) { SetType(kRoute); SetLength(sizeof(*this) - sizeof(NetworkDiagnosticTlv)); } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { + return GetLength() >= sizeof(mRouterIdSequence) + sizeof(mRouterIdMask) && + GetLength() <= sizeof(*this) - sizeof(NetworkDiagnosticTlv); + } + + /** + * This method returns the Router ID Sequence value. + * + * @returns The Router ID Sequence value. + * + */ + uint8_t GetRouterIdSequence(void) const { return mRouterIdSequence; } + + /** + * This method sets the Router ID Sequence value. + * + * @param[in] aSequence The Router ID Sequence value. + * + */ + void SetRouterIdSequence(uint8_t aSequence) { mRouterIdSequence = aSequence; } + + /** + * This method clears the Router ID Mask. + * + */ + void ClearRouterIdMask(void) { memset(mRouterIdMask, 0, sizeof(mRouterIdMask)); } + + /** + * This method indicates whether or not a Router ID bit is set. + * + * @param[in] aRouterId The Router ID. + * + * @retval TRUE If the Router ID bit is set. + * @retval FALSE If the Router ID bit is not set. + * + */ + bool IsRouterIdSet(uint8_t aRouterId) const { + return (mRouterIdMask[aRouterId / 8] & (0x80 >> (aRouterId % 8))) != 0; + } + + /** + * This method sets the Router ID bit. + * + * @param[in] aRouterId The Router ID bit to set. + * + */ + void SetRouterId(uint8_t aRouterId) { mRouterIdMask[aRouterId / 8] |= 0x80 >> (aRouterId % 8); } + + /** + * This method returns the Route Data Length value. + * + * @returns The Route Data Length value. + * + */ + uint8_t GetRouteDataLength(void) const { return GetLength() - sizeof(mRouterIdSequence) - sizeof(mRouterIdMask); } + + /** + * This method sets the Route Data Length value. + * + * @param[in] aLength The Route Data Length value. + * + */ + void SetRouteDataLength(uint8_t aLength) { + SetLength(sizeof(mRouterIdSequence) + sizeof(mRouterIdMask) + aLength); + } + + /** + * This method returns the Route Cost value for a given Router ID. + * + * @returns The Route Cost value for a given Router ID. + * + */ + uint8_t GetRouteCost(uint8_t aRouterId) const { + return mRouteData[aRouterId] & kRouteCostMask; + } + + /** + * This method sets the Route Cost value for a given Router ID. + * + * @param[in] aRouterId The Router ID. + * @param[in] aRouteCost The Route Cost value. + * + */ + void SetRouteCost(uint8_t aRouterId, uint8_t aRouteCost) { + mRouteData[aRouterId] = (mRouteData[aRouterId] & ~kRouteCostMask) | aRouteCost; + } + + /** + * This method returns the Link Quality In value for a given Router ID. + * + * @returns The Link Quality In value for a given Router ID. + * + */ + uint8_t GetLinkQualityIn(uint8_t aRouterId) const { + return (mRouteData[aRouterId] & kLinkQualityInMask) >> kLinkQualityInOffset; + } + + /** + * This method sets the Link Quality In value for a given Router ID. + * + * @param[in] aRouterId The Router ID. + * @param[in] aLinkQuality The Link Quality In value for a given Router ID. + * + */ + void SetLinkQualityIn(uint8_t aRouterId, uint8_t aLinkQuality) { + mRouteData[aRouterId] = + (mRouteData[aRouterId] & ~kLinkQualityInMask) | + ((aLinkQuality << kLinkQualityInOffset) & kLinkQualityInMask); + } + + /** + * This method returns the Link Quality Out value for a given Router ID. + * + * @returns The Link Quality Out value for a given Router ID. + * + */ + uint8_t GetLinkQualityOut(uint8_t aRouterId) const { + return (mRouteData[aRouterId] & kLinkQualityOutMask) >> kLinkQualityOutOffset; + } + + /** + * This method sets the Link Quality Out value for a given Router ID. + * + * @param[in] aRouterId The Router ID. + * @param[in] aLinkQuality The Link Quality Out value for a given Router ID. + * + */ + void SetLinkQualityOut(uint8_t aRouterId, uint8_t aLinkQuality) { + mRouteData[aRouterId] = + (mRouteData[aRouterId] & ~kLinkQualityOutMask) | + ((aLinkQuality << kLinkQualityOutOffset) & kLinkQualityOutMask); + } + +private: + enum + { + kLinkQualityOutOffset = 6, + kLinkQualityOutMask = 3 << kLinkQualityOutOffset, + kLinkQualityInOffset = 4, + kLinkQualityInMask = 3 << kLinkQualityInOffset, + kRouteCostOffset = 0, + kRouteCostMask = 0xf << kRouteCostOffset, + }; + uint8_t mRouterIdSequence; + uint8_t mRouterIdMask[BitVectorBytes(Mle::kMaxRouterId)]; + uint8_t mRouteData[Mle::kMaxRouters]; +} OT_TOOL_PACKED_END; + +/** + * This class implements Leader Data TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class LeaderDataTlv: public NetworkDiagnosticTlv +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) { SetType(kLeaderData); SetLength(sizeof(*this) - sizeof(NetworkDiagnosticTlv)); } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() == sizeof(*this) - sizeof(NetworkDiagnosticTlv); } + + /** + * This method returns the Partition ID value. + * + * @returns The Partition ID value. + * + */ + uint32_t GetPartitionId(void) const { return HostSwap32(mPartitionId); } + + /** + * This method sets the Partition ID value. + * + * @param[in] aPartitionId The Partition ID value. + * + */ + void SetPartitionId(uint32_t aPartitionId) { mPartitionId = HostSwap32(aPartitionId); } + + /** + * This method returns the Weighting value. + * + * @returns The Weighting value. + * + */ + uint8_t GetWeighting(void) const { return mWeighting; } + + /** + * This method sets the Weighting value. + * + * @param[in] aWeighting The Weighting value. + * + */ + void SetWeighting(uint8_t aWeighting) { mWeighting = aWeighting; } + + /** + * This method returns the Data Version value. + * + * @returns The Data Version value. + * + */ + uint8_t GetDataVersion(void) const { return mDataVersion; } + + /** + * This method sets the Data Version value. + * + * @param[in] aVersion The Data Version value. + * + */ + void SetDataVersion(uint8_t aVersion) { mDataVersion = aVersion; } + + /** + * This method returns the Stable Data Version value. + * + * @returns The Stable Data Version value. + * + */ + uint8_t GetStableDataVersion(void) const { return mStableDataVersion; } + + /** + * This method sets the Stable Data Version value. + * + * @param[in] aVersion The Stable Data Version value. + * + */ + void SetStableDataVersion(uint8_t aVersion) { mStableDataVersion = aVersion; } + + /** + * This method returns the Leader Router ID value. + * + * @returns The Leader Router ID value. + * + */ + uint8_t GetLeaderRouterId(void) const { return mLeaderRouterId; } + + /** + * This method sets the Leader Router ID value. + * + * @param[in] aRouterId The Leader Router ID value. + * + */ + void SetLeaderRouterId(uint8_t aRouterId) { mLeaderRouterId = aRouterId; } + +private: + uint32_t mPartitionId; + uint8_t mWeighting; + uint8_t mDataVersion; + uint8_t mStableDataVersion; + uint8_t mLeaderRouterId; +} OT_TOOL_PACKED_END; + +/** + * This class implements Network Data TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class NetworkDataTlv: public NetworkDiagnosticTlv +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) { SetType(kNetworkData); SetLength(sizeof(*this) - sizeof(NetworkDiagnosticTlv)); } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() < sizeof(*this) - sizeof(NetworkDiagnosticTlv); } + + /** + * This method returns a pointer to the Network Data. + * + * @returns A pointer to the Network Data. + * + */ + uint8_t *GetNetworkData(void) { return mNetworkData; } + + /** + * This method sets the Network Data. + * + * @param[in] aNetworkData A pointer to the Network Data. + * + */ + void SetNetworkData(const uint8_t *aNetworkData) { memcpy(mNetworkData, aNetworkData, GetLength()); } + +private: + uint8_t mNetworkData[255]; +} OT_TOOL_PACKED_END; + +/** + * This class implements IPv6 Address List TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class IPv6AddressListTlv: public NetworkDiagnosticTlv +{ +public: + enum + { + kMaxSize = 8, ///< Maximum size in bytes (Thread Specification). + }; + + /** + * This method initializes the TLV. + * + */ + void Init(void) { SetType(kIPv6AddressList); SetLength(sizeof(*this) - sizeof(NetworkDiagnosticTlv)); } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() <= 8 * kMaxSize; } + + /** + * This method returns a pointer to the Challenge value. + * + * @returns A pointer to the Challenge value. + * + */ + const Ip6::Address *GetIPv6Address(uint8_t aIndex) const { return ((aIndex * 8 < GetLength()) ? &mIPv6AddressList[aIndex] : NULL); } + +private: + Ip6::Address mIPv6AddressList[0]; +} OT_TOOL_PACKED_END; + +/** + * This class implements Mac Counters TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class MacCountersTlv: public NetworkDiagnosticTlv +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) { SetType(kMacCounters); SetLength(sizeof(*this) - sizeof(NetworkDiagnosticTlv)); } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() == sizeof(*this) - sizeof(NetworkDiagnosticTlv); } + + /** + * This method returns a pointer to the Response value. + * + * @returns A pointer to the Response value. + * + */ + uint32_t GetIfInUnknownProtos(void) const { return mIfInUnknownProtos; } + + /** + * This method sets the Response value. + * + * @param[in] aResponse A pointer to the Response value. + * + */ + void SetIfInUnknownProtos(const uint32_t aIfInUnknownProtos) { mIfInUnknownProtos = aIfInUnknownProtos; } + + /** + * This method returns a pointer to the Response value. + * + * @returns A pointer to the Response value. + * + */ + uint32_t GetIfInErrors(void) const { return mIfInErrors; } + + /** + * This method sets the Response value. + * + * @param[in] aResponse A pointer to the Response value. + * + */ + void SetIfInErrors(const uint32_t aIfInErrors) { mIfInErrors = aIfInErrors; } + + /** + * This method returns a pointer to the Response value. + * + * @returns A pointer to the Response value. + * + */ + uint32_t GetIfOutErrors(void) const { return mIfOutErrors; } + + /** + * This method sets the Response value. + * + * @param[in] aResponse A pointer to the Response value. + * + */ + void SetIfOutErrors(const uint32_t aIfOutErrors) { mIfOutErrors = aIfOutErrors; } + + /** + * This method returns a pointer to the Response value. + * + * @returns A pointer to the Response value. + * + */ + uint32_t GetIfInUcastPkts(void) const { return mIfInUcastPkts; } + + /** + * This method sets the Response value. + * + * @param[in] aResponse A pointer to the Response value. + * + */ + void SetIfInUcastPkts(const uint32_t aIfInUcastPkts) { mIfInUcastPkts = aIfInUcastPkts; } + + /** + * This method returns a pointer to the Response value. + * + * @returns A pointer to the Response value. + * + */ + uint32_t GetIfInBroadcastPkts(void) const { return mIfInBroadcastPkts; } + + /** + * This method sets the Response value. + * + * @param[in] aResponse A pointer to the Response value. + * + */ + void SetIfInBroadcastPkts(const uint32_t aIfInBroadcastPkts) { mIfInBroadcastPkts = aIfInBroadcastPkts; } + + /** + * This method returns a pointer to the Response value. + * + * @returns A pointer to the Response value. + * + */ + uint32_t GetIfInDiscards(void) const { return mIfInDiscards; } + + /** + * This method sets the Response value. + * + * @param[in] aResponse A pointer to the Response value. + * + */ + void SetIfInDiscards(const uint32_t aIfInDiscards) { mIfInDiscards = aIfInDiscards; } + + /** + * This method returns a pointer to the Response value. + * + * @returns A pointer to the Response value. + * + */ + uint32_t GetIfOutUcastPkts(void) const { return mIfOutUcastPkts; } + + /** + * This method sets the Response value. + * + * @param[in] aResponse A pointer to the Response value. + * + */ + void SetIfOutUcastPkts(const uint32_t aIfOutUcastPkts) { mIfOutUcastPkts = aIfOutUcastPkts; } + + /** + * This method returns a pointer to the Response value. + * + * @returns A pointer to the Response value. + * + */ + uint32_t GetIfOutBroadcastPkts(void) const { return mIfOutBroadcastPkts; } + + /** + * This method sets the Response value. + * + * @param[in] aResponse A pointer to the Response value. + * + */ + void SetIfOutBroadcastPkts(const uint32_t aIfOutBroadcastPkts) { mIfOutBroadcastPkts = aIfOutBroadcastPkts; } + + /** + * This method returns a pointer to the Response value. + * + * @returns A pointer to the Response value. + * + */ + uint32_t GetIfOutDiscards(void) const { return mIfOutDiscards; } + + /** + * This method sets the Response value. + * + * @param[in] aResponse A pointer to the Response value. + * + */ + void SetIfOutDiscards(const uint32_t aIfOutDiscards) { mIfOutDiscards = aIfOutDiscards; } + +private: + uint32_t mIfInUnknownProtos; + uint32_t mIfInErrors; + uint32_t mIfOutErrors; + uint32_t mIfInUcastPkts; + uint32_t mIfInBroadcastPkts; + uint32_t mIfInDiscards; + uint32_t mIfOutUcastPkts; + uint32_t mIfOutBroadcastPkts; + uint32_t mIfOutDiscards; +} OT_TOOL_PACKED_END; + +/** + * This class implements Battery Level TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class BatteryLevelTlv: public NetworkDiagnosticTlv +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) { SetType(kBatteryLevel); SetLength(sizeof(*this) - sizeof(NetworkDiagnosticTlv)); } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() == sizeof(*this) - sizeof(NetworkDiagnosticTlv); } + + /** + * This method returns the Status value. + * + * @returns The Status value. + * + */ + uint8_t GetBatteryLevel(void) const { return mBatteryLevel; } + + /** + * This method sets the Status value. + * + * @param[in] aStatus The Status value. + * + */ + void SetBatteryLevel(uint8_t aBatteryLevel) { mBatteryLevel = aBatteryLevel; } + +private: + uint8_t mBatteryLevel; +} OT_TOOL_PACKED_END; + + +/** + * This class implements Supply Voltage TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class SupplyVoltageTlv: public NetworkDiagnosticTlv +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) { SetType(kSupplyVoltage); SetLength(sizeof(*this) - sizeof(NetworkDiagnosticTlv)); } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() == sizeof(*this) - sizeof(NetworkDiagnosticTlv); } + + /** + * This method returns the Status value. + * + * @returns The Status value. + * + */ + uint16_t GetSupplyVoltage(void) const { return mSupplyVoltage; } + + /** + * This method sets the SupplyVoltage value. + * + * @param[in] aSupplyVoltage The SupplyVoltage value. + * + */ + void SetSupplyVoltage(uint16_t aSupplyVoltage) { mSupplyVoltage = aSupplyVoltage; } + +private: + uint16_t mSupplyVoltage; +} OT_TOOL_PACKED_END; + +/** + * This class implements Child Table Entry generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class ChildTableEntry +{ +public: + /** + * This method returns the Version value. + * + * @returns The Version value. + * + */ + uint8_t GetTimeout(void) const { return mTimeout; } + + /** + * This method sets the Version value. + * + * @param[in] aVersion The Version value. + * + */ + void SetTimeout(uint8_t aTimeout) { mTimeout = aTimeout; } + + /** + * This method returns the Version value. + * + * @returns The Version value. + * + */ + uint16_t GetChildId(void) const { return mChildId; } + + /** + * This method sets the Version value. + * + * @param[in] aVersion The Version value. + * + */ + void SetChildId(uint16_t aChildId) { mChildId = aChildId; } + + /** + * This method returns the Version value. + * + * @returns The Version value. + * + */ + uint8_t GetMode(void) const { return mMode; } + + /** + * This method sets the Version value. + * + * @param[in] aVersion The Version value. + * + */ + void SetMode(uint8_t aMode) { mMode = aMode; } + + /** + * This method returns the Reserved value. + * + * @returns The Reserved value. + * + */ + uint8_t GetReserved(void) const { return mReserved; } + + /** + * This method sets the Reserved value. + * + * @param[in] aReserved The Reserved value. + * + */ + void SetReserved(uint8_t aReserved) { mReserved = aReserved; } + + +private: + uint8_t mTimeout: 5; + uint8_t mReserved: 2; + uint16_t mChildId: 9; + uint8_t mMode: 8; +} OT_TOOL_PACKED_END; + + +/** + * This class implements Child Table TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class ChildTableTlv: public NetworkDiagnosticTlv +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) { SetType(kChildTable); SetLength(sizeof(*this) - sizeof(NetworkDiagnosticTlv)); } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() == sizeof(*this) - sizeof(NetworkDiagnosticTlv); } + + uint8_t GetNumEntries() const { return GetLength() / sizeof(ChildTableEntry); } + + ChildTableEntry &GetEntry(uint8_t i) { + return mChildTableEntry[i]; + } + +private: + ChildTableEntry mChildTableEntry[0]; +} OT_TOOL_PACKED_END; + + +/** + * This class implements Channel Pages TLV generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class ChannelPagesTlv: public NetworkDiagnosticTlv +{ +public: + /** + * This method initializes the TLV. + * + */ + void Init(void) { SetType(kChannelPages); SetLength(sizeof(*this) - sizeof(NetworkDiagnosticTlv)); } + + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() == sizeof(*this) - sizeof(NetworkDiagnosticTlv); } + + uint8_t *GetChannelPages() { return mChannelPages; } + +private: + uint8_t mChannelPages[1]; +} OT_TOOL_PACKED_END; +/** + * @} + * + */ + +} // namespace NetworkDiagnostic + + +} // namespace Thread + +#endif // NETWORK_DIAGNOSTIC_TLVS_HPP_ diff --git a/src/core/thread/thread_netif.cpp b/src/core/thread/thread_netif.cpp index 42dd9ef3d..60760c542 100644 --- a/src/core/thread/thread_netif.cpp +++ b/src/core/thread/thread_netif.cpp @@ -67,6 +67,7 @@ ThreadNetif::ThreadNetif(Ip6::Ip6 &aIp6): mMleRouter(*this), mNetworkDataLocal(*this), mNetworkDataLeader(*this), + mNetworkDiagnostic(*this), #if OPENTHREAD_ENABLE_COMMISSIONER mCommissioner(*this), #endif // OPENTHREAD_ENABLE_COMMISSIONER diff --git a/src/core/thread/thread_netif.hpp b/src/core/thread/thread_netif.hpp index 86ea2c5fa..074757174 100644 --- a/src/core/thread/thread_netif.hpp +++ b/src/core/thread/thread_netif.hpp @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -159,6 +160,14 @@ public: */ AddressResolver &GetAddressResolver(void) { return mAddressResolver; } + /** + * This method returns a pointer to the network diagnostic object. + * + * @returns A reference to the address resolver object. + * + */ + NetworkDiagnostic::NetworkDiagnostic &GetNetworkDiagnostic(void) { return mNetworkDiagnostic; } + /** * This method returns a pointer to the coap server object. * @@ -262,6 +271,7 @@ private: Mle::MleRouter mMleRouter; NetworkData::Local mNetworkDataLocal; NetworkData::Leader mNetworkDataLeader; + NetworkDiagnostic::NetworkDiagnostic mNetworkDiagnostic; bool mIsUp; #if OPENTHREAD_ENABLE_COMMISSIONER diff --git a/src/core/thread/thread_uris.hpp b/src/core/thread/thread_uris.hpp index e9f2c9e81..083f25729 100644 --- a/src/core/thread/thread_uris.hpp +++ b/src/core/thread/thread_uris.hpp @@ -210,6 +210,22 @@ namespace Thread { */ #define OPENTHREAD_URI_COMMISSIONER_SET "c/cs" +/** + * @def OPENTHREAD_URI_DIAGNOSTIC_GET + * + * The URI Path for Network Diagnostic Get. + * + */ +#define OPENTHREAD_URI_DIAGNOSTIC_GET "d/dg" + +/** + * @def OPENTHREAD_URI_DIAG_RST + * + * The URI Path for Network Diagnostic Reset. + * + */ +#define OPENTHREAD_URI_DIAGNOSTIC_RESET "d/dr" + } // namespace Thread #endif // THREAD_URIS_HPP_ diff --git a/tools/harness-thci/ARM.py b/tools/harness-thci/ARM.py index 1c7301e73..d396486bd 100644 --- a/tools/harness-thci/ARM.py +++ b/tools/harness-thci/ARM.py @@ -1570,11 +1570,29 @@ class ARM(IThci): def setSleepyNodePollTime(self): pass - def diagnosticGet(self, strDestinationAddr, TLV_ids=0): - pass + def diagnosticGet(self, strDestinationAddr, listTLV_ids=[]): + if not listTLV_ids: + return - def diagnosticReset(self, strDestinationAddr, iTLV_id): - pass + if not len(listTLV_ids): + return + + cmd = 'networkdiagnostic get %s %s' % (strDestinationAddr, ' '.join([str(tlv) for tlv in listTLV_ids])) + print(cmd) + + return self.__sendCommand(cmd) + + def diagnosticReset(self, strDestinationAddr, listTLV_ids=[]): + if not listTLV_ids: + return + + if not len(listTLV_ids): + return + + cmd = 'networkdiagnostic reset %s %s' % (strDestinationAddr, ' '.join([str(tlv) for tlv in listTLV_ids])) + print(cmd) + + return self.__sendCommand(cmd) def startNativeCommissioner(self, strPSKc='GRLpassWord'): pass