diff --git a/include/commissioner/network_diag_data.hpp b/include/commissioner/network_diag_data.hpp index 91852299..2c765e20 100644 --- a/include/commissioner/network_diag_data.hpp +++ b/include/commissioner/network_diag_data.hpp @@ -127,6 +127,64 @@ struct MacCounters uint32_t mIfOutDiscards = 0; }; +/** + * @brief Has Route Entry + */ +struct HasRouteEntry +{ + uint16_t mRloc16 = 0; + uint8_t mRouterPreference = 0; + bool mIsNat64 = false; +}; + +/** + * @brief Border Router Entry + */ +struct BorderRouterEntry +{ + uint16_t mRloc16 = 0; + uint8_t mPrefixPreference = 0; + bool mIsPreferred = false; + bool mIsSlaac = false; + bool mIsDhcp = false; + bool mIsConfigure = false; + bool mIsDefaultRoute = false; + bool mIsOnMesh = false; + bool mIsNdDns = false; + bool mIsDp = false; +}; + +/** + * @brief 6LoWPAN Context + */ +struct SixLowPanContext +{ + uint8_t mIsCompress = false; + uint8_t mContextId = 0; + uint8_t mContextLength = 0; +}; + +/** + * @brief Prefix + */ +struct PrefixEntry +{ + uint8_t mDomainId = 0; + uint8_t mPrefixLength = 0; + ByteArray mPrefix; + std::vector mHasRouteList; + std::vector mBorderRouterList; + SixLowPanContext mSixLowPanContext; +}; + +/** + * @brief Network Data TLV + */ +struct NetworkDataTlv +{ + std::vector mPrefixList; +}; + /** * @brief network diagnostic data in TMF * @@ -146,6 +204,7 @@ struct NetDiagData std::vector mAddrs; std::vector mChildTable; std::vector mChildIpv6AddrsInfoList; + NetworkDataTlv mNetworkData; /** * Indicates which fields are included in the object. @@ -162,6 +221,7 @@ struct NetDiagData static constexpr uint64_t kEui64Bit = (1ull << 7); static constexpr uint64_t kMacCountersBit = (1ull << 8); static constexpr uint64_t kChildIpv6AddrsInfoListBit = (1ull << 9); + static constexpr uint64_t kNetworkDataBit = (1ull << 10); }; } // namespace commissioner diff --git a/src/java/commissioner.i b/src/java/commissioner.i index bc037df0..ec7e3fba 100644 --- a/src/java/commissioner.i +++ b/src/java/commissioner.i @@ -103,6 +103,9 @@ %template(ChildIpv6AddrInfoVector) std::vector; %template(ChildTableEntryVector) std::vector; %template(RouteDataEntryVector) std::vector; +%template(HasRouteEntryVector) std::vector; +%template(BorderRouterEntryVector) std::vector; +%template(PrefixEntryVector) std::vector; %typemap(jstype) std::string& OUTPUT "String[]" %typemap(jtype) std::string& OUTPUT "String[]" diff --git a/src/library/commissioner_impl.cpp b/src/library/commissioner_impl.cpp index 728472f8..05f4d6d9 100644 --- a/src/library/commissioner_impl.cpp +++ b/src/library/commissioner_impl.cpp @@ -2127,6 +2127,11 @@ ByteArray CommissionerImpl::GetNetDiagTlvTypes(uint64_t aDiagDataFlags) EncodeTlvType(tlvTypes, tlv::Type::kNetworkDiagChildIpv6Address); } + if (aDiagDataFlags & NetDiagData::kNetworkDataBit) + { + EncodeTlvType(tlvTypes, tlv::Type::kNetworkDiagNetworkData); + } + return tlvTypes; } @@ -2214,12 +2219,157 @@ Error internal::DecodeNetDiagData(NetDiagData &aNetDiagData, const ByteArray &aP diagData.mPresentFlags |= NetDiagData::kChildIpv6AddrsInfoListBit; } + if (auto networkData = tlvSet[tlv::Type::kNetworkDiagNetworkData]) + { + const ByteArray &value = networkData->GetValue(); + SuccessOrExit(error = DecodeNetworkData(diagData.mNetworkData, value)); + diagData.mPresentFlags |= NetDiagData::kNetworkDataBit; + } aNetDiagData = diagData; exit: return error; } +Error internal::DecodeNetworkData(NetworkDataTlv &aNetworkData, const ByteArray &aBuf) +{ + Error error; + tlv::TlvSet tlvSet; + tlv::TlvList tlvList; + NetworkDataTlv networkData; + + SuccessOrExit(error = + tlv::GetTlvListByType(tlvList, aBuf, tlv::Type::kNetworkDataPrefix, tlv::Scope::kNetworkData)); + if (tlvList.size() > 0) + { + for (const auto &tlv : tlvList) + { + SuccessOrExit(error = DecodePrefixList(networkData.mPrefixList, tlv.GetValue())); + } + } + + aNetworkData = networkData; + +exit: + return error; +} + +Error internal::DecodePrefixList(std::vector &aPrefixList, const ByteArray &aBuf) +{ + Error error; + size_t length = aBuf.size(); + PrefixEntry prefix; + uint8_t offset = 0; + ByteArray subTlv; + tlv::TlvList tlvList; + tlv::TlvSet tlvSet; + + VerifyOrExit(length >= kPrefixBytes, error = ERROR_BAD_FORMAT("premature end of Prefix")); + offset += kPrefixBytes; + prefix.mDomainId = aBuf[0]; + prefix.mPrefixLength = (aBuf[1] + 7) >> 3; + offset += prefix.mPrefixLength; + VerifyOrExit(length >= offset, error = ERROR_BAD_FORMAT("premature end of Prefix")); + prefix.mPrefix = {aBuf.begin() + kPrefixBytes, aBuf.begin() + kPrefixBytes + offset}; + + if (length > offset) + { + subTlv = {aBuf.begin() + offset, aBuf.end()}; + + // Get the context + SuccessOrExit(error = tlv::GetTlvSet(tlvSet, subTlv, tlv::Scope::kNetworkData)); + if (auto context = tlvSet[tlv::Type::kNetworkData6LowPanContext]) + { + const ByteArray &value = context->GetValue(); + SuccessOrExit(error = DecodeContext(prefix.mSixLowPanContext, value)); + } + + // Get the HasRoute + SuccessOrExit(error = tlv::GetTlvSet(tlvSet, subTlv, tlv::Scope::kNetworkData)); + if (auto hasRoute = tlvSet[tlv::Type::kNetworkDataHasRoute]) + { + const ByteArray &value = hasRoute->GetValue(); + SuccessOrExit(error = DecodeHasRoute(prefix.mHasRouteList, value)); + } + + // Get the BorderRouter + SuccessOrExit(error = tlv::GetTlvSet(tlvSet, subTlv, tlv::Scope::kNetworkData)); + if (auto borderRouter = tlvSet[tlv::Type::kNetworkDataBorderRouter]) + { + const ByteArray &value = borderRouter->GetValue(); + SuccessOrExit(error = DecodeBorderRouter(prefix.mBorderRouterList, value)); + } + + // Add the prefix to the list + aPrefixList.emplace_back(prefix); + } +exit: + return error; +} +Error internal::DecodeHasRoute(std::vector &aHasRouteList, const ByteArray &aBuf) +{ + Error error; + size_t length = aBuf.size(); + HasRouteEntry hasRoute; + uint8_t offset = 0; + tlv::TlvList tlvList; + + VerifyOrExit((length % kHasRouteBytes == 0), error = ERROR_BAD_FORMAT("incorrect size of HasRoute")); + while (offset < length) + { + hasRoute.mRloc16 = utils::Decode(aBuf.data() + offset, kRloc16Bytes); + offset += kRloc16Bytes; + hasRoute.mIsNat64 = (aBuf[offset] >> 5) & 0x01; + hasRoute.mRouterPreference = (aBuf[offset] >> 6) & 0x03; + offset += 1; + aHasRouteList.emplace_back(hasRoute); + } +exit: + return error; +} + +Error internal::DecodeBorderRouter(std::vector &aBorderRouterList, const ByteArray &aBuf) +{ + Error error; + size_t length = aBuf.size(); + BorderRouterEntry borderRouter; + uint8_t offset = 0; + tlv::TlvList tlvList; + VerifyOrExit((length % kBorderRouterBytes == 0), error = ERROR_BAD_FORMAT("incorrect size of BorderRouter")); + while (offset < length) + { + borderRouter.mRloc16 = utils::Decode(aBuf.data() + offset, kRloc16Bytes); + offset += kRloc16Bytes; + borderRouter.mPrefixPreference = (aBuf[offset] >> 6) & 0x03; + borderRouter.mIsPreferred = (aBuf[offset] >> 5) & 0x01; + borderRouter.mIsSlaac = (aBuf[offset] >> 4) & 0x01; + borderRouter.mIsDhcp = (aBuf[offset] >> 3) & 0x01; + borderRouter.mIsConfigure = (aBuf[offset] >> 2) & 0x01; + borderRouter.mIsDefaultRoute = (aBuf[offset] >> 1) & 0x01; + borderRouter.mIsOnMesh = (aBuf[offset] >> 0) & 0x01; + offset += 1; + borderRouter.mIsNdDns = (aBuf[offset] >> 7) & 0x01; + borderRouter.mIsDp = (aBuf[offset] >> 6) & 0x01; + offset += 1; + aBorderRouterList.emplace_back(borderRouter); + } + +exit: + return error; +} + +Error internal::DecodeContext(SixLowPanContext &aSixLowPanContext, const ByteArray &aBuf) +{ + Error error; + size_t length = aBuf.size(); + VerifyOrExit(length == kSixLowPanContextBytes, error = ERROR_BAD_FORMAT("incorrect size of Context")); + aSixLowPanContext.mIsCompress = (aBuf[0] >> 4) & 0x01; + aSixLowPanContext.mContextId = aBuf[0] & 0x0F; + aSixLowPanContext.mContextLength = aBuf[1]; +exit: + return error; +} + Error internal::DecodeIpv6AddressList(std::vector &aAddrs, const ByteArray &aBuf) { Error error; diff --git a/src/library/commissioner_impl_internal.hpp b/src/library/commissioner_impl_internal.hpp index 1f7dc620..ae7974b6 100644 --- a/src/library/commissioner_impl_internal.hpp +++ b/src/library/commissioner_impl_internal.hpp @@ -39,12 +39,16 @@ namespace ot { namespace commissioner { -static constexpr uint8_t kChildTableEntryBytes = 3; -static constexpr uint8_t kIpv6AddressBytes = 16; -static constexpr uint8_t kLeaderDataBytes = 8; -static constexpr uint8_t kMacCountersBytes = 36; -static constexpr uint8_t kRloc16Bytes = 2; -static constexpr uint8_t kRouterIdMaskBytes = 8; +static constexpr uint8_t kChildTableEntryBytes = 3; +static constexpr uint8_t kIpv6AddressBytes = 16; +static constexpr uint8_t kLeaderDataBytes = 8; +static constexpr uint8_t kMacCountersBytes = 36; +static constexpr uint8_t kRloc16Bytes = 2; +static constexpr uint8_t kRouterIdMaskBytes = 8; +static constexpr uint8_t kPrefixBytes = 2; +static constexpr uint8_t kHasRouteBytes = 3; +static constexpr uint8_t kBorderRouterBytes = 4; +static constexpr uint8_t kSixLowPanContextBytes = 2; namespace internal { @@ -59,6 +63,13 @@ Error DecodeRoute64(Route64 &aRoute64, const ByteArray &aBuf); void DecodeRouteDataEntry(RouteDataEntry &aRouteDataEntry, uint8_t aBuf); ByteArray ExtractRouterIds(const ByteArray &aMask); +Error DecodeNetworkData(NetworkDataTlv &aNetworkData, const ByteArray &aBuf); +Error DecodePrefixList(std::vector &aPrefixList, const ByteArray &aBuf); +Error DecodePrefix(PrefixEntry &aPrefix, const ByteArray &aBuf); +Error DecodeHasRoute(std::vector &aHasRouteList, const ByteArray &aBuf); +Error DecodeBorderRouter(std::vector &aBorderRouterList, const ByteArray &aBuf); +Error DecodeContext(SixLowPanContext &aSixLowPanContext, const ByteArray &aBuf); + } // namespace internal } // namespace commissioner diff --git a/src/library/commissioner_impl_test.cpp b/src/library/commissioner_impl_test.cpp index 0e0ab399..9f362791 100644 --- a/src/library/commissioner_impl_test.cpp +++ b/src/library/commissioner_impl_test.cpp @@ -154,7 +154,9 @@ TEST(CommissionerImplTest, ValidInput_DecodeNetDiagData) "00086ac6c2de12b212df0102c80002010f0512e7000400204300300af1f1f1f1f101f1f1f10608360bb9f7415c30210840fd9238a3395d" "0001f9043dfeb7b3edf3fd7d604fb88a0000000000fffe00c800fd7d604fb88a0000fe3e5a4c31acb559fe8000000000000068c6c2de12" "b212df1009601804601d046019041e22c818fdc31ff45feff4e7e580431c60becfabfd110022000000008df846f3ab0c05551e22c802fd" - "c31ff45feff4e75257420f1cbd46f5fd1100220000000034e5d9e28d1952c0"; + "c31ff45feff4e75257420f1cbd46f5fd1100220000000034e5d9e28d1952c0077d030e0007fc0109e400108400109c000003140040fd27" + "fd30e5ce0001070212400504e400f1000b0e8001010d09e4000a000500000e100b0881025cf40d029c0003130060fd6b51760904ffff00" + "00000001039c00e00b1982015d0d149c00fd27fd30e5ce00018e250585edd6f1b0e5ec080b090284000b028dbc080100"; error = utils::Hex(buf, tlvsHexString); EXPECT_EQ(error, ErrorCode::kNone); @@ -167,7 +169,7 @@ TEST(CommissionerImplTest, ValidInput_DecodeNetDiagData) error = utils::Hex(extMacAddrBytes, "6ac6c2de12b212df"); EXPECT_EQ(error, ErrorCode::kNone); - EXPECT_EQ(diagData.mPresentFlags, 639); + EXPECT_EQ(diagData.mPresentFlags, 1663); EXPECT_EQ(diagData.mExtMacAddr, extMacAddrBytes); EXPECT_EQ(diagData.mMacAddr, macAddr); EXPECT_EQ(diagData.mMode.mIsMtd, false); @@ -186,6 +188,34 @@ TEST(CommissionerImplTest, ValidInput_DecodeNetDiagData) EXPECT_EQ(diagData.mChildIpv6AddrsInfoList[1].mChildId, 2); EXPECT_EQ(diagData.mChildIpv6AddrsInfoList[1].mAddrs[0], "fdc3:1ff4:5fef:f4e7:5257:420f:1cbd:46f5"); EXPECT_EQ(diagData.mChildIpv6AddrsInfoList[1].mAddrs[1], "fd11:22::34e5:d9e2:8d19:52c0"); + // Parsing prefix TLV in Network Data + EXPECT_EQ(diagData.mNetworkData.mPrefixList.size(), 3); + EXPECT_EQ(diagData.mNetworkData.mPrefixList[0].mDomainId, 0); + EXPECT_EQ(diagData.mNetworkData.mPrefixList[0].mPrefixLength, 1); + EXPECT_EQ(diagData.mNetworkData.mPrefixList[0].mHasRouteList.size(), 3); + EXPECT_EQ(diagData.mNetworkData.mPrefixList[0].mHasRouteList[0].mRloc16, 58368); + EXPECT_EQ(diagData.mNetworkData.mPrefixList[0].mHasRouteList[0].mRouterPreference, 0); + EXPECT_EQ(diagData.mNetworkData.mPrefixList[0].mHasRouteList[0].mIsNat64, false); + EXPECT_EQ(diagData.mNetworkData.mPrefixList[0].mHasRouteList[1].mRloc16, 33792); + EXPECT_EQ(diagData.mNetworkData.mPrefixList[0].mHasRouteList[1].mRouterPreference, 0); + EXPECT_EQ(diagData.mNetworkData.mPrefixList[0].mHasRouteList[1].mIsNat64, false); + EXPECT_EQ(diagData.mNetworkData.mPrefixList[0].mHasRouteList[2].mRloc16, 39936); + EXPECT_EQ(diagData.mNetworkData.mPrefixList[0].mHasRouteList[2].mRouterPreference, 0); + EXPECT_EQ(diagData.mNetworkData.mPrefixList[0].mHasRouteList[2].mIsNat64, false); + EXPECT_EQ(diagData.mNetworkData.mPrefixList[1].mBorderRouterList.size(), 1); + EXPECT_EQ(diagData.mNetworkData.mPrefixList[1].mBorderRouterList[0].mRloc16, 58368); + EXPECT_EQ(diagData.mNetworkData.mPrefixList[1].mBorderRouterList[0].mPrefixPreference, 3); + EXPECT_EQ(diagData.mNetworkData.mPrefixList[1].mBorderRouterList[0].mIsPreferred, true); + EXPECT_EQ(diagData.mNetworkData.mPrefixList[1].mBorderRouterList[0].mIsSlaac, true); + EXPECT_EQ(diagData.mNetworkData.mPrefixList[1].mBorderRouterList[0].mIsDhcp, false); + EXPECT_EQ(diagData.mNetworkData.mPrefixList[1].mBorderRouterList[0].mIsConfigure, false); + EXPECT_EQ(diagData.mNetworkData.mPrefixList[1].mBorderRouterList[0].mIsDefaultRoute, false); + EXPECT_EQ(diagData.mNetworkData.mPrefixList[1].mBorderRouterList[0].mIsOnMesh, true); + EXPECT_EQ(diagData.mNetworkData.mPrefixList[1].mBorderRouterList[0].mIsNdDns, false); + EXPECT_EQ(diagData.mNetworkData.mPrefixList[1].mBorderRouterList[0].mIsDp, false); + EXPECT_EQ(diagData.mNetworkData.mPrefixList[1].mSixLowPanContext.mIsCompress, true); + EXPECT_EQ(diagData.mNetworkData.mPrefixList[1].mSixLowPanContext.mContextId, 2); + EXPECT_EQ(diagData.mNetworkData.mPrefixList[1].mSixLowPanContext.mContextLength, 64); } } // namespace commissioner diff --git a/src/library/tlv.cpp b/src/library/tlv.cpp index 1638d723..0fec981c 100644 --- a/src/library/tlv.cpp +++ b/src/library/tlv.cpp @@ -152,7 +152,16 @@ TlvPtr Tlv::Deserialize(Error &aError, size_t &aOffset, const ByteArray &aBuf, S TlvPtr tlv = nullptr; VerifyOrExit(offset + 2 <= aBuf.size(), error = ERROR_BAD_FORMAT("premature end of TLV")); - type = aBuf[offset++]; + // Based on spec 5.18,Network Data TLVs use first 7-bit type and the 8th bit to indicate + // the data to be valid for at least MIN_STABLE_LIFETIME. + if (aScope == Scope::kNetworkData) + { + type = aBuf[offset++] >> 1; + } + else + { + type = aBuf[offset++]; + } length = aBuf[offset++]; if (length == kEscapeLength) { @@ -287,6 +296,23 @@ bool Tlv::IsValid() const return false; } } + else if (mScope == Scope::kNetworkData) + { + switch (mType) + { + // Network Data TLVs + case Type::kNetworkDataHasRoute: + return length >= 3; + case Type::kNetworkDataPrefix: + return length >= 2; + case Type::kNetworkDataBorderRouter: + return length >= 4; + case Type::kNetworkData6LowPanContext: + return length >= 2; + default: + return false; + } + } switch (mType) { diff --git a/src/library/tlv.hpp b/src/library/tlv.hpp index 919e63f7..11a740ee 100644 --- a/src/library/tlv.hpp +++ b/src/library/tlv.hpp @@ -64,6 +64,7 @@ enum class Scope : uint8_t kThread, kMeshLink, kNetworkDiag, + kNetworkData, }; enum class Type : uint8_t @@ -172,6 +173,12 @@ enum class Type : uint8_t kNetworkDiagAnswer = 32, kNetworkDiagQueryID = 33, kNetworkDiagMleCounters = 34, + + // Network Data TLVs + kNetworkDataHasRoute = 0, + kNetworkDataPrefix = 1, + kNetworkDataBorderRouter = 2, + kNetworkData6LowPanContext = 3, }; using TlvPtr = std::shared_ptr;