[network-diag] simplify ChildTableTlv generation and parsing (#12712)

This commit updates `ChildTableTlvEntry` to better support the packing
and parsing of child entries in a `ChildTableTlv`. It introduces an
`InitFrom()` method to encode an entry directly from a `Child`
object, and a `Parse()` method to extract values into a `ParseInfo`
struct, improving modularity and simplifying usage.

Additionally, it consolidates the logic for calculating the timeout
exponent and decoding it back to a timeout value directly within the
`ChildTableTlvEntry` class. It also introduces `ParseChildTable()` in
`NetworkDiagnostic::Client` to clean up the child table parsing
loop.
This commit is contained in:
Abtin Keshavarzian
2026-03-20 17:13:52 -05:00
committed by GitHub
parent 5e99ebaa51
commit 7b5871913a
5 changed files with 141 additions and 121 deletions
+28 -44
View File
@@ -82,24 +82,14 @@ Error Server::AppendChildTable(Message &aMessage)
for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
{
uint8_t timeout = 0;
ChildTableEntry entry;
ChildTableTlvEntry entry;
if (++count > kMaxChildEntries)
{
break;
}
while (static_cast<uint32_t>(1 << timeout) < child.GetTimeout())
{
timeout++;
}
entry.Clear();
entry.SetTimeout(timeout + 4);
entry.SetLinkQuality(child.GetLinkQualityIn());
entry.SetChildId(Mle::ChildIdFromRloc16(child.GetRloc16()));
entry.SetMode(child.GetDeviceMode());
entry.InitFrom(child);
SuccessOrExit(error = aMessage.Append(entry));
SuccessOrExit(error = Tlv::AdjustTlv(aMessage, tlvBookmark));
@@ -1149,6 +1139,31 @@ void Client::ReadDiagData(DiagData &aDiagData, const Message &aMessage, const Tl
aDiagData.mCount = static_cast<uint8_t>(aMessage.ReadBytes(offsetRange, aDiagData.m8));
}
Error Client::ParseChildTable(ChildTable &aChildTable, const Message &aMessage, OffsetRange aOffsetRange)
{
Error error = kErrorNone;
// `ChildTable` has a fixed array of Child Table entries. If there
// are more entries in the message, we read and return as many as
// can fit in array and ignore the rest.
aChildTable.mCount = 0;
while (!aOffsetRange.IsEmpty() && (aChildTable.mCount < GetArrayLength(aChildTable.mTable)))
{
ChildTableTlvEntry entry;
SuccessOrExit(error = aMessage.Read(aOffsetRange, entry));
aOffsetRange.AdvanceOffset(sizeof(ChildTableTlvEntry));
entry.Parse(aChildTable.mTable[aChildTable.mCount]);
aChildTable.mCount++;
}
exit:
return error;
}
void Client::ParseIp6AddrList(Ip6AddrList &aIp6Addrs, const Message &aMessage, OffsetRange aOffsetRange)
{
aIp6Addrs.mCount = 0;
@@ -1272,39 +1287,8 @@ Error Client::GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator,
break;
case Tlv::kChildTable:
{
uint16_t childInfoLength = GetArrayLength(aDiagTlv.mData.mChildTable.mTable);
ChildInfo *childInfo = &aDiagTlv.mData.mChildTable.mTable[0];
uint8_t &childCount = aDiagTlv.mData.mChildTable.mCount;
OffsetRange offsetRange;
VerifyOrExit((tlvInfo.GetLength() % sizeof(ChildTableEntry)) == 0, error = kErrorParse);
// `DiagTlv` has a fixed array Child Table entries. If there
// are more entries in the message, we read and return as
// many as can fit in array and ignore the rest.
childCount = 0;
offsetRange = tlvInfo.GetValueOffsetRange();
while (!offsetRange.IsEmpty() && (childCount < childInfoLength))
{
ChildTableEntry entry;
SuccessOrExit(error = aMessage.Read(offsetRange, entry));
childInfo->mTimeout = entry.GetTimeout();
childInfo->mLinkQuality = entry.GetLinkQuality();
childInfo->mChildId = entry.GetChildId();
entry.GetMode().Get(childInfo->mMode);
childCount++;
childInfo++;
offsetRange.AdvanceOffset(sizeof(ChildTableEntry));
}
SuccessOrExit(error = ParseChildTable(aDiagTlv.mData.mChildTable, aMessage, tlvInfo.GetValueOffsetRange()));
break;
}
case Tlv::kChannelPages:
ReadDiagData(aDiagTlv.mData.mChannelPages, aMessage, tlvInfo);
+4 -2
View File
@@ -281,6 +281,7 @@ public:
private:
typedef otNetworkDiagData DiagData;
typedef otNetworkDiagChildTable ChildTable;
typedef otNetworkDiagIp6AddrList Ip6AddrList;
Error SendCommand(Uri aUri,
@@ -301,8 +302,9 @@ private:
template <Uri kUri> void HandleTmf(Coap::Msg &aMsg);
static void ReadDiagData(DiagData &aDiagData, const Message &aMessage, const Tlv::Info &aTlvInfo);
static void ParseIp6AddrList(Ip6AddrList &aIp6Addrs, const Message &aMessage, OffsetRange aOffsetRange);
static void ReadDiagData(DiagData &aDiagData, const Message &aMessage, const Tlv::Info &aTlvInfo);
static Error ParseChildTable(ChildTable &aChildTable, const Message &aMessage, OffsetRange aOffsetRange);
static void ParseIp6AddrList(Ip6AddrList &aIp6Addrs, const Message &aMessage, OffsetRange aOffsetRange);
#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
static const char *UriToString(Uri aUri);
@@ -38,6 +38,69 @@
namespace ot {
namespace NetworkDiagnostic {
//---------------------------------------------------------------------------------------------------------------------
// ChildTableTlvEntry
#if OPENTHREAD_FTD
void ChildTableTlvEntry::InitFrom(const Child &aChild)
{
uint16_t encoded = 0;
// 1 0
// 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |TmoutExp |ILQ| Child ID |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
WriteBits<uint16_t, kTimeoutMask>(encoded, DetermineExponentFromTimeout(aChild.GetTimeout()));
WriteBits<uint16_t, kIlqMask>(encoded, aChild.GetLinkQualityIn());
WriteBits<uint16_t, kChildIdMask>(encoded, Mle::ChildIdFromRloc16(aChild.GetRloc16()));
mTimeoutIlqChildId = BigEndian::HostSwap16(encoded);
mMode = aChild.GetDeviceMode().Get();
}
uint8_t ChildTableTlvEntry::DetermineExponentFromTimeout(uint32_t aTimeout)
{
// The timeout is expressed as `2^(exponent - 4)` seconds. The
// parent fills the exponent field with the closest
// ceiling value based on the Child Timeout value.
uint8_t exponent;
for (exponent = kTimeoutExponentMin; exponent < kTimeoutExponentMax; exponent++)
{
if (DetermineTimeoutFromExponent(exponent) >= aTimeout)
{
break;
}
}
return exponent;
}
#endif // OPENTHREAD_FTD
uint32_t ChildTableTlvEntry::DetermineTimeoutFromExponent(uint8_t aExponent)
{
aExponent = Clamp(aExponent, kTimeoutExponentMin, kTimeoutExponentMax);
return static_cast<uint32_t>(1U) << (aExponent - kTimeoutExponentMin);
}
void ChildTableTlvEntry::Parse(ParseInfo &aParseInfo) const
{
uint16_t encoded = BigEndian::HostSwap16(mTimeoutIlqChildId);
ClearAllBytes(aParseInfo);
aParseInfo.mTimeout = ReadBits<uint16_t, kTimeoutMask>(encoded);
aParseInfo.mLinkQuality = static_cast<LinkQuality>(ReadBits<uint16_t, kIlqMask>(encoded));
aParseInfo.mChildId = ReadBits<uint16_t, kChildIdMask>(encoded);
Mle::DeviceMode(mMode).Get(aParseInfo.mMode);
}
//---------------------------------------------------------------------------------------------------------------------
// EnhancedRouteTlvEntry
+35 -67
View File
@@ -369,101 +369,69 @@ private:
} OT_TOOL_PACKED_END;
/**
* Implements Child Table Entry generation and parsing.
* Implements Child Table TLV Entry generation and parsing.
*/
OT_TOOL_PACKED_BEGIN
class ChildTableEntry : public Clearable<ChildTableEntry>
class ChildTableTlvEntry : public Clearable<ChildTableTlvEntry>
{
public:
typedef otNetworkDiagChildEntry ParseInfo; ///< Parse entry info
#if OPENTHREAD_FTD
/**
* Returns the Timeout value.
* Initializes the `ChildTableTlvEntry` from a given `Child` object.
*
* @returns The Timeout value.
* @param[in] aChild The `Child` to initialize from.
*/
uint8_t GetTimeout(void) const
{
return static_cast<uint8_t>(ReadBits<uint16_t, kTimeoutMask>(GetTimeoutChildId()));
}
void InitFrom(const Child &aChild);
#endif
/**
* Sets the Timeout value.
* Parses the TLV entry and populates the information in a given `ParseInfo` struct.
*
* @param[in] aTimeout The Timeout value.
* @param[out] aParseInfo The `ParseInfo` structure to populate.
*/
void SetTimeout(uint8_t aTimeout)
{
SetTimeoutChildId(UpdateBits<uint16_t, kTimeoutMask>(GetTimeoutChildId(), aTimeout));
}
void Parse(ParseInfo &aParseInfo) const;
/**
* The Link Quality value.
* Determines the timeout value (in seconds) from a given exponent.
*
* @returns The Link Quality value.
* The timeout is expressed as `2^(exponent - 4)` seconds.
*
* @param[in] aExponent The exponent value.
*
* @returns The timeout value (in seconds).
*/
LinkQuality GetLinkQuality(void) const
{
return static_cast<LinkQuality>(ReadBits<uint16_t, kLqiMask>(GetTimeoutChildId()));
}
static uint32_t DetermineTimeoutFromExponent(uint8_t aExponent);
#if OPENTHREAD_FTD
/**
* Set the Link Quality value.
* Determines the exponent to use for a given timeout value (in seconds).
*
* @param[in] aLinkQuality The Link Quality value.
*/
void SetLinkQuality(LinkQuality aLinkQuality)
{
SetTimeoutChildId(UpdateBits<uint16_t, kLqiMask>(GetTimeoutChildId(), aLinkQuality));
}
/**
* Returns the Child ID value.
* @param[in] aTimeout The timeout value (in seconds).
*
* @returns The Child ID value.
* @returns The corresponding exponent.
*/
uint16_t GetChildId(void) const { return ReadBits<uint16_t, kChildIdMask>(GetTimeoutChildId()); }
/**
* Sets the Child ID value.
*
* @param[in] aChildId The Child ID value.
*/
void SetChildId(uint16_t aChildId)
{
SetTimeoutChildId(UpdateBits<uint16_t, kChildIdMask>(GetTimeoutChildId(), aChildId));
}
/**
* Returns the Device Mode
*
* @returns The Device Mode
*/
Mle::DeviceMode GetMode(void) const { return Mle::DeviceMode(mMode); }
/**
* Sets the Device Mode.
*
* @param[in] aMode The Device Mode.
*/
void SetMode(Mle::DeviceMode aMode) { mMode = aMode.Get(); }
static uint8_t DetermineExponentFromTimeout(uint32_t aTimeout);
#endif
private:
// 1 0
// 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Timeout |LQI| Child ID |
// |TmoutExp |ILQ| Child ID |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
static constexpr uint8_t kTimeoutOffset = 11;
static constexpr uint8_t kLqiOffset = 9;
static constexpr uint8_t kChildIdOffset = 0;
static constexpr uint16_t kTimeoutMask = 0x1f << kTimeoutOffset;
static constexpr uint16_t kLqiMask = 0x3 << kLqiOffset;
static constexpr uint16_t kChildIdMask = 0x1ff << kChildIdOffset;
static constexpr uint8_t kTimeoutOffset = 11;
static constexpr uint8_t kIlqOffset = 9;
static constexpr uint8_t kChildIdOffset = 0;
static constexpr uint16_t kTimeoutMask = 0x1f << kTimeoutOffset;
static constexpr uint16_t kIlqMask = 0x3 << kIlqOffset;
static constexpr uint16_t kChildIdMask = 0x1ff << kChildIdOffset;
static constexpr uint8_t kTimeoutExponentMin = 4;
static constexpr uint8_t kTimeoutExponentMax = 0x1f;
uint16_t GetTimeoutChildId(void) const { return BigEndian::HostSwap16(mTimeoutChildId); }
void SetTimeoutChildId(uint16_t aTimeoutChildIf) { mTimeoutChildId = BigEndian::HostSwap16(aTimeoutChildIf); }
uint16_t mTimeoutChildId;
uint16_t mTimeoutIlqChildId;
uint8_t mMode;
} OT_TOOL_PACKED_END;
+11 -8
View File
@@ -556,20 +556,23 @@ exit:
Error MeshDiag::ChildIterator::GetNextChildInfo(ChildInfo &aChildInfo)
{
Error error = kErrorNone;
ChildTableEntry entry;
Error error = kErrorNotFound;
ChildTableTlvEntry entry;
ChildTableTlvEntry::ParseInfo info;
VerifyOrExit(mMessage != nullptr, error = kErrorNotFound);
VerifyOrExit(mMessage != nullptr);
VerifyOrExit(mMessage->Read(mOffsetRange, entry) == kErrorNone, error = kErrorNotFound);
mOffsetRange.AdvanceOffset(sizeof(ChildTableEntry));
SuccessOrExit(mMessage->Read(mOffsetRange, entry));
mOffsetRange.AdvanceOffset(sizeof(ChildTableTlvEntry));
aChildInfo.mRloc16 = mParentRloc16 + entry.GetChildId();
entry.GetMode().Get(aChildInfo.mMode);
aChildInfo.mLinkQuality = entry.GetLinkQuality();
entry.Parse(info);
aChildInfo.mRloc16 = mParentRloc16 + info.mChildId;
aChildInfo.mMode = info.mMode;
aChildInfo.mLinkQuality = info.mLinkQuality;
aChildInfo.mIsThisDevice = mMessage->Get<Mle::Mle>().HasRloc16(aChildInfo.mRloc16);
aChildInfo.mIsBorderRouter = mMessage->Get<NetworkData::Leader>().ContainsBorderRouterWithRloc(aChildInfo.mRloc16);
error = kErrorNone;
exit:
return error;