Skip to content

Commit

Permalink
[diag] add prefix tlv definition and parsing into diagnostic data
Browse files Browse the repository at this point in the history
The Prefix TLV is a TLV included in Network data. This TLV records
information about alll border routers in the network and route
informaiton to external networks. It is helpful for users to understand
the routing status between the Thread network and external networks.

Prefix TLV has three types of sub-tlv:

* hasRoute TLV
* BorderRouter TLV
* 6LoWPAN context ID

Test: unit test
  • Loading branch information
ZhangLe2016 committed Jan 9, 2025
1 parent 5cff6d6 commit 2e074c3
Show file tree
Hide file tree
Showing 6 changed files with 294 additions and 9 deletions.
60 changes: 60 additions & 0 deletions include/commissioner/network_diag_data.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,64 @@ struct MacCounters
uint32_t mIfOutDiscards = 0;
};

/**
* @brief Has Route
*/
struct HasRoute
{
uint16_t mRloc16 = 0;
uint8_t mRouterPreference = 0;
bool mIsNat64 = false;
};

/**
* @brief Border Router
*/
struct BorderRouter
{
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 Prefix
{
uint8_t mDomainId = 0;
uint8_t mPrefixLength = 0;
ByteArray mPrefix;
std::vector<HasRoute> mHasRoutes;
std::vector<BorderRouter> mBorderRouters;
SixLowPanContext mSixLowPanContext;
};

/**
* @brief Network Data
*/
struct NetworkData
{
std::vector<Prefix> mPrefixList;
};

/**
* @brief network diagnostic data in TMF
*
Expand All @@ -146,6 +204,7 @@ struct NetDiagData
std::vector<std::string> mAddrs;
std::vector<ChildTableEntry> mChildTable;
std::vector<ChildIpv6AddrInfo> mChildIpv6AddrsInfoList;
NetworkData mNetworkData;

/**
* Indicates which fields are included in the object.
Expand All @@ -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
Expand Down
151 changes: 151 additions & 0 deletions src/library/commissioner_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -2214,12 +2219,158 @@ 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(NetworkData &aNetworkData, const ByteArray &aBuf)
{
Error error;
tlv::TlvSet tlvSet;
tlv::TlvList tlvList;
NetworkData 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<Prefix> &aPrefixList, const ByteArray &aBuf)
{
Error error;
size_t length = aBuf.size();
Prefix 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.mHasRoutes, 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.mBorderRouters, value));
}

// Add the prefix to the list
aPrefixList.emplace_back(prefix);
}
exit:
return error;
}
Error internal::DecodeHasRoute(std::vector<HasRoute> &aHasRoutes, const ByteArray &aBuf)
{
Error error;
size_t length = aBuf.size();
HasRoute 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<uint16_t>(aBuf.data() + offset, kRloc16Bytes);
offset += kRloc16Bytes;
hasRoute.mIsNat64 = (aBuf[offset] >> 5) & 0x01;
hasRoute.mRouterPreference = (aBuf[offset] >> 6) & 0x03;
offset += 1;
aHasRoutes.emplace_back(hasRoute);
}
exit:
return error;
}

Error internal::DecodeBorderRouter(std::vector<BorderRouter> &aBorderRouters, const ByteArray &aBuf)
{
Error error;
size_t length = aBuf.size();
BorderRouter 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<uint16_t>(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;
aBorderRouters.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<std::string> &aAddrs, const ByteArray &aBuf)
{
Error error;
Expand Down
23 changes: 17 additions & 6 deletions src/library/commissioner_impl_internal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand All @@ -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(NetworkData &aNetworkData, const ByteArray &aBuf);
Error DecodePrefixList(std::vector<Prefix> &aPrefixList, const ByteArray &aBuf);
Error DecodePrefix(Prefix &aPrefix, const ByteArray &aBuf);
Error DecodeHasRoute(std::vector<HasRoute> &aHasRoutes, const ByteArray &aBuf);
Error DecodeBorderRouter(std::vector<BorderRouter> &aBorderRouters, const ByteArray &aBuf);
Error DecodeContext(SixLowPanContext &aSixLowPanContext, const ByteArray &aBuf);

} // namespace internal

} // namespace commissioner
Expand Down
34 changes: 32 additions & 2 deletions src/library/commissioner_impl_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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].mHasRoutes.size(), 3);
EXPECT_EQ(diagData.mNetworkData.mPrefixList[0].mHasRoutes[0].mRloc16, 58368);
EXPECT_EQ(diagData.mNetworkData.mPrefixList[0].mHasRoutes[0].mRouterPreference, 0);
EXPECT_EQ(diagData.mNetworkData.mPrefixList[0].mHasRoutes[0].mIsNat64, false);
EXPECT_EQ(diagData.mNetworkData.mPrefixList[0].mHasRoutes[1].mRloc16, 33792);
EXPECT_EQ(diagData.mNetworkData.mPrefixList[0].mHasRoutes[1].mRouterPreference, 0);
EXPECT_EQ(diagData.mNetworkData.mPrefixList[0].mHasRoutes[1].mIsNat64, false);
EXPECT_EQ(diagData.mNetworkData.mPrefixList[0].mHasRoutes[2].mRloc16, 39936);
EXPECT_EQ(diagData.mNetworkData.mPrefixList[0].mHasRoutes[2].mRouterPreference, 0);
EXPECT_EQ(diagData.mNetworkData.mPrefixList[0].mHasRoutes[2].mIsNat64, false);
EXPECT_EQ(diagData.mNetworkData.mPrefixList[1].mBorderRouters.size(), 1);
EXPECT_EQ(diagData.mNetworkData.mPrefixList[1].mBorderRouters[0].mRloc16, 58368);
EXPECT_EQ(diagData.mNetworkData.mPrefixList[1].mBorderRouters[0].mPrefixPreference, 3);
EXPECT_EQ(diagData.mNetworkData.mPrefixList[1].mBorderRouters[0].mIsPreferred, true);
EXPECT_EQ(diagData.mNetworkData.mPrefixList[1].mBorderRouters[0].mIsSlaac, true);
EXPECT_EQ(diagData.mNetworkData.mPrefixList[1].mBorderRouters[0].mIsDhcp, false);
EXPECT_EQ(diagData.mNetworkData.mPrefixList[1].mBorderRouters[0].mIsConfigure, false);
EXPECT_EQ(diagData.mNetworkData.mPrefixList[1].mBorderRouters[0].mIsDefaultRoute, false);
EXPECT_EQ(diagData.mNetworkData.mPrefixList[1].mBorderRouters[0].mIsOnMesh, true);
EXPECT_EQ(diagData.mNetworkData.mPrefixList[1].mBorderRouters[0].mIsNdDns, false);
EXPECT_EQ(diagData.mNetworkData.mPrefixList[1].mBorderRouters[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
Expand Down
28 changes: 27 additions & 1 deletion src/library/tlv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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)
{
Expand Down
Loading

0 comments on commit 2e074c3

Please sign in to comment.