From 57e2b7d944e27377ce198c2d18005c5a4eda1a57 Mon Sep 17 00:00:00 2001 From: lezhan Date: Wed, 16 Oct 2024 09:29:45 +0000 Subject: [PATCH] [diag] Add network diagnostic data: definition and analysis supported TLVs: - Ipv6addressList - ChildIpv6AddressList - Mode - ChildTable - LeaderData - Route64 - MacCounters - Eui64 - ExtMacAddress - MacAddress --- include/commissioner/network_diag_data.hpp | 187 ++++++++++++++ src/library/commissioner_impl.cpp | 279 +++++++++++++++++++++ src/library/commissioner_impl.hpp | 13 + 3 files changed, 479 insertions(+) create mode 100644 include/commissioner/network_diag_data.hpp diff --git a/include/commissioner/network_diag_data.hpp b/include/commissioner/network_diag_data.hpp new file mode 100644 index 00000000..f1a716a0 --- /dev/null +++ b/include/commissioner/network_diag_data.hpp @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2024, The OpenThread Commissioner 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 defines the types of Thread Network Diagnostic TLVs used for network diagnostics. + */ + +#ifndef OT_COMM_NETWORK_DIAG_TLVS_HPP_ +#define OT_COMM_NETWORK_DIAG_TLVS_HPP_ + +#include +#include +#include +#include +#include + +#include "defines.hpp" +#include "error.hpp" + +#define CHILD_TABLE_ENTRY_BYTES 3 +#define IPV6_ADDRESS_BYTES 16 +#define LEADER_DATA_BYTES 8 +#define MAC_COUNTERS_BYTES 36 +#define RLOC16_BYTES 2 +#define ROUTER_ID_MASK_BYTES 8 + +namespace ot { + +namespace commissioner { + +/** + * @brief Mode Data + */ +struct ModeData +{ + bool mIsRxOnWhenIdleMode = false; + bool mIsMtd = false; + bool mIsStableNetworkDataRequired = false; +}; + +/** + * @brief Child Entry in Child Table + */ +struct ChildTableEntry +{ + uint32_t mTimeout = 0; + uint8_t mIncomingLinkQuality = 0; + uint16_t mChildId = 0; + ModeData mModeData; +}; + +/** + * @brief IPv6 Addresses List + */ +struct Ipv6AddressList +{ + std::vector mIpv6Addresses; +}; + +/** + * @brief Leader Data + */ +struct LeaderData +{ + uint32_t mPartitionId = 0; + uint8_t mWeighting = 0; + uint8_t mDataVersion = 0; + uint8_t mStableDataVersion = 0; + uint8_t mRouterId = 0; +}; + +/** + * @brief Route Data Entry of RouteData in Route64 + */ +struct RouteDataEntry +{ + uint8_t mRouterId = 0; + uint8_t mOutgoingLinkQuality = 0; + uint8_t mIncomingLinkQuality = 0; + uint8_t mRouteCost = 0; +}; + +/** + * @brief Route64 Data + */ +struct Route64 +{ + uint8_t mIdSequence = 0; + ByteArray mMask; + std::vector mRouteData; +}; + +/** + * @brief Child IPv6 Address List + */ +struct ChildIpv6AddressList +{ + uint16_t mRloc16 = 0; + Ipv6AddressList mIpv6AddressList; +}; + +/** + * @brief MAC Counters + */ +struct MacCounters +{ + uint32_t mIfInUnknownProtos = 0; + uint32_t mIfInErrors = 0; + uint32_t mIfOutErrors = 0; + uint32_t mIfInUcastPkts = 0; + uint32_t mIfInBroadcastPkts = 0; + uint32_t mIfInDiscards = 0; + uint32_t mIfOutUcastPkts = 0; + uint32_t mIfOutBroadcastPkts = 0; + uint32_t mIfOutDiscards = 0; +}; + +/** + * @brief network diagnostic data in TMF + * + * Each data field of Diagnostic TLVs is optional. The field is + * meaningful only when associative PresentFlags is included in + * `mPresentFlags`. + */ +struct NetDiagData +{ + ByteArray mExtMacAddress; + uint16_t mMacAddress = 0; + ModeData mMode; + Route64 mRoute64; + LeaderData mLeaderData; + Ipv6AddressList mIpv6AddressList; + std::vector mChildTable; + ByteArray mEui64; + ByteArray mTlvTypeList; + ChildIpv6AddressList mChildIpv6AddressList; + MacCounters mMacCounters; + + /** + * Indicates which fields are included in the dataset. + */ + uint64_t mPresentFlags; + + static constexpr uint64_t kExtMacAddressBit = (1ull << 0); + static constexpr uint64_t kMacAddressBit = (1ull << 1); + static constexpr uint64_t kModeBit = (1ull << 2); + static constexpr uint64_t kRoute64Bit = (1ull << 3); + static constexpr uint64_t kLeaderDataBit = (1ull << 4); + static constexpr uint64_t kIpv6AddressBit = (1ull << 5); + static constexpr uint64_t kChildTableBit = (1ull << 6); + static constexpr uint64_t kEui64Bit = (1ull << 7); + static constexpr uint64_t kTlvTypeBit = (1ull << 8); + static constexpr uint64_t kMacCountersBit = (1ull << 9); + static constexpr uint64_t kChildIpv6AddressBit = (1ull << 10); +}; + +} // namespace commissioner + +} // namespace ot + +#endif // OT_COMM_NETWORK_DIAG_TLVS_HPP_ diff --git a/src/library/commissioner_impl.cpp b/src/library/commissioner_impl.cpp index baa32cb1..62088f12 100644 --- a/src/library/commissioner_impl.cpp +++ b/src/library/commissioner_impl.cpp @@ -1955,6 +1955,285 @@ ByteArray CommissionerImpl::GetCommissionerDatasetTlvs(uint16_t aDatasetFlags) return tlvTypes; } +ByteArray CommissionerImpl::GetDiagTypeTlvList(uint64_t aDiagTlvFlags) +{ + ByteArray tlvTypes; + + if (aDiagTlvFlags & NetDiagData::kExtMacAddressBit) + { + EncodeTlvType(tlvTypes, tlv::Type::kNetworkDiagExtMacAddress); + } + if (aDiagTlvFlags & NetDiagData::kMacAddressBit) + { + EncodeTlvType(tlvTypes, tlv::Type::kNetworkDiagMacAddress); + } + if (aDiagTlvFlags & NetDiagData::kModeBit) + { + EncodeTlvType(tlvTypes, tlv::Type::kNetworkDiagMode); + } + if (aDiagTlvFlags & NetDiagData::kRoute64Bit) + { + EncodeTlvType(tlvTypes, tlv::Type::kNetworkDiagRoute64); + } + if (aDiagTlvFlags & NetDiagData::kLeaderDataBit) + { + EncodeTlvType(tlvTypes, tlv::Type::kNetworkDiagLeaderData); + } + if (aDiagTlvFlags & NetDiagData::kIpv6AddressBit) + { + EncodeTlvType(tlvTypes, tlv::Type::kNetworkDiagIpv6Address); + } + if (aDiagTlvFlags & NetDiagData::kMacCountersBit) + { + EncodeTlvType(tlvTypes, tlv::Type::kNetworkDiagMacCounters); + } + if (aDiagTlvFlags & NetDiagData::kChildTableBit) + { + EncodeTlvType(tlvTypes, tlv::Type::kNetworkDiagChildTable); + } + if (aDiagTlvFlags & NetDiagData::kEui64Bit) + { + EncodeTlvType(tlvTypes, tlv::Type::kNetworkDiagEui64); + } + if (aDiagTlvFlags & NetDiagData::kChildIpv6AddressBit) + { + EncodeTlvType(tlvTypes, tlv::Type::kNetworkDiagChildIpv6Address); + } + + return tlvTypes; +} + +Error CommissionerImpl::DecodeNetDiagData(NetDiagData &aNetDiagData, const ByteArray &aPayload) +{ + Error error; + tlv::TlvSet tlvSet; + NetDiagData dataset; + // Clear all data fields + dataset.mPresentFlags = 0; + SuccessOrExit(error = tlv::GetTlvSet(tlvSet, aPayload, tlv::Scope::kNetworkDiag)); + + if (auto extMacAddress = tlvSet[tlv::Type::kNetworkDiagExtMacAddress]) + { + const ByteArray &value = extMacAddress->GetValue(); + dataset.mExtMacAddress = value; + dataset.mPresentFlags |= NetDiagData::kExtMacAddressBit; + } + + if (auto macAddress = tlvSet[tlv::Type::kNetworkDiagMacAddress]) + { + uint16_t value; + value = utils::Decode(macAddress->GetValue()); + dataset.mMacAddress = value; + dataset.mPresentFlags |= NetDiagData::kMacAddressBit; + } + + if (auto mode = tlvSet[tlv::Type::kNetworkDiagMode]) + { + SuccessOrExit(error = DecodeModeData(dataset.mMode, mode->GetValue())); + dataset.mPresentFlags |= NetDiagData::kModeBit; + } + + if (auto route64 = tlvSet[tlv::Type::kNetworkDiagRoute64]) + { + const ByteArray &value = route64->GetValue(); + SuccessOrExit(error = DecodeRoute64(dataset.mRoute64, value)); + dataset.mPresentFlags |= NetDiagData::kRoute64Bit; + } + + if (auto leaderData = tlvSet[tlv::Type::kNetworkDiagLeaderData]) + { + const ByteArray &value = leaderData->GetValue(); + SuccessOrExit(error = DecodeLeaderData(dataset.mLeaderData, value)); + dataset.mPresentFlags |= NetDiagData::kLeaderDataBit; + } + + if (auto ipv6Addresses = tlvSet[tlv::Type::kNetworkDiagIpv6Address]) + { + const ByteArray &value = ipv6Addresses->GetValue(); + SuccessOrExit(error = DecodeIpv6AddressList(dataset.mIpv6AddressList, value)); + dataset.mPresentFlags |= NetDiagData::kIpv6AddressBit; + } + + if (auto macCounters = tlvSet[tlv::Type::kNetworkDiagMacCounters]) + { + const ByteArray &value = macCounters->GetValue(); + SuccessOrExit(error = DecodeMacCounters(dataset.mMacCounters, value)); + dataset.mPresentFlags |= NetDiagData::kMacCountersBit; + } + + if (auto childTable = tlvSet[tlv::Type::kNetworkDiagChildTable]) + { + const ByteArray &value = childTable->GetValue(); + SuccessOrExit(error = DecodeChildTable(dataset.mChildTable, value)); + dataset.mPresentFlags |= NetDiagData::kChildTableBit; + } + + if (auto eui64 = tlvSet[tlv::Type::kNetworkDiagEui64]) + { + const ByteArray &value = eui64->GetValue(); + dataset.mEui64 = value; + dataset.mPresentFlags |= NetDiagData::kEui64Bit; + } + + if (auto childIpv6Address = tlvSet[tlv::Type::kNetworkDiagChildIpv6Address]) + { + const ByteArray &value = childIpv6Address->GetValue(); + SuccessOrExit(error = DecodeChildIpv6AddressList(dataset.mChildIpv6AddressList, value)); + dataset.mPresentFlags |= NetDiagData::kChildIpv6AddressBit; + } + + aNetDiagData = dataset; + +exit: + return error; +} + +Error CommissionerImpl::DecodeIpv6AddressList(Ipv6AddressList &aIpv6AddressList, const ByteArray &aBuf) +{ + Error error; + size_t length = aBuf.size(); + size_t offset = 0; + while (offset < length) + { + VerifyOrExit(offset + IPV6_ADDRESS_BYTES <= length, error = ERROR_BAD_FORMAT("premature end of IPv6 Address")); + Address addr; + const ByteArray &value = {aBuf.data() + offset, aBuf.data() + offset + IPV6_ADDRESS_BYTES}; + SuccessOrExit(error = addr.Set(value)); + aIpv6AddressList.mIpv6Addresses.emplace_back(addr.ToString()); + offset += IPV6_ADDRESS_BYTES; + } +exit: + return error; +} + +Error CommissionerImpl::DecodeChildIpv6AddressList(ChildIpv6AddressList &aChildIpv6AddressList, const ByteArray &aBuf) +{ + Error error; + size_t length = aBuf.size(); + VerifyOrExit((length - RLOC16_BYTES) % IPV6_ADDRESS_BYTES == 0, + error = ERROR_BAD_FORMAT("premature end of Child IPv6 Address")); + aChildIpv6AddressList.mRloc16 = utils::Decode(aBuf.data(), RLOC16_BYTES); + ; + SuccessOrExit(error = DecodeIpv6AddressList(aChildIpv6AddressList.mIpv6AddressList, + {aBuf.begin() + RLOC16_BYTES, aBuf.end()})); +exit: + return error; +} + +Error CommissionerImpl::DecodeModeData(ModeData &aModeData, const ByteArray &aBuf) +{ + Error error; + size_t length = aBuf.size(); + VerifyOrExit(length == 1, error = ERROR_BAD_FORMAT("incorrect size of ModeData")); + aModeData.mIsRxOnWhenIdleMode = (aBuf[0] & 0x01) != 0; + aModeData.mIsMtd = (aBuf[0] & 0x02) != 0; + aModeData.mIsStableNetworkDataRequired = (aBuf[0] & 0x04) != 0; +exit: + return error; +} + +Error CommissionerImpl::DecodeChildTable(std::vector &aChildTable, const ByteArray &aBuf) +{ + Error error; + size_t length = aBuf.size(); + size_t offset = 0; + + while (offset < length) + { + ChildTableEntry entry; + VerifyOrExit(offset + CHILD_TABLE_ENTRY_BYTES <= length, + error = ERROR_BAD_FORMAT("premature end of Child Table")); + entry.mTimeout = 1 << (((aBuf[offset] & 0xF8) >> 3) - 4); + entry.mIncomingLinkQuality = (aBuf[offset] & 0x06) >> 1; + entry.mChildId = ((aBuf[offset] & 0x01) << 9) | aBuf[1]; + SuccessOrExit(error = DecodeModeData(entry.mModeData, {aBuf.begin() + offset + 2, aBuf.begin() + offset + 3})); + aChildTable.emplace_back(entry); + offset += CHILD_TABLE_ENTRY_BYTES; + } +exit: + return error; +} + +Error CommissionerImpl::DecodeLeaderData(LeaderData &aLeaderData, const ByteArray &aBuf) +{ + Error error; + size_t length = aBuf.size(); + VerifyOrExit(length == LEADER_DATA_BYTES, error = ERROR_BAD_FORMAT("incorrect size of LeaderData")); + aLeaderData.mPartitionId = utils::Decode(aBuf.data(), 4); + aLeaderData.mWeighting = aBuf[4]; + aLeaderData.mDataVersion = aBuf[5]; + aLeaderData.mStableDataVersion = aBuf[6]; + aLeaderData.mRouterId = aBuf[7]; +exit: + return error; +} + +Error CommissionerImpl::DecodeRoute64(Route64 &aRoute64, const ByteArray &aBuf) +{ + Error error; + size_t length = aBuf.size(); + size_t offset = 0; + ByteArray routerIdList; + + VerifyOrExit(length >= ROUTER_ID_MASK_BYTES + 1, error = ERROR_BAD_FORMAT("incorrect size of Route64")); + aRoute64.mIdSequence = aBuf[offset++]; + aRoute64.mMask = {aBuf.begin() + offset, aBuf.begin() + offset + ROUTER_ID_MASK_BYTES}; + offset += ROUTER_ID_MASK_BYTES; + + routerIdList = ExtractRouterIds(aRoute64.mMask); + VerifyOrExit((length - offset) == routerIdList.size(), error = ERROR_BAD_FORMAT("incorrect size of RouteData")); + while (offset < length) + { + RouteDataEntry entry; + entry.mRouterId = routerIdList[offset - ROUTER_ID_MASK_BYTES - 1]; + DecodeRouteDataEntry(entry, aBuf[offset]); + aRoute64.mRouteData.emplace_back(entry); + offset++; + } +exit: + return error; +} + +void CommissionerImpl::DecodeRouteDataEntry(RouteDataEntry &aRouteDataEntry, uint8_t aBuf) +{ + aRouteDataEntry.mOutgoingLinkQuality = (aBuf >> 6) & 0x03; + aRouteDataEntry.mIncomingLinkQuality = (aBuf >> 4) & 0x03; + aRouteDataEntry.mRouteCost = aBuf & 0x0F; +} + +ByteArray CommissionerImpl::ExtractRouterIds(const ByteArray &aMask) +{ + ByteArray routerIdList; + + for (size_t i = 0; i < ROUTER_ID_MASK_BYTES * 8; i++) + { + if ((aMask[i / 8] & (0x80 >> (i % 8))) != 0) + { + routerIdList.push_back(i); + } + } + + return routerIdList; +} + +Error CommissionerImpl::DecodeMacCounters(MacCounters &aMacCounters, const ByteArray &aBuf) +{ + Error error; + size_t length = aBuf.size(); + VerifyOrExit(length == MAC_COUNTERS_BYTES, error = ERROR_BAD_FORMAT("incorrect size of MacCounters")); + aMacCounters.mIfInUnknownProtos = utils::Decode(aBuf.data(), 4); + aMacCounters.mIfInErrors = utils::Decode(aBuf.data() + 4, 4); + aMacCounters.mIfOutErrors = utils::Decode(aBuf.data() + 8, 4); + aMacCounters.mIfInUcastPkts = utils::Decode(aBuf.data() + 12, 4); + aMacCounters.mIfInBroadcastPkts = utils::Decode(aBuf.data() + 16, 4); + aMacCounters.mIfInDiscards = utils::Decode(aBuf.data() + 20, 4); + aMacCounters.mIfOutUcastPkts = utils::Decode(aBuf.data() + 24, 4); + aMacCounters.mIfOutBroadcastPkts = utils::Decode(aBuf.data() + 28, 4); + aMacCounters.mIfOutDiscards = utils::Decode(aBuf.data() + 32, 4); +exit: + return error; +} + void CommissionerImpl::SendProxyMessage(ErrorHandler aHandler, const std::string &aDstAddr, const std::string &aUriPath) { Error error; diff --git a/src/library/commissioner_impl.hpp b/src/library/commissioner_impl.hpp index be58fe8a..b8c17f0a 100644 --- a/src/library/commissioner_impl.hpp +++ b/src/library/commissioner_impl.hpp @@ -40,6 +40,7 @@ #include "commissioner/defines.hpp" #include "commissioner/error.hpp" #include "commissioner/network_data.hpp" +#include "commissioner/network_diag_data.hpp" #include "common/error_macros.hpp" #include "common/time.hpp" #include "event2/event.h" @@ -225,6 +226,18 @@ class CommissionerImpl : public Commissioner static Error EncodeCommissionerDataset(coap::Request &aRequest, const CommissionerDataset &aDataset); static ByteArray GetCommissionerDatasetTlvs(uint16_t aDatasetFlags); + static Error DecodeNetDiagData(NetDiagData &aNetDiagData, const ByteArray &aPayload); + static ByteArray GetDiagTypeTlvList(uint64_t aDiagTlvFlags); + static Error DecodeIpv6AddressList(Ipv6AddressList &aIpv6AddressList, const ByteArray &aBuf); + static Error DecodeChildIpv6AddressList(ChildIpv6AddressList &aChildIpv6AddressList, const ByteArray &aBuf); + static Error DecodeModeData(ModeData &aModeData, const ByteArray &aBuf); + static Error DecodeChildTable(std::vector &aChildTable, const ByteArray &aBuf); + static Error DecodeLeaderData(LeaderData &aLeaderData, const ByteArray &aBuf); + static Error DecodeMacCounters(MacCounters &aMacCounters, const ByteArray &aBuf); + static Error DecodeRoute64(Route64 &aRoute64, const ByteArray &aBuf); + static void DecodeRouteDataEntry(RouteDataEntry &aRouteDataEntry, uint8_t aBuf); + static ByteArray ExtractRouterIds(const ByteArray &aMask); + void SendPetition(PetitionHandler aHandler); // Set @p aKeepAlive to false to resign the commissioner role.